]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - gcc/config/mips/mips.c
Imported gcc-4.4.3
[msp430-gcc.git] / gcc / config / mips / mips.c
index 476b55c5fd358e7d8c728d52e0458903d3e69d2a..2c19c1c307e5ef9065348ba4198650254c38e2ab 100644 (file)
@@ -1,34 +1,32 @@
-/* Subroutines for insn-output.c for MIPS
+/* Subroutines used for MIPS code generation.
    Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   Free Software Foundation, Inc.
    Contributed by A. Lichnewsky, lich@inria.inria.fr.
    Changes by Michael Meissner, meissner@osf.org.
-   64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
+   64-bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
    Brendan Eich, brendan@microunity.com.
 
-This file is part of GNU CC.
+This file is part of GCC.
 
-GNU CC is free software; you can redistribute it and/or modify
+GCC is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
-GNU CC is distributed in the hope that it will be useful,
+GCC is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
-
-/* ??? The TARGET_FP_CALL_32 macros are intended to simulate a 32 bit
-   calling convention in 64 bit mode.  It doesn't work though, and should
-   be replaced with something better designed.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include <signal.h>
 #include "rtl.h"
 #include "regs.h"
@@ -43,9 +41,10 @@ Boston, MA 02111-1307, USA.  */
 #include "tree.h"
 #include "function.h"
 #include "expr.h"
+#include "optabs.h"
+#include "libfuncs.h"
 #include "flags.h"
 #include "reload.h"
-#include "output.h"
 #include "tm_p.h"
 #include "ggc.h"
 #include "gstab.h"
@@ -53,327 +52,455 @@ Boston, MA 02111-1307, USA.  */
 #include "debug.h"
 #include "target.h"
 #include "target-def.h"
+#include "integrate.h"
+#include "langhooks.h"
+#include "cfglayout.h"
+#include "sched-int.h"
+#include "gimple.h"
+#include "bitmap.h"
+#include "diagnostic.h"
+
+/* True if X is an UNSPEC wrapper around a SYMBOL_REF or LABEL_REF.  */
+#define UNSPEC_ADDRESS_P(X)                                    \
+  (GET_CODE (X) == UNSPEC                                      \
+   && XINT (X, 1) >= UNSPEC_ADDRESS_FIRST                      \
+   && XINT (X, 1) < UNSPEC_ADDRESS_FIRST + NUM_SYMBOL_TYPES)
+
+/* Extract the symbol or label from UNSPEC wrapper X.  */
+#define UNSPEC_ADDRESS(X) \
+  XVECEXP (X, 0, 0)
+
+/* Extract the symbol type from UNSPEC wrapper X.  */
+#define UNSPEC_ADDRESS_TYPE(X) \
+  ((enum mips_symbol_type) (XINT (X, 1) - UNSPEC_ADDRESS_FIRST))
+
+/* The maximum distance between the top of the stack frame and the
+   value $sp has when we save and restore registers.
+
+   The value for normal-mode code must be a SMALL_OPERAND and must
+   preserve the maximum stack alignment.  We therefore use a value
+   of 0x7ff0 in this case.
+
+   MIPS16e SAVE and RESTORE instructions can adjust the stack pointer by
+   up to 0x7f8 bytes and can usually save or restore all the registers
+   that we need to save or restore.  (Note that we can only use these
+   instructions for o32, for which the stack alignment is 8 bytes.)
+
+   We use a maximum gap of 0x100 or 0x400 for MIPS16 code when SAVE and
+   RESTORE are not available.  We can then use unextended instructions
+   to save and restore registers, and to allocate and deallocate the top
+   part of the frame.  */
+#define MIPS_MAX_FIRST_STACK_STEP                                      \
+  (!TARGET_MIPS16 ? 0x7ff0                                             \
+   : GENERATE_MIPS16E_SAVE_RESTORE ? 0x7f8                             \
+   : TARGET_64BIT ? 0x100 : 0x400)
+
+/* True if INSN is a mips.md pattern or asm statement.  */
+#define USEFUL_INSN_P(INSN)                                            \
+  (INSN_P (INSN)                                                       \
+   && GET_CODE (PATTERN (INSN)) != USE                                 \
+   && GET_CODE (PATTERN (INSN)) != CLOBBER                             \
+   && GET_CODE (PATTERN (INSN)) != ADDR_VEC                            \
+   && GET_CODE (PATTERN (INSN)) != ADDR_DIFF_VEC)
+
+/* If INSN is a delayed branch sequence, return the first instruction
+   in the sequence, otherwise return INSN itself.  */
+#define SEQ_BEGIN(INSN)                                                        \
+  (INSN_P (INSN) && GET_CODE (PATTERN (INSN)) == SEQUENCE              \
+   ? XVECEXP (PATTERN (INSN), 0, 0)                                    \
+   : (INSN))
+
+/* Likewise for the last instruction in a delayed branch sequence.  */
+#define SEQ_END(INSN)                                                  \
+  (INSN_P (INSN) && GET_CODE (PATTERN (INSN)) == SEQUENCE              \
+   ? XVECEXP (PATTERN (INSN), 0, XVECLEN (PATTERN (INSN), 0) - 1)      \
+   : (INSN))
+
+/* Execute the following loop body with SUBINSN set to each instruction
+   between SEQ_BEGIN (INSN) and SEQ_END (INSN) inclusive.  */
+#define FOR_EACH_SUBINSN(SUBINSN, INSN)                                        \
+  for ((SUBINSN) = SEQ_BEGIN (INSN);                                   \
+       (SUBINSN) != NEXT_INSN (SEQ_END (INSN));                                \
+       (SUBINSN) = NEXT_INSN (SUBINSN))
+
+/* True if bit BIT is set in VALUE.  */
+#define BITSET_P(VALUE, BIT) (((VALUE) & (1 << (BIT))) != 0)
+
+/* Classifies an address.
+
+   ADDRESS_REG
+       A natural register + offset address.  The register satisfies
+       mips_valid_base_register_p and the offset is a const_arith_operand.
+
+   ADDRESS_LO_SUM
+       A LO_SUM rtx.  The first operand is a valid base register and
+       the second operand is a symbolic address.
+
+   ADDRESS_CONST_INT
+       A signed 16-bit constant address.
+
+   ADDRESS_SYMBOLIC:
+       A constant symbolic address.  */
+enum mips_address_type {
+  ADDRESS_REG,
+  ADDRESS_LO_SUM,
+  ADDRESS_CONST_INT,
+  ADDRESS_SYMBOLIC
+};
 
-#ifdef HALF_PIC_DEBUG
-#include "halfpic.h"
-#endif
+/* Enumerates the setting of the -mr10k-cache-barrier option.  */
+enum mips_r10k_cache_barrier_setting {
+  R10K_CACHE_BARRIER_NONE,
+  R10K_CACHE_BARRIER_STORE,
+  R10K_CACHE_BARRIER_LOAD_STORE
+};
 
-#ifdef __GNU_STAB__
-#define STAB_CODE_TYPE enum __stab_debug_code
-#else
-#define STAB_CODE_TYPE int
-#endif
+/* Macros to create an enumeration identifier for a function prototype.  */
+#define MIPS_FTYPE_NAME1(A, B) MIPS_##A##_FTYPE_##B
+#define MIPS_FTYPE_NAME2(A, B, C) MIPS_##A##_FTYPE_##B##_##C
+#define MIPS_FTYPE_NAME3(A, B, C, D) MIPS_##A##_FTYPE_##B##_##C##_##D
+#define MIPS_FTYPE_NAME4(A, B, C, D, E) MIPS_##A##_FTYPE_##B##_##C##_##D##_##E
+
+/* Classifies the prototype of a built-in function.  */
+enum mips_function_type {
+#define DEF_MIPS_FTYPE(NARGS, LIST) MIPS_FTYPE_NAME##NARGS LIST,
+#include "config/mips/mips-ftypes.def"
+#undef DEF_MIPS_FTYPE
+  MIPS_MAX_FTYPE_MAX
+};
 
-extern tree   lookup_name PARAMS ((tree));
-
-/* Enumeration for all of the relational tests, so that we can build
-   arrays indexed by the test type, and not worry about the order
-   of EQ, NE, etc.  */
-
-enum internal_test {
-    ITEST_EQ,
-    ITEST_NE,
-    ITEST_GT,
-    ITEST_GE,
-    ITEST_LT,
-    ITEST_LE,
-    ITEST_GTU,
-    ITEST_GEU,
-    ITEST_LTU,
-    ITEST_LEU,
-    ITEST_MAX
-  };
+/* Specifies how a built-in function should be converted into rtl.  */
+enum mips_builtin_type {
+  /* The function corresponds directly to an .md pattern.  The return
+     value is mapped to operand 0 and the arguments are mapped to
+     operands 1 and above.  */
+  MIPS_BUILTIN_DIRECT,
+
+  /* The function corresponds directly to an .md pattern.  There is no return
+     value and the arguments are mapped to operands 0 and above.  */
+  MIPS_BUILTIN_DIRECT_NO_TARGET,
+
+  /* The function corresponds to a comparison instruction followed by
+     a mips_cond_move_tf_ps pattern.  The first two arguments are the
+     values to compare and the second two arguments are the vector
+     operands for the movt.ps or movf.ps instruction (in assembly order).  */
+  MIPS_BUILTIN_MOVF,
+  MIPS_BUILTIN_MOVT,
+
+  /* The function corresponds to a V2SF comparison instruction.  Operand 0
+     of this instruction is the result of the comparison, which has mode
+     CCV2 or CCV4.  The function arguments are mapped to operands 1 and
+     above.  The function's return value is an SImode boolean that is
+     true under the following conditions:
+
+     MIPS_BUILTIN_CMP_ANY: one of the registers is true
+     MIPS_BUILTIN_CMP_ALL: all of the registers are true
+     MIPS_BUILTIN_CMP_LOWER: the first register is true
+     MIPS_BUILTIN_CMP_UPPER: the second register is true.  */
+  MIPS_BUILTIN_CMP_ANY,
+  MIPS_BUILTIN_CMP_ALL,
+  MIPS_BUILTIN_CMP_UPPER,
+  MIPS_BUILTIN_CMP_LOWER,
+
+  /* As above, but the instruction only sets a single $fcc register.  */
+  MIPS_BUILTIN_CMP_SINGLE,
+
+  /* For generating bposge32 branch instructions in MIPS32 DSP ASE.  */
+  MIPS_BUILTIN_BPOSGE32
+};
 
+/* Invoke MACRO (COND) for each C.cond.fmt condition.  */
+#define MIPS_FP_CONDITIONS(MACRO) \
+  MACRO (f),   \
+  MACRO (un),  \
+  MACRO (eq),  \
+  MACRO (ueq), \
+  MACRO (olt), \
+  MACRO (ult), \
+  MACRO (ole), \
+  MACRO (ule), \
+  MACRO (sf),  \
+  MACRO (ngle),        \
+  MACRO (seq), \
+  MACRO (ngl), \
+  MACRO (lt),  \
+  MACRO (nge), \
+  MACRO (le),  \
+  MACRO (ngt)
+
+/* Enumerates the codes above as MIPS_FP_COND_<X>.  */
+#define DECLARE_MIPS_COND(X) MIPS_FP_COND_ ## X
+enum mips_fp_condition {
+  MIPS_FP_CONDITIONS (DECLARE_MIPS_COND)
+};
 
-struct constant;
-static enum internal_test map_test_to_internal_test    PARAMS ((enum rtx_code));
-static int mips16_simple_memory_operand                PARAMS ((rtx, rtx,
-                                                       enum machine_mode));
-static int m16_check_op                                PARAMS ((rtx, int, int, int));
-static void block_move_loop                    PARAMS ((rtx, rtx,
-                                                        unsigned int,
-                                                        int,
-                                                        rtx, rtx));
-static void block_move_call                    PARAMS ((rtx, rtx, rtx));
-static rtx mips_add_large_offset_to_sp         PARAMS ((HOST_WIDE_INT,
-                                                        FILE *));
-static void mips_annotate_frame_insn           PARAMS ((rtx, rtx));
-static rtx mips_frame_set                      PARAMS ((enum machine_mode,
-                                                        int, int));
-static void mips_emit_frame_related_store      PARAMS ((rtx, rtx,
-                                                        HOST_WIDE_INT));
-static void save_restore_insns                 PARAMS ((int, rtx,
-                                                       long, FILE *));
-static void mips16_output_gp_offset            PARAMS ((FILE *, rtx));
-static void mips16_fp_args                     PARAMS ((FILE *, int, int));
-static void build_mips16_function_stub         PARAMS ((FILE *));
-static void mips16_optimize_gp                 PARAMS ((rtx));
-static rtx add_constant                                PARAMS ((struct constant **,
-                                                       rtx,
-                                                       enum machine_mode));
-static void dump_constants                     PARAMS ((struct constant *,
-                                                       rtx));
-static rtx mips_find_symbol                    PARAMS ((rtx));
-static void abort_with_insn                    PARAMS ((rtx, const char *))
-  ATTRIBUTE_NORETURN;
-static int symbolic_expression_p                PARAMS ((rtx));
-static void mips_add_gc_roots                   PARAMS ((void));
-static bool mips_assemble_integer        PARAMS ((rtx, unsigned int, int));
-static void mips_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
-static void mips_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
-static enum processor_type mips_parse_cpu       PARAMS ((const char *));
-static void copy_file_data                     PARAMS ((FILE *, FILE *));
-#ifdef TARGET_IRIX6
-static void iris6_asm_named_section_1          PARAMS ((const char *,
-                                                        unsigned int,
-                                                        unsigned int));
-static void iris6_asm_named_section            PARAMS ((const char *,
-                                                        unsigned int));
-static int iris_section_align_entry_eq         PARAMS ((const PTR, const PTR));
-static hashval_t iris_section_align_entry_hash PARAMS ((const PTR));
-static int iris6_section_align_1               PARAMS ((void **, void *));
-#endif
-static int mips_adjust_cost                    PARAMS ((rtx, rtx, rtx, int));
+/* Index X provides the string representation of MIPS_FP_COND_<X>.  */
+#define STRINGIFY(X) #X
+static const char *const mips_fp_conditions[] = {
+  MIPS_FP_CONDITIONS (STRINGIFY)
+};
 
-/* Global variables for machine-dependent things.  */
+/* Information about a function's frame layout.  */
+struct mips_frame_info GTY(()) {
+  /* The size of the frame in bytes.  */
+  HOST_WIDE_INT total_size;
 
-/* Threshold for data being put into the small data/bss area, instead
-   of the normal data area (references to the small data/bss area take
-   1 instruction, and use the global pointer, references to the normal
-   data area takes 2 instructions).  */
-int mips_section_threshold = -1;
+  /* The number of bytes allocated to variables.  */
+  HOST_WIDE_INT var_size;
 
-/* Count the number of .file directives, so that .loc is up to date.  */
-int num_source_filenames = 0;
+  /* The number of bytes allocated to outgoing function arguments.  */
+  HOST_WIDE_INT args_size;
 
-/* Count the number of sdb related labels are generated (to find block
-   start and end boundaries).  */
-int sdb_label_count = 0;
+  /* The number of bytes allocated to the .cprestore slot, or 0 if there
+     is no such slot.  */
+  HOST_WIDE_INT cprestore_size;
 
-/* Next label # for each statement for Silicon Graphics IRIS systems.  */
-int sym_lineno = 0;
+  /* Bit X is set if the function saves or restores GPR X.  */
+  unsigned int mask;
 
-/* Non-zero if inside of a function, because the stupid MIPS asm can't
-   handle .files inside of functions.  */
-int inside_function = 0;
+  /* Likewise FPR X.  */
+  unsigned int fmask;
 
-/* Files to separate the text and the data output, so that all of the data
-   can be emitted before the text, which will mean that the assembler will
-   generate smaller code, based on the global pointer.  */
-FILE *asm_out_data_file;
-FILE *asm_out_text_file;
+  /* The number of GPRs and FPRs saved.  */
+  unsigned int num_gp;
+  unsigned int num_fp;
 
-/* Linked list of all externals that are to be emitted when optimizing
-   for the global pointer if they haven't been declared by the end of
-   the program with an appropriate .comm or initialization.  */
+  /* The offset of the topmost GPR and FPR save slots from the top of
+     the frame, or zero if no such slots are needed.  */
+  HOST_WIDE_INT gp_save_offset;
+  HOST_WIDE_INT fp_save_offset;
 
-struct extern_list
-{
-  struct extern_list *next;    /* next external */
-  const char *name;            /* name of the external */
-  int size;                    /* size in bytes */
-} *extern_head = 0;
+  /* Likewise, but giving offsets from the bottom of the frame.  */
+  HOST_WIDE_INT gp_sp_offset;
+  HOST_WIDE_INT fp_sp_offset;
 
-/* Name of the file containing the current function.  */
-const char *current_function_file = "";
+  /* The offset of arg_pointer_rtx from frame_pointer_rtx.  */
+  HOST_WIDE_INT arg_pointer_offset;
 
-/* Warning given that Mips ECOFF can't support changing files
-   within a function.  */
-int file_in_function_warning = FALSE;
+  /* The offset of hard_frame_pointer_rtx from frame_pointer_rtx.  */
+  HOST_WIDE_INT hard_frame_pointer_offset;
+};
 
-/* Whether to suppress issuing .loc's because the user attempted
-   to change the filename within a function.  */
-int ignore_line_number = FALSE;
+struct machine_function GTY(()) {
+  /* The register returned by mips16_gp_pseudo_reg; see there for details.  */
+  rtx mips16_gp_pseudo_rtx;
 
-/* Number of nested .set noreorder, noat, nomacro, and volatile requests.  */
-int set_noreorder;
-int set_noat;
-int set_nomacro;
-int set_volatile;
+  /* The number of extra stack bytes taken up by register varargs.
+     This area is allocated by the callee at the very top of the frame.  */
+  int varargs_size;
+
+  /* The current frame information, calculated by mips_compute_frame_info.  */
+  struct mips_frame_info frame;
 
-/* The next branch instruction is a branch likely, not branch normal.  */
-int mips_branch_likely;
+  /* The register to use as the function's global pointer, or INVALID_REGNUM
+     if the function doesn't need one.  */
+  unsigned int global_pointer;
 
-/* Count of delay slots and how many are filled.  */
-int dslots_load_total;
-int dslots_load_filled;
-int dslots_jump_total;
-int dslots_jump_filled;
+  /* True if mips_adjust_insn_length should ignore an instruction's
+     hazard attribute.  */
+  bool ignore_hazard_length_p;
 
-/* # of nops needed by previous insn */
-int dslots_number_nops;
+  /* True if the whole function is suitable for .set noreorder and
+     .set nomacro.  */
+  bool all_noreorder_p;
 
-/* Number of 1/2/3 word references to data items (ie, not jal's).  */
-int num_refs[3];
+  /* True if the function is known to have an instruction that needs $gp.  */
+  bool has_gp_insn_p;
 
-/* registers to check for load delay */
-rtx mips_load_reg, mips_load_reg2, mips_load_reg3, mips_load_reg4;
+  /* True if we have emitted an instruction to initialize
+     mips16_gp_pseudo_rtx.  */
+  bool initialized_mips16_gp_pseudo_p;
+};
 
-/* Cached operands, and operator to compare for use in set/branch/trap
-   on condition codes.  */
-rtx branch_cmp[2];
+/* Information about a single argument.  */
+struct mips_arg_info {
+  /* True if the argument is passed in a floating-point register, or
+     would have been if we hadn't run out of registers.  */
+  bool fpr_p;
 
-/* what type of branch to use */
-enum cmp_type branch_type;
+  /* The number of words passed in registers, rounded up.  */
+  unsigned int reg_words;
 
-/* Number of previously seen half-pic pointers and references.  */
-static int prev_half_pic_ptrs = 0;
-static int prev_half_pic_refs = 0;
+  /* For EABI, the offset of the first register from GP_ARG_FIRST or
+     FP_ARG_FIRST.  For other ABIs, the offset of the first register from
+     the start of the ABI's argument structure (see the CUMULATIVE_ARGS
+     comment for details).
 
-/* The target cpu for code generation.  */
-enum processor_type mips_arch;
+     The value is MAX_ARGS_IN_REGISTERS if the argument is passed entirely
+     on the stack.  */
+  unsigned int reg_offset;
 
-/* The target cpu for optimization and scheduling.  */
-enum processor_type mips_tune;
+  /* The number of words that must be passed on the stack, rounded up.  */
+  unsigned int stack_words;
 
-/* which instruction set architecture to use.  */
-int mips_isa;
+  /* The offset from the start of the stack overflow area of the argument's
+     first stack word.  Only meaningful when STACK_WORDS is nonzero.  */
+  unsigned int stack_offset;
+};
+
+/* Information about an address described by mips_address_type.
+
+   ADDRESS_CONST_INT
+       No fields are used.
+
+   ADDRESS_REG
+       REG is the base register and OFFSET is the constant offset.
+
+   ADDRESS_LO_SUM
+       REG and OFFSET are the operands to the LO_SUM and SYMBOL_TYPE
+       is the type of symbol it references.
+
+   ADDRESS_SYMBOLIC
+       SYMBOL_TYPE is the type of symbol that the address references.  */
+struct mips_address_info {
+  enum mips_address_type type;
+  rtx reg;
+  rtx offset;
+  enum mips_symbol_type symbol_type;
+};
 
-/* which abi to use.  */
-int mips_abi;
+/* One stage in a constant building sequence.  These sequences have
+   the form:
 
-/* Strings to hold which cpu and instruction set architecture to use.  */
-const char *mips_cpu_string;   /* for -mcpu=<xxx> */
-const char *mips_arch_string;   /* for -march=<xxx> */
-const char *mips_tune_string;   /* for -mtune=<xxx> */
-const char *mips_isa_string;   /* for -mips{1,2,3,4} */
-const char *mips_abi_string;   /* for -mabi={32,n32,64,eabi} */
+       A = VALUE[0]
+       A = A CODE[1] VALUE[1]
+       A = A CODE[2] VALUE[2]
+       ...
 
-/* Whether we are generating mips16 code.  This is a synonym for
-   TARGET_MIPS16, and exists for use as an attribute.  */
-int mips16;
+   where A is an accumulator, each CODE[i] is a binary rtl operation
+   and each VALUE[i] is a constant integer.  CODE[0] is undefined.  */
+struct mips_integer_op {
+  enum rtx_code code;
+  unsigned HOST_WIDE_INT value;
+};
 
-/* This variable is set by -mno-mips16.  We only care whether
-   -mno-mips16 appears or not, and using a string in this fashion is
-   just a way to avoid using up another bit in target_flags.  */
-const char *mips_no_mips16_string;
+/* The largest number of operations needed to load an integer constant.
+   The worst accepted case for 64-bit constants is LUI,ORI,SLL,ORI,SLL,ORI.
+   When the lowest bit is clear, we can try, but reject a sequence with
+   an extra SLL at the end.  */
+#define MIPS_MAX_INTEGER_OPS 7
 
-/* This is only used to determine if an type size setting option was
-   explicitly specified (-mlong64, -mint64, -mlong32).  The specs
-   set this option if such an option is used.  */
-const char *mips_explicit_type_size_string;
+/* Information about a MIPS16e SAVE or RESTORE instruction.  */
+struct mips16e_save_restore_info {
+  /* The number of argument registers saved by a SAVE instruction.
+     0 for RESTORE instructions.  */
+  unsigned int nargs;
 
-/* Whether we are generating mips16 hard float code.  In mips16 mode
-   we always set TARGET_SOFT_FLOAT; this variable is nonzero if
-   -msoft-float was not specified by the user, which means that we
-   should arrange to call mips32 hard floating point code.  */
-int mips16_hard_float;
+  /* Bit X is set if the instruction saves or restores GPR X.  */
+  unsigned int mask;
 
-/* This variable is set by -mentry.  We only care whether -mentry
-   appears or not, and using a string in this fashion is just a way to
-   avoid using up another bit in target_flags.  */
-const char *mips_entry_string;
+  /* The total number of bytes to allocate.  */
+  HOST_WIDE_INT size;
+};
 
-const char *mips_cache_flush_func = CACHE_FLUSH_FUNC;
+/* Global variables for machine-dependent things.  */
 
-/* Whether we should entry and exit pseudo-ops in mips16 mode.  */
-int mips_entry;
+/* The -G setting, or the configuration's default small-data limit if
+   no -G option is given.  */
+static unsigned int mips_small_data_threshold;
 
-/* If TRUE, we split addresses into their high and low parts in the RTL.  */
-int mips_split_addresses;
+/* The number of file directives written by mips_output_filename.  */
+int num_source_filenames;
 
-/* Generating calls to position independent functions?  */
-enum mips_abicalls_type mips_abicalls;
+/* The name that appeared in the last .file directive written by
+   mips_output_filename, or "" if mips_output_filename hasn't
+   written anything yet.  */
+const char *current_function_file = "";
 
-/* High and low marks for floating point values which we will accept
-   as legitimate constants for LEGITIMATE_CONSTANT_P.  These are
-   initialized in override_options.  */
-REAL_VALUE_TYPE dfhigh, dflow, sfhigh, sflow;
+/* A label counter used by PUT_SDB_BLOCK_START and PUT_SDB_BLOCK_END.  */
+int sdb_label_count;
 
-/* Mode used for saving/restoring general purpose registers.  */
-static enum machine_mode gpr_mode;
+/* Arrays that map GCC register numbers to debugger register numbers.  */
+int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
+int mips_dwarf_regno[FIRST_PSEUDO_REGISTER];
 
-/* Array giving truth value on whether or not a given hard register
-   can support a given mode.  */
-char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
+/* The nesting depth of the PRINT_OPERAND '%(', '%<' and '%[' constructs.  */
+int set_noreorder;
+int set_nomacro;
+static int set_noat;
 
-/* Current frame information calculated by compute_frame_size.  */
-struct mips_frame_info current_frame_info;
+/* True if we're writing out a branch-likely instruction rather than a
+   normal branch.  */
+static bool mips_branch_likely;
 
-/* Zero structure to initialize current_frame_info.  */
-struct mips_frame_info zero_frame_info;
+/* The operands passed to the last cmpMM expander.  */
+rtx cmp_operands[2];
 
-/* Pseudo-reg holding the address of the current function when
-   generating embedded PIC code.  Created by LEGITIMIZE_ADDRESS, used
-   by mips_finalize_pic if it was created.  */
-rtx embedded_pic_fnaddr_rtx;
+/* The current instruction-set architecture.  */
+enum processor_type mips_arch;
+const struct mips_cpu_info *mips_arch_info;
 
-/* The length of all strings seen when compiling for the mips16.  This
-   is used to tell how many strings are in the constant pool, so that
-   we can see if we may have an overflow.  This is reset each time the
-   constant pool is output.  */
-int mips_string_length;
+/* The processor that we should tune the code for.  */
+enum processor_type mips_tune;
+const struct mips_cpu_info *mips_tune_info;
 
-/* Pseudo-reg holding the value of $28 in a mips16 function which
-   refers to GP relative global variables.  */
-rtx mips16_gp_pseudo_rtx;
+/* The ISA level associated with mips_arch.  */
+int mips_isa;
 
-/* In mips16 mode, we build a list of all the string constants we see
-   in a particular function.  */
+/* The architecture selected by -mipsN, or null if -mipsN wasn't used.  */
+static const struct mips_cpu_info *mips_isa_option_info;
 
-struct string_constant
-{
-  struct string_constant *next;
-  const char *label;
-};
+/* Which ABI to use.  */
+int mips_abi = MIPS_ABI_DEFAULT;
 
-static struct string_constant *string_constants;
+/* Which cost information to use.  */
+const struct mips_rtx_cost_data *mips_cost;
 
-/* List of all MIPS punctuation characters used by print_operand.  */
-char mips_print_operand_punct[256];
+/* The ambient target flags, excluding MASK_MIPS16.  */
+static int mips_base_target_flags;
 
-/* Map GCC register number to debugger register number.  */
-int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
+/* True if MIPS16 is the default mode.  */
+bool mips_base_mips16;
 
-/* Buffer to use to enclose a load/store operation with %{ %} to
-   turn on .set volatile.  */
-static char volatile_buffer[60];
-
-/* Hardware names for the registers.  If -mrnames is used, this
-   will be overwritten with mips_sw_reg_names.  */
-
-char mips_reg_names[][8] =
-{
- "$0",   "$1",   "$2",   "$3",   "$4",   "$5",   "$6",   "$7",
- "$8",   "$9",   "$10",  "$11",  "$12",  "$13",  "$14",  "$15",
- "$16",  "$17",  "$18",  "$19",  "$20",  "$21",  "$22",  "$23",
- "$24",  "$25",  "$26",  "$27",  "$28",  "$sp",  "$fp",  "$31",
- "$f0",  "$f1",  "$f2",  "$f3",  "$f4",  "$f5",  "$f6",  "$f7",
- "$f8",  "$f9",  "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
- "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
- "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
- "hi",   "lo",   "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
- "$fcc5","$fcc6","$fcc7","$rap"
-};
+/* The ambient values of other global variables.  */
+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 */
 
-/* Mips software names for the registers, used to overwrite the
-   mips_reg_names array.  */
-
-static const char mips_sw_reg_names[][8] =
-{
-  "$zero","$at",  "$v0",  "$v1",  "$a0",  "$a1",  "$a2",  "$a3",
-  "$t0",  "$t1",  "$t2",  "$t3",  "$t4",  "$t5",  "$t6",  "$t7",
-  "$s0",  "$s1",  "$s2",  "$s3",  "$s4",  "$s5",  "$s6",  "$s7",
-  "$t8",  "$t9",  "$k0",  "$k1",  "$gp",  "$sp",  "$fp",  "$ra",
-  "$f0",  "$f1",  "$f2",  "$f3",  "$f4",  "$f5",  "$f6",  "$f7",
-  "$f8",  "$f9",  "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
-  "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
-  "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
-  "hi",   "lo",   "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
-  "$fcc5","$fcc6","$fcc7","$rap"
-};
+/* The -mcode-readable setting.  */
+enum mips_code_readable_setting mips_code_readable = CODE_READABLE_YES;
 
-/* Map hard register number to register class */
-const enum reg_class mips_regno_to_class[] =
-{
-  GR_REGS,     GR_REGS,        M16_NA_REGS,    M16_NA_REGS,
+/* The -mr10k-cache-barrier setting.  */
+static enum mips_r10k_cache_barrier_setting mips_r10k_cache_barrier;
+
+/* Index [M][R] is true if register R is allowed to hold a value of mode M.  */
+bool mips_hard_regno_mode_ok[(int) MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
+
+/* Index C is true if character C is a valid PRINT_OPERAND punctation
+   character.  */
+bool mips_print_operand_punct[256];
+
+static GTY (()) int mips_output_filename_first_time = 1;
+
+/* mips_split_p[X] is true if symbols of type X can be split by
+   mips_split_symbol.  */
+bool mips_split_p[NUM_SYMBOL_TYPES];
+
+/* mips_split_hi_p[X] is true if the high parts of symbols of type X
+   can be split by mips_split_symbol.  */
+bool mips_split_hi_p[NUM_SYMBOL_TYPES];
+
+/* mips_lo_relocs[X] is the relocation to use when a symbol of type X
+   appears in a LO_SUM.  It can be null if such LO_SUMs aren't valid or
+   if they are matched by a special .md file pattern.  */
+static const char *mips_lo_relocs[NUM_SYMBOL_TYPES];
+
+/* Likewise for HIGHs.  */
+static const char *mips_hi_relocs[NUM_SYMBOL_TYPES];
+
+/* Index R is the smallest register class that contains register R.  */
+const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
+  LEA_REGS,    LEA_REGS,       M16_REGS,       V1_REG,
   M16_REGS,    M16_REGS,       M16_REGS,       M16_REGS,
-  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
-  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
-  M16_NA_REGS, M16_NA_REGS,    GR_REGS,        GR_REGS,
-  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
-  T_REG,       GR_REGS,        GR_REGS,        GR_REGS,
-  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
+  LEA_REGS,    LEA_REGS,       LEA_REGS,       LEA_REGS,
+  LEA_REGS,    LEA_REGS,       LEA_REGS,       LEA_REGS,
+  M16_REGS,    M16_REGS,       LEA_REGS,       LEA_REGS,
+  LEA_REGS,    LEA_REGS,       LEA_REGS,       LEA_REGS,
+  T_REG,       PIC_FN_ADDR_REG, LEA_REGS,      LEA_REGS,
+  LEA_REGS,    LEA_REGS,       LEA_REGS,       LEA_REGS,
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
@@ -382,9889 +509,13974 @@ const enum reg_class mips_regno_to_class[] =
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
-  HI_REG,      LO_REG,         HILO_REG,       ST_REGS,
+  MD0_REG,     MD1_REG,        NO_REGS,        ST_REGS,
   ST_REGS,     ST_REGS,        ST_REGS,        ST_REGS,
-  ST_REGS,     ST_REGS,        ST_REGS,        GR_REGS
+  ST_REGS,     ST_REGS,        ST_REGS,        NO_REGS,
+  NO_REGS,     FRAME_REGS,     FRAME_REGS,     NO_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP0_REGS,   COP0_REGS,      COP0_REGS,      COP0_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP2_REGS,   COP2_REGS,      COP2_REGS,      COP2_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  COP3_REGS,   COP3_REGS,      COP3_REGS,      COP3_REGS,
+  DSP_ACC_REGS,        DSP_ACC_REGS,   DSP_ACC_REGS,   DSP_ACC_REGS,
+  DSP_ACC_REGS,        DSP_ACC_REGS,   ALL_REGS,       ALL_REGS,
+  ALL_REGS,    ALL_REGS,       ALL_REGS,       ALL_REGS
 };
 
-/* Map register constraint character to register class.  */
-enum reg_class mips_char_to_class[256] =
-{
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+/* The value of TARGET_ATTRIBUTE_TABLE.  */
+const struct attribute_spec mips_attribute_table[] = {
+  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+  { "long_call",   0, 0, false, true,  true,  NULL },
+  { "far",                0, 0, false, true,  true,  NULL },
+  { "near",        0, 0, false, true,  true,  NULL },
+  /* We would really like to treat "mips16" and "nomips16" as type
+     attributes, but GCC doesn't provide the hooks we need to support
+     the right conversion rules.  As declaration attributes, they affect
+     code generation but don't carry other semantics.  */
+  { "mips16",     0, 0, true,  false, false, NULL },
+  { "nomips16",    0, 0, true,  false, false, NULL },
+  { NULL,         0, 0, false, false, false, NULL }
 };
 \f
-/* 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_INTEGER
-#define TARGET_ASM_INTEGER mips_assemble_integer
-
-#if TARGET_IRIX5 && !TARGET_IRIX6
-#undef TARGET_ASM_UNALIGNED_HI_OP
-#define TARGET_ASM_UNALIGNED_HI_OP "\t.align 0\n\t.half\t"
-#undef TARGET_ASM_UNALIGNED_SI_OP
-#define TARGET_ASM_UNALIGNED_SI_OP "\t.align 0\n\t.word\t"
-#endif
-
-#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
+/* A table describing all the processors GCC knows about.  Names are
+   matched in the order listed.  The first mention of an ISA level is
+   taken as the canonical name for that ISA.
+
+   To ease comparison, please keep this table in the same order
+   as GAS's mips_cpu_info_table.  Please also make sure that
+   MIPS_ISA_LEVEL_SPEC and MIPS_ARCH_FLOAT_SPEC handle all -march
+   options correctly.  */
+static const struct mips_cpu_info mips_cpu_info_table[] = {
+  /* Entries for generic ISAs.  */
+  { "mips1", PROCESSOR_R3000, 1, 0 },
+  { "mips2", PROCESSOR_R6000, 2, 0 },
+  { "mips3", PROCESSOR_R4000, 3, 0 },
+  { "mips4", PROCESSOR_R8000, 4, 0 },
+  /* Prefer not to use branch-likely instructions for generic MIPS32rX
+     and MIPS64rX code.  The instructions were officially deprecated
+     in revisions 2 and earlier, but revision 3 is likely to downgrade
+     that to a recommendation to avoid the instructions in code that
+     isn't tuned to a specific processor.  */
+  { "mips32", PROCESSOR_4KC, 32, PTF_AVOID_BRANCHLIKELY },
+  { "mips32r2", PROCESSOR_M4K, 33, PTF_AVOID_BRANCHLIKELY },
+  { "mips64", PROCESSOR_5KC, 64, PTF_AVOID_BRANCHLIKELY },
+  /* ??? For now just tune the generic MIPS64r2 for 5KC as well.   */
+  { "mips64r2", PROCESSOR_5KC, 65, PTF_AVOID_BRANCHLIKELY },
+
+  /* MIPS I processors.  */
+  { "r3000", PROCESSOR_R3000, 1, 0 },
+  { "r2000", PROCESSOR_R3000, 1, 0 },
+  { "r3900", PROCESSOR_R3900, 1, 0 },
+
+  /* MIPS II processors.  */
+  { "r6000", PROCESSOR_R6000, 2, 0 },
+
+  /* MIPS III processors.  */
+  { "r4000", PROCESSOR_R4000, 3, 0 },
+  { "vr4100", PROCESSOR_R4100, 3, 0 },
+  { "vr4111", PROCESSOR_R4111, 3, 0 },
+  { "vr4120", PROCESSOR_R4120, 3, 0 },
+  { "vr4130", PROCESSOR_R4130, 3, 0 },
+  { "vr4300", PROCESSOR_R4300, 3, 0 },
+  { "r4400", PROCESSOR_R4000, 3, 0 },
+  { "r4600", PROCESSOR_R4600, 3, 0 },
+  { "orion", PROCESSOR_R4600, 3, 0 },
+  { "r4650", PROCESSOR_R4650, 3, 0 },
+  /* ST Loongson 2E/2F processors.  */
+  { "loongson2e", PROCESSOR_LOONGSON_2E, 3, PTF_AVOID_BRANCHLIKELY },
+  { "loongson2f", PROCESSOR_LOONGSON_2F, 3, PTF_AVOID_BRANCHLIKELY },
+
+  /* MIPS IV processors. */
+  { "r8000", PROCESSOR_R8000, 4, 0 },
+  { "r10000", PROCESSOR_R10000, 4, 0 },
+  { "r12000", PROCESSOR_R10000, 4, 0 },
+  { "r14000", PROCESSOR_R10000, 4, 0 },
+  { "r16000", PROCESSOR_R10000, 4, 0 },
+  { "vr5000", PROCESSOR_R5000, 4, 0 },
+  { "vr5400", PROCESSOR_R5400, 4, 0 },
+  { "vr5500", PROCESSOR_R5500, 4, PTF_AVOID_BRANCHLIKELY },
+  { "rm7000", PROCESSOR_R7000, 4, 0 },
+  { "rm9000", PROCESSOR_R9000, 4, 0 },
+
+  /* MIPS32 processors.  */
+  { "4kc", PROCESSOR_4KC, 32, 0 },
+  { "4km", PROCESSOR_4KC, 32, 0 },
+  { "4kp", PROCESSOR_4KP, 32, 0 },
+  { "4ksc", PROCESSOR_4KC, 32, 0 },
+
+  /* MIPS32 Release 2 processors.  */
+  { "m4k", PROCESSOR_M4K, 33, 0 },
+  { "4kec", PROCESSOR_4KC, 33, 0 },
+  { "4kem", PROCESSOR_4KC, 33, 0 },
+  { "4kep", PROCESSOR_4KP, 33, 0 },
+  { "4ksd", PROCESSOR_4KC, 33, 0 },
+
+  { "24kc", PROCESSOR_24KC, 33, 0 },
+  { "24kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+  { "24kf", PROCESSOR_24KF2_1, 33, 0 },
+  { "24kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+  { "24kfx", PROCESSOR_24KF1_1, 33, 0 },
+  { "24kx", PROCESSOR_24KF1_1, 33, 0 },
+
+  { "24kec", PROCESSOR_24KC, 33, 0 }, /* 24K with DSP.  */
+  { "24kef2_1", PROCESSOR_24KF2_1, 33, 0 },
+  { "24kef", PROCESSOR_24KF2_1, 33, 0 },
+  { "24kef1_1", PROCESSOR_24KF1_1, 33, 0 },
+  { "24kefx", PROCESSOR_24KF1_1, 33, 0 },
+  { "24kex", PROCESSOR_24KF1_1, 33, 0 },
+
+  { "34kc", PROCESSOR_24KC, 33, 0 }, /* 34K with MT/DSP.  */
+  { "34kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+  { "34kf", PROCESSOR_24KF2_1, 33, 0 },
+  { "34kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+  { "34kfx", PROCESSOR_24KF1_1, 33, 0 },
+  { "34kx", PROCESSOR_24KF1_1, 33, 0 },
+
+  { "74kc", PROCESSOR_74KC, 33, 0 }, /* 74K with DSPr2.  */
+  { "74kf2_1", PROCESSOR_74KF2_1, 33, 0 },
+  { "74kf", PROCESSOR_74KF2_1, 33, 0 },
+  { "74kf1_1", PROCESSOR_74KF1_1, 33, 0 },
+  { "74kfx", PROCESSOR_74KF1_1, 33, 0 },
+  { "74kx", PROCESSOR_74KF1_1, 33, 0 },
+  { "74kf3_2", PROCESSOR_74KF3_2, 33, 0 },
+
+  /* MIPS64 processors.  */
+  { "5kc", PROCESSOR_5KC, 64, 0 },
+  { "5kf", PROCESSOR_5KF, 64, 0 },
+  { "20kc", PROCESSOR_20KC, 64, PTF_AVOID_BRANCHLIKELY },
+  { "sb1", PROCESSOR_SB1, 64, PTF_AVOID_BRANCHLIKELY },
+  { "sb1a", PROCESSOR_SB1A, 64, PTF_AVOID_BRANCHLIKELY },
+  { "sr71000", PROCESSOR_SR71000, 64, PTF_AVOID_BRANCHLIKELY },
+  { "xlr", PROCESSOR_XLR, 64, 0 },
+
+  /* MIPS64 Release 2 processors.  */
+  { "octeon", PROCESSOR_OCTEON, 65, PTF_AVOID_BRANCHLIKELY }
+};
 
-#undef TARGET_SCHED_ADJUST_COST
-#define TARGET_SCHED_ADJUST_COST mips_adjust_cost
+/* Default costs.  If these are used for a processor we should look
+   up the actual costs.  */
+#define DEFAULT_COSTS COSTS_N_INSNS (6),  /* fp_add */       \
+                      COSTS_N_INSNS (7),  /* fp_mult_sf */   \
+                      COSTS_N_INSNS (8),  /* fp_mult_df */   \
+                      COSTS_N_INSNS (23), /* fp_div_sf */    \
+                      COSTS_N_INSNS (36), /* fp_div_df */    \
+                      COSTS_N_INSNS (10), /* int_mult_si */  \
+                      COSTS_N_INSNS (10), /* int_mult_di */  \
+                      COSTS_N_INSNS (69), /* int_div_si */   \
+                      COSTS_N_INSNS (69), /* int_div_di */   \
+                                       2, /* branch_cost */  \
+                                       4  /* memory_latency */
+
+/* Floating-point costs for processors without an FPU.  Just assume that
+   all floating-point libcalls are very expensive.  */
+#define SOFT_FP_COSTS COSTS_N_INSNS (256), /* fp_add */       \
+                      COSTS_N_INSNS (256), /* fp_mult_sf */   \
+                      COSTS_N_INSNS (256), /* fp_mult_df */   \
+                      COSTS_N_INSNS (256), /* fp_div_sf */    \
+                      COSTS_N_INSNS (256)  /* fp_div_df */
+
+/* Costs to use when optimizing for size.  */
+static const struct mips_rtx_cost_data mips_rtx_cost_optimize_size = {
+  COSTS_N_INSNS (1),            /* fp_add */
+  COSTS_N_INSNS (1),            /* fp_mult_sf */
+  COSTS_N_INSNS (1),            /* fp_mult_df */
+  COSTS_N_INSNS (1),            /* fp_div_sf */
+  COSTS_N_INSNS (1),            /* fp_div_df */
+  COSTS_N_INSNS (1),            /* int_mult_si */
+  COSTS_N_INSNS (1),            /* int_mult_di */
+  COSTS_N_INSNS (1),            /* int_div_si */
+  COSTS_N_INSNS (1),            /* int_div_di */
+                  2,           /* branch_cost */
+                  4            /* memory_latency */
+};
 
-struct gcc_target targetm = TARGET_INITIALIZER;
+/* Costs to use when optimizing for speed, indexed by processor.  */
+static const struct mips_rtx_cost_data mips_rtx_cost_data[PROCESSOR_MAX] = {
+  { /* R3000 */
+    COSTS_N_INSNS (2),            /* fp_add */
+    COSTS_N_INSNS (4),            /* fp_mult_sf */
+    COSTS_N_INSNS (5),            /* fp_mult_df */
+    COSTS_N_INSNS (12),           /* fp_div_sf */
+    COSTS_N_INSNS (19),           /* fp_div_df */
+    COSTS_N_INSNS (12),           /* int_mult_si */
+    COSTS_N_INSNS (12),           /* int_mult_di */
+    COSTS_N_INSNS (35),           /* int_div_si */
+    COSTS_N_INSNS (35),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 4KC */
+    SOFT_FP_COSTS,
+    COSTS_N_INSNS (6),            /* int_mult_si */
+    COSTS_N_INSNS (6),            /* int_mult_di */
+    COSTS_N_INSNS (36),           /* int_div_si */
+    COSTS_N_INSNS (36),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 4KP */
+    SOFT_FP_COSTS,
+    COSTS_N_INSNS (36),           /* int_mult_si */
+    COSTS_N_INSNS (36),           /* int_mult_di */
+    COSTS_N_INSNS (37),           /* int_div_si */
+    COSTS_N_INSNS (37),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 5KC */
+    SOFT_FP_COSTS,
+    COSTS_N_INSNS (4),            /* int_mult_si */
+    COSTS_N_INSNS (11),           /* int_mult_di */
+    COSTS_N_INSNS (36),           /* int_div_si */
+    COSTS_N_INSNS (68),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 5KF */
+    COSTS_N_INSNS (4),            /* fp_add */
+    COSTS_N_INSNS (4),            /* fp_mult_sf */
+    COSTS_N_INSNS (5),            /* fp_mult_df */
+    COSTS_N_INSNS (17),           /* fp_div_sf */
+    COSTS_N_INSNS (32),           /* fp_div_df */
+    COSTS_N_INSNS (4),            /* int_mult_si */
+    COSTS_N_INSNS (11),           /* int_mult_di */
+    COSTS_N_INSNS (36),           /* int_div_si */
+    COSTS_N_INSNS (68),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 20KC */
+    COSTS_N_INSNS (4),            /* fp_add */
+    COSTS_N_INSNS (4),            /* fp_mult_sf */
+    COSTS_N_INSNS (5),            /* fp_mult_df */
+    COSTS_N_INSNS (17),           /* fp_div_sf */
+    COSTS_N_INSNS (32),           /* fp_div_df */
+    COSTS_N_INSNS (4),            /* int_mult_si */
+    COSTS_N_INSNS (7),            /* int_mult_di */
+    COSTS_N_INSNS (42),           /* int_div_si */
+    COSTS_N_INSNS (72),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 24KC */
+    SOFT_FP_COSTS,
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (5),            /* int_mult_di */
+    COSTS_N_INSNS (41),           /* int_div_si */
+    COSTS_N_INSNS (41),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 24KF2_1 */
+    COSTS_N_INSNS (8),            /* fp_add */
+    COSTS_N_INSNS (8),            /* fp_mult_sf */
+    COSTS_N_INSNS (10),           /* fp_mult_df */
+    COSTS_N_INSNS (34),           /* fp_div_sf */
+    COSTS_N_INSNS (64),           /* fp_div_df */
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (5),            /* int_mult_di */
+    COSTS_N_INSNS (41),           /* int_div_si */
+    COSTS_N_INSNS (41),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 24KF1_1 */
+    COSTS_N_INSNS (4),            /* fp_add */
+    COSTS_N_INSNS (4),            /* fp_mult_sf */
+    COSTS_N_INSNS (5),            /* fp_mult_df */
+    COSTS_N_INSNS (17),           /* fp_div_sf */
+    COSTS_N_INSNS (32),           /* fp_div_df */
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (5),            /* int_mult_di */
+    COSTS_N_INSNS (41),           /* int_div_si */
+    COSTS_N_INSNS (41),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 74KC */
+    SOFT_FP_COSTS,
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (5),            /* int_mult_di */
+    COSTS_N_INSNS (41),           /* int_div_si */
+    COSTS_N_INSNS (41),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 74KF2_1 */
+    COSTS_N_INSNS (8),            /* fp_add */
+    COSTS_N_INSNS (8),            /* fp_mult_sf */
+    COSTS_N_INSNS (10),           /* fp_mult_df */
+    COSTS_N_INSNS (34),           /* fp_div_sf */
+    COSTS_N_INSNS (64),           /* fp_div_df */
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (5),            /* int_mult_di */
+    COSTS_N_INSNS (41),           /* int_div_si */
+    COSTS_N_INSNS (41),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 74KF1_1 */
+    COSTS_N_INSNS (4),            /* fp_add */
+    COSTS_N_INSNS (4),            /* fp_mult_sf */
+    COSTS_N_INSNS (5),            /* fp_mult_df */
+    COSTS_N_INSNS (17),           /* fp_div_sf */
+    COSTS_N_INSNS (32),           /* fp_div_df */
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (5),            /* int_mult_di */
+    COSTS_N_INSNS (41),           /* int_div_si */
+    COSTS_N_INSNS (41),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* 74KF3_2 */
+    COSTS_N_INSNS (6),            /* fp_add */
+    COSTS_N_INSNS (6),            /* fp_mult_sf */
+    COSTS_N_INSNS (7),            /* fp_mult_df */
+    COSTS_N_INSNS (25),           /* fp_div_sf */
+    COSTS_N_INSNS (48),           /* fp_div_df */
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (5),            /* int_mult_di */
+    COSTS_N_INSNS (41),           /* int_div_si */
+    COSTS_N_INSNS (41),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* Loongson-2E */
+    DEFAULT_COSTS
+  },
+  { /* Loongson-2F */
+    DEFAULT_COSTS
+  },
+  { /* M4k */
+    DEFAULT_COSTS
+  },
+    /* Octeon */
+  {
+    SOFT_FP_COSTS,
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (5),            /* int_mult_di */
+    COSTS_N_INSNS (72),           /* int_div_si */
+    COSTS_N_INSNS (72),           /* int_div_di */
+                     1,                  /* branch_cost */
+                     4           /* memory_latency */
+  },
+  { /* R3900 */
+    COSTS_N_INSNS (2),            /* fp_add */
+    COSTS_N_INSNS (4),            /* fp_mult_sf */
+    COSTS_N_INSNS (5),            /* fp_mult_df */
+    COSTS_N_INSNS (12),           /* fp_div_sf */
+    COSTS_N_INSNS (19),           /* fp_div_df */
+    COSTS_N_INSNS (2),            /* int_mult_si */
+    COSTS_N_INSNS (2),            /* int_mult_di */
+    COSTS_N_INSNS (35),           /* int_div_si */
+    COSTS_N_INSNS (35),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* R6000 */
+    COSTS_N_INSNS (3),            /* fp_add */
+    COSTS_N_INSNS (5),            /* fp_mult_sf */
+    COSTS_N_INSNS (6),            /* fp_mult_df */
+    COSTS_N_INSNS (15),           /* fp_div_sf */
+    COSTS_N_INSNS (16),           /* fp_div_df */
+    COSTS_N_INSNS (17),           /* int_mult_si */
+    COSTS_N_INSNS (17),           /* int_mult_di */
+    COSTS_N_INSNS (38),           /* int_div_si */
+    COSTS_N_INSNS (38),           /* int_div_di */
+                    2,           /* branch_cost */
+                    6            /* memory_latency */
+  },
+  { /* R4000 */
+     COSTS_N_INSNS (6),           /* fp_add */
+     COSTS_N_INSNS (7),           /* fp_mult_sf */
+     COSTS_N_INSNS (8),           /* fp_mult_df */
+     COSTS_N_INSNS (23),          /* fp_div_sf */
+     COSTS_N_INSNS (36),          /* fp_div_df */
+     COSTS_N_INSNS (10),          /* int_mult_si */
+     COSTS_N_INSNS (10),          /* int_mult_di */
+     COSTS_N_INSNS (69),          /* int_div_si */
+     COSTS_N_INSNS (69),          /* int_div_di */
+                     2,          /* branch_cost */
+                     6           /* memory_latency */
+  },
+  { /* R4100 */
+    DEFAULT_COSTS
+  },
+  { /* R4111 */
+    DEFAULT_COSTS
+  },
+  { /* R4120 */
+    DEFAULT_COSTS
+  },
+  { /* R4130 */
+    /* The only costs that appear to be updated here are
+       integer multiplication.  */
+    SOFT_FP_COSTS,
+    COSTS_N_INSNS (4),            /* int_mult_si */
+    COSTS_N_INSNS (6),            /* int_mult_di */
+    COSTS_N_INSNS (69),           /* int_div_si */
+    COSTS_N_INSNS (69),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* R4300 */
+    DEFAULT_COSTS
+  },
+  { /* R4600 */
+    DEFAULT_COSTS
+  },
+  { /* R4650 */
+    DEFAULT_COSTS
+  },
+  { /* R5000 */
+    COSTS_N_INSNS (6),            /* fp_add */
+    COSTS_N_INSNS (4),            /* fp_mult_sf */
+    COSTS_N_INSNS (5),            /* fp_mult_df */
+    COSTS_N_INSNS (23),           /* fp_div_sf */
+    COSTS_N_INSNS (36),           /* fp_div_df */
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (5),            /* int_mult_di */
+    COSTS_N_INSNS (36),           /* int_div_si */
+    COSTS_N_INSNS (36),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* R5400 */
+    COSTS_N_INSNS (6),            /* fp_add */
+    COSTS_N_INSNS (5),            /* fp_mult_sf */
+    COSTS_N_INSNS (6),            /* fp_mult_df */
+    COSTS_N_INSNS (30),           /* fp_div_sf */
+    COSTS_N_INSNS (59),           /* fp_div_df */
+    COSTS_N_INSNS (3),            /* int_mult_si */
+    COSTS_N_INSNS (4),            /* int_mult_di */
+    COSTS_N_INSNS (42),           /* int_div_si */
+    COSTS_N_INSNS (74),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* R5500 */
+    COSTS_N_INSNS (6),            /* fp_add */
+    COSTS_N_INSNS (5),            /* fp_mult_sf */
+    COSTS_N_INSNS (6),            /* fp_mult_df */
+    COSTS_N_INSNS (30),           /* fp_div_sf */
+    COSTS_N_INSNS (59),           /* fp_div_df */
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (9),            /* int_mult_di */
+    COSTS_N_INSNS (42),           /* int_div_si */
+    COSTS_N_INSNS (74),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* R7000 */
+    /* The only costs that are changed here are
+       integer multiplication.  */
+    COSTS_N_INSNS (6),            /* fp_add */
+    COSTS_N_INSNS (7),            /* fp_mult_sf */
+    COSTS_N_INSNS (8),            /* fp_mult_df */
+    COSTS_N_INSNS (23),           /* fp_div_sf */
+    COSTS_N_INSNS (36),           /* fp_div_df */
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (9),            /* int_mult_di */
+    COSTS_N_INSNS (69),           /* int_div_si */
+    COSTS_N_INSNS (69),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* R8000 */
+    DEFAULT_COSTS
+  },
+  { /* R9000 */
+    /* The only costs that are changed here are
+       integer multiplication.  */
+    COSTS_N_INSNS (6),            /* fp_add */
+    COSTS_N_INSNS (7),            /* fp_mult_sf */
+    COSTS_N_INSNS (8),            /* fp_mult_df */
+    COSTS_N_INSNS (23),           /* fp_div_sf */
+    COSTS_N_INSNS (36),           /* fp_div_df */
+    COSTS_N_INSNS (3),            /* int_mult_si */
+    COSTS_N_INSNS (8),            /* int_mult_di */
+    COSTS_N_INSNS (69),           /* int_div_si */
+    COSTS_N_INSNS (69),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* R1x000 */
+    COSTS_N_INSNS (2),            /* fp_add */
+    COSTS_N_INSNS (2),            /* fp_mult_sf */
+    COSTS_N_INSNS (2),            /* fp_mult_df */
+    COSTS_N_INSNS (12),           /* fp_div_sf */
+    COSTS_N_INSNS (19),           /* fp_div_df */
+    COSTS_N_INSNS (5),            /* int_mult_si */
+    COSTS_N_INSNS (9),            /* int_mult_di */
+    COSTS_N_INSNS (34),           /* int_div_si */
+    COSTS_N_INSNS (66),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* SB1 */
+    /* These costs are the same as the SB-1A below.  */
+    COSTS_N_INSNS (4),            /* fp_add */
+    COSTS_N_INSNS (4),            /* fp_mult_sf */
+    COSTS_N_INSNS (4),            /* fp_mult_df */
+    COSTS_N_INSNS (24),           /* fp_div_sf */
+    COSTS_N_INSNS (32),           /* fp_div_df */
+    COSTS_N_INSNS (3),            /* int_mult_si */
+    COSTS_N_INSNS (4),            /* int_mult_di */
+    COSTS_N_INSNS (36),           /* int_div_si */
+    COSTS_N_INSNS (68),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* SB1-A */
+    /* These costs are the same as the SB-1 above.  */
+    COSTS_N_INSNS (4),            /* fp_add */
+    COSTS_N_INSNS (4),            /* fp_mult_sf */
+    COSTS_N_INSNS (4),            /* fp_mult_df */
+    COSTS_N_INSNS (24),           /* fp_div_sf */
+    COSTS_N_INSNS (32),           /* fp_div_df */
+    COSTS_N_INSNS (3),            /* int_mult_si */
+    COSTS_N_INSNS (4),            /* int_mult_di */
+    COSTS_N_INSNS (36),           /* int_div_si */
+    COSTS_N_INSNS (68),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  },
+  { /* SR71000 */
+    DEFAULT_COSTS
+  },
+  { /* XLR */
+    /* Need to replace first five with the costs of calling the appropriate 
+       libgcc routine.  */
+    COSTS_N_INSNS (256),          /* fp_add */
+    COSTS_N_INSNS (256),          /* fp_mult_sf */
+    COSTS_N_INSNS (256),          /* fp_mult_df */
+    COSTS_N_INSNS (256),          /* fp_div_sf */
+    COSTS_N_INSNS (256),          /* fp_div_df */
+    COSTS_N_INSNS (8),            /* int_mult_si */
+    COSTS_N_INSNS (8),            /* int_mult_di */
+    COSTS_N_INSNS (72),           /* int_div_si */
+    COSTS_N_INSNS (72),           /* int_div_di */
+                    1,           /* branch_cost */
+                    4            /* memory_latency */
+  }
+};
 \f
-/* Return truth value of whether OP can be used as an operands
-   where a register or 16 bit unsigned integer is needed.  */
+/* This hash table keeps track of implicit "mips16" and "nomips16" attributes
+   for -mflip_mips16.  It maps decl names onto a boolean mode setting.  */
+struct mflip_mips16_entry GTY (()) {
+  const char *name;
+  bool mips16_p;
+};
+static GTY ((param_is (struct mflip_mips16_entry))) htab_t mflip_mips16_htab;
 
-int
-uns_arith_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+/* Hash table callbacks for mflip_mips16_htab.  */
+
+static hashval_t
+mflip_mips16_htab_hash (const void *entry)
 {
-  if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op))
-    return 1;
+  return htab_hash_string (((const struct mflip_mips16_entry *) entry)->name);
+}
 
-  return register_operand (op, mode);
+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;
 }
 
-/* Return truth value of whether OP can be used as an operands
-   where a 16 bit integer is needed  */
+/* True if -mflip-mips16 should next add an attribute for the default MIPS16
+   mode, false if it should next add an attribute for the opposite mode.  */
+static GTY(()) bool mips16_flipper;
 
-int
-arith_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+/* DECL is a function that needs a default "mips16" or "nomips16" attribute
+   for -mflip-mips16.  Return true if it should use "mips16" and false if
+   it should use "nomips16".  */
+
+static bool
+mflip_mips16_use_mips16_p (tree decl)
 {
-  if (GET_CODE (op) == CONST_INT && SMALL_INT (op))
-    return 1;
+  struct mflip_mips16_entry *entry;
+  const char *name;
+  hashval_t hash;
+  void **slot;
+
+  /* Use the opposite of the command-line setting for anonymous decls.  */
+  if (!DECL_NAME (decl))
+    return !mips_base_mips16;
+
+  if (!mflip_mips16_htab)
+    mflip_mips16_htab = htab_create_ggc (37, mflip_mips16_htab_hash,
+                                        mflip_mips16_htab_eq, NULL);
+
+  name = IDENTIFIER_POINTER (DECL_NAME (decl));
+  hash = htab_hash_string (name);
+  slot = htab_find_slot_with_hash (mflip_mips16_htab, name, hash, INSERT);
+  entry = (struct mflip_mips16_entry *) *slot;
+  if (!entry)
+    {
+      mips16_flipper = !mips16_flipper;
+      entry = GGC_NEW (struct mflip_mips16_entry);
+      entry->name = name;
+      entry->mips16_p = mips16_flipper ? !mips_base_mips16 : mips_base_mips16;
+      *slot = entry;
+    }
+  return entry->mips16_p;
+}
+\f
+/* Predicates to test for presence of "near" and "far"/"long_call"
+   attributes on the given TYPE.  */
 
-  /* On the mips16, a GP relative value is a signed 16 bit offset.  */
-  if (TARGET_MIPS16 && GET_CODE (op) == CONST && mips16_gp_offset_p (op))
-    return 1;
+static bool
+mips_near_type_p (const_tree type)
+{
+  return lookup_attribute ("near", TYPE_ATTRIBUTES (type)) != NULL;
+}
 
-  return register_operand (op, mode);
+static bool
+mips_far_type_p (const_tree type)
+{
+  return (lookup_attribute ("long_call", TYPE_ATTRIBUTES (type)) != NULL
+         || lookup_attribute ("far", TYPE_ATTRIBUTES (type)) != NULL);
 }
 
-/* Return truth value of whether OP can be used as an operand in a two
-   address arithmetic insn (such as set 123456,%o4) of mode MODE.  */
+/* Similar predicates for "mips16"/"nomips16" function attributes.  */
 
-int
-arith32_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static bool
+mips_mips16_decl_p (const_tree decl)
 {
-  if (GET_CODE (op) == CONST_INT)
-    return 1;
+  return lookup_attribute ("mips16", DECL_ATTRIBUTES (decl)) != NULL;
+}
 
-  return register_operand (op, mode);
+static bool
+mips_nomips16_decl_p (const_tree decl)
+{
+  return lookup_attribute ("nomips16", DECL_ATTRIBUTES (decl)) != NULL;
 }
 
-/* Return truth value of whether OP is an integer which fits in 16 bits.  */
+/* Return true if function DECL is a MIPS16 function.  Return the ambient
+   setting if DECL is null.  */
 
-int
-small_int (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+static bool
+mips_use_mips16_mode_p (tree decl)
 {
-  return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
+  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 truth value of whether OP is a 32 bit integer which is too big to
-   be loaded with one instruction.  */
+/* Implement TARGET_COMP_TYPE_ATTRIBUTES.  */
 
-int
-large_int (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+static int
+mips_comp_type_attributes (const_tree type1, const_tree type2)
 {
-  HOST_WIDE_INT value;
-
-  if (GET_CODE (op) != CONST_INT)
+  /* Disallow mixed near/far attributes.  */
+  if (mips_far_type_p (type1) && mips_near_type_p (type2))
     return 0;
-
-  value = INTVAL (op);
-
-  /* ior reg,$r0,value */
-  if ((value & ~ ((HOST_WIDE_INT) 0x0000ffff)) == 0)
+  if (mips_near_type_p (type1) && mips_far_type_p (type2))
     return 0;
+  return 1;
+}
 
-  /* subu reg,$r0,value */
-  if (((unsigned HOST_WIDE_INT) (value + 32768)) <= 32767)
-    return 0;
+/* Implement TARGET_INSERT_ATTRIBUTES.  */
 
-  /* lui reg,value>>16 */
-  if ((value & 0x0000ffff) == 0)
-    return 0;
+static void
+mips_insert_attributes (tree decl, tree *attributes)
+{
+  const char *name;
+  bool mips16_p, nomips16_p;
 
-  return 1;
+  /* 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);
+       }
+    }
 }
 
-/* Return truth value of whether OP is a register or the constant 0.
-   In mips16 mode, we only accept a register, since the mips16 does
-   not have $0.  */
+/* Implement TARGET_MERGE_DECL_ATTRIBUTES.  */
 
-int
-reg_or_0_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+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.  */
+
+static void
+mips_split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr)
 {
-  switch (GET_CODE (op))
+  if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
     {
-    case CONST_INT:
-      if (TARGET_MIPS16)
-       return 0;
-      return INTVAL (op) == 0;
+      *base_ptr = XEXP (x, 0);
+      *offset_ptr = INTVAL (XEXP (x, 1));
+    }
+  else
+    {
+      *base_ptr = x;
+      *offset_ptr = 0;
+    }
+}
+\f
+static unsigned int mips_build_integer (struct mips_integer_op *,
+                                       unsigned HOST_WIDE_INT);
 
-    case CONST_DOUBLE:
-      if (TARGET_MIPS16)
-       return 0;
-      return op == CONST0_RTX (mode);
+/* A subroutine of mips_build_integer, with the same interface.
+   Assume that the final action in the sequence should be a left shift.  */
 
-    case REG:
-    case SUBREG:
-      return register_operand (op, mode);
+static unsigned int
+mips_build_shift (struct mips_integer_op *codes, HOST_WIDE_INT value)
+{
+  unsigned int i, shift;
 
-    default:
-      break;
-    }
+  /* 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++;
 
-  return 0;
+  i = mips_build_integer (codes, value);
+  codes[i].code = ASHIFT;
+  codes[i].value = shift;
+  return i + 1;
 }
 
-/* Return truth value of whether OP is a register or the constant 0,
-   even in mips16 mode.  */
+/* As for mips_build_shift, but assume that the final action will be
+   an IOR or PLUS operation.  */
 
-int
-true_reg_or_0_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static unsigned int
+mips_build_lower (struct mips_integer_op *codes, unsigned HOST_WIDE_INT value)
 {
-  switch (GET_CODE (op))
+  unsigned HOST_WIDE_INT high;
+  unsigned int i;
+
+  high = value & ~(unsigned HOST_WIDE_INT) 0xffff;
+  if (!LUI_OPERAND (high) && (value & 0x18000) == 0x18000)
     {
-    case CONST_INT:
-      return INTVAL (op) == 0;
+      /* The constant is too complex to load with a simple LUI/ORI pair,
+        so we want to give the recursive call 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
+    {
+      /* Either this is a simple LUI/ORI pair, or clearing the lowest 16
+        bits gives a value with at least 17 trailing zeros.  */
+      i = mips_build_integer (codes, high);
+      codes[i].code = IOR;
+      codes[i].value = value & 0xffff;
+    }
+  return i + 1;
+}
 
-    case CONST_DOUBLE:
-      return op == CONST0_RTX (mode);
+/* Fill CODES with a sequence of rtl operations to load VALUE.
+   Return the number of operations needed.  */
 
-    case REG:
-    case SUBREG:
-      return register_operand (op, mode);
+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 symbols of type TYPE require a GOT access.  */
+
+static bool
+mips_got_symbol_type_p (enum mips_symbol_type type)
+{
+  switch (type)
+    {
+    case SYMBOL_GOT_PAGE_OFST:
+    case SYMBOL_GOT_DISP:
+      return true;
 
     default:
-      break;
+      return false;
     }
+}
 
-  return 0;
+/* Return true if X is a thread-local symbol.  */
+
+static bool
+mips_tls_symbol_p (rtx x)
+{
+  return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0;
 }
 
-/* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant.  */
+/* Return true if SYMBOL_REF X is associated with a global symbol
+   (in the STB_GLOBAL sense).  */
 
-int
-mips_const_double_ok (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static bool
+mips_global_symbol_p (const_rtx x)
 {
-  REAL_VALUE_TYPE d;
+  const_tree decl = SYMBOL_REF_DECL (x);
 
-  if (GET_CODE (op) != CONST_DOUBLE)
-    return 0;
+  if (!decl)
+    return !SYMBOL_REF_LOCAL_P (x) || SYMBOL_REF_EXTERNAL_P (x);
 
-  if (mode == VOIDmode)
-    return 1;
+  /* Weakref symbols are not TREE_PUBLIC, but their targets are global
+     or weak symbols.  Relocations in the object file will be against
+     the target symbol, so it's that symbol's binding that matters here.  */
+  return DECL_P (decl) && (TREE_PUBLIC (decl) || DECL_WEAK (decl));
+}
 
-  if (mode != SFmode && mode != DFmode)
-    return 0;
+/* Return true if function X is a libgcc MIPS16 stub function.  */
 
-  if (op == CONST0_RTX (mode))
-    return 1;
+static bool
+mips16_stub_function_p (const_rtx x)
+{
+  return (GET_CODE (x) == SYMBOL_REF
+         && strncmp (XSTR (x, 0), "__mips16_", 9) == 0);
+}
 
-  /* ??? li.s does not work right with SGI's Irix 6 assembler.  */
-  if (mips_abi != ABI_32 && mips_abi != ABI_O64 && mips_abi != ABI_EABI)
-    return 0;
+/* Return true if function X is a locally-defined and locally-binding
+   MIPS16 function.  */
 
-  REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+static bool
+mips16_local_function_p (const_rtx x)
+{
+  return (GET_CODE (x) == SYMBOL_REF
+         && SYMBOL_REF_LOCAL_P (x)
+         && !SYMBOL_REF_EXTERNAL_P (x)
+         && mips_use_mips16_mode_p (SYMBOL_REF_DECL (x)));
+}
 
-  if (REAL_VALUE_ISNAN (d))
-    return FALSE;
+/* Return true if SYMBOL_REF X binds locally.  */
 
-  if (REAL_VALUE_NEGATIVE (d))
-    d = REAL_VALUE_NEGATE (d);
+static bool
+mips_symbol_binds_local_p (const_rtx x)
+{
+  return (SYMBOL_REF_DECL (x)
+         ? targetm.binds_local_p (SYMBOL_REF_DECL (x))
+         : SYMBOL_REF_LOCAL_P (x));
+}
 
-  if (mode == DFmode)
-    {
-      if (REAL_VALUES_LESS (d, dfhigh)
-         && REAL_VALUES_LESS (dflow, d))
-       return 1;
-    }
-  else
-    {
-      if (REAL_VALUES_LESS (d, sfhigh)
-         && REAL_VALUES_LESS (sflow, d))
-       return 1;
-    }
+/* Return true if rtx constants of mode MODE should be put into a small
+   data section.  */
 
-  return 0;
+static bool
+mips_rtx_constant_in_small_data_p (enum machine_mode mode)
+{
+  return (!TARGET_EMBEDDED_DATA
+         && TARGET_LOCAL_SDATA
+         && GET_MODE_SIZE (mode) <= mips_small_data_threshold);
 }
 
-/* Accept the floating point constant 1 in the appropriate mode.  */
+/* 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.  */
 
-int
-const_float_1_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  REAL_VALUE_TYPE d;
-  static REAL_VALUE_TYPE onedf;
-  static REAL_VALUE_TYPE onesf;
-  static int one_initialized;
-
-  if (GET_CODE (op) != CONST_DOUBLE
-      || mode != GET_MODE (op)
-      || (mode != DFmode && mode != SFmode))
-    return 0;
+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 calls to X might need $25 to be valid on entry.  */
+
+bool
+mips_use_pic_fn_addr_reg_p (const_rtx x)
+{
+  if (!TARGET_USE_PIC_FN_ADDR_REG)
+    return false;
 
-  REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+  /* MIPS16 stub functions are guaranteed not to use $25.  */
+  if (mips16_stub_function_p (x))
+    return false;
 
-  /* We only initialize these values if we need them, since we will
-     never get called unless mips_isa >= 4.  */
-  if (! one_initialized)
+  if (GET_CODE (x) == SYMBOL_REF)
     {
-      onedf = REAL_VALUE_ATOF ("1.0", DFmode);
-      onesf = REAL_VALUE_ATOF ("1.0", SFmode);
-      one_initialized = 1;
+      /* If PLTs and copy relocations are available, the static linker
+        will make sure that $25 is valid on entry to the target function.  */
+      if (TARGET_ABICALLS_PIC0)
+       return false;
+
+      /* Locally-defined functions use absolute accesses to set up
+        the global pointer.  */
+      if (TARGET_ABSOLUTE_ABICALLS
+         && mips_symbol_binds_local_p (x)
+         && !SYMBOL_REF_EXTERNAL_P (x))
+       return false;
     }
 
-  if (mode == DFmode)
-    return REAL_VALUES_EQUAL (d, onedf);
-  else
-    return REAL_VALUES_EQUAL (d, onesf);
+  return true;
 }
 
-/* Return true if a memory load or store of REG plus OFFSET in MODE
-   can be represented in a single word on the mips16.  */
+/* Return the method that should be used to access SYMBOL_REF or
+   LABEL_REF X in context CONTEXT.  */
 
-static int
-mips16_simple_memory_operand (reg, offset, mode)
-     rtx reg;
-     rtx offset;
-     enum machine_mode mode;
+static enum mips_symbol_type
+mips_classify_symbol (const_rtx x, enum mips_symbol_context context)
 {
-  unsigned int size;
-  int off;
+  if (TARGET_RTP_PIC)
+    return SYMBOL_GOT_DISP;
 
-  if (mode == BLKmode)
+  if (GET_CODE (x) == LABEL_REF)
     {
-      /* We can't tell, because we don't know how the value will
-         eventually be accessed.  Returning 0 here does no great
-         harm; it just prevents some possible instruction scheduling.  */
-      return 0;
+      /* LABEL_REFs are used for jump tables as well as text labels.
+        Only return SYMBOL_PC_RELATIVE if we know the label is in
+        the text section.  */
+      if (TARGET_MIPS16_SHORT_JUMP_TABLES)
+       return SYMBOL_PC_RELATIVE;
+
+      if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS)
+       return SYMBOL_GOT_PAGE_OFST;
+
+      return SYMBOL_ABSOLUTE;
     }
 
-  size = GET_MODE_SIZE (mode);
+  gcc_assert (GET_CODE (x) == SYMBOL_REF);
 
-  if (INTVAL (offset) % size != 0)
-    return 0;
-  if (REGNO (reg) == STACK_POINTER_REGNUM && GET_MODE_SIZE (mode) == 4)
-    off = 0x100;
-  else
-    off = 0x20;
-  if (INTVAL (offset) >= 0 && INTVAL (offset) < (HOST_WIDE_INT)(off * size))
-    return 1;
-  return 0;
-}
+  if (SYMBOL_REF_TLS_MODEL (x))
+    return SYMBOL_TLS;
 
-/* Return truth value if a memory operand fits in a single instruction
-   (ie, register + small offset).  */
+  if (CONSTANT_POOL_ADDRESS_P (x))
+    {
+      if (TARGET_MIPS16_TEXT_LOADS)
+       return SYMBOL_PC_RELATIVE;
 
-int
-simple_memory_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  rtx addr, plus0, plus1;
+      if (TARGET_MIPS16_PCREL_LOADS && context == SYMBOL_CONTEXT_MEM)
+       return SYMBOL_PC_RELATIVE;
 
-  /* Eliminate non-memory operations */
-  if (GET_CODE (op) != MEM)
-    return 0;
+      if (mips_rtx_constant_in_small_data_p (get_pool_mode (x)))
+       return SYMBOL_GP_RELATIVE;
+    }
 
-  /* dword operations really put out 2 instructions, so eliminate them.  */
-  /* ??? This isn't strictly correct.  It is OK to accept multiword modes
-     here, since the length attributes are being set correctly, but only
-     if the address is offsettable.  LO_SUM is not offsettable.  */
-  if (GET_MODE_SIZE (GET_MODE (op)) > (unsigned) UNITS_PER_WORD)
-    return 0;
+  /* Do not use small-data accesses for weak symbols; they may end up
+     being zero.  */
+  if (TARGET_GPOPT && SYMBOL_REF_SMALL_P (x) && !SYMBOL_REF_WEAK (x))
+    return SYMBOL_GP_RELATIVE;
 
-  /* Decode the address now.  */
-  addr = XEXP (op, 0);
-  switch (GET_CODE (addr))
+  /* Don't use GOT accesses for locally-binding symbols when -mno-shared
+     is in effect.  */
+  if (TARGET_ABICALLS_PIC2
+      && !(TARGET_ABSOLUTE_ABICALLS && mips_symbol_binds_local_p (x)))
     {
-    case REG:
-    case LO_SUM:
-      return 1;
+      /* There are three cases to consider:
 
-    case CONST_INT:
-      if (TARGET_MIPS16)
-       return 0;
-      return SMALL_INT (addr);
+           - o32 PIC (either with or without explicit relocs)
+           - n32/n64 PIC without explicit relocs
+           - n32/n64 PIC with explicit relocs
 
-    case PLUS:
-      plus0 = XEXP (addr, 0);
-      plus1 = XEXP (addr, 1);
-      if (GET_CODE (plus0) == REG
-         && GET_CODE (plus1) == CONST_INT && SMALL_INT (plus1)
-         && (! TARGET_MIPS16
-             || mips16_simple_memory_operand (plus0, plus1, mode)))
-       return 1;
+        In the first case, both local and global accesses will use an
+        R_MIPS_GOT16 relocation.  We must correctly predict which of
+        the two semantics (local or global) the assembler and linker
+        will apply.  The choice depends on the symbol's binding rather
+        than its visibility.
 
-      else if (GET_CODE (plus1) == REG
-              && GET_CODE (plus0) == CONST_INT && SMALL_INT (plus0)
-              && (! TARGET_MIPS16
-                  || mips16_simple_memory_operand (plus1, plus0, mode)))
-       return 1;
+        In the second case, the assembler will not use R_MIPS_GOT16
+        relocations, but it chooses between local and global accesses
+        in the same way as for o32 PIC.
 
-      else
-       return 0;
+        In the third case we have more freedom since both forms of
+        access will work for any kind of symbol.  However, there seems
+        little point in doing things differently.  */
+      if (mips_global_symbol_p (x))
+       return SYMBOL_GOT_DISP;
 
-#if 0
-      /* We used to allow small symbol refs here (ie, stuff in .sdata
-        or .sbss), but this causes some bugs in G++.  Also, it won't
-        interfere if the MIPS linker rewrites the store instruction
-        because the function is PIC.  */
+      return SYMBOL_GOT_PAGE_OFST;
+    }
 
-    case LABEL_REF:            /* never gp relative */
-      break;
+  if (TARGET_MIPS16_PCREL_LOADS && context != SYMBOL_CONTEXT_CALL)
+    return SYMBOL_FORCE_TO_MEM;
 
-    case CONST:
-      /* If -G 0, we can never have a GP relative memory operation.
-        Also, save some time if not optimizing.  */
-      if (!TARGET_GP_OPT)
-       return 0;
+  return SYMBOL_ABSOLUTE;
+}
 
-      {
-       rtx offset = const0_rtx;
-       addr = eliminate_constant_term (XEXP (addr, 0), &offset);
-       if (GET_CODE (op) != SYMBOL_REF)
-         return 0;
-
-       /* let's be paranoid....  */
-       if (! SMALL_INT (offset))
-         return 0;
-      }
+/* Classify the base of symbolic expression X, given that X appears in
+   context CONTEXT.  */
 
-      /* fall through */
+static enum mips_symbol_type
+mips_classify_symbolic_expression (rtx x, enum mips_symbol_context context)
+{
+  rtx offset;
 
-    case SYMBOL_REF:
-      return SYMBOL_REF_FLAG (addr);
-#endif
+  split_const (x, &x, &offset);
+  if (UNSPEC_ADDRESS_P (x))
+    return UNSPEC_ADDRESS_TYPE (x);
 
-      /* This SYMBOL_REF case is for the mips16.  If the above case is
-         reenabled, this one should be merged in.  */
-    case SYMBOL_REF:
-      /* References to the constant pool on the mips16 use a small
-         offset if the function is small.  The only time we care about
-         getting this right is during delayed branch scheduling, so
-         don't need to check until then.  The machine_dependent_reorg
-         function will set the total length of the instructions used
-         in the function in current_frame_info.  If that is small
-         enough, we know for sure that this is a small offset.  It
-         would be better if we could take into account the location of
-         the instruction within the function, but we can't, because we
-         don't know where we are.  */
-      if (TARGET_MIPS16
-         && CONSTANT_POOL_ADDRESS_P (addr)
-         && current_frame_info.insns_len > 0)
-       {
-         long size;
+  return mips_classify_symbol (x, context);
+}
 
-         size = current_frame_info.insns_len + get_pool_size ();
-         if (GET_MODE_SIZE (mode) == 4)
-           return size < 4 * 0x100;
-         else if (GET_MODE_SIZE (mode) == 8)
-           return size < 8 * 0x20;
-         else
-           return 0;
-       }
+/* Return true if OFFSET is within the range [0, ALIGN), where ALIGN
+   is the alignment in bytes of SYMBOL_REF X.  */
 
-      return 0;
+static bool
+mips_offset_within_alignment_p (rtx x, HOST_WIDE_INT offset)
+{
+  HOST_WIDE_INT align;
 
-    default:
-      break;
+  align = SYMBOL_REF_DECL (x) ? DECL_ALIGN_UNIT (SYMBOL_REF_DECL (x)) : 1;
+  return IN_RANGE (offset, 0, align - 1);
+}
+
+/* Return true if X is a symbolic constant that can be used in context
+   CONTEXT.  If it is, store the type of the symbol in *SYMBOL_TYPE.  */
+
+bool
+mips_symbolic_constant_p (rtx x, enum mips_symbol_context context,
+                         enum mips_symbol_type *symbol_type)
+{
+  rtx offset;
+
+  split_const (x, &x, &offset);
+  if (UNSPEC_ADDRESS_P (x))
+    {
+      *symbol_type = UNSPEC_ADDRESS_TYPE (x);
+      x = UNSPEC_ADDRESS (x);
+    }
+  else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+    {
+      *symbol_type = mips_classify_symbol (x, context);
+      if (*symbol_type == SYMBOL_TLS)
+       return false;
     }
+  else
+    return false;
+
+  if (offset == const0_rtx)
+    return true;
+
+  /* Check whether a nonzero offset is valid for the underlying
+     relocations.  */
+  switch (*symbol_type)
+    {
+    case SYMBOL_ABSOLUTE:
+    case SYMBOL_FORCE_TO_MEM:
+    case SYMBOL_32_HIGH:
+    case SYMBOL_64_HIGH:
+    case SYMBOL_64_MID:
+    case SYMBOL_64_LOW:
+      /* If the target has 64-bit pointers and the object file only
+        supports 32-bit symbols, the values of those symbols will be
+        sign-extended.  In this case we can't allow an arbitrary offset
+        in case the 32-bit value X + OFFSET has a different sign from X.  */
+      if (Pmode == DImode && !ABI_HAS_64BIT_SYMBOLS)
+       return offset_within_block_p (x, INTVAL (offset));
+
+      /* In other cases the relocations can handle any offset.  */
+      return true;
 
-  return 0;
+    case SYMBOL_PC_RELATIVE:
+      /* Allow constant pool references to be converted to LABEL+CONSTANT.
+        In this case, we no longer have access to the underlying constant,
+        but the original symbol-based access was known to be valid.  */
+      if (GET_CODE (x) == LABEL_REF)
+       return true;
+
+      /* Fall through.  */
+
+    case SYMBOL_GP_RELATIVE:
+      /* Make sure that the offset refers to something within the
+        same object block.  This should guarantee that the final
+        PC- or GP-relative offset is within the 16-bit limit.  */
+      return offset_within_block_p (x, INTVAL (offset));
+
+    case SYMBOL_GOT_PAGE_OFST:
+    case SYMBOL_GOTOFF_PAGE:
+      /* If the symbol is global, the GOT entry will contain the symbol's
+        address, and we will apply a 16-bit offset after loading it.
+        If the symbol is local, the linker should provide enough local
+        GOT entries for a 16-bit offset, but larger offsets may lead
+        to GOT overflow.  */
+      return SMALL_INT (offset);
+
+    case SYMBOL_TPREL:
+    case SYMBOL_DTPREL:
+      /* There is no carry between the HI and LO REL relocations, so the
+        offset is only valid if we know it won't lead to such a carry.  */
+      return mips_offset_within_alignment_p (x, INTVAL (offset));
+
+    case SYMBOL_GOT_DISP:
+    case SYMBOL_GOTOFF_DISP:
+    case SYMBOL_GOTOFF_CALL:
+    case SYMBOL_GOTOFF_LOADGP:
+    case SYMBOL_TLSGD:
+    case SYMBOL_TLSLDM:
+    case SYMBOL_GOTTPREL:
+    case SYMBOL_TLS:
+    case SYMBOL_HALF:
+      return false;
+    }
+  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.  */
 
-/* Return nonzero for a memory address that can be used to load or store
-   a doubleword.  */
+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:
 
-int
-double_memory_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  if (GET_CODE (op) != MEM
-      || ! memory_operand (op, mode))
-    {
-      /* During reload, we accept a pseudo register if it has an
-        appropriate memory address.  If we don't do this, we will
-        wind up reloading into a register, and then reloading that
-        register from memory, when we could just reload directly from
-        memory.  */
-      if (reload_in_progress
-         && GET_CODE (op) == REG
-         && REGNO (op) >= FIRST_PSEUDO_REGISTER
-         && reg_renumber[REGNO (op)] < 0
-         && reg_equiv_mem[REGNO (op)] != 0
-         && double_memory_operand (reg_equiv_mem[REGNO (op)], mode))
+            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 and 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_PC_RELATIVE:
+      /* PC-relative constants can be only be used with ADDIUPC,
+        DADDIUPC, LWPC and LDPC.  */
+      if (mode == MAX_MACHINE_MODE
+         || GET_MODE_SIZE (mode) == 4
+         || GET_MODE_SIZE (mode) == 8)
        return 1;
 
-      /* All reloaded addresses are valid in TARGET_64BIT mode.  This is
-        the same test performed for 'm' in find_reloads.  */
+      /* The constant must be loaded using ADDIUPC or DADDIUPC first.  */
+      return 0;
 
-      if (reload_in_progress
-         && TARGET_64BIT
-         && (GET_CODE (op) == MEM
-             || (GET_CODE (op) == REG
-                 && REGNO (op) >= FIRST_PSEUDO_REGISTER
-                 && reg_renumber[REGNO (op)] < 0)))
+    case SYMBOL_FORCE_TO_MEM:
+      /* LEAs will be converted into constant-pool references by
+        mips_reorg.  */
+      if (mode == MAX_MACHINE_MODE)
        return 1;
 
-      if (reload_in_progress
-         && TARGET_MIPS16
-         && GET_CODE (op) == MEM)
-       {
-         rtx addr;
-
-         addr = XEXP (op, 0);
-
-         /* During reload on the mips16, we accept a large offset
-            from the frame pointer or the stack pointer.  This large
-            address will get reloaded anyhow.  */
-         if (GET_CODE (addr) == PLUS
-             && GET_CODE (XEXP (addr, 0)) == REG
-             && (REGNO (XEXP (addr, 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM
-                 || REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
-             && ((GET_CODE (XEXP (addr, 1)) == CONST_INT
-                  && ! SMALL_INT (XEXP (addr, 1)))
-                 || (GET_CODE (XEXP (addr, 1)) == SYMBOL_REF
-                     && CONSTANT_POOL_ADDRESS_P (XEXP (addr, 1)))))
-           return 1;
-
-         /* Similarly, we accept a case where the memory address is
-             itself on the stack, and will be reloaded.  */
-         if (GET_CODE (addr) == MEM)
-           {
-             rtx maddr;
-
-             maddr = XEXP (addr, 0);
-             if (GET_CODE (maddr) == PLUS
-                 && GET_CODE (XEXP (maddr, 0)) == REG
-                 && (REGNO (XEXP (maddr, 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM
-                     || REGNO (XEXP (maddr, 0)) == STACK_POINTER_REGNUM)
-                 && ((GET_CODE (XEXP (maddr, 1)) == CONST_INT
-                      && ! SMALL_INT (XEXP (maddr, 1)))
-                     || (GET_CODE (XEXP (maddr, 1)) == SYMBOL_REF
-                         && CONSTANT_POOL_ADDRESS_P (XEXP (maddr, 1)))))
-               return 1;
-           }
+      /* The constant must be loaded and then dereferenced.  */
+      return 0;
 
-         /* We also accept the same case when we have a 16 bit signed
-            offset mixed in as well.  The large address will get
-            reloaded, and the 16 bit offset will be OK.  */
-         if (GET_CODE (addr) == PLUS
-             && GET_CODE (XEXP (addr, 0)) == MEM
-             && GET_CODE (XEXP (addr, 1)) == CONST_INT
-             && SMALL_INT (XEXP (addr, 1)))
-           {
-             addr = XEXP (XEXP (addr, 0), 0);
-             if (GET_CODE (addr) == PLUS
-                 && GET_CODE (XEXP (addr, 0)) == REG
-                 && (REGNO (XEXP (addr, 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM
-                     || REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
-                 && ((GET_CODE (XEXP (addr, 1)) == CONST_INT
-                      && ! SMALL_INT (XEXP (addr, 1)))
-                     || (GET_CODE (XEXP (addr, 1)) == SYMBOL_REF
-                         && CONSTANT_POOL_ADDRESS_P (XEXP (addr, 1)))))
-               return 1;
-           }
-       }
+    case SYMBOL_GOT_DISP:
+      /* The constant will have to be loaded from the GOT before it
+        is used in an address.  */
+      if (mode != MAX_MACHINE_MODE)
+       return 0;
 
+      /* Fall through.  */
+
+    case SYMBOL_GOT_PAGE_OFST:
+      /* Unless -funit-at-a-time is in effect, we can't be sure whether the
+        local/global classification is accurate.  The worst cases are:
+
+        (1) For local symbols when generating o32 or o64 code.  The assembler
+            will use:
+
+                lw           $at,%got(symbol)
+                nop
+
+            ...and the final address will be $at + %lo(symbol).
+
+        (2) For global symbols when -mxgot.  The assembler will use:
+
+                lui     $at,%got_hi(symbol)
+                (d)addu $at,$at,$gp
+
+            ...and the final address will be $at + %got_lo(symbol).  */
+      return 3;
+
+    case SYMBOL_GOTOFF_PAGE:
+    case SYMBOL_GOTOFF_DISP:
+    case SYMBOL_GOTOFF_CALL:
+    case SYMBOL_GOTOFF_LOADGP:
+    case SYMBOL_32_HIGH:
+    case SYMBOL_64_HIGH:
+    case SYMBOL_64_MID:
+    case SYMBOL_64_LOW:
+    case SYMBOL_TLSGD:
+    case SYMBOL_TLSLDM:
+    case SYMBOL_DTPREL:
+    case SYMBOL_GOTTPREL:
+    case SYMBOL_TPREL:
+    case SYMBOL_HALF:
+      /* A 16-bit constant formed by a single relocation, or a 32-bit
+        constant formed from a high 16-bit relocation and a low 16-bit
+        relocation.  Use mips_split_p to determine which.  32-bit
+        constants need an "lui; addiu" sequence for normal mode and
+        an "li; sll; addiu" sequence for MIPS16 mode.  */
+      return !mips_split_p[type] ? 1 : TARGET_MIPS16 ? 3 : 2;
+
+    case SYMBOL_TLS:
+      /* We don't treat a bare TLS symbol as a constant.  */
       return 0;
     }
+  gcc_unreachable ();
+}
 
-  if (TARGET_64BIT)
-    {
-      /* In this case we can use an instruction like sd.  */
-      return 1;
-    }
+/* If MODE is MAX_MACHINE_MODE, return the number of instructions needed
+   to load symbols of type TYPE into a register.  Return 0 if the given
+   type of symbol cannot be used as an immediate operand.
 
-  /* Make sure that 4 added to the address is a valid memory address.
-     This essentially just checks for overflow in an added constant.  */
+   Otherwise, return the number of instructions needed to load or store
+   values of mode MODE to or from addresses of type TYPE.  Return 0 if
+   the given type of symbol is not valid in addresses.
 
-  if (CONSTANT_ADDRESS_P (XEXP (op, 0)))
-    return 1;
+   In both cases, treat extended MIPS16 instructions as two instructions.  */
 
-  op = adjust_address_nv (op, GET_MODE_CLASS (mode) == MODE_INT
-                         ? SImode : SFmode, 4);
-  return memory_address_p (GET_MODE (op), XEXP (op, 0));
+static int
+mips_symbol_insns (enum mips_symbol_type type, enum machine_mode mode)
+{
+  return mips_symbol_insns_1 (type, mode) * (TARGET_MIPS16 ? 2 : 1);
 }
+\f
+/* A for_each_rtx callback.  Stop the search if *X references a
+   thread-local symbol.  */
 
-/* Return nonzero if the code of this rtx pattern is EQ or NE.  */
-
-int
-equality_op (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static int
+mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
 {
-  if (mode != GET_MODE (op))
-    return 0;
-
-  return GET_CODE (op) == EQ || GET_CODE (op) == NE;
+  return mips_tls_symbol_p (*x);
 }
 
-/* Return nonzero if the code is a relational operations (EQ, LE, etc.) */
+/* Implement TARGET_CANNOT_FORCE_CONST_MEM.  */
 
-int
-cmp_op (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static bool
+mips_cannot_force_const_mem (rtx x)
 {
-  if (mode != GET_MODE (op))
-    return 0;
+  enum mips_symbol_type type;
+  rtx base, offset;
 
-  return GET_RTX_CLASS (GET_CODE (op)) == '<';
-}
+  /* There is no assembler syntax for expressing an address-sized
+     high part.  */
+  if (GET_CODE (x) == HIGH)
+    return true;
 
-/* Return nonzero if the code is a relational operation suitable for a
-   conditional trap instructuion (only EQ, NE, LT, LTU, GE, GEU).
-   We need this in the insn that expands `trap_if' in order to prevent
-   combine from erroneously altering the condition.  */
+  /* As an optimization, reject constants that mips_legitimize_move
+     can expand inline.
 
-int
-trap_cmp_op (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  if (mode != GET_MODE (op))
-    return 0;
+     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 && LEGITIMATE_CONSTANT_P (x))
+    return true;
 
-  switch (GET_CODE (op))
+  split_const (x, &base, &offset);
+  if (mips_symbolic_constant_p (base, SYMBOL_CONTEXT_LEA, &type)
+      && type != SYMBOL_FORCE_TO_MEM)
     {
-    case EQ:
-    case NE:
-    case LT:
-    case LTU:
-    case GE:
-    case GEU:
-      return 1;
+      /* The same optimization as for CONST_INT.  */
+      if (SMALL_INT (offset) && mips_symbol_insns (type, MAX_MACHINE_MODE) > 0)
+       return true;
 
-    default:
-      return 0;
+      /* If MIPS16 constant pools live in the text section, they should
+        not refer to anything that might need run-time relocation.  */
+      if (TARGET_MIPS16_PCREL_LOADS && mips_got_symbol_type_p (type))
+       return true;
     }
+
+  /* TLS symbols must be computed by mips_legitimize_move.  */
+  if (for_each_rtx (&x, &mips_tls_symbol_ref_1, NULL))
+    return true;
+
+  return false;
 }
 
-/* Return nonzero if the operand is either the PC or a label_ref.  */
+/* 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
+/* Return true if register REGNO is a valid base register for mode MODE.
+   STRICT_P is true if REG_OK_STRICT is in effect.  */
 
 int
-pc_or_label_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+mips_regno_mode_ok_for_base_p (int regno, enum machine_mode mode,
+                              bool strict_p)
 {
-  if (op == pc_rtx)
-    return 1;
+  if (!HARD_REGISTER_NUM_P (regno))
+    {
+      if (!strict_p)
+       return true;
+      regno = reg_renumber[regno];
+    }
 
-  if (GET_CODE (op) == LABEL_REF)
-    return 1;
+  /* 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;
 
-  return 0;
-}
+  /* In MIPS16 mode, the stack pointer can only address word and doubleword
+     values, nothing smaller.  There are two problems here:
 
-/* Test for a valid operand for a call instruction.
-   Don't allow the arg pointer register or virtual regs
-   since they may change into reg + const, which the patterns
-   can't handle yet.  */
+       (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.
 
-int
-call_insn_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (CONSTANT_ADDRESS_P (op)
-         || (GET_CODE (op) == REG && op != arg_pointer_rtx
-             && ! (REGNO (op) >= FIRST_PSEUDO_REGISTER
-                   && REGNO (op) <= LAST_VIRTUAL_REGISTER)));
-}
+       (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).
 
-/* Return nonzero if OPERAND is valid as a source operand for a move
-   instruction.  */
+     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_p || GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
 
-int
-move_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  /* Accept any general operand after reload has started; doing so
-     avoids losing if reload does an in-place replacement of a register
-     with a SYMBOL_REF or CONST.  */
-  return (general_operand (op, mode)
-         && (! (mips_split_addresses && mips_check_split (op, mode))
-             || reload_in_progress || reload_completed)
-         && ! (TARGET_MIPS16
-               && GET_CODE (op) == SYMBOL_REF
-               && ! mips16_constant (op, mode, 1, 0)));
-}
-
-/* Return nonzero if OPERAND is valid as a source operand for movdi.
-   This accepts not only general_operand, but also sign extended
-   constants and registers.  We need to accept sign extended constants
-   in case a sign extended register which is used in an expression,
-   and is equivalent to a constant, is spilled.  */
+  return TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
+}
 
-int
-movdi_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  if (TARGET_64BIT
-      && mode == DImode
-      && GET_CODE (op) == SIGN_EXTEND
-      && GET_MODE (op) == DImode
-      && (GET_MODE (XEXP (op, 0)) == SImode
-         || (GET_CODE (XEXP (op, 0)) == CONST_INT
-             && GET_MODE (XEXP (op, 0)) == VOIDmode))
-      && (register_operand (XEXP (op, 0), SImode)
-         || immediate_operand (XEXP (op, 0), SImode)))
-    return 1;
-
-  return (general_operand (op, mode)
-         && ! (TARGET_MIPS16
-               && GET_CODE (op) == SYMBOL_REF
-               && ! mips16_constant (op, mode, 1, 0)));
-}
-
-/* Like register_operand, but when in 64 bit mode also accept a sign
-   extend of a 32 bit register, since the value is known to be already
-   sign extended.  */
+/* Return true if X is a valid base register for mode MODE.
+   STRICT_P is true if REG_OK_STRICT is in effect.  */
 
-int
-se_register_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static bool
+mips_valid_base_register_p (rtx x, enum machine_mode mode, bool strict_p)
 {
-  if (TARGET_64BIT
-      && mode == DImode
-      && GET_CODE (op) == SIGN_EXTEND
-      && GET_MODE (op) == DImode
-      && GET_MODE (XEXP (op, 0)) == SImode
-      && register_operand (XEXP (op, 0), SImode))
-    return 1;
+  if (!strict_p && GET_CODE (x) == SUBREG)
+    x = SUBREG_REG (x);
 
-  return register_operand (op, mode);
+  return (REG_P (x)
+         && mips_regno_mode_ok_for_base_p (REGNO (x), mode, strict_p));
 }
 
-/* Like reg_or_0_operand, but when in 64 bit mode also accept a sign
-   extend of a 32 bit register, since the value is known to be already
-   sign extended.  */
+/* Return true if, for every base register BASE_REG, (plus BASE_REG X)
+   can address a value of mode MODE.  */
 
-int
-se_reg_or_0_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static bool
+mips_valid_offset_p (rtx x, enum machine_mode mode)
 {
-  if (TARGET_64BIT
-      && mode == DImode
-      && GET_CODE (op) == SIGN_EXTEND
-      && GET_MODE (op) == DImode
-      && GET_MODE (XEXP (op, 0)) == SImode
-      && register_operand (XEXP (op, 0), SImode))
-    return 1;
+  /* Check that X is a signed 16-bit number.  */
+  if (!const_arith_operand (x, Pmode))
+    return false;
 
-  return reg_or_0_operand (op, mode);
+  /* We may need to split multiword moves, so make sure that every word
+     is accessible.  */
+  if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+      && !SMALL_OPERAND (INTVAL (x) + GET_MODE_SIZE (mode) - UNITS_PER_WORD))
+    return false;
+
+  return true;
 }
 
-/* Like uns_arith_operand, but when in 64 bit mode also accept a sign
-   extend of a 32 bit register, since the value is known to be already
-   sign extended.  */
+/* Return true if a LO_SUM can address a value of mode MODE when the
+   LO_SUM symbol has type SYMBOL_TYPE.  */
 
-int
-se_uns_arith_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static bool
+mips_valid_lo_sum_p (enum mips_symbol_type symbol_type, enum machine_mode mode)
 {
-  if (TARGET_64BIT
-      && mode == DImode
-      && GET_CODE (op) == SIGN_EXTEND
-      && GET_MODE (op) == DImode
-      && GET_MODE (XEXP (op, 0)) == SImode
-      && register_operand (XEXP (op, 0), SImode))
-    return 1;
+  /* Check that symbols of type SYMBOL_TYPE can be used to access values
+     of mode MODE.  */
+  if (mips_symbol_insns (symbol_type, mode) == 0)
+    return false;
+
+  /* Check that there is a known low-part relocation.  */
+  if (mips_lo_relocs[symbol_type] == NULL)
+    return false;
 
-  return uns_arith_operand (op, mode);
+  /* We may need to split multiword moves, so make sure that each word
+     can be accessed without inducing a carry.  This is mainly needed
+     for o64, which has historically only guaranteed 64-bit alignment
+     for 128-bit types.  */
+  if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+      && GET_MODE_BITSIZE (mode) > GET_MODE_ALIGNMENT (mode))
+    return false;
+
+  return true;
 }
 
-/* Like arith_operand, but when in 64 bit mode also accept a sign
-   extend of a 32 bit register, since the value is known to be already
-   sign extended.  */
+/* Return true if X is a valid address for machine mode MODE.  If it is,
+   fill in INFO appropriately.  STRICT_P is true if REG_OK_STRICT is in
+   effect.  */
 
-int
-se_arith_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static bool
+mips_classify_address (struct mips_address_info *info, rtx x,
+                      enum machine_mode mode, bool strict_p)
 {
-  if (TARGET_64BIT
-      && mode == DImode
-      && GET_CODE (op) == SIGN_EXTEND
-      && GET_MODE (op) == DImode
-      && GET_MODE (XEXP (op, 0)) == SImode
-      && register_operand (XEXP (op, 0), SImode))
-    return 1;
+  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_p);
+
+    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_p)
+             && mips_valid_offset_p (info->offset, mode));
+
+    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_p)
+             && mips_valid_lo_sum_p (info->symbol_type, mode));
+
+    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]);
 
-  return arith_operand (op, mode);
+    default:
+      return false;
+    }
 }
 
-/* Like nonmemory_operand, but when in 64 bit mode also accept a sign
-   extend of a 32 bit register, since the value is known to be already
-   sign extended.  */
+/* Return true if X is a legitimate address for a memory operand of mode
+   MODE.  STRICT_P is true if REG_OK_STRICT is in effect.  */
 
-int
-se_nonmemory_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+bool
+mips_legitimate_address_p (enum machine_mode mode, rtx x, bool strict_p)
 {
-  if (TARGET_64BIT
-      && mode == DImode
-      && GET_CODE (op) == SIGN_EXTEND
-      && GET_MODE (op) == DImode
-      && GET_MODE (XEXP (op, 0)) == SImode
-      && register_operand (XEXP (op, 0), SImode))
-    return 1;
+  struct mips_address_info addr;
 
-  return nonmemory_operand (op, mode);
+  return mips_classify_address (&addr, x, mode, strict_p);
 }
 
-/* Like nonimmediate_operand, but when in 64 bit mode also accept a
-   sign extend of a 32 bit register, since the value is known to be
-   already sign extended.  */
+/* Return true if X is a legitimate $sp-based address for mode MDOE.  */
 
-int
-se_nonimmediate_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+bool
+mips_stack_address_p (rtx x, enum machine_mode mode)
 {
-  if (TARGET_64BIT
-      && mode == DImode
-      && GET_CODE (op) == SIGN_EXTEND
-      && GET_MODE (op) == DImode
-      && GET_MODE (XEXP (op, 0)) == SImode
-      && register_operand (XEXP (op, 0), SImode))
-    return 1;
+  struct mips_address_info addr;
 
-  return nonimmediate_operand (op, mode);
+  return (mips_classify_address (&addr, x, mode, false)
+         && addr.type == ADDRESS_REG
+         && addr.reg == stack_pointer_rtx);
 }
 
-/* Accept any operand that can appear in a mips16 constant table
-   instruction.  We can't use any of the standard operand functions
-   because for these instructions we accept values that are not
-   accepted by LEGITIMATE_CONSTANT, such as arbitrary SYMBOL_REFs.  */
+/* Return true if ADDR matches the pattern for the LWXS load scaled indexed
+   address instruction.  Note that such addresses are not considered
+   legitimate in the GO_IF_LEGITIMATE_ADDRESS sense, because their use
+   is so restricted.  */
 
-int
-consttable_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+static bool
+mips_lwxs_address_p (rtx addr)
 {
-  return CONSTANT_P (op);
+  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;
 }
+\f
+/* Return true if a value at OFFSET bytes from base register BASE can be
+   accessed using an unextended MIPS16 instruction.  MODE is the mode of
+   the value.
 
-/* Return nonzero if we split the address into high and low parts.  */
+   Usually the offset in an unextended instruction is a 5-bit field.
+   The offset is unsigned and shifted left once for LH and SH, twice
+   for LW and SW, and so on.  An exception is LWSP and SWSP, which have
+   an 8-bit immediate field that's shifted left twice.  */
+
+static bool
+mips16_unextended_reference_p (enum machine_mode mode, rtx base,
+                              unsigned HOST_WIDE_INT offset)
+{
+  if (offset % GET_MODE_SIZE (mode) == 0)
+    {
+      if (GET_MODE_SIZE (mode) == 4 && base == stack_pointer_rtx)
+       return offset < 256U * GET_MODE_SIZE (mode);
+      return offset < 32U * GET_MODE_SIZE (mode);
+    }
+  return false;
+}
 
-/* ??? We should also handle reg+array somewhere.  We get four
-   instructions currently, lui %hi/addui %lo/addui reg/lw.  Better is
-   lui %hi/addui reg/lw %lo.  Fixing GO_IF_LEGITIMATE_ADDRESS to accept
-   (plus (reg) (symbol_ref)) doesn't work because the SYMBOL_REF is broken
-   out of the address, then we have 4 instructions to combine.  Perhaps
-   add a 3->2 define_split for combine.  */
+/* Return the number of instructions needed to load or store a value
+   of mode MODE at address 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.
 
-/* ??? We could also split a CONST_INT here if it is a large_int().
-   However, it doesn't seem to be very useful to have %hi(constant).
-   We would be better off by doing the masking ourselves and then putting
-   the explicit high part of the constant in the RTL.  This will give better
-   optimization.  Also, %hi(constant) needs assembler changes to work.
-   There is already a define_split that does this.  */
+   For MIPS16 code, count extended instructions as two instructions.  */
 
 int
-mips_check_split (address, mode)
-     rtx address;
-     enum machine_mode mode;
+mips_address_insns (rtx x, enum machine_mode mode, bool might_split_p)
 {
-  /* ??? This is the same check used in simple_memory_operand.
-     We use it here because LO_SUM is not offsettable.  */
-  if (GET_MODE_SIZE (mode) > (unsigned) UNITS_PER_WORD)
-    return 0;
+  struct mips_address_info addr;
+  int factor;
 
-  if ((GET_CODE (address) == SYMBOL_REF && ! SYMBOL_REF_FLAG (address))
-      || (GET_CODE (address) == CONST
-         && GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF
-         && ! SYMBOL_REF_FLAG (XEXP (XEXP (address, 0), 0)))
-      || GET_CODE (address) == LABEL_REF)
-    return 1;
+  /* BLKmode is used for single unaligned loads and stores and should
+     not count as a multiword mode.  (GET_MODE_SIZE (BLKmode) is pretty
+     meaningless, so we have to single it out as a special case one way
+     or the other.)  */
+  if (mode != BLKmode && might_split_p)
+    factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+  else
+    factor = 1;
 
-  return 0;
-}
+  if (mips_classify_address (&addr, x, mode, false))
+    switch (addr.type)
+      {
+      case ADDRESS_REG:
+       if (TARGET_MIPS16
+           && !mips16_unextended_reference_p (mode, addr.reg,
+                                              UINTVAL (addr.offset)))
+         return factor * 2;
+       return factor;
 
-/* This function is used to implement REG_MODE_OK_FOR_BASE_P.  */
+      case ADDRESS_LO_SUM:
+       return TARGET_MIPS16 ? factor * 2 : factor;
 
-int
-mips_reg_mode_ok_for_base_p (reg, mode, strict)
-     rtx reg;
-     enum machine_mode mode;
-     int strict;
-{
-  return (strict
-         ? REGNO_MODE_OK_FOR_BASE_P (REGNO (reg), mode)
-         : GP_REG_OR_PSEUDO_NONSTRICT_P (REGNO (reg), mode));
+      case ADDRESS_CONST_INT:
+       return factor;
+
+      case ADDRESS_SYMBOLIC:
+       return factor * mips_symbol_insns (addr.symbol_type, mode);
+      }
+  return 0;
 }
 
-/* This function is used to implement GO_IF_LEGITIMATE_ADDRESS.  It
-   returns a nonzero value if XINSN is a legitimate address for a
-   memory operand of the indicated MODE.  STRICT is non-zero if this
-   function is called during reload.  */
+/* Return the number of instructions needed to load constant X.
+   Return 0 if X isn't a valid constant.  */
 
 int
-mips_legitimate_address_p (mode, xinsn, strict)
-     enum machine_mode mode;
-     rtx xinsn;
-     int strict;
+mips_const_insns (rtx x)
 {
-  if (TARGET_DEBUG_B_MODE)
-    {
-      GO_PRINTF2 ("\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n",
-                 strict ? "" : "not ");
-      GO_DEBUG_RTX (xinsn);
-    }
+  struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS];
+  enum mips_symbol_type symbol_type;
+  rtx offset;
 
-  /* Check for constant before stripping off SUBREG, so that we don't
-     accept (subreg (const_int)) which will fail to reload.  */
-  if (CONSTANT_ADDRESS_P (xinsn)
-      && ! (mips_split_addresses && mips_check_split (xinsn, mode))
-      && (! TARGET_MIPS16 || mips16_constant (xinsn, mode, 1, 0)))
-    return 1;
-
-  while (GET_CODE (xinsn) == SUBREG)
-    xinsn = SUBREG_REG (xinsn);
-
-  /* The mips16 can only use the stack pointer as a base register when
-     loading SImode or DImode values.  */
-  if (GET_CODE (xinsn) == REG
-      && mips_reg_mode_ok_for_base_p (xinsn, mode, strict))
-    return 1;
-
-  if (GET_CODE (xinsn) == LO_SUM && mips_split_addresses)
+  switch (GET_CODE (x))
     {
-      register rtx xlow0 = XEXP (xinsn, 0);
-      register rtx xlow1 = XEXP (xinsn, 1);
-
-      while (GET_CODE (xlow0) == SUBREG)
-       xlow0 = SUBREG_REG (xlow0);
-      if (GET_CODE (xlow0) == REG
-         && mips_reg_mode_ok_for_base_p (xlow0, mode, strict)
-         && mips_check_split (xlow1, mode))
-       return 1;
-    }
+    case HIGH:
+      if (!mips_symbolic_constant_p (XEXP (x, 0), SYMBOL_CONTEXT_LEA,
+                                    &symbol_type)
+         || !mips_split_p[symbol_type])
+       return 0;
 
-  if (GET_CODE (xinsn) == PLUS)
-    {
-      register rtx xplus0 = XEXP (xinsn, 0);
-      register rtx xplus1 = XEXP (xinsn, 1);
-      register enum rtx_code code0;
-      register enum rtx_code code1;
+      /* This is simply an LUI for normal mode.  It is an extended
+        LI followed by an extended SLL for MIPS16.  */
+      return TARGET_MIPS16 ? 4 : 1;
 
-      while (GET_CODE (xplus0) == SUBREG)
-       xplus0 = SUBREG_REG (xplus0);
-      code0 = GET_CODE (xplus0);
+    case CONST_INT:
+      if (TARGET_MIPS16)
+       /* Unsigned 8-bit constants can be loaded using an unextended
+          LI instruction.  Unsigned 16-bit constants can be loaded
+          using an extended LI.  Negative constants must be loaded
+          using LI and then negated.  */
+       return (IN_RANGE (INTVAL (x), 0, 255) ? 1
+               : SMALL_OPERAND_UNSIGNED (INTVAL (x)) ? 2
+               : IN_RANGE (-INTVAL (x), 0, 255) ? 2
+               : SMALL_OPERAND_UNSIGNED (-INTVAL (x)) ? 3
+               : 0);
+
+      return mips_build_integer (codes, INTVAL (x));
 
-      while (GET_CODE (xplus1) == SUBREG)
-       xplus1 = SUBREG_REG (xplus1);
-      code1 = GET_CODE (xplus1);
+    case CONST_DOUBLE:
+    case CONST_VECTOR:
+      /* Allow zeros for normal mode, where we can use $0.  */
+      return !TARGET_MIPS16 && x == CONST0_RTX (GET_MODE (x)) ? 1 : 0;
 
-      /* The mips16 can only use the stack pointer as a base register
-         when loading SImode or DImode values.  */
-      if (code0 == REG
-         && mips_reg_mode_ok_for_base_p (xplus0, mode, strict))
-       {
-         if (code1 == CONST_INT && SMALL_INT (xplus1))
-           return 1;
+    case CONST:
+      if (CONST_GP_P (x))
+       return 1;
 
-         /* On the mips16, we represent GP relative offsets in RTL.
-             These are 16 bit signed values, and can serve as register
-             offsets.  */
-         if (TARGET_MIPS16
-             && mips16_gp_offset_p (xplus1))
-           return 1;
-
-         /* For some code sequences, you actually get better code by
-            pretending that the MIPS supports an address mode of a
-            constant address + a register, even though the real
-            machine doesn't support it.  This is because the
-            assembler can use $r1 to load just the high 16 bits, add
-            in the register, and fold the low 16 bits into the memory
-            reference, whereas the compiler generates a 4 instruction
-            sequence.  On the other hand, CSE is not as effective.
-            It would be a win to generate the lui directly, but the
-            MIPS assembler does not have syntax to generate the
-            appropriate relocation.  */
-
-         /* Also accept CONST_INT addresses here, so no else.  */
-         /* Reject combining an embedded PIC text segment reference
-            with a register.  That requires an additional
-            instruction.  */
-          /* ??? Reject combining an address with a register for the MIPS
-            64 bit ABI, because the SGI assembler can not handle this.  */
-         if (!TARGET_DEBUG_A_MODE
-             && (mips_abi == ABI_32
-                 || mips_abi == ABI_O64
-                 || mips_abi == ABI_EABI)
-             && CONSTANT_ADDRESS_P (xplus1)
-             && ! mips_split_addresses
-             && (!TARGET_EMBEDDED_PIC
-                 || code1 != CONST
-                 || GET_CODE (XEXP (xplus1, 0)) != MINUS)
-             /* When assembling for machines with 64 bit registers,
-                the assembler will sign-extend the constant "foo"
-                in "la x, foo(x)" yielding the wrong result for:
-                (set (blah:DI) (plus x y)).  */
-             && (!TARGET_64BIT
-                 || (code1 == CONST_INT
-                     && trunc_int_for_mode (INTVAL (xplus1),
-                                            SImode) == INTVAL (xplus1)))
-             && !TARGET_MIPS16)
-           return 1;
+      /* See if we can refer to X directly.  */
+      if (mips_symbolic_constant_p (x, SYMBOL_CONTEXT_LEA, &symbol_type))
+       return mips_symbol_insns (symbol_type, MAX_MACHINE_MODE);
+
+      /* Otherwise try splitting the constant into a base and offset.
+        If the offset is a 16-bit value, we can load the base address
+        into a register and then use (D)ADDIU to add in the offset.
+        If the offset is larger, we can load the base and offset
+        into separate registers and add them together with (D)ADDU.
+        However, the latter is only possible before reload; during
+        and after reload, we must have the option of forcing the
+        constant into the pool instead.  */
+      split_const (x, &x, &offset);
+      if (offset != 0)
+       {
+         int n = mips_const_insns (x);
+         if (n != 0)
+           {
+             if (SMALL_INT (offset))
+               return n + 1;
+             else if (!targetm.cannot_force_const_mem (x))
+               return n + 1 + mips_build_integer (codes, INTVAL (offset));
+           }
        }
-    }
+      return 0;
 
-  if (TARGET_DEBUG_B_MODE)
-    GO_PRINTF ("Not a legitimate address\n");
+    case SYMBOL_REF:
+    case LABEL_REF:
+      return mips_symbol_insns (mips_classify_symbol (x, SYMBOL_CONTEXT_LEA),
+                               MAX_MACHINE_MODE);
 
-  /* The address was not legitimate.  */
-  return 0;
+    default:
+      return 0;
+    }
 }
 
-\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
-   there aren't nearly enough letters available.  */
-
-static int
-m16_check_op (op, low, high, mask)
-     rtx op;
-     int low;
-     int high;
-     int mask;
-{
-  return (GET_CODE (op) == CONST_INT
-         && INTVAL (op) >= low
-         && INTVAL (op) <= high
-         && (INTVAL (op) & mask) == 0);
-}
+/* X is a doubleword constant that can be handled by splitting it into
+   two words and loading each word separately.  Return the number of
+   instructions required to do this.  */
 
 int
-m16_uimm3_b (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+mips_split_const_insns (rtx x)
 {
-  return m16_check_op (op, 0x1, 0x8, 0);
-}
+  unsigned int low, high;
 
-int
-m16_simm4_1 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return m16_check_op (op, - 0x8, 0x7, 0);
+  low = mips_const_insns (mips_subword (x, false));
+  high = mips_const_insns (mips_subword (x, true));
+  gcc_assert (low > 0 && high > 0);
+  return low + high;
 }
 
-int
-m16_nsimm4_1 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return m16_check_op (op, - 0x7, 0x8, 0);
-}
+/* Return the number of instructions needed to implement INSN,
+   given that it loads from or stores to MEM.  Count extended
+   MIPS16 instructions as two instructions.  */
 
 int
-m16_simm5_1 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+mips_load_store_insns (rtx mem, rtx insn)
 {
-  return m16_check_op (op, - 0x10, 0xf, 0);
-}
+  enum machine_mode mode;
+  bool might_split_p;
+  rtx set;
 
-int
-m16_nsimm5_1 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return m16_check_op (op, - 0xf, 0x10, 0);
-}
+  gcc_assert (MEM_P (mem));
+  mode = GET_MODE (mem);
 
-int
-m16_uimm5_4 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return m16_check_op (op, (- 0x10) << 2, 0xf << 2, 3);
-}
+  /* Try to prove that INSN does not need to be split.  */
+  might_split_p = true;
+  if (GET_MODE_BITSIZE (mode) == 64)
+    {
+      set = single_set (insn);
+      if (set && !mips_split_64bit_move_p (SET_DEST (set), SET_SRC (set)))
+       might_split_p = false;
+    }
 
-int
-m16_nuimm5_4 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return m16_check_op (op, (- 0xf) << 2, 0x10 << 2, 3);
+  return mips_address_insns (XEXP (mem, 0), mode, might_split_p);
 }
 
-int
-m16_simm8_1 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return m16_check_op (op, - 0x80, 0x7f, 0);
-}
+/* Return the number of instructions needed for an integer division.  */
 
 int
-m16_nsimm8_1 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+mips_idiv_insns (void)
 {
-  return m16_check_op (op, - 0x7f, 0x80, 0);
-}
+  int count;
 
-int
-m16_uimm8_1 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return m16_check_op (op, 0x0, 0xff, 0);
-}
+  count = 1;
+  if (TARGET_CHECK_ZERO_DIV)
+    {
+      if (GENERATE_DIVIDE_TRAPS)
+        count++;
+      else
+        count += 2;
+    }
 
-int
-m16_nuimm8_1 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return m16_check_op (op, - 0xff, 0x0, 0);
+  if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
+    count++;
+  return count;
 }
+\f
+/* Emit a move from SRC to DEST.  Assume that the move expanders can
+   handle all moves if !can_create_pseudo_p ().  The distinction is
+   important because, unlike emit_move_insn, the move expanders know
+   how to force Pmode objects into the constant pool even when the
+   constant pool address is not itself legitimate.  */
 
-int
-m16_uimm8_m1_1 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+rtx
+mips_emit_move (rtx dest, rtx src)
 {
-  return m16_check_op (op, - 0x1, 0xfe, 0);
+  return (can_create_pseudo_p ()
+         ? emit_move_insn (dest, src)
+         : emit_move_insn_1 (dest, src));
 }
 
-int
-m16_uimm8_4 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return m16_check_op (op, 0x0, 0xff << 2, 3);
-}
+/* Emit an instruction of the form (set TARGET (CODE OP0)).  */
 
-int
-m16_nuimm8_4 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+static void
+mips_emit_unary (enum rtx_code code, rtx target, rtx op0)
 {
-  return m16_check_op (op, (- 0xff) << 2, 0x0, 3);
+  emit_insn (gen_rtx_SET (VOIDmode, target,
+                         gen_rtx_fmt_e (code, GET_MODE (op0), op0)));
 }
 
-int
-m16_simm8_8 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+/* Compute (CODE OP0) and store the result in a new register of mode MODE.
+   Return that new register.  */
+
+static rtx
+mips_force_unary (enum machine_mode mode, enum rtx_code code, rtx op0)
 {
-  return m16_check_op (op, (- 0x80) << 3, 0x7f << 3, 7);
+  rtx reg;
+
+  reg = gen_reg_rtx (mode);
+  mips_emit_unary (code, reg, op0);
+  return reg;
 }
 
-int
-m16_nsimm8_8 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+/* 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)
 {
-  return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7);
+  emit_insn (gen_rtx_SET (VOIDmode, target,
+                         gen_rtx_fmt_ee (code, GET_MODE (target), op0, op1)));
 }
 
-/* References to the string table on the mips16 only use a small
-   offset if the function is small.  See the comment in the SYMBOL_REF
-   case in simple_memory_operand.  We can't check for LABEL_REF here,
-   because the offset is always large if the label is before the
-   referencing instruction.  */
+/* Compute (CODE OP0 OP1) and store the result in a new register
+   of mode MODE.  Return that new register.  */
 
-int
-m16_usym8_4 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+static rtx
+mips_force_binary (enum machine_mode mode, enum rtx_code code, rtx op0, rtx op1)
 {
-  if (GET_CODE (op) == SYMBOL_REF
-      && SYMBOL_REF_FLAG (op)
-      && current_frame_info.insns_len > 0
-      && XSTR (op, 0)[0] == '*'
-      && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX,
-                 sizeof LOCAL_LABEL_PREFIX - 1) == 0
-      && (current_frame_info.insns_len + get_pool_size () + mips_string_length
-         < 4 * 0x100))
-    {
-      struct string_constant *l;
-
-      /* Make sure this symbol is on thelist of string constants to be
-         output for this function.  It is possible that it has already
-         been output, in which case this requires a large offset.  */
-      for (l = string_constants; l != NULL; l = l->next)
-       if (strcmp (l->label, XSTR (op, 0)) == 0)
-         return 1;
-    }
+  rtx reg;
 
-  return 0;
+  reg = gen_reg_rtx (mode);
+  mips_emit_binary (code, reg, op0, op1);
+  return reg;
 }
 
-int
-m16_usym5_4 (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+/* Copy VALUE to a register and return that register.  If new pseudos
+   are allowed, copy it into a new register, otherwise use DEST.  */
+
+static rtx
+mips_force_temporary (rtx dest, rtx value)
 {
-  if (GET_CODE (op) == SYMBOL_REF
-      && SYMBOL_REF_FLAG (op)
-      && current_frame_info.insns_len > 0
-      && XSTR (op, 0)[0] == '*'
-      && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX,
-                 sizeof LOCAL_LABEL_PREFIX - 1) == 0
-      && (current_frame_info.insns_len + get_pool_size () + mips_string_length
-         < 4 * 0x20))
+  if (can_create_pseudo_p ())
+    return force_reg (Pmode, value);
+  else
     {
-      struct string_constant *l;
-
-      /* Make sure this symbol is on thelist of string constants to be
-         output for this function.  It is possible that it has already
-         been output, in which case this requires a large offset.  */
-      for (l = string_constants; l != NULL; l = l->next)
-       if (strcmp (l->label, XSTR (op, 0)) == 0)
-         return 1;
+      mips_emit_move (dest, value);
+      return dest;
     }
-
-  return 0;
 }
-\f
-/* Returns an operand string for the given instruction's delay slot,
-   after updating filled delay slot statistics.
-
-   We assume that operands[0] is the target register that is set.
 
-   In order to check the next insn, most of this functionality is moved
-   to FINAL_PRESCAN_INSN, and we just set the global variables that
-   it needs.  */
+/* Emit a call sequence with call pattern PATTERN and return the call
+   instruction itself (which is not necessarily the last instruction
+   emitted).  ORIG_ADDR is the original, unlegitimized address,
+   ADDR is the legitimized form, and LAZY_P is true if the call
+   address is lazily-bound.  */
 
-/* ??? This function no longer does anything useful, because final_prescan_insn
-   now will never emit a nop.  */
-
-const char *
-mips_fill_delay_slot (ret, type, operands, cur_insn)
-     const char *ret;          /* normal string to return */
-     enum delay_type type;     /* type of delay */
-     rtx operands[];           /* operands to use */
-     rtx cur_insn;             /* current insn */
+static rtx
+mips_emit_call_insn (rtx pattern, rtx orig_addr, rtx addr, bool lazy_p)
 {
-  register rtx set_reg;
-  register enum machine_mode mode;
-  register rtx next_insn = cur_insn ? NEXT_INSN (cur_insn) : NULL_RTX;
-  register int num_nops;
+  rtx insn, reg;
 
-  if (type == DELAY_LOAD || type == DELAY_FCMP)
-    num_nops = 1;
+  insn = emit_call_insn (pattern);
 
-  else if (type == DELAY_HILO)
-    num_nops = 2;
+  if (TARGET_MIPS16 && mips_use_pic_fn_addr_reg_p (orig_addr))
+    {
+      /* MIPS16 JALRs only take MIPS16 registers.  If the target
+        function requires $25 to be valid on entry, we must copy it
+        there separately.  The move instruction can be put in the
+        call's delay slot.  */
+      reg = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+      emit_insn_before (gen_move_insn (reg, addr), insn);
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg);
+    }
 
-  else
-    num_nops = 0;
-
-  /* Make sure that we don't put nop's after labels.  */
-  next_insn = NEXT_INSN (cur_insn);
-  while (next_insn != 0 && GET_CODE (next_insn) == NOTE)
-    next_insn = NEXT_INSN (next_insn);
-
-  dslots_load_total += num_nops;
-  if (TARGET_DEBUG_F_MODE
-      || !optimize
-      || type == DELAY_NONE
-      || operands == 0
-      || cur_insn == 0
-      || next_insn == 0
-      || GET_CODE (next_insn) == CODE_LABEL
-      || (set_reg = operands[0]) == 0)
-    {
-      dslots_number_nops = 0;
-      mips_load_reg  = 0;
-      mips_load_reg2 = 0;
-      mips_load_reg3 = 0;
-      mips_load_reg4 = 0;
-      return ret;
-    }
-
-  set_reg = operands[0];
-  if (set_reg == 0)
-    return ret;
-
-  while (GET_CODE (set_reg) == SUBREG)
-    set_reg = SUBREG_REG (set_reg);
-
-  mode = GET_MODE (set_reg);
-  dslots_number_nops = num_nops;
-  mips_load_reg = set_reg;
-  if (GET_MODE_SIZE (mode)
-      > (unsigned) (FP_REG_P (REGNO (set_reg)) ? UNITS_PER_FPREG : UNITS_PER_WORD))
-    mips_load_reg2 = gen_rtx_REG (SImode, REGNO (set_reg) + 1);
-  else
-    mips_load_reg2 = 0;
+  if (lazy_p)
+    /* Lazy-binding stubs require $gp to be valid on entry.  */
+    use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
 
-  if (type == DELAY_HILO)
+  if (TARGET_USE_GOT)
     {
-      mips_load_reg3 = gen_rtx_REG (SImode, MD_REG_FIRST);
-      mips_load_reg4 = gen_rtx_REG (SImode, MD_REG_FIRST+1);
+      /* See the comment above load_call<mode> for details.  */
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
+              gen_rtx_REG (Pmode, GOT_VERSION_REGNUM));
+      emit_insn (gen_update_got_version ());
     }
-  else
-    {
-      mips_load_reg3 = 0;
-      mips_load_reg4 = 0;
-    }
-
-  return ret;
+  return insn;
 }
-
 \f
-/* Determine whether a memory reference takes one (based off of the GP
-   pointer), two (normal), or three (label + reg) instructions, and bump the
-   appropriate counter for -mstats.  */
+/* Wrap symbol or label BASE in an UNSPEC address of type SYMBOL_TYPE,
+   then add CONST_INT OFFSET to the result.  */
 
-void
-mips_count_memory_refs (op, num)
-     rtx op;
-     int num;
+static rtx
+mips_unspec_address_offset (rtx base, rtx offset,
+                           enum mips_symbol_type symbol_type)
 {
-  int additional = 0;
-  int n_words = 0;
-  rtx addr, plus0, plus1;
-  enum rtx_code code0, code1;
-  int looping;
+  base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
+                        UNSPEC_ADDRESS_FIRST + symbol_type);
+  if (offset != const0_rtx)
+    base = gen_rtx_PLUS (Pmode, base, offset);
+  return gen_rtx_CONST (Pmode, base);
+}
 
-  if (TARGET_DEBUG_B_MODE)
-    {
-      fprintf (stderr, "\n========== mips_count_memory_refs:\n");
-      debug_rtx (op);
-    }
+/* Return an UNSPEC address with underlying address ADDRESS and symbol
+   type SYMBOL_TYPE.  */
 
-  /* Skip MEM if passed, otherwise handle movsi of address.  */
-  addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0);
+rtx
+mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
+{
+  rtx base, offset;
 
-  /* Loop, going through the address RTL.  */
-  do
-    {
-      looping = FALSE;
-      switch (GET_CODE (addr))
-       {
-       case REG:
-       case CONST_INT:
-       case LO_SUM:
-         break;
+  split_const (address, &base, &offset);
+  return mips_unspec_address_offset (base, offset, symbol_type);
+}
 
-       case PLUS:
-         plus0 = XEXP (addr, 0);
-         plus1 = XEXP (addr, 1);
-         code0 = GET_CODE (plus0);
-         code1 = GET_CODE (plus1);
+/* If mips_unspec_address (ADDR, SYMBOL_TYPE) is a 32-bit value, add the
+   high part to BASE and return the result.  Just return BASE otherwise.
+   TEMP is as for mips_force_temporary.
 
-         if (code0 == REG)
-           {
-             additional++;
-             addr = plus1;
-             looping = 1;
-             continue;
-           }
+   The returned expression can be used as the first operand to a LO_SUM.  */
 
-         if (code0 == CONST_INT)
-           {
-             addr = plus1;
-             looping = 1;
-             continue;
-           }
+static rtx
+mips_unspec_offset_high (rtx temp, rtx base, rtx addr,
+                        enum mips_symbol_type symbol_type)
+{
+  if (mips_split_p[symbol_type])
+    {
+      addr = gen_rtx_HIGH (Pmode, mips_unspec_address (addr, symbol_type));
+      addr = mips_force_temporary (temp, addr);
+      base = mips_force_temporary (temp, gen_rtx_PLUS (Pmode, addr, base));
+    }
+  return base;
+}
+\f
+/* Return an instruction that copies $gp into register REG.  We want
+   GCC to treat the register's value as constant, so that its value
+   can be rematerialized on demand.  */
 
-         if (code1 == REG)
-           {
-             additional++;
-             addr = plus0;
-             looping = 1;
-             continue;
-           }
+static rtx
+gen_load_const_gp (rtx reg)
+{
+  return (Pmode == SImode
+         ? gen_load_const_gp_si (reg)
+         : gen_load_const_gp_di (reg));
+}
 
-         if (code1 == CONST_INT)
-           {
-             addr = plus0;
-             looping = 1;
-             continue;
-           }
+/* Return a pseudo register that contains the value of $gp throughout
+   the current function.  Such registers are needed by MIPS16 functions,
+   for which $gp itself is not a valid base register or addition operand.  */
 
-         if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST)
-           {
-             addr = plus0;
-             looping = 1;
-             continue;
-           }
+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);
 
-         if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST)
-           {
-             addr = plus1;
-             looping = 1;
-             continue;
-           }
+  /* Don't emit an instruction to 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;
 
-         break;
+      push_topmost_sequence ();
 
-       case LABEL_REF:
-         n_words = 2;          /* always 2 words */
-         break;
+      scan = get_insns ();
+      while (NEXT_INSN (scan) && !INSN_P (NEXT_INSN (scan)))
+       scan = NEXT_INSN (scan);
 
-       case CONST:
-         addr = XEXP (addr, 0);
-         looping = 1;
-         continue;
+      insn = gen_load_const_gp (cfun->machine->mips16_gp_pseudo_rtx);
+      emit_insn_after (insn, scan);
 
-       case SYMBOL_REF:
-         n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2;
-         break;
+      pop_topmost_sequence ();
 
-       default:
-         break;
-       }
+      cfun->machine->initialized_mips16_gp_pseudo_p = true;
     }
-  while (looping);
-
-  if (n_words == 0)
-    return;
-
-  n_words += additional;
-  if (n_words > 3)
-    n_words = 3;
 
-  num_refs[n_words-1] += num;
+  return cfun->machine->mips16_gp_pseudo_rtx;
 }
 
-\f
-/* Return RTL for the offset from the current function to the argument.
-
-   ??? Which argument is this?  */
+/* Return a base register that holds pic_offset_table_rtx.
+   TEMP, if nonnull, is a scratch Pmode base register.  */
 
 rtx
-embedded_pic_offset (x)
-     rtx x;
-{
-  if (embedded_pic_fnaddr_rtx == NULL)
-    {
-      rtx seq;
-
-      embedded_pic_fnaddr_rtx = gen_reg_rtx (Pmode);
-
-      /* Output code at function start to initialize the pseudo-reg.  */
-      /* ??? We used to do this in FINALIZE_PIC, but that does not work for
-        inline functions, because it is called after RTL for the function
-        has been copied.  The pseudo-reg in embedded_pic_fnaddr_rtx however
-        does not get copied, and ends up not matching the rest of the RTL.
-        This solution works, but means that we get unnecessary code to
-        initialize this value every time a function is inlined into another
-        function.  */
-      start_sequence ();
-      emit_insn (gen_get_fnaddr (embedded_pic_fnaddr_rtx,
-                                XEXP (DECL_RTL (current_function_decl), 0)));
-      seq = gen_sequence ();
-      end_sequence ();
-      push_topmost_sequence ();
-      emit_insn_after (seq, get_insns ());
-      pop_topmost_sequence ();
-    }
+mips_pic_base_register (rtx temp)
+{
+  if (!TARGET_MIPS16)
+    return pic_offset_table_rtx;
 
-  return
-    gen_rtx_CONST (Pmode,
-                  gen_rtx_MINUS (Pmode, x,
-                                 XEXP (DECL_RTL (current_function_decl), 0)));
-}
+  if (can_create_pseudo_p ())
+    return mips16_gp_pseudo_reg ();
 
-/* Return the appropriate instructions to move one operand to another.  */
+  if (TARGET_USE_GOT)
+    /* The first post-reload split exposes all references to $gp
+       (both uses and definitions).  All references must remain
+       explicit after that point.
 
-const char *
-mips_move_1word (operands, insn, unsignedp)
-     rtx operands[];
-     rtx insn;
-     int unsignedp;
-{
-  const char *ret = 0;
-  rtx op0 = operands[0];
-  rtx op1 = operands[1];
-  enum rtx_code code0 = GET_CODE (op0);
-  enum rtx_code code1 = GET_CODE (op1);
-  enum machine_mode mode = GET_MODE (op0);
-  int subreg_offset0 = 0;
-  int subreg_offset1 = 0;
-  enum delay_type delay = DELAY_NONE;
-
-  while (code0 == SUBREG)
-    {
-      subreg_offset0 += subreg_regno_offset (REGNO (SUBREG_REG (op0)),
-                                            GET_MODE (SUBREG_REG (op0)),
-                                            SUBREG_BYTE (op0),
-                                            GET_MODE (op0));
-      op0 = SUBREG_REG (op0);
-      code0 = GET_CODE (op0);
-    }
-
-  while (code1 == SUBREG)
-    {
-      subreg_offset1 += subreg_regno_offset (REGNO (SUBREG_REG (op1)),
-                                            GET_MODE (SUBREG_REG (op1)),
-                                            SUBREG_BYTE (op1),
-                                            GET_MODE (op1));
-      op1 = SUBREG_REG (op1);
-      code1 = GET_CODE (op1);
-    }
-
-  /* For our purposes, a condition code mode is the same as SImode.  */
-  if (mode == CCmode)
-    mode = SImode;
+       It is safe to introduce uses of $gp at any time, so for
+       simplicity, we do that before the split too.  */
+    mips_emit_move (temp, pic_offset_table_rtx);
+  else
+    emit_insn (gen_load_const_gp (temp));
+  return temp;
+}
 
-  if (code0 == REG)
-    {
-      int regno0 = REGNO (op0) + subreg_offset0;
+/* Create and return a GOT reference of type TYPE for address ADDR.
+   TEMP, if nonnull, is a scratch Pmode base register.  */
 
-      if (code1 == REG)
-       {
-         int regno1 = REGNO (op1) + subreg_offset1;
+rtx
+mips_got_load (rtx temp, rtx addr, enum mips_symbol_type type)
+{
+  rtx base, high, lo_sum_symbol;
 
-         /* Just in case, don't do anything for assigning a register
-            to itself, unless we are filling a delay slot.  */
-         if (regno0 == regno1 && set_nomacro == 0)
-           ret = "";
+  base = mips_pic_base_register (temp);
 
-         else if (GP_REG_P (regno0))
-           {
-             if (GP_REG_P (regno1))
-               ret = "move\t%0,%1";
+  /* If we used the temporary register to load $gp, we can't use
+     it for the high part as well.  */
+  if (temp != NULL && reg_overlap_mentioned_p (base, temp))
+    temp = NULL;
 
-             else if (MD_REG_P (regno1))
-               {
-                 delay = DELAY_HILO;
-                 if (regno1 != HILO_REGNUM)
-                   ret = "mf%1\t%0";
-                 else
-                   ret = "mflo\t%0";
-               }
+  high = mips_unspec_offset_high (temp, base, addr, type);
+  lo_sum_symbol = mips_unspec_address (addr, type);
 
-             else if (ST_REG_P (regno1) && ISA_HAS_8CC)
-               ret = "li\t%0,1\n\tmovf\t%0,%.,%1";
+  if (type == SYMBOL_GOTOFF_CALL)
+    return (Pmode == SImode
+           ? gen_unspec_callsi (high, lo_sum_symbol)
+           : gen_unspec_calldi (high, lo_sum_symbol));
+  else
+    return (Pmode == SImode
+           ? gen_unspec_gotsi (high, lo_sum_symbol)
+           : gen_unspec_gotdi (high, lo_sum_symbol));
+}
 
-             else
-               {
-                 delay = DELAY_LOAD;
-                 if (FP_REG_P (regno1))
-                   ret = "mfc1\t%0,%1";
+/* If MODE is MAX_MACHINE_MODE, ADDR appears as a move operand, otherwise
+   it appears in a MEM of that mode.  Return true if ADDR is a legitimate
+   constant in that context and can be split into high and low parts.
+   If so, and if LOW_OUT is nonnull, emit the high part and store the
+   low part in *LOW_OUT.  Leave *LOW_OUT unchanged otherwise.
 
-                 else if (regno1 == FPSW_REGNUM && ! ISA_HAS_8CC)
-                   ret = "cfc1\t%0,$31";
-               }
-           }
+   TEMP is as for mips_force_temporary and is used to load the high
+   part into a register.
 
-         else if (FP_REG_P (regno0))
-           {
-             if (GP_REG_P (regno1))
-               {
-                 delay = DELAY_LOAD;
-                 ret = "mtc1\t%1,%0";
-               }
+   When MODE is MAX_MACHINE_MODE, the low part is guaranteed to be
+   a legitimize SET_SRC for an .md pattern, otherwise the low part
+   is guaranteed to be a legitimate address for mode MODE.  */
 
-             if (FP_REG_P (regno1))
-               ret = "mov.s\t%0,%1";
-           }
+bool
+mips_split_symbol (rtx temp, rtx addr, enum machine_mode mode, rtx *low_out)
+{
+  enum mips_symbol_context context;
+  enum mips_symbol_type symbol_type;
+  rtx high;
 
-         else if (MD_REG_P (regno0))
-           {
-             if (GP_REG_P (regno1))
-               {
-                 delay = DELAY_HILO;
-                 if (regno0 != HILO_REGNUM && ! TARGET_MIPS16)
-                   ret = "mt%0\t%1";
-               }
-           }
+  context = (mode == MAX_MACHINE_MODE
+            ? SYMBOL_CONTEXT_LEA
+            : SYMBOL_CONTEXT_MEM);
+  if (GET_CODE (addr) == HIGH && context == SYMBOL_CONTEXT_LEA)
+    {
+      addr = XEXP (addr, 0);
+      if (mips_symbolic_constant_p (addr, context, &symbol_type)
+         && mips_symbol_insns (symbol_type, mode) > 0
+         && mips_split_hi_p[symbol_type])
+       {
+         if (low_out)
+           switch (symbol_type)
+             {
+             case SYMBOL_GOT_PAGE_OFST:
+               /* The high part of a page/ofst pair is loaded from the GOT.  */
+               *low_out = mips_got_load (temp, addr, SYMBOL_GOTOFF_PAGE);
+               break;
 
-         else if (regno0 == FPSW_REGNUM && ! ISA_HAS_8CC)
-           {
-             if (GP_REG_P (regno1))
-               {
-                 delay = DELAY_LOAD;
-                 ret = "ctc1\t%0,$31";
-               }
-           }
+             default:
+               gcc_unreachable ();
+             }
+         return true;
        }
-
-      else if (code1 == MEM)
+    }
+  else
+    {
+      if (mips_symbolic_constant_p (addr, context, &symbol_type)
+         && mips_symbol_insns (symbol_type, mode) > 0
+         && mips_split_p[symbol_type])
        {
-         delay = DELAY_LOAD;
-
-         if (TARGET_STATS)
-           mips_count_memory_refs (op1, 1);
-
-         if (GP_REG_P (regno0))
-           {
-             /* For loads, use the mode of the memory item, instead of the
-                target, so zero/sign extend can use this code as well.  */
-             switch (GET_MODE (op1))
-               {
-               default:
-                 break;
-               case SFmode:
-                 ret = "lw\t%0,%1";
-                 break;
-               case SImode:
-               case CCmode:
-                 ret = ((unsignedp && TARGET_64BIT)
-                        ? "lwu\t%0,%1"
-                        : "lw\t%0,%1");
-                 break;
-               case HImode:
-                 ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1";
-                 break;
-               case QImode:
-                 ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1";
-                 break;
-               }
-           }
+         if (low_out)
+           switch (symbol_type)
+             {
+             case SYMBOL_GOT_DISP:
+               /* SYMBOL_GOT_DISP symbols are loaded from the GOT.  */
+               *low_out = mips_got_load (temp, addr, SYMBOL_GOTOFF_DISP);
+               break;
+
+             case SYMBOL_GP_RELATIVE:
+               high = mips_pic_base_register (temp);
+               *low_out = gen_rtx_LO_SUM (Pmode, high, addr);
+               break;
+
+             default:
+               high = gen_rtx_HIGH (Pmode, copy_rtx (addr));
+               high = mips_force_temporary (temp, high);
+               *low_out = gen_rtx_LO_SUM (Pmode, high, addr);
+               break;
+             }
+         return true;
+       }
+    }
+  return false;
+}
 
-         else if (FP_REG_P (regno0) && (mode == SImode || mode == SFmode))
-           ret = "l.s\t%0,%1";
+/* Return a legitimate address for REG + OFFSET.  TEMP is as for
+   mips_force_temporary; it is only needed when OFFSET is not a
+   SMALL_OPERAND.  */
 
-         if (ret != (char *)0 && MEM_VOLATILE_P (op1))
-           {
-             size_t i = strlen (ret);
-             if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
-               abort ();
+static rtx
+mips_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset)
+{
+  if (!SMALL_OPERAND (offset))
+    {
+      rtx high;
 
-             sprintf (volatile_buffer, "%%{%s%%}", ret);
-             ret = volatile_buffer;
-           }
+      if (TARGET_MIPS16)
+       {
+         /* Load the full offset into a register so that we can use
+            an unextended instruction for the address itself.  */
+         high = GEN_INT (offset);
+         offset = 0;
        }
-
-      else if (code1 == CONST_INT
-              || (code1 == CONST_DOUBLE
-                  && GET_MODE (op1) == VOIDmode))
+      else
        {
-         if (code1 == CONST_DOUBLE)
-           {
-             /* This can happen when storing constants into long long
-                 bitfields.  Just store the least significant word of
-                 the value.  */
-             operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
-           }
+         /* Leave OFFSET as a 16-bit offset and put the excess in HIGH.  */
+         high = GEN_INT (CONST_HIGH_PART (offset));
+         offset = CONST_LOW_PART (offset);
+       }
+      high = mips_force_temporary (temp, high);
+      reg = mips_force_temporary (temp, gen_rtx_PLUS (Pmode, high, reg));
+    }
+  return plus_constant (reg, offset);
+}
+\f
+/* The __tls_get_attr symbol.  */
+static GTY(()) rtx mips_tls_symbol;
 
-         if (INTVAL (op1) == 0 && ! TARGET_MIPS16)
-           {
-             if (GP_REG_P (regno0))
-               ret = "move\t%0,%z1";
+/* Return an instruction sequence that calls __tls_get_addr.  SYM is
+   the TLS symbol we are referencing and TYPE is the symbol type to use
+   (either global dynamic or local dynamic).  V0 is an RTX for the
+   return value location.  */
 
-             else if (FP_REG_P (regno0))
-               {
-                 delay = DELAY_LOAD;
-                 ret = "mtc1\t%z1,%0";
-               }
+static rtx
+mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0)
+{
+  rtx insn, loc, a0;
 
-             else if (MD_REG_P (regno0))
-               {
-                 delay = DELAY_HILO;
-                 ret = "mt%0\t%.";
-               }
-           }
+  a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST);
 
-         else if (GP_REG_P (regno0))
-           {
-             /* Don't use X format, because that will give out of
-                range numbers for 64 bit host and 32 bit target.  */
-             if (! TARGET_MIPS16)
-               ret = "li\t%0,%1\t\t\t# %X1";
-             else
-               {
-                 if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
-                   ret = "li\t%0,%1";
-                 else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
-                   ret = "li\t%0,%n1\n\tneg\t%0";
-               }
-           }
-       }
+  if (!mips_tls_symbol)
+    mips_tls_symbol = init_one_libfunc ("__tls_get_addr");
 
-      else if (code1 == CONST_DOUBLE && mode == SFmode)
-       {
-         if (op1 == CONST0_RTX (SFmode))
-           {
-             if (GP_REG_P (regno0))
-               ret = "move\t%0,%.";
+  loc = mips_unspec_address (sym, type);
 
-             else if (FP_REG_P (regno0))
-               {
-                 delay = DELAY_LOAD;
-                 ret = "mtc1\t%.,%0";
-               }
-           }
+  start_sequence ();
 
-         else
-           {
-             delay = DELAY_LOAD;
-             ret = "li.s\t%0,%1";
-           }
-       }
+  emit_insn (gen_rtx_SET (Pmode, a0,
+                         gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc)));
+  insn = mips_expand_call (MIPS_CALL_NORMAL, v0, mips_tls_symbol,
+                          const0_rtx, NULL_RTX, false);
+  RTL_CONST_CALL_P (insn) = 1;
+  use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0);
+  insn = get_insns ();
 
-      else if (code1 == LABEL_REF)
-       {
-         if (TARGET_STATS)
-           mips_count_memory_refs (op1, 1);
+  end_sequence ();
 
-         ret = "la\t%0,%a1";
-       }
+  return insn;
+}
 
-      else if (code1 == SYMBOL_REF || code1 == CONST)
-       {
-         if (HALF_PIC_P () && CONSTANT_P (op1) && HALF_PIC_ADDRESS_P (op1))
-           {
-             rtx offset = const0_rtx;
+/* Return a pseudo register that contains the current thread pointer.  */
 
-             if (GET_CODE (op1) == CONST)
-               op1 = eliminate_constant_term (XEXP (op1, 0), &offset);
+static rtx
+mips_get_tp (void)
+{
+  rtx tp;
 
-             if (GET_CODE (op1) == SYMBOL_REF)
-               {
-                 operands[2] = HALF_PIC_PTR (op1);
+  tp = gen_reg_rtx (Pmode);
+  if (Pmode == DImode)
+    emit_insn (gen_tls_get_tp_di (tp));
+  else
+    emit_insn (gen_tls_get_tp_si (tp));
+  return tp;
+}
 
-                 if (TARGET_STATS)
-                   mips_count_memory_refs (operands[2], 1);
+/* Generate the code to access LOC, a thread-local SYMBOL_REF, and return
+   its address.  The return value will be both a valid address and a valid
+   SET_SRC (either a REG or a LO_SUM).  */
 
-                 if (INTVAL (offset) == 0)
-                   {
-                     delay = DELAY_LOAD;
-                     ret = (unsignedp && TARGET_64BIT
-                            ? "lwu\t%0,%2"
-                            : "lw\t%0,%2");
-                   }
-                 else
-                   {
-                     dslots_load_total++;
-                     operands[3] = offset;
-                     if (unsignedp && TARGET_64BIT)
-                       ret = (SMALL_INT (offset)
-                              ? "lwu\t%0,%2%#\n\tadd\t%0,%0,%3"
-                              : "lwu\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]");
-                     else
-                       ret = (SMALL_INT (offset)
-                              ? "lw\t%0,%2%#\n\tadd\t%0,%0,%3"
-                              : "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]");
-                   }
-               }
-           }
-         else if (TARGET_MIPS16
-                  && code1 == CONST
-                  && GET_CODE (XEXP (op1, 0)) == REG
-                  && REGNO (XEXP (op1, 0)) == GP_REG_FIRST + 28)
-           {
-             /* This case arises on the mips16; see
-                 mips16_gp_pseudo_reg.  */
-             ret = "move\t%0,%+";
-           }
-         else if (TARGET_MIPS16
-                  && code1 == SYMBOL_REF
-                  && SYMBOL_REF_FLAG (op1)
-                  && (XSTR (op1, 0)[0] != '*'
-                      || strncmp (XSTR (op1, 0) + 1,
-                                  LOCAL_LABEL_PREFIX,
-                                  sizeof LOCAL_LABEL_PREFIX - 1) != 0))
-           {
-             /* This can occur when reloading the address of a GP
-                 relative symbol on the mips16.  */
-             ret = "move\t%0,%+\n\taddu\t%0,%%gprel(%a1)";
-           }
-         else
-           {
-             if (TARGET_STATS)
-               mips_count_memory_refs (op1, 1);
+static rtx
+mips_legitimize_tls_address (rtx loc)
+{
+  rtx dest, insn, v0, tp, tmp1, tmp2, eqv;
+  enum tls_model model;
 
-             ret = "la\t%0,%a1";
-           }
-       }
+  if (TARGET_MIPS16)
+    {
+      sorry ("MIPS16 TLS");
+      return gen_reg_rtx (Pmode);
+    }
 
-      else if (code1 == PLUS)
-       {
-         rtx add_op0 = XEXP (op1, 0);
-         rtx add_op1 = XEXP (op1, 1);
+  model = SYMBOL_REF_TLS_MODEL (loc);
+  /* Only TARGET_ABICALLS code can have more than one module; other
+     code must be be static and should not use a GOT.  All TLS models
+     reduce to local exec in this situation.  */
+  if (!TARGET_ABICALLS)
+    model = TLS_MODEL_LOCAL_EXEC;
 
-         if (GET_CODE (XEXP (op1, 1)) == REG
-             && GET_CODE (XEXP (op1, 0)) == CONST_INT)
-           add_op0 = XEXP (op1, 1), add_op1 = XEXP (op1, 0);
+  switch (model)
+    {
+    case TLS_MODEL_GLOBAL_DYNAMIC:
+      v0 = gen_rtx_REG (Pmode, GP_RETURN);
+      insn = mips_call_tls_get_addr (loc, SYMBOL_TLSGD, v0);
+      dest = gen_reg_rtx (Pmode);
+      emit_libcall_block (insn, dest, v0, loc);
+      break;
 
-         operands[2] = add_op0;
-         operands[3] = add_op1;
-         ret = "add%:\t%0,%2,%3";
-       }
+    case TLS_MODEL_LOCAL_DYNAMIC:
+      v0 = gen_rtx_REG (Pmode, GP_RETURN);
+      insn = mips_call_tls_get_addr (loc, SYMBOL_TLSLDM, v0);
+      tmp1 = gen_reg_rtx (Pmode);
 
-      else if (code1 == HIGH)
-       {
-         operands[1] = XEXP (op1, 0);
-         ret = "lui\t%0,%%hi(%1)";
-       }
-    }
+      /* Attach a unique REG_EQUIV, to allow the RTL optimizers to
+        share the LDM result with other LD model accesses.  */
+      eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
+                           UNSPEC_TLS_LDM);
+      emit_libcall_block (insn, tmp1, v0, eqv);
 
-  else if (code0 == MEM)
-    {
-      if (TARGET_STATS)
-       mips_count_memory_refs (op0, 1);
+      tmp2 = mips_unspec_offset_high (NULL, tmp1, loc, SYMBOL_DTPREL);
+      dest = gen_rtx_LO_SUM (Pmode, tmp2,
+                            mips_unspec_address (loc, SYMBOL_DTPREL));
+      break;
 
-      if (code1 == REG)
-       {
-         int regno1 = REGNO (op1) + subreg_offset1;
+    case TLS_MODEL_INITIAL_EXEC:
+      tp = mips_get_tp ();
+      tmp1 = gen_reg_rtx (Pmode);
+      tmp2 = mips_unspec_address (loc, SYMBOL_GOTTPREL);
+      if (Pmode == DImode)
+       emit_insn (gen_load_gotdi (tmp1, pic_offset_table_rtx, tmp2));
+      else
+       emit_insn (gen_load_gotsi (tmp1, pic_offset_table_rtx, tmp2));
+      dest = gen_reg_rtx (Pmode);
+      emit_insn (gen_add3_insn (dest, tmp1, tp));
+      break;
 
-         if (GP_REG_P (regno1))
-           {
-             switch (mode)
-               {
-               case SFmode: ret = "sw\t%1,%0"; break;
-               case SImode: ret = "sw\t%1,%0"; break;
-               case HImode: ret = "sh\t%1,%0"; break;
-               case QImode: ret = "sb\t%1,%0"; break;
-               default: break;
-               }
-           }
+    case TLS_MODEL_LOCAL_EXEC:
+      tp = mips_get_tp ();
+      tmp1 = mips_unspec_offset_high (NULL, tp, loc, SYMBOL_TPREL);
+      dest = gen_rtx_LO_SUM (Pmode, tmp1,
+                            mips_unspec_address (loc, SYMBOL_TPREL));
+      break;
 
-         else if (FP_REG_P (regno1) && (mode == SImode || mode == SFmode))
-           ret = "s.s\t%1,%0";
-       }
+    default:
+      gcc_unreachable ();
+    }
+  return dest;
+}
+\f
+/* If X is not a valid address for mode MODE, force it into a register.  */
 
-      else if (code1 == CONST_INT && INTVAL (op1) == 0)
-       {
-         switch (mode)
-           {
-           case SFmode: ret = "sw\t%z1,%0"; break;
-           case SImode: ret = "sw\t%z1,%0"; break;
-           case HImode: ret = "sh\t%z1,%0"; break;
-           case QImode: ret = "sb\t%z1,%0"; break;
-           default: break;
-           }
-       }
+static rtx
+mips_force_address (rtx x, enum machine_mode mode)
+{
+  if (!mips_legitimate_address_p (mode, x, false))
+    x = force_reg (Pmode, x);
+  return x;
+}
 
-      else if (code1 == CONST_DOUBLE && op1 == CONST0_RTX (mode))
-       {
-         switch (mode)
-           {
-           case SFmode: ret = "sw\t%.,%0"; break;
-           case SImode: ret = "sw\t%.,%0"; break;
-           case HImode: ret = "sh\t%.,%0"; break;
-           case QImode: ret = "sb\t%.,%0"; break;
-           default: break;
-           }
-       }
+/* This function is used to implement LEGITIMIZE_ADDRESS.  If *XLOC can
+   be legitimized in a way that the generic machinery might not expect,
+   put the new address in *XLOC and return true.  MODE is the mode of
+   the memory being accessed.  */
 
-      if (ret != 0 && MEM_VOLATILE_P (op0))
-       {
-         size_t i = strlen (ret);
+bool
+mips_legitimize_address (rtx *xloc, enum machine_mode mode)
+{
+  rtx base, addr;
+  HOST_WIDE_INT offset;
 
-         if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
-           abort ();
+  if (mips_tls_symbol_p (*xloc))
+    {
+      *xloc = mips_legitimize_tls_address (*xloc);
+      return true;
+    }
 
-         sprintf (volatile_buffer, "%%{%s%%}", ret);
-         ret = volatile_buffer;
-       }
+  /* See if the address can split into a high part and a LO_SUM.  */
+  if (mips_split_symbol (NULL, *xloc, mode, &addr))
+    {
+      *xloc = mips_force_address (addr, mode);
+      return true;
     }
 
-  if (ret == 0)
+  /* Handle BASE + OFFSET using mips_add_offset.  */
+  mips_split_plus (*xloc, &base, &offset);
+  if (offset != 0)
     {
-      abort_with_insn (insn, "bad move");
-      return 0;
+      if (!mips_valid_base_register_p (base, mode, false))
+       base = copy_to_mode_reg (Pmode, base);
+      addr = mips_add_offset (NULL, base, offset);
+      *xloc = mips_force_address (addr, mode);
+      return true;
     }
+  return false;
+}
+
+/* Load VALUE into DEST.  TEMP is as for mips_force_temporary.  */
+
+void
+mips_move_integer (rtx temp, rtx dest, unsigned HOST_WIDE_INT value)
+{
+  struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS];
+  enum machine_mode mode;
+  unsigned int i, num_ops;
+  rtx x;
 
-  if (delay != DELAY_NONE)
-    return mips_fill_delay_slot (ret, delay, operands, insn);
+  mode = GET_MODE (dest);
+  num_ops = mips_build_integer (codes, value);
 
-  return ret;
+  /* Apply each binary operation to X.  Invariant: X is a legitimate
+     source operand for a SET pattern.  */
+  x = GEN_INT (codes[0].value);
+  for (i = 1; i < num_ops; i++)
+    {
+      if (!can_create_pseudo_p ())
+       {
+         emit_insn (gen_rtx_SET (VOIDmode, temp, x));
+         x = temp;
+       }
+      else
+       x = force_reg (mode, x);
+      x = gen_rtx_fmt_ee (codes[i].code, mode, x, GEN_INT (codes[i].value));
+    }
+
+  emit_insn (gen_rtx_SET (VOIDmode, dest, x));
 }
 
-\f
-/* Return the appropriate instructions to move 2 words */
+/* Subroutine of mips_legitimize_move.  Move constant SRC into register
+   DEST given that SRC satisfies immediate_operand but doesn't satisfy
+   move_operand.  */
 
-const char *
-mips_move_2words (operands, insn)
-     rtx operands[];
-     rtx insn;
+static void
+mips_legitimize_const_move (enum machine_mode mode, rtx dest, rtx src)
 {
-  const char *ret = 0;
-  rtx op0 = operands[0];
-  rtx op1 = operands[1];
-  enum rtx_code code0 = GET_CODE (operands[0]);
-  enum rtx_code code1 = GET_CODE (operands[1]);
-  int subreg_offset0 = 0;
-  int subreg_offset1 = 0;
-  enum delay_type delay = DELAY_NONE;
+  rtx base, offset;
 
-  while (code0 == SUBREG)
+  /* Split moves of big integers into smaller pieces.  */
+  if (splittable_const_int_operand (src, mode))
     {
-      subreg_offset0 += subreg_regno_offset (REGNO (SUBREG_REG (op0)),
-                                            GET_MODE (SUBREG_REG (op0)),
-                                            SUBREG_BYTE (op0),
-                                            GET_MODE (op0));
-      op0 = SUBREG_REG (op0);
-      code0 = GET_CODE (op0);
+      mips_move_integer (dest, dest, INTVAL (src));
+      return;
     }
 
-  if (code1 == SIGN_EXTEND)
+  /* Split moves of symbolic constants into high/low pairs.  */
+  if (mips_split_symbol (dest, src, MAX_MACHINE_MODE, &src))
     {
-      op1 = XEXP (op1, 0);
-      code1 = GET_CODE (op1);
+      emit_insn (gen_rtx_SET (VOIDmode, dest, src));
+      return;
     }
 
-  while (code1 == SUBREG)
+  /* Generate the appropriate access sequences for TLS symbols.  */
+  if (mips_tls_symbol_p (src))
     {
-      subreg_offset1 += subreg_regno_offset (REGNO (SUBREG_REG (op1)),
-                                            GET_MODE (SUBREG_REG (op1)),
-                                            SUBREG_BYTE (op1),
-                                            GET_MODE (op1));
-      op1 = SUBREG_REG (op1);
-      code1 = GET_CODE (op1);
+      mips_emit_move (dest, mips_legitimize_tls_address (src));
+      return;
     }
 
-  /* Sanity check.  */
-  if (GET_CODE (operands[1]) == SIGN_EXTEND
-      && code1 != REG
-      && code1 != CONST_INT
-      /* The following three can happen as the result of a questionable
-        cast.  */
-      && code1 != LABEL_REF
-      && code1 != SYMBOL_REF
-      && code1 != CONST)
-    abort ();
-
-  if (code0 == REG)
+  /* If we have (const (plus symbol offset)), and that expression cannot
+     be forced into memory, load the symbol first and add in the offset.
+     In non-MIPS16 mode, prefer to do this even if the constant _can_ be
+     forced into memory, as it usually produces better code.  */
+  split_const (src, &base, &offset);
+  if (offset != const0_rtx
+      && (targetm.cannot_force_const_mem (src)
+         || (!TARGET_MIPS16 && can_create_pseudo_p ())))
     {
-      int regno0 = REGNO (op0) + subreg_offset0;
-
-      if (code1 == REG)
-       {
-         int regno1 = REGNO (op1) + subreg_offset1;
+      base = mips_force_temporary (dest, base);
+      mips_emit_move (dest, mips_add_offset (NULL, base, INTVAL (offset)));
+      return;
+    }
 
-         /* Just in case, don't do anything for assigning a register
-            to itself, unless we are filling a delay slot.  */
-         if (regno0 == regno1 && set_nomacro == 0)
-           ret = "";
+  src = force_const_mem (mode, src);
 
-         else if (FP_REG_P (regno0))
-           {
-             if (FP_REG_P (regno1))
-               ret = "mov.d\t%0,%1";
+  /* When using explicit relocs, constant pool references are sometimes
+     not legitimate addresses.  */
+  mips_split_symbol (dest, XEXP (src, 0), mode, &XEXP (src, 0));
+  mips_emit_move (dest, src);
+}
 
-             else
-               {
-                 delay = DELAY_LOAD;
-                 if (TARGET_FLOAT64)
-                   {
-                     if (!TARGET_64BIT)
-                       abort_with_insn (insn, "bad move");
+/* If (set DEST SRC) is not a valid move instruction, emit an equivalent
+   sequence that is valid.  */
 
-#ifdef TARGET_FP_CALL_32
-                     if (FP_CALL_GP_REG_P (regno1))
-                       ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tdmtc1\t%1,%0";
-                     else
-#endif
-                       ret = "dmtc1\t%1,%0";
-                   }
-                 else
-                   ret = "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0";
-               }
+bool
+mips_legitimize_move (enum machine_mode mode, rtx dest, rtx src)
+{
+  if (!register_operand (dest, mode) && !reg_or_0_operand (src, mode))
+    {
+      mips_emit_move (dest, force_reg (mode, src));
+      return true;
+    }
+
+  /* We need to deal with constants that would be legitimate
+     immediate_operands but aren't legitimate move_operands.  */
+  if (CONSTANT_P (src) && !move_operand (src, mode))
+    {
+      mips_legitimize_const_move (mode, dest, src);
+      set_unique_reg_note (get_last_insn (), REG_EQUAL, copy_rtx (src));
+      return true;
+    }
+  return false;
+}
+\f
+/* Return true if value 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 (mips_lo_relocs[SYMBOL_GP_RELATIVE]
+         && !mips_split_p[SYMBOL_GP_RELATIVE]
+         && 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, NULL);
+}
+
+/* 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;
+}
+
+/* Rewrite instruction pattern PATTERN so that it refers to small data
+   using explicit relocations.  */
+
+rtx
+mips_rewrite_small_data (rtx pattern)
+{
+  pattern = copy_insn (pattern);
+  for_each_rtx (&pattern, mips_rewrite_small_data_1, NULL);
+  return pattern;
+}
+\f
+/* We need a lot of little routines to check the range of MIPS16 immediate
+   operands.  */
+
+static int
+m16_check_op (rtx op, int low, int high, int mask)
+{
+  return (GET_CODE (op) == CONST_INT
+         && IN_RANGE (INTVAL (op), low, high)
+         && (INTVAL (op) & mask) == 0);
+}
+
+int
+m16_uimm3_b (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, 0x1, 0x8, 0);
+}
+
+int
+m16_simm4_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0x8, 0x7, 0);
+}
+
+int
+m16_nsimm4_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0x7, 0x8, 0);
+}
+
+int
+m16_simm5_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0x10, 0xf, 0);
+}
+
+int
+m16_nsimm5_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0xf, 0x10, 0);
+}
+
+int
+m16_uimm5_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0x10 << 2, 0xf << 2, 3);
+}
+
+int
+m16_nuimm5_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0xf << 2, 0x10 << 2, 3);
+}
+
+int
+m16_simm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0x80, 0x7f, 0);
+}
+
+int
+m16_nsimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0x7f, 0x80, 0);
+}
+
+int
+m16_uimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, 0x0, 0xff, 0);
+}
+
+int
+m16_nuimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0xff, 0x0, 0);
+}
+
+int
+m16_uimm8_m1_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0x1, 0xfe, 0);
+}
+
+int
+m16_uimm8_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, 0x0, 0xff << 2, 3);
+}
+
+int
+m16_nuimm8_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0xff << 2, 0x0, 3);
+}
+
+int
+m16_simm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0x80 << 3, 0x7f << 3, 7);
+}
+
+int
+m16_nsimm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return m16_check_op (op, -0x7f << 3, 0x80 << 3, 7);
+}
+\f
+/* The cost of loading values from the constant pool.  It should be
+   larger than the cost of any constant we want to synthesize inline.  */
+#define CONSTANT_POOL_COST COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 8)
+
+/* Return the cost of X when used as an operand to the MIPS16 instruction
+   that implements CODE.  Return -1 if there is no such instruction, or if
+   X is not a valid immediate operand for it.  */
+
+static int
+mips16_constant_cost (int code, HOST_WIDE_INT x)
+{
+  switch (code)
+    {
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      /* Shifts by between 1 and 8 bits (inclusive) are unextended,
+        other shifts are extended.  The shift patterns truncate the shift
+        count to the right size, so there are no out-of-range values.  */
+      if (IN_RANGE (x, 1, 8))
+       return 0;
+      return COSTS_N_INSNS (1);
+
+    case PLUS:
+      if (IN_RANGE (x, -128, 127))
+       return 0;
+      if (SMALL_OPERAND (x))
+       return COSTS_N_INSNS (1);
+      return -1;
+
+    case LEU:
+      /* Like LE, but reject the always-true case.  */
+      if (x == -1)
+       return -1;
+    case LE:
+      /* We add 1 to the immediate and use SLT.  */
+      x += 1;
+    case XOR:
+      /* We can use CMPI for an xor with an unsigned 16-bit X.  */
+    case LT:
+    case LTU:
+      if (IN_RANGE (x, 0, 255))
+       return 0;
+      if (SMALL_OPERAND_UNSIGNED (x))
+       return COSTS_N_INSNS (1);
+      return -1;
+
+    case EQ:
+    case NE:
+      /* Equality comparisons with 0 are cheap.  */
+      if (x == 0)
+       return 0;
+      return -1;
+
+    default:
+      return -1;
+    }
+}
+
+/* Return true if there is a non-MIPS16 instruction that implements CODE
+   and if that instruction accepts X as an immediate operand.  */
+
+static int
+mips_immediate_operand_p (int code, HOST_WIDE_INT x)
+{
+  switch (code)
+    {
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      /* All shift counts are truncated to a valid constant.  */
+      return true;
+
+    case ROTATE:
+    case ROTATERT:
+      /* Likewise rotates, if the target supports rotates at all.  */
+      return ISA_HAS_ROR;
+
+    case AND:
+    case IOR:
+    case XOR:
+      /* These instructions take 16-bit unsigned immediates.  */
+      return SMALL_OPERAND_UNSIGNED (x);
+
+    case PLUS:
+    case LT:
+    case LTU:
+      /* These instructions take 16-bit signed immediates.  */
+      return SMALL_OPERAND (x);
+
+    case EQ:
+    case NE:
+    case GT:
+    case GTU:
+      /* The "immediate" forms of these instructions are really
+        implemented as comparisons with register 0.  */
+      return x == 0;
+
+    case GE:
+    case GEU:
+      /* Likewise, meaning that the only valid immediate operand is 1.  */
+      return x == 1;
+
+    case LE:
+      /* We add 1 to the immediate and use SLT.  */
+      return SMALL_OPERAND (x + 1);
+
+    case LEU:
+      /* Likewise SLTU, but reject the always-true case.  */
+      return SMALL_OPERAND (x + 1) && x + 1 != 0;
+
+    case SIGN_EXTRACT:
+    case ZERO_EXTRACT:
+      /* The bit position and size are immediate operands.  */
+      return ISA_HAS_EXT_INS;
+
+    default:
+      /* By default assume that $0 can be used for 0.  */
+      return x == 0;
+    }
+}
+
+/* Return the cost of binary operation X, given that the instruction
+   sequence for a word-sized or smaller operation has cost SINGLE_COST
+   and that the sequence of a double-word operation has cost DOUBLE_COST.  */
+
+static int
+mips_binary_cost (rtx x, int single_cost, int double_cost)
+{
+  int cost;
+
+  if (GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD * 2)
+    cost = double_cost;
+  else
+    cost = single_cost;
+  return (cost
+         + rtx_cost (XEXP (x, 0), 0, !optimize_size)
+         + rtx_cost (XEXP (x, 1), GET_CODE (x), !optimize_size));
+}
+
+/* Return the cost of floating-point multiplications of mode MODE.  */
+
+static int
+mips_fp_mult_cost (enum machine_mode mode)
+{
+  return mode == DFmode ? mips_cost->fp_mult_df : mips_cost->fp_mult_sf;
+}
+
+/* Return the cost of floating-point divisions of mode MODE.  */
+
+static int
+mips_fp_div_cost (enum machine_mode mode)
+{
+  return mode == DFmode ? mips_cost->fp_div_df : mips_cost->fp_div_sf;
+}
+
+/* Return the cost of sign-extending OP to mode MODE, not including the
+   cost of OP itself.  */
+
+static int
+mips_sign_extend_cost (enum machine_mode mode, rtx op)
+{
+  if (MEM_P (op))
+    /* Extended loads are as cheap as unextended ones.  */
+    return 0;
+
+  if (TARGET_64BIT && mode == DImode && GET_MODE (op) == SImode)
+    /* A sign extension from SImode to DImode in 64-bit mode is free.  */
+    return 0;
+
+  if (ISA_HAS_SEB_SEH || GENERATE_MIPS16E)
+    /* We can use SEB or SEH.  */
+    return COSTS_N_INSNS (1);
+
+  /* We need to use a shift left and a shift right.  */
+  return COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 2);
+}
+
+/* Return the cost of zero-extending OP to mode MODE, not including the
+   cost of OP itself.  */
+
+static int
+mips_zero_extend_cost (enum machine_mode mode, rtx op)
+{
+  if (MEM_P (op))
+    /* Extended loads are as cheap as unextended ones.  */
+    return 0;
+
+  if (TARGET_64BIT && mode == DImode && GET_MODE (op) == SImode)
+    /* We need a shift left by 32 bits and a shift right by 32 bits.  */
+    return COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 2);
+
+  if (GENERATE_MIPS16E)
+    /* We can use ZEB or ZEH.  */
+    return COSTS_N_INSNS (1);
+
+  if (TARGET_MIPS16)
+    /* We need to load 0xff or 0xffff into a register and use AND.  */
+    return COSTS_N_INSNS (GET_MODE (op) == QImode ? 2 : 3);
+
+  /* We can use ANDI.  */
+  return COSTS_N_INSNS (1);
+}
+
+/* Implement TARGET_RTX_COSTS.  */
+
+static bool
+mips_rtx_costs (rtx x, int code, int outer_code, int *total,
+               bool speed)
+{
+  enum machine_mode mode = GET_MODE (x);
+  bool float_mode_p = FLOAT_MODE_P (mode);
+  int cost;
+  rtx addr;
+
+  /* The cost of a COMPARE is hard to define for MIPS.  COMPAREs don't
+     appear in the instruction stream, and the cost of a comparison is
+     really the cost of the branch or scc condition.  At the time of
+     writing, GCC only uses an explicit outer COMPARE code when optabs
+     is testing whether a constant is expensive enough to force into a
+     register.  We want optabs to pass such constants through the MIPS
+     expanders instead, so make all constants very cheap here.  */
+  if (outer_code == COMPARE)
+    {
+      gcc_assert (CONSTANT_P (x));
+      *total = 0;
+      return true;
+    }
+
+  switch (code)
+    {
+    case CONST_INT:
+      /* Treat *clear_upper32-style ANDs as having zero cost in the
+        second operand.  The cost is entirely in the first operand.
+
+        ??? This is needed because we would otherwise try to CSE
+        the constant operand.  Although that's the right thing for
+        instructions that continue to be a register operation throughout
+        compilation, it is disastrous for instructions that could
+        later be converted into a memory operation.  */
+      if (TARGET_64BIT
+         && outer_code == AND
+         && UINTVAL (x) == 0xffffffff)
+       {
+         *total = 0;
+         return true;
+       }
+
+      if (TARGET_MIPS16)
+       {
+         cost = mips16_constant_cost (outer_code, INTVAL (x));
+         if (cost >= 0)
+           {
+             *total = cost;
+             return true;
+           }
+       }
+      else
+       {
+         /* When not optimizing for size, we care more about the cost
+            of hot code, and hot code is often in a loop.  If a constant
+            operand needs to be forced into a register, we will often be
+            able to hoist the constant load out of the loop, so the load
+            should not contribute to the cost.  */
+         if (!optimize_size
+             || mips_immediate_operand_p (outer_code, INTVAL (x)))
+           {
+             *total = 0;
+             return true;
            }
+       }
+      /* Fall through.  */
+
+    case CONST:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case CONST_DOUBLE:
+      if (force_to_mem_operand (x, VOIDmode))
+       {
+         *total = COSTS_N_INSNS (1);
+         return true;
+       }
+      cost = mips_const_insns (x);
+      if (cost > 0)
+       {
+         /* If the constant is likely to be stored in a GPR, SETs of
+            single-insn constants are as cheap as register sets; we
+            never want to CSE them.
+
+            Don't reduce the cost of storing a floating-point zero in
+            FPRs.  If we have a zero in an FPR for other reasons, we
+            can get better cfg-cleanup and delayed-branch results by
+            using it consistently, rather than using $0 sometimes and
+            an FPR at other times.  Also, moves between floating-point
+            registers are sometimes cheaper than (D)MTC1 $0.  */
+         if (cost == 1
+             && outer_code == SET
+             && !(float_mode_p && TARGET_HARD_FLOAT))
+           cost = 0;
+         /* When non-MIPS16 code loads a constant N>1 times, we rarely
+            want to CSE the constant itself.  It is usually better to
+            have N copies of the last operation in the sequence and one
+            shared copy of the other operations.  (Note that this is
+            not true for MIPS16 code, where the final operation in the
+            sequence is often an extended instruction.)
+
+            Also, if we have a CONST_INT, we don't know whether it is
+            for a word or doubleword operation, so we cannot rely on
+            the result of mips_build_integer.  */
+         else if (!TARGET_MIPS16
+                  && (outer_code == SET || mode == VOIDmode))
+           cost = 1;
+         *total = COSTS_N_INSNS (cost);
+         return true;
+       }
+      /* The value will need to be fetched from the constant pool.  */
+      *total = CONSTANT_POOL_COST;
+      return true;
+
+    case MEM:
+      /* If the address is legitimate, return the number of
+        instructions it needs.  */
+      addr = XEXP (x, 0);
+      cost = mips_address_insns (addr, mode, true);
+      if (cost > 0)
+       {
+         *total = COSTS_N_INSNS (cost + 1);
+         return true;
+       }
+      /* Check for a scaled indexed address.  */
+      if (mips_lwxs_address_p (addr))
+       {
+         *total = COSTS_N_INSNS (2);
+         return true;
+       }
+      /* Otherwise use the default handling.  */
+      return false;
+
+    case FFS:
+      *total = COSTS_N_INSNS (6);
+      return false;
+
+    case NOT:
+      *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 2 : 1);
+      return false;
+
+    case AND:
+      /* Check for a *clear_upper32 pattern and treat it like a zero
+        extension.  See the pattern's comment for details.  */
+      if (TARGET_64BIT
+         && mode == DImode
+         && CONST_INT_P (XEXP (x, 1))
+         && UINTVAL (XEXP (x, 1)) == 0xffffffff)
+       {
+         *total = (mips_zero_extend_cost (mode, XEXP (x, 0))
+                   + rtx_cost (XEXP (x, 0), 0, speed));
+         return true;
+       }
+      /* Fall through.  */
+
+    case IOR:
+    case XOR:
+      /* Double-word operations use two single-word operations.  */
+      *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (2));
+      return true;
+
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+    case ROTATE:
+    case ROTATERT:
+      if (CONSTANT_P (XEXP (x, 1)))
+       *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4));
+      else
+       *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (12));
+      return true;
+
+    case ABS:
+      if (float_mode_p)
+        *total = mips_cost->fp_add;
+      else
+        *total = COSTS_N_INSNS (4);
+      return false;
+
+    case LO_SUM:
+      /* Low-part immediates need an extended MIPS16 instruction.  */
+      *total = (COSTS_N_INSNS (TARGET_MIPS16 ? 2 : 1)
+               + rtx_cost (XEXP (x, 0), 0, speed));
+      return true;
+
+    case LT:
+    case LTU:
+    case LE:
+    case LEU:
+    case GT:
+    case GTU:
+    case GE:
+    case GEU:
+    case EQ:
+    case NE:
+    case UNORDERED:
+    case LTGT:
+      /* Branch comparisons have VOIDmode, so use the first operand's
+        mode instead.  */
+      mode = GET_MODE (XEXP (x, 0));
+      if (FLOAT_MODE_P (mode))
+       {
+         *total = mips_cost->fp_add;
+         return false;
+       }
+      *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4));
+      return true;
+
+    case MINUS:
+      if (float_mode_p
+         && (ISA_HAS_NMADD4_NMSUB4 (mode) || ISA_HAS_NMADD3_NMSUB3 (mode))
+         && TARGET_FUSED_MADD
+         && !HONOR_NANS (mode)
+         && !HONOR_SIGNED_ZEROS (mode))
+       {
+         /* See if we can use NMADD or NMSUB.  See mips.md for the
+            associated patterns.  */
+         rtx op0 = XEXP (x, 0);
+         rtx op1 = XEXP (x, 1);
+         if (GET_CODE (op0) == MULT && GET_CODE (XEXP (op0, 0)) == NEG)
+           {
+             *total = (mips_fp_mult_cost (mode)
+                       + rtx_cost (XEXP (XEXP (op0, 0), 0), 0, speed)
+                       + rtx_cost (XEXP (op0, 1), 0, speed)
+                       + rtx_cost (op1, 0, speed));
+             return true;
+           }
+         if (GET_CODE (op1) == MULT)
+           {
+             *total = (mips_fp_mult_cost (mode)
+                       + rtx_cost (op0, 0, speed)
+                       + rtx_cost (XEXP (op1, 0), 0, speed)
+                       + rtx_cost (XEXP (op1, 1), 0, speed));
+             return true;
+           }
+       }
+      /* Fall through.  */
+
+    case PLUS:
+      if (float_mode_p)
+       {
+         /* If this is part of a MADD or MSUB, treat the PLUS as
+            being free.  */
+         if (ISA_HAS_FP4
+             && TARGET_FUSED_MADD
+             && GET_CODE (XEXP (x, 0)) == MULT)
+           *total = 0;
+         else
+           *total = mips_cost->fp_add;
+         return false;
+       }
+
+      /* Double-word operations require three single-word operations and
+        an SLTU.  The MIPS16 version then needs to move the result of
+        the SLTU from $24 to a MIPS16 register.  */
+      *total = mips_binary_cost (x, COSTS_N_INSNS (1),
+                                COSTS_N_INSNS (TARGET_MIPS16 ? 5 : 4));
+      return true;
+
+    case NEG:
+      if (float_mode_p
+         && (ISA_HAS_NMADD4_NMSUB4 (mode) || ISA_HAS_NMADD3_NMSUB3 (mode))
+         && TARGET_FUSED_MADD
+         && !HONOR_NANS (mode)
+         && HONOR_SIGNED_ZEROS (mode))
+       {
+         /* See if we can use NMADD or NMSUB.  See mips.md for the
+            associated patterns.  */
+         rtx op = XEXP (x, 0);
+         if ((GET_CODE (op) == PLUS || GET_CODE (op) == MINUS)
+             && GET_CODE (XEXP (op, 0)) == MULT)
+           {
+             *total = (mips_fp_mult_cost (mode)
+                       + rtx_cost (XEXP (XEXP (op, 0), 0), 0, speed)
+                       + rtx_cost (XEXP (XEXP (op, 0), 1), 0, speed)
+                       + rtx_cost (XEXP (op, 1), 0, speed));
+             return true;
+           }
+       }
+
+      if (float_mode_p)
+       *total = mips_cost->fp_add;
+      else
+       *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 4 : 1);
+      return false;
+
+    case MULT:
+      if (float_mode_p)
+       *total = mips_fp_mult_cost (mode);
+      else if (mode == DImode && !TARGET_64BIT)
+       /* Synthesized from 2 mulsi3s, 1 mulsidi3 and two additions,
+          where the mulsidi3 always includes an MFHI and an MFLO.  */
+       *total = (optimize_size
+                 ? COSTS_N_INSNS (ISA_HAS_MUL3 ? 7 : 9)
+                 : mips_cost->int_mult_si * 3 + 6);
+      else if (optimize_size)
+       *total = (ISA_HAS_MUL3 ? 1 : 2);
+      else if (mode == DImode)
+       *total = mips_cost->int_mult_di;
+      else
+       *total = mips_cost->int_mult_si;
+      return false;
+
+    case DIV:
+      /* Check for a reciprocal.  */
+      if (float_mode_p
+         && ISA_HAS_FP4
+         && flag_unsafe_math_optimizations
+         && XEXP (x, 0) == CONST1_RTX (mode))
+       {
+         if (outer_code == SQRT || GET_CODE (XEXP (x, 1)) == SQRT)
+           /* An rsqrt<mode>a or rsqrt<mode>b pattern.  Count the
+              division as being free.  */
+           *total = rtx_cost (XEXP (x, 1), 0, speed);
+         else
+           *total = mips_fp_div_cost (mode) + rtx_cost (XEXP (x, 1), 0, speed);
+         return true;
+       }
+      /* Fall through.  */
+
+    case SQRT:
+    case MOD:
+      if (float_mode_p)
+       {
+         *total = mips_fp_div_cost (mode);
+         return false;
+       }
+      /* Fall through.  */
+
+    case UDIV:
+    case UMOD:
+      if (optimize_size)
+       {
+         /* It is our responsibility to make division by a power of 2
+            as cheap as 2 register additions if we want the division
+            expanders to be used for such operations; see the setting
+            of sdiv_pow2_cheap in optabs.c.  Using (D)DIV for MIPS16
+            should always produce shorter code than using
+            expand_sdiv2_pow2.  */
+         if (TARGET_MIPS16
+             && CONST_INT_P (XEXP (x, 1))
+             && exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
+           {
+             *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), 0, speed);
+             return true;
+           }
+         *total = COSTS_N_INSNS (mips_idiv_insns ());
+       }
+      else if (mode == DImode)
+        *total = mips_cost->int_div_di;
+      else
+       *total = mips_cost->int_div_si;
+      return false;
+
+    case SIGN_EXTEND:
+      *total = mips_sign_extend_cost (mode, XEXP (x, 0));
+      return false;
+
+    case ZERO_EXTEND:
+      *total = mips_zero_extend_cost (mode, XEXP (x, 0));
+      return false;
+
+    case FLOAT:
+    case UNSIGNED_FLOAT:
+    case FIX:
+    case FLOAT_EXTEND:
+    case FLOAT_TRUNCATE:
+      *total = mips_cost->fp_add;
+      return false;
+
+    default:
+      return false;
+    }
+}
+
+/* Implement TARGET_ADDRESS_COST.  */
+
+static int
+mips_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED)
+{
+  return mips_address_insns (addr, SImode, false);
+}
+\f
+/* Return one word of double-word value OP, taking into account the fixed
+   endianness of certain registers.  HIGH_P is true to select the high part,
+   false to select the low part.  */
+
+rtx
+mips_subword (rtx op, bool high_p)
+{
+  unsigned int byte, offset;
+  enum machine_mode mode;
+
+  mode = GET_MODE (op);
+  if (mode == VOIDmode)
+    mode = TARGET_64BIT ? TImode : DImode;
+
+  if (TARGET_BIG_ENDIAN ? !high_p : high_p)
+    byte = UNITS_PER_WORD;
+  else
+    byte = 0;
+
+  if (FP_REG_RTX_P (op))
+    {
+      /* Paired FPRs are always ordered little-endian.  */
+      offset = (UNITS_PER_WORD < UNITS_PER_HWFPVALUE ? high_p : byte != 0);
+      return gen_rtx_REG (word_mode, REGNO (op) + offset);
+    }
+
+  if (MEM_P (op))
+    return mips_rewrite_small_data (adjust_address (op, word_mode, byte));
+
+  return simplify_gen_subreg (word_mode, op, mode, byte);
+}
+
+/* Return true if a 64-bit move from SRC to DEST should be split into two.  */
+
+bool
+mips_split_64bit_move_p (rtx dest, rtx src)
+{
+  if (TARGET_64BIT)
+    return false;
+
+  /* FPR-to-FPR moves can be done in a single instruction, if they're
+     allowed at all.  */
+  if (FP_REG_RTX_P (src) && FP_REG_RTX_P (dest))
+    return false;
+
+  /* Check for floating-point loads and stores.  */
+  if (ISA_HAS_LDC1_SDC1)
+    {
+      if (FP_REG_RTX_P (dest) && MEM_P (src))
+       return false;
+      if (FP_REG_RTX_P (src) && MEM_P (dest))
+       return false;
+    }
+  return true;
+}
+
+/* Split a doubleword move from SRC to DEST.  On 32-bit targets,
+   this function handles 64-bit moves for which mips_split_64bit_move_p
+   holds.  For 64-bit targets, this function handles 128-bit moves.  */
+
+void
+mips_split_doubleword_move (rtx dest, rtx src)
+{
+  rtx low_dest;
+
+  if (FP_REG_RTX_P (dest) || FP_REG_RTX_P (src))
+    {
+      if (!TARGET_64BIT && GET_MODE (dest) == DImode)
+       emit_insn (gen_move_doubleword_fprdi (dest, src));
+      else if (!TARGET_64BIT && GET_MODE (dest) == DFmode)
+       emit_insn (gen_move_doubleword_fprdf (dest, src));
+      else if (!TARGET_64BIT && GET_MODE (dest) == V2SFmode)
+       emit_insn (gen_move_doubleword_fprv2sf (dest, src));
+      else if (!TARGET_64BIT && GET_MODE (dest) == V2SImode)
+       emit_insn (gen_move_doubleword_fprv2si (dest, src));
+      else if (!TARGET_64BIT && GET_MODE (dest) == V4HImode)
+       emit_insn (gen_move_doubleword_fprv4hi (dest, src));
+      else if (!TARGET_64BIT && GET_MODE (dest) == V8QImode)
+       emit_insn (gen_move_doubleword_fprv8qi (dest, src));
+      else if (TARGET_64BIT && GET_MODE (dest) == TFmode)
+       emit_insn (gen_move_doubleword_fprtf (dest, src));
+      else
+       gcc_unreachable ();
+    }
+  else if (REG_P (dest) && REGNO (dest) == MD_REG_FIRST)
+    {
+      low_dest = mips_subword (dest, false);
+      mips_emit_move (low_dest, mips_subword (src, false));
+      if (TARGET_64BIT)
+       emit_insn (gen_mthidi_ti (dest, mips_subword (src, true), low_dest));
+      else
+       emit_insn (gen_mthisi_di (dest, mips_subword (src, true), low_dest));
+    }
+  else if (REG_P (src) && REGNO (src) == MD_REG_FIRST)
+    {
+      mips_emit_move (mips_subword (dest, false), mips_subword (src, false));
+      if (TARGET_64BIT)
+       emit_insn (gen_mfhidi_ti (mips_subword (dest, true), src));
+      else
+       emit_insn (gen_mfhisi_di (mips_subword (dest, true), src));
+    }
+  else
+    {
+      /* The operation can be split into two normal moves.  Decide in
+        which order to do them.  */
+      low_dest = mips_subword (dest, false);
+      if (REG_P (low_dest)
+         && reg_overlap_mentioned_p (low_dest, src))
+       {
+         mips_emit_move (mips_subword (dest, true), mips_subword (src, true));
+         mips_emit_move (low_dest, mips_subword (src, false));
+       }
+      else
+       {
+         mips_emit_move (low_dest, mips_subword (src, false));
+         mips_emit_move (mips_subword (dest, true), mips_subword (src, true));
+       }
+    }
+}
+\f
+/* Return the appropriate instructions to move SRC into DEST.  Assume
+   that SRC is operand 1 and DEST is operand 0.  */
+
+const char *
+mips_output_move (rtx dest, rtx src)
+{
+  enum rtx_code dest_code, src_code;
+  enum machine_mode mode;
+  enum mips_symbol_type symbol_type;
+  bool dbl_p;
+
+  dest_code = GET_CODE (dest);
+  src_code = GET_CODE (src);
+  mode = GET_MODE (dest);
+  dbl_p = (GET_MODE_SIZE (mode) == 8);
+
+  if (dbl_p && mips_split_64bit_move_p (dest, src))
+    return "#";
+
+  if ((src_code == REG && GP_REG_P (REGNO (src)))
+      || (!TARGET_MIPS16 && src == CONST0_RTX (mode)))
+    {
+      if (dest_code == REG)
+       {
+         if (GP_REG_P (REGNO (dest)))
+           return "move\t%0,%z1";
+
+         /* Moves to HI are handled by special .md insns.  */
+         if (REGNO (dest) == LO_REGNUM)
+           return "mtlo\t%z1";
+
+         if (DSP_ACC_REG_P (REGNO (dest)))
+           {
+             static char retval[] = "mt__\t%z1,%q0";
+
+             retval[2] = reg_names[REGNO (dest)][4];
+             retval[3] = reg_names[REGNO (dest)][5];
+             return retval;
+           }
+
+         if (FP_REG_P (REGNO (dest)))
+           return dbl_p ? "dmtc1\t%z1,%0" : "mtc1\t%z1,%0";
+
+         if (ALL_COP_REG_P (REGNO (dest)))
+           {
+             static char retval[] = "dmtc_\t%z1,%0";
+
+             retval[4] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (dest));
+             return dbl_p ? retval : retval + 1;
+           }
+       }
+      if (dest_code == MEM)
+       switch (GET_MODE_SIZE (mode))
+         {
+         case 1: return "sb\t%z1,%0";
+         case 2: return "sh\t%z1,%0";
+         case 4: return "sw\t%z1,%0";
+         case 8: return "sd\t%z1,%0";
+         }
+    }
+  if (dest_code == REG && GP_REG_P (REGNO (dest)))
+    {
+      if (src_code == REG)
+       {
+         /* Moves from HI are handled by special .md insns.  */
+         if (REGNO (src) == LO_REGNUM)
+           {
+             /* When generating VR4120 or VR4130 code, we use MACC and
+                DMACC instead of MFLO.  This avoids both the normal
+                MIPS III HI/LO hazards and the errata related to
+                -mfix-vr4130.  */
+             if (ISA_HAS_MACCHI)
+               return dbl_p ? "dmacc\t%0,%.,%." : "macc\t%0,%.,%.";
+             return "mflo\t%0";
+           }
+
+         if (DSP_ACC_REG_P (REGNO (src)))
+           {
+             static char retval[] = "mf__\t%0,%q1";
+
+             retval[2] = reg_names[REGNO (src)][4];
+             retval[3] = reg_names[REGNO (src)][5];
+             return retval;
+           }
+
+         if (FP_REG_P (REGNO (src)))
+           return dbl_p ? "dmfc1\t%0,%1" : "mfc1\t%0,%1";
+
+         if (ALL_COP_REG_P (REGNO (src)))
+           {
+             static char retval[] = "dmfc_\t%0,%1";
+
+             retval[4] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (src));
+             return dbl_p ? retval : retval + 1;
+           }
+
+         if (ST_REG_P (REGNO (src)) && ISA_HAS_8CC)
+           return "lui\t%0,0x3f80\n\tmovf\t%0,%.,%1";
+       }
+
+      if (src_code == MEM)
+       switch (GET_MODE_SIZE (mode))
+         {
+         case 1: return "lbu\t%0,%1";
+         case 2: return "lhu\t%0,%1";
+         case 4: return "lw\t%0,%1";
+         case 8: return "ld\t%0,%1";
+         }
+
+      if (src_code == CONST_INT)
+       {
+         /* Don't use the X format for the operand itself, because that
+            will give out-of-range numbers for 64-bit hosts and 32-bit
+            targets.  */
+         if (!TARGET_MIPS16)
+           return "li\t%0,%1\t\t\t# %X1";
+
+         if (SMALL_OPERAND_UNSIGNED (INTVAL (src)))
+           return "li\t%0,%1";
+
+         if (SMALL_OPERAND_UNSIGNED (-INTVAL (src)))
+           return "#";
+       }
+
+      if (src_code == HIGH)
+       return TARGET_MIPS16 ? "#" : "lui\t%0,%h1";
+
+      if (CONST_GP_P (src))
+       return "move\t%0,%1";
+
+      if (mips_symbolic_constant_p (src, SYMBOL_CONTEXT_LEA, &symbol_type)
+         && mips_lo_relocs[symbol_type] != 0)
+       {
+         /* A signed 16-bit constant formed by applying a relocation
+            operator to a symbolic address.  */
+         gcc_assert (!mips_split_p[symbol_type]);
+         return "li\t%0,%R1";
+       }
+
+      if (symbolic_operand (src, VOIDmode))
+       {
+         gcc_assert (TARGET_MIPS16
+                     ? TARGET_MIPS16_TEXT_LOADS
+                     : !TARGET_EXPLICIT_RELOCS);
+         return dbl_p ? "dla\t%0,%1" : "la\t%0,%1";
+       }
+    }
+  if (src_code == REG && FP_REG_P (REGNO (src)))
+    {
+      if (dest_code == REG && FP_REG_P (REGNO (dest)))
+       {
+         if (GET_MODE (dest) == V2SFmode)
+           return "mov.ps\t%0,%1";
+         else
+           return dbl_p ? "mov.d\t%0,%1" : "mov.s\t%0,%1";
+       }
+
+      if (dest_code == MEM)
+       return dbl_p ? "sdc1\t%1,%0" : "swc1\t%1,%0";
+    }
+  if (dest_code == REG && FP_REG_P (REGNO (dest)))
+    {
+      if (src_code == MEM)
+       return dbl_p ? "ldc1\t%0,%1" : "lwc1\t%0,%1";
+    }
+  if (dest_code == REG && ALL_COP_REG_P (REGNO (dest)) && src_code == MEM)
+    {
+      static char retval[] = "l_c_\t%0,%1";
+
+      retval[1] = (dbl_p ? 'd' : 'w');
+      retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (dest));
+      return retval;
+    }
+  if (dest_code == MEM && src_code == REG && ALL_COP_REG_P (REGNO (src)))
+    {
+      static char retval[] = "s_c_\t%1,%0";
+
+      retval[1] = (dbl_p ? 'd' : 'w');
+      retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (src));
+      return retval;
+    }
+  gcc_unreachable ();
+}
+\f
+/* Return true if CMP1 is a suitable second operand for integer ordering
+   test CODE.  See also the *sCC patterns in mips.md.  */
+
+static bool
+mips_int_order_operand_ok_p (enum rtx_code code, rtx cmp1)
+{
+  switch (code)
+    {
+    case GT:
+    case GTU:
+      return reg_or_0_operand (cmp1, VOIDmode);
+
+    case GE:
+    case GEU:
+      return !TARGET_MIPS16 && cmp1 == const1_rtx;
+
+    case LT:
+    case LTU:
+      return arith_operand (cmp1, VOIDmode);
+
+    case LE:
+      return sle_operand (cmp1, VOIDmode);
+
+    case LEU:
+      return sleu_operand (cmp1, VOIDmode);
+
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Return true if *CMP1 (of mode MODE) is a valid second operand for
+   integer ordering test *CODE, or if an equivalent combination can
+   be formed by adjusting *CODE and *CMP1.  When returning true, update
+   *CODE and *CMP1 with the chosen code and operand, otherwise leave
+   them alone.  */
+
+static bool
+mips_canonicalize_int_order_test (enum rtx_code *code, rtx *cmp1,
+                                 enum machine_mode mode)
+{
+  HOST_WIDE_INT plus_one;
+
+  if (mips_int_order_operand_ok_p (*code, *cmp1))
+    return true;
+
+  if (GET_CODE (*cmp1) == CONST_INT)
+    switch (*code)
+      {
+      case LE:
+       plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode);
+       if (INTVAL (*cmp1) < plus_one)
+         {
+           *code = LT;
+           *cmp1 = force_reg (mode, GEN_INT (plus_one));
+           return true;
+         }
+       break;
+
+      case LEU:
+       plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode);
+       if (plus_one != 0)
+         {
+           *code = LTU;
+           *cmp1 = force_reg (mode, GEN_INT (plus_one));
+           return true;
+         }
+       break;
+
+      default:
+       break;
+      }
+  return false;
+}
+
+/* Compare CMP0 and CMP1 using ordering test CODE and store the result
+   in TARGET.  CMP0 and TARGET are register_operands.  If INVERT_PTR
+   is nonnull, it's OK to set TARGET to the inverse of the result and
+   flip *INVERT_PTR instead.  */
+
+static void
+mips_emit_int_order_test (enum rtx_code code, bool *invert_ptr,
+                         rtx target, rtx cmp0, rtx cmp1)
+{
+  enum machine_mode mode;
+
+  /* First see if there is a MIPS instruction that can do this operation.
+     If not, try doing the same for the inverse operation.  If that also
+     fails, force CMP1 into a register and try again.  */
+  mode = GET_MODE (cmp0);
+  if (mips_canonicalize_int_order_test (&code, &cmp1, mode))
+    mips_emit_binary (code, target, cmp0, cmp1);
+  else
+    {
+      enum rtx_code inv_code = reverse_condition (code);
+      if (!mips_canonicalize_int_order_test (&inv_code, &cmp1, mode))
+       {
+         cmp1 = force_reg (mode, cmp1);
+         mips_emit_int_order_test (code, invert_ptr, target, cmp0, cmp1);
+       }
+      else if (invert_ptr == 0)
+       {
+         rtx inv_target;
+
+         inv_target = mips_force_binary (GET_MODE (target),
+                                         inv_code, cmp0, cmp1);
+         mips_emit_binary (XOR, target, inv_target, const1_rtx);
+       }
+      else
+       {
+         *invert_ptr = !*invert_ptr;
+         mips_emit_binary (inv_code, target, cmp0, cmp1);
+       }
+    }
+}
+
+/* Return a register that is zero iff CMP0 and CMP1 are equal.
+   The register will have the same mode as CMP0.  */
+
+static rtx
+mips_zero_if_equal (rtx cmp0, rtx cmp1)
+{
+  if (cmp1 == const0_rtx)
+    return cmp0;
+
+  if (uns_arith_operand (cmp1, VOIDmode))
+    return expand_binop (GET_MODE (cmp0), xor_optab,
+                        cmp0, cmp1, 0, 0, OPTAB_DIRECT);
+
+  return expand_binop (GET_MODE (cmp0), sub_optab,
+                      cmp0, cmp1, 0, 0, OPTAB_DIRECT);
+}
+
+/* Convert *CODE into a code that can be used in a floating-point
+   scc instruction (C.cond.fmt).  Return true if the values of
+   the condition code registers will be inverted, with 0 indicating
+   that the condition holds.  */
+
+static bool
+mips_reversed_fp_cond (enum rtx_code *code)
+{
+  switch (*code)
+    {
+    case NE:
+    case LTGT:
+    case ORDERED:
+      *code = reverse_condition_maybe_unordered (*code);
+      return true;
+
+    default:
+      return false;
+    }
+}
+
+/* Convert a comparison into something that can be used in a branch or
+   conditional move.  cmp_operands[0] and cmp_operands[1] are the values
+   being compared and *CODE is the code used to compare them.
+
+   Update *CODE, *OP0 and *OP1 so that they describe the final comparison.
+   If NEED_EQ_NE_P, then only EQ or NE comparisons against zero are possible,
+   otherwise any standard branch condition can be used.  The standard branch
+   conditions are:
+
+      - EQ or NE between two registers.
+      - any comparison between a register and zero.  */
+
+static void
+mips_emit_compare (enum rtx_code *code, rtx *op0, rtx *op1, bool need_eq_ne_p)
+{
+  if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) == MODE_INT)
+    {
+      if (!need_eq_ne_p && cmp_operands[1] == const0_rtx)
+       {
+         *op0 = cmp_operands[0];
+         *op1 = cmp_operands[1];
+       }
+      else if (*code == EQ || *code == NE)
+       {
+         if (need_eq_ne_p)
+           {
+             *op0 = mips_zero_if_equal (cmp_operands[0], cmp_operands[1]);
+             *op1 = const0_rtx;
+           }
+         else
+           {
+             *op0 = cmp_operands[0];
+             *op1 = force_reg (GET_MODE (*op0), cmp_operands[1]);
+           }
+       }
+      else
+       {
+         /* The comparison needs a separate scc instruction.  Store the
+            result of the scc in *OP0 and compare it against zero.  */
+         bool invert = false;
+         *op0 = gen_reg_rtx (GET_MODE (cmp_operands[0]));
+         mips_emit_int_order_test (*code, &invert, *op0,
+                                   cmp_operands[0], cmp_operands[1]);
+         *code = (invert ? EQ : NE);
+         *op1 = const0_rtx;
+       }
+    }
+  else if (ALL_FIXED_POINT_MODE_P (GET_MODE (cmp_operands[0])))
+    {
+      *op0 = gen_rtx_REG (CCDSPmode, CCDSP_CC_REGNUM);
+      mips_emit_binary (*code, *op0, cmp_operands[0], cmp_operands[1]);
+      *code = NE;
+      *op1 = const0_rtx;
+    }
+  else
+    {
+      enum rtx_code cmp_code;
+
+      /* Floating-point tests use a separate C.cond.fmt comparison to
+        set a condition code register.  The branch or conditional move
+        will then compare that register against zero.
+
+        Set CMP_CODE to the code of the comparison instruction and
+        *CODE to the code that the branch or move should use.  */
+      cmp_code = *code;
+      *code = mips_reversed_fp_cond (&cmp_code) ? EQ : NE;
+      *op0 = (ISA_HAS_8CC
+             ? gen_reg_rtx (CCmode)
+             : gen_rtx_REG (CCmode, FPSW_REGNUM));
+      *op1 = const0_rtx;
+      mips_emit_binary (cmp_code, *op0, cmp_operands[0], cmp_operands[1]);
+    }
+}
+\f
+/* Try comparing cmp_operands[0] and cmp_operands[1] using rtl code CODE.
+   Store the result in TARGET and return true if successful.
+
+   On 64-bit targets, TARGET may be narrower than cmp_operands[0].  */
+
+bool
+mips_expand_scc (enum rtx_code code, rtx target)
+{
+  if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) != MODE_INT)
+    return false;
+
+  if (code == EQ || code == NE)
+    {
+      if (ISA_HAS_SEQ_SNE
+         && reg_imm10_operand (cmp_operands[1], GET_MODE (cmp_operands[1])))
+       mips_emit_binary (code, target, cmp_operands[0], cmp_operands[1]);
+      else
+       {
+         rtx zie = mips_zero_if_equal (cmp_operands[0], cmp_operands[1]);
+         mips_emit_binary (code, target, zie, const0_rtx);
+       }
+    }
+  else
+    mips_emit_int_order_test (code, 0, target,
+                             cmp_operands[0], cmp_operands[1]);
+  return true;
+}
+
+/* Compare cmp_operands[0] with cmp_operands[1] using comparison code
+   CODE and jump to OPERANDS[0] if the condition holds.  */
+
+void
+mips_expand_conditional_branch (rtx *operands, enum rtx_code code)
+{
+  rtx op0, op1, condition;
+
+  mips_emit_compare (&code, &op0, &op1, TARGET_MIPS16);
+  condition = gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
+  emit_jump_insn (gen_condjump (condition, operands[0]));
+}
+
+/* Implement:
+
+   (set temp (COND:CCV2 CMP_OP0 CMP_OP1))
+   (set DEST (unspec [TRUE_SRC FALSE_SRC temp] UNSPEC_MOVE_TF_PS))  */
+
+void
+mips_expand_vcondv2sf (rtx dest, rtx true_src, rtx false_src,
+                      enum rtx_code cond, rtx cmp_op0, rtx cmp_op1)
+{
+  rtx cmp_result;
+  bool reversed_p;
+
+  reversed_p = mips_reversed_fp_cond (&cond);
+  cmp_result = gen_reg_rtx (CCV2mode);
+  emit_insn (gen_scc_ps (cmp_result,
+                        gen_rtx_fmt_ee (cond, VOIDmode, cmp_op0, cmp_op1)));
+  if (reversed_p)
+    emit_insn (gen_mips_cond_move_tf_ps (dest, false_src, true_src,
+                                        cmp_result));
+  else
+    emit_insn (gen_mips_cond_move_tf_ps (dest, true_src, false_src,
+                                        cmp_result));
+}
+
+/* Compare cmp_operands[0] with cmp_operands[1] using the code of
+   OPERANDS[1].  Move OPERANDS[2] into OPERANDS[0] if the condition
+   holds, otherwise move OPERANDS[3] into OPERANDS[0].  */
+
+void
+mips_expand_conditional_move (rtx *operands)
+{
+  enum rtx_code code;
+  rtx cond, op0, op1;
+
+  code = GET_CODE (operands[1]);
+  mips_emit_compare (&code, &op0, &op1, true);
+  cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1),
+  emit_insn (gen_rtx_SET (VOIDmode, operands[0],
+                         gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), cond,
+                                               operands[2], operands[3])));
+}
+
+/* Compare cmp_operands[0] with cmp_operands[1] using rtl code CODE,
+   then trap if the condition holds.  */
+
+void
+mips_expand_conditional_trap (enum rtx_code code)
+{
+  rtx op0, op1;
+  enum machine_mode mode;
+
+  /* MIPS conditional trap instructions don't have GT or LE flavors,
+     so we must swap the operands and convert to LT and GE respectively.  */
+  switch (code)
+    {
+    case GT:
+    case LE:
+    case GTU:
+    case LEU:
+      code = swap_condition (code);
+      op0 = cmp_operands[1];
+      op1 = cmp_operands[0];
+      break;
+
+    default:
+      op0 = cmp_operands[0];
+      op1 = cmp_operands[1];
+      break;
+    }
+
+  mode = GET_MODE (cmp_operands[0]);
+  op0 = force_reg (mode, op0);
+  if (!arith_operand (op1, mode))
+    op1 = force_reg (mode, op1);
+
+  emit_insn (gen_rtx_TRAP_IF (VOIDmode,
+                             gen_rtx_fmt_ee (code, mode, op0, op1),
+                             const0_rtx));
+}
+\f
+/* Initialize *CUM for a call to a function of type FNTYPE.  */
+
+void
+mips_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype)
+{
+  memset (cum, 0, sizeof (*cum));
+  cum->prototype = (fntype && prototype_p (fntype));
+  cum->gp_reg_found = (cum->prototype && stdarg_p (fntype));
+}
+
+/* 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_get_arg_info (struct mips_arg_info *info, const CUMULATIVE_ARGS *cum,
+                  enum machine_mode mode, tree type, int named)
+{
+  bool doubleword_aligned_p;
+  unsigned int num_bytes, num_words, max_regs;
+
+  /* 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;
+
+  /* Decide whether it should go in a floating-point register, assuming
+     one is free.  Later code checks for availability.
+
+     The checks against UNITS_PER_FPVALUE handle the soft-float and
+     single-float cases.  */
+  switch (mips_abi)
+    {
+    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;
+
+    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;
+
+    case ABI_N32:
+    case ABI_64:
+      /* Scalar, complex and vector floating-point types are passed in
+        floating-point registers, as long as this is a named rather
+        than a variable argument.  */
+      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);
+
+      /* ??? 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.
+
+        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;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  /* See whether the argument has doubleword alignment.  */
+  doubleword_aligned_p = FUNCTION_ARG_BOUNDARY (mode, type) > BITS_PER_WORD;
+
+  /* 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;
+
+  max_regs = MAX_ARGS_IN_REGISTERS - info->reg_offset;
+
+  /* Partition the argument between registers and stack.  */
+  info->reg_words = MIN (num_words, max_regs);
+  info->stack_words = num_words - info->reg_words;
+}
+
+/* INFO describes a register argument that has the normal format for the
+   argument's mode.  Return the register it uses, assuming that FPRs are
+   available if HARD_FLOAT_P.  */
+
+static unsigned int
+mips_arg_regno (const struct mips_arg_info *info, bool hard_float_p)
+{
+  if (!info->fpr_p || !hard_float_p)
+    return GP_ARG_FIRST + info->reg_offset;
+  else if (mips_abi == ABI_32 && TARGET_DOUBLE_FLOAT && info->reg_offset > 0)
+    /* In o32, the second argument is always passed in $f14
+       for TARGET_DOUBLE_FLOAT, regardless of whether the
+       first argument was a word or doubleword.  */
+    return FP_ARG_FIRST + 2;
+  else
+    return FP_ARG_FIRST + info->reg_offset;
+}
+
+/* Implement TARGET_STRICT_ARGUMENT_NAMING.  */
+
+static bool
+mips_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
+{
+  return !TARGET_OLDABI;
+}
+
+/* Implement FUNCTION_ARG.  */
+
+rtx
+mips_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                  tree type, int named)
+{
+  struct mips_arg_info info;
+
+  /* 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 expander.
+     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);
+      else
+       return NULL;
+    }
+
+  mips_get_arg_info (&info, cum, mode, type, named);
+
+  /* Return straight away if the whole argument is passed on the stack.  */
+  if (info.reg_offset == MAX_ARGS_IN_REGISTERS)
+    return NULL;
+
+  /* The n32 and 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.  */
+  if (TARGET_NEWABI
+      && TARGET_HARD_FLOAT
+      && named
+      && type != 0
+      && TREE_CODE (type) == RECORD_TYPE
+      && TYPE_SIZE_UNIT (type)
+      && host_integerp (TYPE_SIZE_UNIT (type), 1))
+    {
+      tree field;
+
+      /* 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
+           && SCALAR_FLOAT_TYPE_P (TREE_TYPE (field))
+           && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
+           && host_integerp (bit_position (field), 0)
+           && int_bit_position (field) % BITS_PER_WORD == 0)
+         break;
+
+      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;
+
+         /* 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));
+
+         bitpos = 0;
+         field = TYPE_FIELDS (type);
+         for (i = 0; i < info.reg_words; i++)
+           {
+             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
+                 && SCALAR_FLOAT_TYPE_P (TREE_TYPE (field))
+                 && 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)
+    {
+      rtx real, imag;
+      enum machine_mode inner;
+      unsigned int regno;
+
+      inner = GET_MODE_INNER (mode);
+      regno = 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, regno);
+       }
+      else
+       {
+         gcc_assert (info.stack_words == 0);
+         real = gen_rtx_EXPR_LIST (VOIDmode,
+                                   gen_rtx_REG (inner, regno),
+                                   const0_rtx);
+         imag = gen_rtx_EXPR_LIST (VOIDmode,
+                                   gen_rtx_REG (inner,
+                                                regno + info.reg_words / 2),
+                                   GEN_INT (GET_MODE_SIZE (inner)));
+         return gen_rtx_PARALLEL (mode, gen_rtvec (2, real, imag));
+       }
+    }
+
+  return gen_rtx_REG (mode, mips_arg_regno (&info, TARGET_HARD_FLOAT));
+}
+
+/* Implement FUNCTION_ARG_ADVANCE.  */
+
+void
+mips_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                          tree type, int named)
+{
+  struct mips_arg_info info;
+
+  mips_get_arg_info (&info, cum, mode, type, named);
+
+  if (!info.fpr_p)
+    cum->gp_reg_found = true;
+
+  /* See the comment above the CUMULATIVE_ARGS structure in mips.h for
+     an explanation of what this code does.  It assumes that we're using
+     either the o32 or the o64 ABI, both of which pass at most 2 arguments
+     in FPRs.  */
+  if (cum->arg_number < 2 && info.fpr_p)
+    cum->fp_code += (mode == SFmode ? 1 : 2) << (cum->arg_number * 2);
+
+  /* Advance the register count.  This has the effect of setting
+     num_gprs to MAX_ARGS_IN_REGISTERS if a doubleword-aligned
+     argument required us to skip the final GPR and pass the whole
+     argument on the stack.  */
+  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;
+
+  /* Advance the stack word count.  */
+  if (info.stack_words > 0)
+    cum->stack_words = info.stack_offset + info.stack_words;
+
+  cum->arg_number++;
+}
+
+/* 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_get_arg_info (&info, cum, mode, type, named);
+  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
+mips_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)
+{
+  /* 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))
+      : (SCALAR_INT_MODE_P (mode)
+        || 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;
+  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 nonzero when an argument must be passed by reference.  */
+
+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;
+
+      /* ??? How should SCmode be handled?  */
+      if (mode == DImode || mode == DFmode
+         || mode == DQmode || mode == UDQmode
+         || mode == DAmode || mode == UDAmode)
+       return 0;
+
+      size = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+      return size == -1 || size > UNITS_PER_WORD;
+    }
+  else
+    {
+      /* If we have a variable-sized parameter, we have no choice.  */
+      return targetm.calls.must_pass_in_stack (mode, type);
+    }
+}
+
+/* Implement TARGET_CALLEE_COPIES.  */
+
+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.
+
+   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 int
+mips_fpr_return_fields (const_tree valtype, tree *fields)
+{
+  tree field;
+  int i;
+
+  if (!TARGET_NEWABI)
+    return 0;
+
+  if (TREE_CODE (valtype) != RECORD_TYPE)
+    return 0;
+
+  i = 0;
+  for (field = TYPE_FIELDS (valtype); field != 0; field = TREE_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+       continue;
+
+      if (!SCALAR_FLOAT_TYPE_P (TREE_TYPE (field)))
+       return 0;
+
+      if (i == 2)
+       return 0;
+
+      fields[i++] = field;
+    }
+  return i;
+}
+
+/* Implement TARGET_RETURN_IN_MSB.  For n32 & n64, we should return
+   a value in the most significant part of $2/$3 if:
+
+      - the target is big-endian;
+
+      - the value has a structure or union type (we generalize this to
+       cover aggregates from other languages too); and
+
+      - the structure is not returned in floating-point registers.  */
+
+static bool
+mips_return_in_msb (const_tree valtype)
+{
+  tree fields[2];
+
+  return (TARGET_NEWABI
+         && TARGET_BIG_ENDIAN
+         && AGGREGATE_TYPE_P (valtype)
+         && mips_fpr_return_fields (valtype, fields) == 0);
+}
+
+/* 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 the representation of an FPR return register when the
+   value being returned in FP_RETURN has mode VALUE_MODE and the
+   return type itself has mode TYPE_MODE.  On NewABI targets,
+   the two modes may be different for structures like:
+
+       struct __attribute__((packed)) foo { float f; }
+
+   where we return the SFmode value of "f" in FP_RETURN, but where
+   the structure itself has mode BLKmode.  */
+
+static rtx
+mips_return_fpr_single (enum machine_mode type_mode,
+                       enum machine_mode value_mode)
+{
+  rtx x;
+
+  x = gen_rtx_REG (value_mode, FP_RETURN);
+  if (type_mode != value_mode)
+    {
+      x = gen_rtx_EXPR_LIST (VOIDmode, x, const0_rtx);
+      x = gen_rtx_PARALLEL (type_mode, gen_rtvec (1, x));
+    }
+  return x;
+}
+
+/* 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.
+
+   For n32 & n64, $f0 always holds the first value and $f2 the second.
+   Otherwise the values are packed together as closely as possible.  */
+
+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;
+
+  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))));
+
+}
+
+/* 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.  */
+
+rtx
+mips_function_value (const_tree valtype, enum machine_mode mode)
+{
+  if (valtype)
+    {
+      tree fields[2];
+      int unsigned_p;
+
+      mode = TYPE_MODE (valtype);
+      unsigned_p = TYPE_UNSIGNED (valtype);
+
+      /* Since TARGET_PROMOTE_FUNCTION_RETURN unconditionally returns true,
+        we must promote the mode just as PROMOTE_MODE does.  */
+      mode = promote_mode (valtype, mode, &unsigned_p, 1);
+
+      /* Handle structures whose fields are returned in $f0/$f2.  */
+      switch (mips_fpr_return_fields (valtype, fields))
+       {
+       case 1:
+         return mips_return_fpr_single (mode,
+                                        TYPE_MODE (TREE_TYPE (fields[0])));
+
+       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);
+    }
+
+  if (!TARGET_MIPS16)
+    {
+      /* Handle long doubles for n32 & n64.  */
+      if (mode == TFmode)
+       return mips_return_fpr_pair (mode,
+                                    DImode, 0,
+                                    DImode, GET_MODE_SIZE (mode) / 2);
+
+      if (mips_return_mode_in_fpr_p (mode))
+       {
+         if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+           return mips_return_fpr_pair (mode,
+                                        GET_MODE_INNER (mode), 0,
+                                        GET_MODE_INNER (mode),
+                                        GET_MODE_SIZE (mode) / 2);
+         else
+           return gen_rtx_REG (mode, FP_RETURN);
+       }
+    }
+
+  return gen_rtx_REG (mode, GP_RETURN);
+}
+
+/* Implement TARGET_RETURN_IN_MEMORY.  Under the o32 and o64 ABIs,
+   all BLKmode objects are returned in memory.  Under the n32, n64
+   and embedded ABIs, small structures are returned in a register.
+   Objects with varying size must still be returned in memory, of
+   course.  */
+
+static bool
+mips_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED)
+{
+  return (TARGET_OLDABI
+         ? TYPE_MODE (type) == BLKmode
+         : !IN_RANGE (int_size_in_bytes (type), 0, 2 * UNITS_PER_WORD));
+}
+\f
+/* Implement TARGET_SETUP_INCOMING_VARARGS.  */
+
+static void
+mips_setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                            tree type, int *pretend_size ATTRIBUTE_UNUSED,
+                            int no_rtl)
+{
+  CUMULATIVE_ARGS local_cum;
+  int gp_saved, fp_saved;
+
+  /* The caller has advanced CUM up to, but not beyond, the last named
+     argument.  Advance a local copy of CUM past the last "real" named
+     argument, to find out how many registers are left over.  */
+  local_cum = *cum;
+  FUNCTION_ARG_ADVANCE (local_cum, mode, type, true);
+
+  /* Found out how many registers we need to save.  */
+  gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs;
+  fp_saved = (EABI_FLOAT_VARARGS_P
+             ? MAX_ARGS_IN_REGISTERS - local_cum.num_fprs
+             : 0);
+
+  if (!no_rtl)
+    {
+      if (gp_saved > 0)
+       {
+         rtx ptr, mem;
+
+         ptr = plus_constant (virtual_incoming_args_rtx,
+                              REG_PARM_STACK_SPACE (cfun->decl)
+                              - gp_saved * UNITS_PER_WORD);
+         mem = gen_frame_mem (BLKmode, ptr);
+         set_mem_alias_set (mem, get_varargs_alias_set ());
+
+         move_block_from_reg (local_cum.num_gprs + GP_ARG_FIRST,
+                              mem, gp_saved);
+       }
+      if (fp_saved > 0)
+       {
+         /* We can't use move_block_from_reg, because it will use
+            the wrong mode.  */
+         enum machine_mode mode;
+         int off, i;
+
+         /* Set OFF to the offset from virtual_incoming_args_rtx of
+            the first float register.  The FP save area lies below
+            the integer one, and is aligned to UNITS_PER_FPVALUE bytes.  */
+         off = (-gp_saved * UNITS_PER_WORD) & -UNITS_PER_FPVALUE;
+         off -= fp_saved * UNITS_PER_FPREG;
+
+         mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
+
+         for (i = local_cum.num_fprs; i < MAX_ARGS_IN_REGISTERS;
+              i += MAX_FPRS_PER_FMT)
+           {
+             rtx ptr, mem;
+
+             ptr = plus_constant (virtual_incoming_args_rtx, off);
+             mem = gen_frame_mem (mode, ptr);
+             set_mem_alias_set (mem, get_varargs_alias_set ());
+             mips_emit_move (mem, gen_rtx_REG (mode, FP_ARG_FIRST + i));
+             off += UNITS_PER_HWFPVALUE;
+           }
+       }
+    }
+  if (REG_PARM_STACK_SPACE (cfun->decl) == 0)
+    cfun->machine->varargs_size = (gp_saved * UNITS_PER_WORD
+                                  + fp_saved * UNITS_PER_FPREG);
+}
+
+/* Implement TARGET_BUILTIN_VA_LIST.  */
+
+static tree
+mips_build_builtin_va_list (void)
+{
+  if (EABI_FLOAT_VARARGS_P)
+    {
+      /* We keep 3 pointers, and two offsets.
+
+        Two pointers are to the overflow area, which starts at the CFA.
+        One of these is constant, for addressing into the GPR save area
+        below it.  The other is advanced up the stack through the
+        overflow region.
+
+        The third pointer is to the bottom of the GPR save area.
+        Since the FPR save area is just below it, we can address
+        FPR slots off this pointer.
+
+        We also keep two one-byte offsets, which are to be subtracted
+        from the constant pointers to yield addresses in the GPR and
+        FPR save areas.  These are downcounted as float or non-float
+        arguments are used, and when they get to zero, the argument
+        must be obtained from the overflow region.  */
+      tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff, f_res, record;
+      tree array, index;
+
+      record = lang_hooks.types.make_type (RECORD_TYPE);
+
+      f_ovfl = build_decl (FIELD_DECL, get_identifier ("__overflow_argptr"),
+                          ptr_type_node);
+      f_gtop = build_decl (FIELD_DECL, get_identifier ("__gpr_top"),
+                          ptr_type_node);
+      f_ftop = build_decl (FIELD_DECL, get_identifier ("__fpr_top"),
+                          ptr_type_node);
+      f_goff = build_decl (FIELD_DECL, get_identifier ("__gpr_offset"),
+                          unsigned_char_type_node);
+      f_foff = build_decl (FIELD_DECL, get_identifier ("__fpr_offset"),
+                          unsigned_char_type_node);
+      /* Explicitly pad to the size of a pointer, so that -Wpadded won't
+        warn on every user file.  */
+      index = build_int_cst (NULL_TREE, GET_MODE_SIZE (ptr_mode) - 2 - 1);
+      array = build_array_type (unsigned_char_type_node,
+                               build_index_type (index));
+      f_res = build_decl (FIELD_DECL, get_identifier ("__reserved"), array);
+
+      DECL_FIELD_CONTEXT (f_ovfl) = record;
+      DECL_FIELD_CONTEXT (f_gtop) = record;
+      DECL_FIELD_CONTEXT (f_ftop) = record;
+      DECL_FIELD_CONTEXT (f_goff) = record;
+      DECL_FIELD_CONTEXT (f_foff) = record;
+      DECL_FIELD_CONTEXT (f_res) = record;
+
+      TYPE_FIELDS (record) = f_ovfl;
+      TREE_CHAIN (f_ovfl) = f_gtop;
+      TREE_CHAIN (f_gtop) = f_ftop;
+      TREE_CHAIN (f_ftop) = f_goff;
+      TREE_CHAIN (f_goff) = f_foff;
+      TREE_CHAIN (f_foff) = f_res;
+
+      layout_type (record);
+      return record;
+    }
+  else if (TARGET_IRIX && TARGET_IRIX6)
+    /* On IRIX 6, this type is 'char *'.  */
+    return build_pointer_type (char_type_node);
+  else
+    /* Otherwise, we use 'void *'.  */
+    return ptr_type_node;
+}
+
+/* Implement TARGET_EXPAND_BUILTIN_VA_START.  */
+
+static void
+mips_va_start (tree valist, rtx nextarg)
+{
+  if (EABI_FLOAT_VARARGS_P)
+    {
+      const CUMULATIVE_ARGS *cum;
+      tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
+      tree ovfl, gtop, ftop, goff, foff;
+      tree t;
+      int gpr_save_area_size;
+      int fpr_save_area_size;
+      int fpr_offset;
+
+      cum = &crtl->args.info;
+      gpr_save_area_size
+       = (MAX_ARGS_IN_REGISTERS - cum->num_gprs) * UNITS_PER_WORD;
+      fpr_save_area_size
+       = (MAX_ARGS_IN_REGISTERS - cum->num_fprs) * UNITS_PER_FPREG;
+
+      f_ovfl = TYPE_FIELDS (va_list_type_node);
+      f_gtop = TREE_CHAIN (f_ovfl);
+      f_ftop = TREE_CHAIN (f_gtop);
+      f_goff = TREE_CHAIN (f_ftop);
+      f_foff = TREE_CHAIN (f_goff);
+
+      ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
+                    NULL_TREE);
+      gtop = build3 (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop,
+                    NULL_TREE);
+      ftop = build3 (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop,
+                    NULL_TREE);
+      goff = build3 (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff,
+                    NULL_TREE);
+      foff = build3 (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff,
+                    NULL_TREE);
+
+      /* Emit code to initialize OVFL, which points to the next varargs
+        stack argument.  CUM->STACK_WORDS gives the number of stack
+        words used by named arguments.  */
+      t = make_tree (TREE_TYPE (ovfl), virtual_incoming_args_rtx);
+      if (cum->stack_words > 0)
+       t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovfl), t,
+                   size_int (cum->stack_words * UNITS_PER_WORD));
+      t = build2 (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+      /* Emit code to initialize GTOP, the top of the GPR save area.  */
+      t = make_tree (TREE_TYPE (gtop), virtual_incoming_args_rtx);
+      t = build2 (MODIFY_EXPR, TREE_TYPE (gtop), gtop, t);
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+      /* Emit code to initialize FTOP, the top of the FPR save area.
+        This address is gpr_save_area_bytes below GTOP, rounded
+        down to the next fp-aligned boundary.  */
+      t = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
+      fpr_offset = gpr_save_area_size + UNITS_PER_FPVALUE - 1;
+      fpr_offset &= -UNITS_PER_FPVALUE;
+      if (fpr_offset)
+       t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ftop), t,
+                   size_int (-fpr_offset));
+      t = build2 (MODIFY_EXPR, TREE_TYPE (ftop), ftop, t);
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+      /* Emit code to initialize GOFF, the offset from GTOP of the
+        next GPR argument.  */
+      t = build2 (MODIFY_EXPR, TREE_TYPE (goff), goff,
+                 build_int_cst (TREE_TYPE (goff), gpr_save_area_size));
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+      /* Likewise emit code to initialize FOFF, the offset from FTOP
+        of the next FPR argument.  */
+      t = build2 (MODIFY_EXPR, TREE_TYPE (foff), foff,
+                 build_int_cst (TREE_TYPE (foff), fpr_save_area_size));
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
+  else
+    {
+      nextarg = plus_constant (nextarg, -cfun->machine->varargs_size);
+      std_expand_builtin_va_start (valist, nextarg);
+    }
+}
+
+/* Implement TARGET_GIMPLIFY_VA_ARG_EXPR.  */
+
+static tree
+mips_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
+                          gimple_seq *post_p)
+{
+  tree addr;
+  bool indirect_p;
+
+  indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+  if (indirect_p)
+    type = build_pointer_type (type);
+
+  if (!EABI_FLOAT_VARARGS_P)
+    addr = std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
+  else
+    {
+      tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
+      tree ovfl, top, off, align;
+      HOST_WIDE_INT size, rsize, osize;
+      tree t, u;
+
+      f_ovfl = TYPE_FIELDS (va_list_type_node);
+      f_gtop = TREE_CHAIN (f_ovfl);
+      f_ftop = TREE_CHAIN (f_gtop);
+      f_goff = TREE_CHAIN (f_ftop);
+      f_foff = TREE_CHAIN (f_goff);
+
+      /* Let:
+
+        TOP be the top of the GPR or FPR save area;
+        OFF be the offset from TOP of the next register;
+        ADDR_RTX be the address of the argument;
+        SIZE be the number of bytes in the argument type;
+        RSIZE be the number of bytes used to store the argument
+          when it's in the register save area; and
+        OSIZE be the number of bytes used to store it when it's
+          in the stack overflow area.
+
+        The code we want is:
+
+        1: off &= -rsize;        // round down
+        2: if (off != 0)
+        3:   {
+        4:     addr_rtx = top - off + (BYTES_BIG_ENDIAN ? RSIZE - SIZE : 0);
+        5:     off -= rsize;
+        6:   }
+        7: else
+        8:   {
+        9:     ovfl = ((intptr_t) ovfl + osize - 1) & -osize;
+        10:    addr_rtx = ovfl + (BYTES_BIG_ENDIAN ? OSIZE - SIZE : 0);
+        11:    ovfl += osize;
+        14:  }
+
+        [1] and [9] can sometimes be optimized away.  */
+
+      ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
+                    NULL_TREE);
+      size = int_size_in_bytes (type);
+
+      if (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_FLOAT
+         && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FPVALUE)
+       {
+         top = build3 (COMPONENT_REF, TREE_TYPE (f_ftop),
+                       unshare_expr (valist), f_ftop, NULL_TREE);
+         off = build3 (COMPONENT_REF, TREE_TYPE (f_foff),
+                       unshare_expr (valist), f_foff, NULL_TREE);
+
+         /* When va_start saves FPR arguments to the stack, each slot
+            takes up UNITS_PER_HWFPVALUE bytes, regardless of the
+            argument's precision.  */
+         rsize = UNITS_PER_HWFPVALUE;
+
+         /* Overflow arguments are padded to UNITS_PER_WORD bytes
+            (= PARM_BOUNDARY bits).  This can be different from RSIZE
+            in two cases:
+
+            (1) On 32-bit targets when TYPE is a structure such as:
+
+            struct s { float f; };
+
+            Such structures are passed in paired FPRs, so RSIZE
+            will be 8 bytes.  However, the structure only takes
+            up 4 bytes of memory, so OSIZE will only be 4.
+
+            (2) In combinations such as -mgp64 -msingle-float
+            -fshort-double.  Doubles passed in registers will then take
+            up 4 (UNITS_PER_HWFPVALUE) bytes, but those passed on the
+            stack take up UNITS_PER_WORD bytes.  */
+         osize = MAX (GET_MODE_SIZE (TYPE_MODE (type)), UNITS_PER_WORD);
+       }
+      else
+       {
+         top = build3 (COMPONENT_REF, TREE_TYPE (f_gtop),
+                       unshare_expr (valist), f_gtop, NULL_TREE);
+         off = build3 (COMPONENT_REF, TREE_TYPE (f_goff),
+                       unshare_expr (valist), f_goff, NULL_TREE);
+         rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
+         if (rsize > UNITS_PER_WORD)
+           {
+             /* [1] Emit code for: off &= -rsize.      */
+             t = build2 (BIT_AND_EXPR, TREE_TYPE (off), unshare_expr (off),
+                         build_int_cst (TREE_TYPE (off), -rsize));
+             gimplify_assign (unshare_expr (off), t, pre_p);
+           }
+         osize = rsize;
+       }
+
+      /* [2] Emit code to branch if off == 0.  */
+      t = build2 (NE_EXPR, boolean_type_node, off,
+                 build_int_cst (TREE_TYPE (off), 0));
+      addr = build3 (COND_EXPR, ptr_type_node, t, NULL_TREE, NULL_TREE);
+
+      /* [5] Emit code for: off -= rsize.  We do this as a form of
+        post-decrement not available to C.  */
+      t = fold_convert (TREE_TYPE (off), build_int_cst (NULL_TREE, rsize));
+      t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (off), off, t);
+
+      /* [4] Emit code for:
+        addr_rtx = top - off + (BYTES_BIG_ENDIAN ? RSIZE - SIZE : 0).  */
+      t = fold_convert (sizetype, t);
+      t = fold_build1 (NEGATE_EXPR, sizetype, t);
+      t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (top), top, t);
+      if (BYTES_BIG_ENDIAN && rsize > size)
+       {
+         u = size_int (rsize - size);
+         t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, u);
+       }
+      COND_EXPR_THEN (addr) = t;
+
+      if (osize > UNITS_PER_WORD)
+       {
+         /* [9] Emit: ovfl = ((intptr_t) ovfl + osize - 1) & -osize.  */
+         u = size_int (osize - 1);
+         t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovfl),
+                     unshare_expr (ovfl), u);
+         t = fold_convert (sizetype, t);
+         u = size_int (-osize);
+         t = build2 (BIT_AND_EXPR, sizetype, t, u);
+         t = fold_convert (TREE_TYPE (ovfl), t);
+         align = build2 (MODIFY_EXPR, TREE_TYPE (ovfl),
+                         unshare_expr (ovfl), t);
+       }
+      else
+       align = NULL;
+
+      /* [10, 11] Emit code for:
+        addr_rtx = ovfl + (BYTES_BIG_ENDIAN ? OSIZE - SIZE : 0)
+        ovfl += osize.  */
+      u = fold_convert (TREE_TYPE (ovfl), build_int_cst (NULL_TREE, osize));
+      t = build2 (POSTINCREMENT_EXPR, TREE_TYPE (ovfl), ovfl, u);
+      if (BYTES_BIG_ENDIAN && osize > size)
+       {
+         u = size_int (osize - size);
+         t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, u);
+       }
+
+      /* String [9] and [10, 11] together.  */
+      if (align)
+       t = build2 (COMPOUND_EXPR, TREE_TYPE (t), align, t);
+      COND_EXPR_ELSE (addr) = t;
+
+      addr = fold_convert (build_pointer_type (type), addr);
+      addr = build_va_arg_indirect_ref (addr);
+    }
+
+  if (indirect_p)
+    addr = build_va_arg_indirect_ref (addr);
+
+  return addr;
+}
+\f
+/* Start a definition of function NAME.  MIPS16_P indicates whether the
+   function contains MIPS16 code.  */
+
+static void
+mips_start_function_definition (const char *name, bool mips16_p)
+{
+  if (mips16_p)
+    fprintf (asm_out_file, "\t.set\tmips16\n");
+  else
+    fprintf (asm_out_file, "\t.set\tnomips16\n");
+
+  if (!flag_inhibit_size_directive)
+    {
+      fputs ("\t.ent\t", asm_out_file);
+      assemble_name (asm_out_file, name);
+      fputs ("\n", asm_out_file);
+    }
+
+  ASM_OUTPUT_TYPE_DIRECTIVE (asm_out_file, name, "function");
+
+  /* Start the definition proper.  */
+  assemble_name (asm_out_file, name);
+  fputs (":\n", asm_out_file);
+}
+
+/* End a function definition started by mips_start_function_definition.  */
+
+static void
+mips_end_function_definition (const char *name)
+{
+  if (!flag_inhibit_size_directive)
+    {
+      fputs ("\t.end\t", asm_out_file);
+      assemble_name (asm_out_file, name);
+      fputs ("\n", asm_out_file);
+    }
+}
+\f
+/* Return true if calls to X can use R_MIPS_CALL* relocations.  */
+
+static bool
+mips_ok_for_lazy_binding_p (rtx x)
+{
+  return (TARGET_USE_GOT
+         && GET_CODE (x) == SYMBOL_REF
+         && !SYMBOL_REF_BIND_NOW_P (x)
+         && !mips_symbol_binds_local_p (x));
+}
+
+/* Load function address ADDR into register DEST.  TYPE is as for
+   mips_expand_call.  Return true if we used an explicit lazy-binding
+   sequence.  */
+
+static bool
+mips_load_call_address (enum mips_call_type type, rtx dest, rtx addr)
+{
+  /* If we're generating PIC, and this call is to a global function,
+     try to allow its address to be resolved lazily.  This isn't
+     possible for sibcalls when $gp is call-saved because the value
+     of $gp on entry to the stub would be our caller's gp, not ours.  */
+  if (TARGET_EXPLICIT_RELOCS
+      && !(type == MIPS_CALL_SIBCALL && TARGET_CALL_SAVED_GP)
+      && mips_ok_for_lazy_binding_p (addr))
+    {
+      addr = mips_got_load (dest, addr, SYMBOL_GOTOFF_CALL);
+      emit_insn (gen_rtx_SET (VOIDmode, dest, addr));
+      return true;
+    }
+  else
+    {
+      mips_emit_move (dest, addr);
+      return false;
+    }
+}
+\f
+/* Each locally-defined hard-float MIPS16 function has a local symbol
+   associated with it.  This hash table maps the function symbol (FUNC)
+   to the local symbol (LOCAL). */
+struct mips16_local_alias GTY(()) {
+  rtx func;
+  rtx local;
+};
+static GTY ((param_is (struct mips16_local_alias))) htab_t mips16_local_aliases;
+
+/* Hash table callbacks for mips16_local_aliases.  */
+
+static hashval_t
+mips16_local_aliases_hash (const void *entry)
+{
+  const struct mips16_local_alias *alias;
+
+  alias = (const struct mips16_local_alias *) entry;
+  return htab_hash_string (XSTR (alias->func, 0));
+}
+
+static int
+mips16_local_aliases_eq (const void *entry1, const void *entry2)
+{
+  const struct mips16_local_alias *alias1, *alias2;
+
+  alias1 = (const struct mips16_local_alias *) entry1;
+  alias2 = (const struct mips16_local_alias *) entry2;
+  return rtx_equal_p (alias1->func, alias2->func);
+}
+
+/* FUNC is the symbol for a locally-defined hard-float MIPS16 function.
+   Return a local alias for it, creating a new one if necessary.  */
+
+static rtx
+mips16_local_alias (rtx func)
+{
+  struct mips16_local_alias *alias, tmp_alias;
+  void **slot;
+
+  /* Create the hash table if this is the first call.  */
+  if (mips16_local_aliases == NULL)
+    mips16_local_aliases = htab_create_ggc (37, mips16_local_aliases_hash,
+                                           mips16_local_aliases_eq, NULL);
+
+  /* Look up the function symbol, creating a new entry if need be.  */
+  tmp_alias.func = func;
+  slot = htab_find_slot (mips16_local_aliases, &tmp_alias, INSERT);
+  gcc_assert (slot != NULL);
+
+  alias = (struct mips16_local_alias *) *slot;
+  if (alias == NULL)
+    {
+      const char *func_name, *local_name;
+      rtx local;
+
+      /* Create a new SYMBOL_REF for the local symbol.  The choice of
+        __fn_local_* is based on the __fn_stub_* names that we've
+        traditionally used for the non-MIPS16 stub.  */
+      func_name = targetm.strip_name_encoding (XSTR (func, 0));
+      local_name = ACONCAT (("__fn_local_", func_name, NULL));
+      local = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (local_name));
+      SYMBOL_REF_FLAGS (local) = SYMBOL_REF_FLAGS (func) | SYMBOL_FLAG_LOCAL;
+
+      /* Create a new structure to represent the mapping.  */
+      alias = GGC_NEW (struct mips16_local_alias);
+      alias->func = func;
+      alias->local = local;
+      *slot = alias;
+    }
+  return alias->local;
+}
+\f
+/* A chained list of functions for which mips16_build_call_stub has already
+   generated a stub.  NAME is the name of the function and FP_RET_P is true
+   if the function returns a value in floating-point registers.  */
+struct mips16_stub {
+  struct mips16_stub *next;
+  char *name;
+  bool fp_ret_p;
+};
+static struct mips16_stub *mips16_stubs;
+
+/* Return a SYMBOL_REF for a MIPS16 function called NAME.  */
+
+static rtx
+mips16_stub_function (const char *name)
+{
+  rtx x;
+
+  x = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+  SYMBOL_REF_FLAGS (x) |= (SYMBOL_FLAG_EXTERNAL | SYMBOL_FLAG_FUNCTION);
+  return x;
+}
+
+/* Return the two-character string that identifies floating-point
+   return mode MODE in the name of a MIPS16 function stub.  */
+
+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 ();
+}
+
+/* Write instructions to move a 32-bit value between general register
+   GPREG and floating-point register FPREG.  DIRECTION is 't' to move
+   from GPREG to FPREG and 'f' to move in the opposite direction.  */
+
+static void
+mips_output_32bit_xfer (char direction, unsigned int gpreg, unsigned int fpreg)
+{
+  fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+          reg_names[gpreg], reg_names[fpreg]);
+}
+
+/* Likewise for 64-bit values.  */
+
+static void
+mips_output_64bit_xfer (char direction, unsigned int gpreg, unsigned int fpreg)
+{
+  if (TARGET_64BIT)
+    fprintf (asm_out_file, "\tdm%cc1\t%s,%s\n", direction,
+            reg_names[gpreg], reg_names[fpreg]);
+  else if (TARGET_FLOAT64)
+    {
+      fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+              reg_names[gpreg + TARGET_BIG_ENDIAN], reg_names[fpreg]);
+      fprintf (asm_out_file, "\tm%chc1\t%s,%s\n", direction,
+              reg_names[gpreg + TARGET_LITTLE_ENDIAN], reg_names[fpreg]);
+    }
+  else
+    {
+      /* Move the least-significant word.  */
+      fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+              reg_names[gpreg + TARGET_BIG_ENDIAN], reg_names[fpreg]);
+      /* ...then the most significant word.  */
+      fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+              reg_names[gpreg + TARGET_LITTLE_ENDIAN], reg_names[fpreg + 1]);
+    }
+}
+
+/* Write out code to move floating-point arguments into or out of
+   general registers.  FP_CODE is the code describing which arguments
+   are present (see the comment above the definition of CUMULATIVE_ARGS
+   in mips.h).  DIRECTION is as for mips_output_32bit_xfer.  */
+
+static void
+mips_output_args_xfer (int fp_code, char direction)
+{
+  unsigned int gparg, fparg, f;
+  CUMULATIVE_ARGS cum;
+
+  /* This code only works for o32 and o64.  */
+  gcc_assert (TARGET_OLDABI);
+
+  mips_init_cumulative_args (&cum, NULL);
+
+  for (f = (unsigned int) fp_code; f != 0; f >>= 2)
+    {
+      enum machine_mode mode;
+      struct mips_arg_info info;
+
+      if ((f & 3) == 1)
+       mode = SFmode;
+      else if ((f & 3) == 2)
+       mode = DFmode;
+      else
+       gcc_unreachable ();
+
+      mips_get_arg_info (&info, &cum, mode, NULL, true);
+      gparg = mips_arg_regno (&info, false);
+      fparg = mips_arg_regno (&info, true);
+
+      if (mode == SFmode)
+       mips_output_32bit_xfer (direction, gparg, fparg);
+      else
+       mips_output_64bit_xfer (direction, gparg, fparg);
+
+      mips_function_arg_advance (&cum, mode, NULL, true);
+    }
+}
+
+/* Write a MIPS16 stub for the current function.  This stub is used
+   for functions which take arguments in the floating-point registers.
+   It is normal-mode code that moves the floating-point arguments
+   into the general registers and then jumps to the MIPS16 code.  */
+
+static void
+mips16_build_function_stub (void)
+{
+  const char *fnname, *alias_name, *separator;
+  char *secname, *stubname;
+  tree stubdecl;
+  unsigned int f;
+  rtx symbol, alias;
+
+  /* Create the name of the stub, and its unique section.  */
+  symbol = XEXP (DECL_RTL (current_function_decl), 0);
+  alias = mips16_local_alias (symbol);
+
+  fnname = targetm.strip_name_encoding (XSTR (symbol, 0));
+  alias_name = targetm.strip_name_encoding (XSTR (alias, 0));
+  secname = ACONCAT ((".mips16.fn.", fnname, NULL));
+  stubname = ACONCAT (("__fn_stub_", fnname, NULL));
+
+  /* Build a decl for the stub.  */
+  stubdecl = build_decl (FUNCTION_DECL, get_identifier (stubname),
+                        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);
+
+  /* Output a comment.  */
+  fprintf (asm_out_file, "\t# Stub function for %s (",
+          current_function_name ());
+  separator = "";
+  for (f = (unsigned int) crtl->args.info.fp_code; f != 0; f >>= 2)
+    {
+      fprintf (asm_out_file, "%s%s", separator,
+              (f & 3) == 1 ? "float" : "double");
+      separator = ", ";
+    }
+  fprintf (asm_out_file, ")\n");
+
+  /* Start the function definition.  */
+  assemble_start_function (stubdecl, stubname);
+  mips_start_function_definition (stubname, false);
+
+  /* If generating pic2 code, either set up the global pointer or
+     switch to pic0.  */
+  if (TARGET_ABICALLS_PIC2)
+    {
+      if (TARGET_ABSOLUTE_ABICALLS)
+       fprintf (asm_out_file, "\t.option\tpic0\n");
+      else
+       {
+         output_asm_insn ("%(.cpload\t%^%)", NULL);
+         /* Emit an R_MIPS_NONE relocation to tell the linker what the
+            target function is.  Use a local GOT access when loading the
+            symbol, to cut down on the number of unnecessary GOT entries
+            for stubs that aren't needed.  */
+         output_asm_insn (".reloc\t0,R_MIPS_NONE,%0", &symbol);
+         symbol = alias;
+       }
+    }
+
+  /* Load the address of the MIPS16 function into $25.  Do this first so
+     that targets with coprocessor interlocks can use an MFC1 to fill the
+     delay slot.  */
+  output_asm_insn ("la\t%^,%0", &symbol);
+
+  /* Move the arguments from floating-point registers to general registers.  */
+  mips_output_args_xfer (crtl->args.info.fp_code, 'f');
+
+  /* Jump to the MIPS16 function.  */
+  output_asm_insn ("jr\t%^", NULL);
+
+  if (TARGET_ABICALLS_PIC2 && TARGET_ABSOLUTE_ABICALLS)
+    fprintf (asm_out_file, "\t.option\tpic2\n");
+
+  mips_end_function_definition (stubname);
+
+  /* If the linker needs to create a dynamic symbol for the target
+     function, it will associate the symbol with the stub (which,
+     unlike the target function, follows the proper calling conventions).
+     It is therefore useful to have a local alias for the target function,
+     so that it can still be identified as MIPS16 code.  As an optimization,
+     this symbol can also be used for indirect MIPS16 references from
+     within this file.  */
+  ASM_OUTPUT_DEF (asm_out_file, alias_name, fnname);
+
+  switch_to_section (function_section (current_function_decl));
+}
+
+/* The current function is a MIPS16 function that returns a value in an FPR.
+   Copy the return value from its soft-float to its hard-float location.
+   libgcc2 has special non-MIPS16 helper functions for each case.  */
+
+static void
+mips16_copy_fpr_return_value (void)
+{
+  rtx fn, insn, retval;
+  tree return_type;
+  enum machine_mode return_mode;
+  const char *name;
+
+  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));
+  fn = mips16_stub_function (name);
+
+  /* The function takes arguments in $2 (and possibly $3), so calls
+     to it cannot be lazily bound.  */
+  SYMBOL_REF_FLAGS (fn) |= SYMBOL_FLAG_BIND_NOW;
+
+  /* Model the call as something that takes the GPR return value as
+     argument and returns an "updated" value.  */
+  retval = gen_rtx_REG (return_mode, GP_RETURN);
+  insn = mips_expand_call (MIPS_CALL_EPILOGUE, retval, fn,
+                          const0_rtx, NULL_RTX, false);
+  use_reg (&CALL_INSN_FUNCTION_USAGE (insn), retval);
+}
+
+/* Consider building a stub for a MIPS16 call to function *FN_PTR.
+   RETVAL is the location of the return value, or null if this is
+   a "call" rather than a "call_value".  ARGS_SIZE is the size of the
+   arguments and FP_CODE is the code built by mips_function_arg;
+   see the comment above CUMULATIVE_ARGS for details.
+
+   There are three alternatives:
+
+   - If a stub was needed, emit the call and return the call insn itself.
+
+   - If we can avoid using a stub by redirecting the call, set *FN_PTR
+     to the new target and return null.
+
+   - If *FN_PTR doesn't need a stub, return null and leave *FN_PTR
+     unmodified.
+
+   A stub is needed for calls to functions that, in normal mode,
+   receive arguments in FPRs or return values in FPRs.  The stub
+   copies the arguments from their soft-float positions to their
+   hard-float positions, calls the real function, then copies the
+   return value from its hard-float position to its soft-float
+   position.
+
+   We can emit a JAL to *FN_PTR even when *FN_PTR might need a stub.
+   If *FN_PTR turns out to be to a non-MIPS16 function, the linker
+   automatically redirects the JAL to the stub, otherwise the JAL
+   continues to call FN directly.  */
+
+static rtx
+mips16_build_call_stub (rtx retval, rtx *fn_ptr, rtx args_size, int fp_code)
+{
+  const char *fnname;
+  bool fp_ret_p;
+  struct mips16_stub *l;
+  rtx insn, fn;
+
+  /* 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 NULL_RTX;
+
+  /* Figure out whether the value might come back in a floating-point
+     register.  */
+  fp_ret_p = retval && mips_return_mode_in_fpr_p (GET_MODE (retval));
+
+  /* We don't need to do anything if there were no floating-point
+     arguments and the value will not be returned in a floating-point
+     register.  */
+  if (fp_code == 0 && !fp_ret_p)
+    return NULL_RTX;
+
+  /* We don't need to do anything if this is a call to a special
+     MIPS16 support function.  */
+  fn = *fn_ptr;
+  if (mips16_stub_function_p (fn))
+    return NULL_RTX;
+
+  /* This code will only work for o32 and o64 abis.  The other ABI's
+     require more sophisticated support.  */
+  gcc_assert (TARGET_OLDABI);
+
+  /* If we're calling via a function pointer, use one of the magic
+     libgcc.a stubs provided for each (FP_CODE, FP_RET_P) combination.
+     Each stub expects the function address to arrive in register $2.  */
+  if (GET_CODE (fn) != SYMBOL_REF
+      || !call_insn_operand (fn, VOIDmode))
+    {
+      char buf[30];
+      rtx stub_fn, insn, addr;
+      bool lazy_p;
+
+      /* If this is a locally-defined and locally-binding function,
+        avoid the stub by calling the local alias directly.  */
+      if (mips16_local_function_p (fn))
+       {
+         *fn_ptr = mips16_local_alias (fn);
+         return NULL_RTX;
+       }
+
+      /* Create a SYMBOL_REF for the libgcc.a function.  */
+      if (fp_ret_p)
+       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);
+      stub_fn = mips16_stub_function (buf);
+
+      /* The function uses $2 as an argument, so calls to it
+        cannot be lazily bound.  */
+      SYMBOL_REF_FLAGS (stub_fn) |= SYMBOL_FLAG_BIND_NOW;
+
+      /* Load the target function into $2.  */
+      addr = gen_rtx_REG (Pmode, GP_REG_FIRST + 2);
+      lazy_p = mips_load_call_address (MIPS_CALL_NORMAL, addr, fn);
+
+      /* Emit the call.  */
+      insn = mips_expand_call (MIPS_CALL_NORMAL, retval, stub_fn,
+                              args_size, NULL_RTX, lazy_p);
+
+      /* Tell GCC that this call does indeed use the value of $2.  */
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), addr);
+
+      /* 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 (fp_ret_p)
+       CALL_INSN_FUNCTION_USAGE (insn) =
+         gen_rtx_EXPR_LIST (VOIDmode,
+                            gen_rtx_CLOBBER (VOIDmode,
+                                             gen_rtx_REG (word_mode, 18)),
+                            CALL_INSN_FUNCTION_USAGE (insn));
+
+      return insn;
+    }
+
+  /* We know the function we are going to call.  If we have already
+     built a stub, we don't need to do anything further.  */
+  fnname = targetm.strip_name_encoding (XSTR (fn, 0));
+  for (l = mips16_stubs; l != NULL; l = l->next)
+    if (strcmp (l->name, fnname) == 0)
+      break;
+
+  if (l == NULL)
+    {
+      const char *separator;
+      char *secname, *stubname;
+      tree stubid, stubdecl;
+      unsigned int f;
+
+      /* If the function does not return in FPRs, the special stub
+        section is named
+            .mips16.call.FNNAME
+
+        If the function does return in FPRs, the stub section is named
+            .mips16.call.fp.FNNAME
+
+        Build a decl for the stub.  */
+      secname = ACONCAT ((".mips16.call.", fp_ret_p ? "fp." : "",
+                         fnname, NULL));
+      stubname = ACONCAT (("__call_stub_", fp_ret_p ? "fp_" : "",
+                          fnname, NULL));
+      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);
+
+      /* Output a comment.  */
+      fprintf (asm_out_file, "\t# Stub function to call %s%s (",
+              (fp_ret_p
+               ? (GET_MODE (retval) == SFmode ? "float " : "double ")
+               : ""),
+              fnname);
+      separator = "";
+      for (f = (unsigned int) fp_code; f != 0; f >>= 2)
+       {
+         fprintf (asm_out_file, "%s%s", separator,
+                  (f & 3) == 1 ? "float" : "double");
+         separator = ", ";
+       }
+      fprintf (asm_out_file, ")\n");
+
+      /* Start the function definition.  */
+      assemble_start_function (stubdecl, stubname);
+      mips_start_function_definition (stubname, false);
+
+      if (!fp_ret_p)
+       {
+         /* Load the address of the MIPS16 function into $25.  Do this
+            first so that targets with coprocessor interlocks can use
+            an MFC1 to fill the delay slot.  */
+         if (TARGET_EXPLICIT_RELOCS)
+           {
+             output_asm_insn ("lui\t%^,%%hi(%0)", &fn);
+             output_asm_insn ("addiu\t%^,%^,%%lo(%0)", &fn);
+           }
+         else
+           output_asm_insn ("la\t%^,%0", &fn);
+       }
+
+      /* Move the arguments from general registers to floating-point
+        registers.  */
+      mips_output_args_xfer (fp_code, 't');
+
+      if (!fp_ret_p)
+       {
+         /* Jump to the previously-loaded address.  */
+         output_asm_insn ("jr\t%^", NULL);
+       }
+      else
+       {
+         /* Save the return address in $18 and call the non-MIPS16 function.
+            The stub's caller knows that $18 might be clobbered, even though
+            $18 is usually a call-saved register.  */
+         fprintf (asm_out_file, "\tmove\t%s,%s\n",
+                  reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]);
+         output_asm_insn (MIPS_CALL ("jal", &fn, 0), &fn);
+
+         /* Move the result from floating-point registers to
+            general registers.  */
+         switch (GET_MODE (retval))
+           {
+           case SCmode:
+             mips_output_32bit_xfer ('f', GP_RETURN + 1,
+                                     FP_REG_FIRST + MAX_FPRS_PER_FMT);
+             /* Fall though.  */
+           case SFmode:
+             mips_output_32bit_xfer ('f', GP_RETURN, FP_REG_FIRST);
+             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_RETURN + TARGET_LITTLE_ENDIAN],
+                          reg_names[GP_RETURN + TARGET_LITTLE_ENDIAN]);
+                 fprintf (asm_out_file, "\tor\t%s,%s,%s\n",
+                          reg_names[GP_RETURN],
+                          reg_names[GP_RETURN],
+                          reg_names[GP_RETURN + 1]);
+               }
+             break;
+
+           case DCmode:
+             mips_output_64bit_xfer ('f', GP_RETURN + (8 / UNITS_PER_WORD),
+                                     FP_REG_FIRST + MAX_FPRS_PER_FMT);
+             /* Fall though.  */
+           case DFmode:
+           case V2SFmode:
+             mips_output_64bit_xfer ('f', GP_RETURN, FP_REG_FIRST);
+             break;
+
+           default:
+             gcc_unreachable ();
+           }
+         fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 18]);
+       }
+
+#ifdef ASM_DECLARE_FUNCTION_SIZE
+      ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
+#endif
+
+      mips_end_function_definition (stubname);
+
+      /* Record this stub.  */
+      l = XNEW (struct mips16_stub);
+      l->name = xstrdup (fnname);
+      l->fp_ret_p = fp_ret_p;
+      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 (fp_ret_p && !l->fp_ret_p)
+    error ("cannot handle inconsistent calls to %qs", fnname);
+
+  if (retval == NULL_RTX)
+    insn = gen_call_internal_direct (fn, args_size);
+  else
+    insn = gen_call_value_internal_direct (retval, fn, args_size);
+  insn = mips_emit_call_insn (insn, fn, fn, false);
+
+  /* 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 (fp_ret_p)
+    CALL_INSN_FUNCTION_USAGE (insn) =
+      gen_rtx_EXPR_LIST (VOIDmode,
+                        gen_rtx_CLOBBER (VOIDmode,
+                                         gen_rtx_REG (word_mode, 18)),
+                        CALL_INSN_FUNCTION_USAGE (insn));
+
+  return insn;
+}
+\f
+/* Expand a call of type TYPE.  RESULT is where the result will go (null
+   for "call"s and "sibcall"s), 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.  LAZY_P is true if this call already
+   involves a lazily-bound function address (such as when calling
+   functions through a MIPS16 hard-float stub).
+
+   Return the call itself.  */
+
+rtx
+mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
+                 rtx args_size, rtx aux, bool lazy_p)
+{
+  rtx orig_addr, pattern, insn;
+  int fp_code;
+
+  fp_code = aux == 0 ? 0 : (int) GET_MODE (aux);
+  insn = mips16_build_call_stub (result, &addr, args_size, fp_code);
+  if (insn)
+    {
+      gcc_assert (!lazy_p && type == MIPS_CALL_NORMAL);
+      return insn;
+    }
+                                ;
+  orig_addr = addr;
+  if (!call_insn_operand (addr, VOIDmode))
+    {
+      if (type == MIPS_CALL_EPILOGUE)
+       addr = MIPS_EPILOGUE_TEMP (Pmode);
+      else
+       addr = gen_reg_rtx (Pmode);
+      lazy_p |= mips_load_call_address (type, addr, orig_addr);
+    }
+
+  if (result == 0)
+    {
+      rtx (*fn) (rtx, rtx);
+
+      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
+       fn = gen_call_split;
+      else if (type == MIPS_CALL_SIBCALL)
+       fn = gen_sibcall_internal;
+      else
+       fn = gen_call_internal;
+
+      pattern = fn (addr, args_size);
+    }
+  else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2)
+    {
+      /* Handle return values created by mips_return_fpr_pair.  */
+      rtx (*fn) (rtx, rtx, rtx, rtx);
+      rtx reg1, reg2;
+
+      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
+       fn = gen_call_value_multiple_split;
+      else if (type == MIPS_CALL_SIBCALL)
+       fn = gen_sibcall_value_multiple_internal;
+      else
+       fn = gen_call_value_multiple_internal;
+
+      reg1 = XEXP (XVECEXP (result, 0, 0), 0);
+      reg2 = XEXP (XVECEXP (result, 0, 1), 0);
+      pattern = fn (reg1, addr, args_size, reg2);
+    }
+  else
+    {
+      rtx (*fn) (rtx, rtx, rtx);
+
+      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
+       fn = gen_call_value_split;
+      else if (type == MIPS_CALL_SIBCALL)
+       fn = gen_sibcall_value_internal;
+      else
+       fn = gen_call_value_internal;
+
+      /* Handle return values created by mips_return_fpr_single.  */
+      if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 1)
+       result = XEXP (XVECEXP (result, 0, 0), 0);
+      pattern = fn (result, addr, args_size);
+    }
+
+  return mips_emit_call_insn (pattern, orig_addr, addr, lazy_p);
+}
+
+/* Split call instruction INSN into a $gp-clobbering call and
+   (where necessary) an instruction to restore $gp from its save slot.
+   CALL_PATTERN is the pattern of the new call.  */
+
+void
+mips_split_call (rtx insn, rtx call_pattern)
+{
+  rtx new_insn;
+
+  new_insn = emit_call_insn (call_pattern);
+  CALL_INSN_FUNCTION_USAGE (new_insn)
+    = copy_rtx (CALL_INSN_FUNCTION_USAGE (insn));
+  if (!find_reg_note (insn, REG_NORETURN, 0))
+    /* Pick a temporary register that is suitable for both MIPS16 and
+       non-MIPS16 code.  $4 and $5 are used for returning complex double
+       values in soft-float code, so $6 is the first suitable candidate.  */
+    mips_restore_gp (gen_rtx_REG (Pmode, GP_ARG_FIRST + 2));
+}
+
+/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL.  */
+
+static bool
+mips_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
+{
+  if (!TARGET_SIBCALLS)
+    return false;
+
+  /* We can't do a sibcall if the called function is a MIPS16 function
+     because there is no direct "jx" instruction equivalent to "jalx" to
+     switch the ISA mode.  We only care about cases where the sibling
+     and normal calls would both be direct.  */
+  if (decl
+      && mips_use_mips16_mode_p (decl)
+      && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
+    return false;
+
+  /* When -minterlink-mips16 is in effect, assume that non-locally-binding
+     functions could be MIPS16 ones unless an attribute explicitly tells
+     us otherwise.  */
+  if (TARGET_INTERLINK_MIPS16
+      && decl
+      && (DECL_EXTERNAL (decl) || !targetm.binds_local_p (decl))
+      && !mips_nomips16_decl_p (decl)
+      && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
+    return false;
+
+  /* Otherwise OK.  */
+  return true;
+}
+\f
+/* Emit code to move general operand SRC into condition-code
+   register DEST given that SCRATCH is a scratch TFmode FPR.
+   The sequence is:
+
+       FP1 = SRC
+       FP2 = 0.0f
+       DEST = FP2 < FP1
+
+   where FP1 and FP2 are single-precision FPRs taken from SCRATCH.  */
+
+void
+mips_expand_fcc_reload (rtx dest, rtx src, rtx scratch)
+{
+  rtx fp1, fp2;
+
+  /* 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));
+
+  fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
+  fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + MAX_FPRS_PER_FMT);
+
+  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.  */
+
+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
+    bits = BITS_PER_WORD;
+
+  mode = mode_for_size (bits, MODE_INT, 0);
+  delta = bits / BITS_PER_UNIT;
+
+  /* Allocate a buffer for the temporary registers.  */
+  regs = XALLOCAVEC (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++)
+    {
+      regs[i] = gen_reg_rtx (mode);
+      if (MEM_ALIGN (src) >= bits)
+       mips_emit_move (regs[i], adjust_address (src, mode, offset));
+      else
+       {
+         rtx part = adjust_address (src, BLKmode, offset);
+         if (!mips_expand_ext_as_unaligned_load (regs[i], part, bits, 0))
+           gcc_unreachable ();
+       }
+    }
+
+  /* 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_ins_as_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);
+    }
+}
+
+/* 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.  */
+
+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));
+
+  /* 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));
+}
+
+/* Move LENGTH bytes from SRC to DEST using a loop that moves BYTES_PER_ITER
+   bytes at a time.  LENGTH must be at least BYTES_PER_ITER.  Assume that
+   the memory regions do not overlap.  */
+
+static void
+mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length,
+                     HOST_WIDE_INT bytes_per_iter)
+{
+  rtx label, src_reg, dest_reg, final_src;
+  HOST_WIDE_INT leftover;
+
+  leftover = length % bytes_per_iter;
+  length -= leftover;
+
+  /* Create registers and memory references for use within the loop.  */
+  mips_adjust_block_mem (src, bytes_per_iter, &src_reg, &src);
+  mips_adjust_block_mem (dest, bytes_per_iter, &dest_reg, &dest);
+
+  /* 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, bytes_per_iter);
+
+  /* Move on to the next block.  */
+  mips_emit_move (src_reg, plus_constant (src_reg, bytes_per_iter));
+  mips_emit_move (dest_reg, plus_constant (dest_reg, bytes_per_iter));
+
+  /* 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, which copies LENGTH bytes from
+   memory reference SRC to memory reference DEST.  */
+
+bool
+mips_expand_block_move (rtx dest, rtx src, rtx length)
+{
+  if (GET_CODE (length) == CONST_INT)
+    {
+      if (INTVAL (length) <= MIPS_MAX_MOVE_BYTES_STRAIGHT)
+       {
+         mips_block_move_straight (dest, src, INTVAL (length));
+         return true;
+       }
+      else if (optimize)
+       {
+         mips_block_move_loop (dest, src, INTVAL (length),
+                               MIPS_MAX_MOVE_BYTES_PER_LOOP_ITER);
+         return true;
+       }
+    }
+  return false;
+}
+\f
+/* Expand a loop of synci insns for the address range [BEGIN, END).  */
+
+void
+mips_expand_synci_loop (rtx begin, rtx end)
+{
+  rtx inc, label, end_label, cmp_result, mask, length;
+
+  /* Create end_label.  */
+  end_label = gen_label_rtx ();
+
+  /* Check if begin equals end.  */
+  cmp_result = gen_rtx_EQ (VOIDmode, begin, end);
+  emit_jump_insn (gen_condjump (cmp_result, end_label));
+
+  /* Load INC with the cache line size (rdhwr INC,$1).  */
+  inc = gen_reg_rtx (Pmode);
+  emit_insn (Pmode == SImode
+            ? gen_rdhwr_synci_step_si (inc)
+            : gen_rdhwr_synci_step_di (inc));
+
+  /* Check if inc is 0.  */
+  cmp_result = gen_rtx_EQ (VOIDmode, inc, const0_rtx);
+  emit_jump_insn (gen_condjump (cmp_result, end_label));
+
+  /* Calculate mask.  */
+  mask = mips_force_unary (Pmode, NEG, inc);
+
+  /* Mask out begin by mask.  */
+  begin = mips_force_binary (Pmode, AND, begin, mask);
+
+  /* Calculate length.  */
+  length = mips_force_binary (Pmode, MINUS, end, begin);
+
+  /* Loop back to here.  */
+  label = gen_label_rtx ();
+  emit_label (label);
+
+  emit_insn (gen_synci (begin));
+
+  /* Update length.  */
+  mips_emit_binary (MINUS, length, length, inc);
+
+  /* Update begin.  */
+  mips_emit_binary (PLUS, begin, begin, inc);
+
+  /* Check if length is greater than 0.  */
+  cmp_result = gen_rtx_GT (VOIDmode, length, const0_rtx);
+  emit_jump_insn (gen_condjump (cmp_result, label));
+
+  emit_label (end_label);
+}
+\f
+/* Expand a QI or HI mode atomic memory operation.
+
+   GENERATOR contains a pointer to the gen_* function that generates
+   the SI mode underlying atomic operation using masks that we
+   calculate.
+
+   RESULT is the return register for the operation.  Its value is NULL
+   if unused.
+
+   MEM is the location of the atomic access.
+
+   OLDVAL is the first operand for the operation.
+
+   NEWVAL is the optional second operand for the operation.  Its value
+   is NULL if unused.  */
+
+void
+mips_expand_atomic_qihi (union mips_gen_fn_ptrs generator,
+                         rtx result, rtx mem, rtx oldval, rtx newval)
+{
+  rtx orig_addr, memsi_addr, memsi, shift, shiftsi, unshifted_mask;
+  rtx unshifted_mask_reg, mask, inverted_mask, si_op;
+  rtx res = NULL;
+  enum machine_mode mode;
+
+  mode = GET_MODE (mem);
+
+  /* Compute the address of the containing SImode value.  */
+  orig_addr = force_reg (Pmode, XEXP (mem, 0));
+  memsi_addr = mips_force_binary (Pmode, AND, orig_addr,
+                                 force_reg (Pmode, GEN_INT (-4)));
+
+  /* Create a memory reference for it.  */
+  memsi = gen_rtx_MEM (SImode, memsi_addr);
+  set_mem_alias_set (memsi, ALIAS_SET_MEMORY_BARRIER);
+  MEM_VOLATILE_P (memsi) = MEM_VOLATILE_P (mem);
+
+  /* Work out the byte offset of the QImode or HImode value,
+     counting from the least significant byte.  */
+  shift = mips_force_binary (Pmode, AND, orig_addr, GEN_INT (3));
+  if (TARGET_BIG_ENDIAN)
+    mips_emit_binary (XOR, shift, shift, GEN_INT (mode == QImode ? 3 : 2));
+
+  /* Multiply by eight to convert the shift value from bytes to bits.  */
+  mips_emit_binary (ASHIFT, shift, shift, GEN_INT (3));
+
+  /* Make the final shift an SImode value, so that it can be used in
+     SImode operations.  */
+  shiftsi = force_reg (SImode, gen_lowpart (SImode, shift));
+
+  /* Set MASK to an inclusive mask of the QImode or HImode value.  */
+  unshifted_mask = GEN_INT (GET_MODE_MASK (mode));
+  unshifted_mask_reg = force_reg (SImode, unshifted_mask);
+  mask = mips_force_binary (SImode, ASHIFT, unshifted_mask_reg, shiftsi);
+
+  /* Compute the equivalent exclusive mask.  */
+  inverted_mask = gen_reg_rtx (SImode);
+  emit_insn (gen_rtx_SET (VOIDmode, inverted_mask,
+                         gen_rtx_NOT (SImode, mask)));
+
+  /* Shift the old value into place.  */
+  if (oldval != const0_rtx)
+    {
+      oldval = convert_modes (SImode, mode, oldval, true);
+      oldval = force_reg (SImode, oldval);
+      oldval = mips_force_binary (SImode, ASHIFT, oldval, shiftsi);
+    }
+
+  /* Do the same for the new value.  */
+  if (newval && newval != const0_rtx)
+    {
+      newval = convert_modes (SImode, mode, newval, true);
+      newval = force_reg (SImode, newval);
+      newval = mips_force_binary (SImode, ASHIFT, newval, shiftsi);
+    }
+
+  /* Do the SImode atomic access.  */
+  if (result)
+    res = gen_reg_rtx (SImode);
+  if (newval)
+    si_op = generator.fn_6 (res, memsi, mask, inverted_mask, oldval, newval);
+  else if (result)
+    si_op = generator.fn_5 (res, memsi, mask, inverted_mask, oldval);
+  else
+    si_op = generator.fn_4 (memsi, mask, inverted_mask, oldval);
+
+  emit_insn (si_op);
+
+  if (result)
+    {
+      /* Shift and convert the result.  */
+      mips_emit_binary (AND, res, res, mask);
+      mips_emit_binary (LSHIFTRT, res, res, shiftsi);
+      mips_emit_move (result, gen_lowpart (GET_MODE (result), res));
+    }
+}
+
+/* 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.  */
+
+static bool
+mips_get_unaligned_mem (rtx *op, HOST_WIDE_INT width, HOST_WIDE_INT bitpos,
+                       rtx *left, rtx *right)
+{
+  rtx first, last;
+
+  /* Check that the operand really is a MEM.  Not all the extv and
+     extzv predicates are checked.  */
+  if (!MEM_P (*op))
+    return false;
+
+  /* Check that the size is valid.  */
+  if (width != 32 && (!TARGET_64BIT || width != 64))
+    return false;
+
+  /* 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;
+
+  /* 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;
+
+  /* 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));
+
+  /* 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);
+
+  /* Allocate to LEFT and RIGHT according to endianness.  LEFT should
+     correspond to the MSB and RIGHT to the LSB.  */
+  if (TARGET_BIG_ENDIAN)
+    *left = first, *right = last;
+  else
+    *left = last, *right = first;
+
+  return true;
+}
+
+/* Try to use left/right loads to expand an "extv" or "extzv" pattern.
+   DEST, SRC, WIDTH and BITPOS are the operands passed to the expander;
+   the operation is the equivalent of:
+
+      (set DEST (*_extract SRC WIDTH BITPOS))
+
+   Return true on success.  */
+
+bool
+mips_expand_ext_as_unaligned_load (rtx dest, rtx src, HOST_WIDE_INT width,
+                                  HOST_WIDE_INT bitpos)
+{
+  rtx left, right, temp;
+
+  /* If TARGET_64BIT, the destination of a 32-bit "extz" or "extzv" 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
+      && 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;
+}
+
+/* Try to use left/right stores to expand an "ins" pattern.  DEST, WIDTH,
+   BITPOS and SRC are the operands passed to the expander; the operation
+   is the equivalent of:
+
+       (set (zero_extract DEST WIDTH BITPOS) SRC)
+
+   Return true on success.  */
+
+bool
+mips_expand_ins_as_unaligned_store (rtx dest, rtx src, HOST_WIDE_INT width,
+                                   HOST_WIDE_INT bitpos)
+{
+  rtx left, right;
+  enum machine_mode mode;
+
+  if (!mips_get_unaligned_mem (&dest, width, bitpos, &left, &right))
+    return false;
+
+  mode = mode_for_size (width, MODE_INT, 0);
+  src = gen_lowpart (mode, src);
+  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;
+}
+
+/* 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;
+
+  if (!MEM_P (x))
+    return false;
+
+  size = MEM_SIZE (x);
+  return size && INTVAL (size) == GET_MODE_SIZE (mode);
+}
+
+/* Return true if (zero_extract OP WIDTH BITPOS) 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:
+
+     0 <= BITPOS < GET_MODE_BITSIZE (GET_MODE (op))
+     0 < WIDTH <= GET_MODE_BITSIZE (GET_MODE (op))
+     0 < BITPOS + WIDTH <= 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, HOST_WIDE_INT width, HOST_WIDE_INT bitpos)
+{
+  if (!ISA_HAS_EXT_INS
+      || !register_operand (op, VOIDmode)
+      || GET_MODE_BITSIZE (GET_MODE (op)) > BITS_PER_WORD)
+    return false;
+
+  if (!IN_RANGE (width, 1, GET_MODE_BITSIZE (GET_MODE (op)) - 1))
+    return false;
+
+  if (bitpos < 0 || bitpos + width > GET_MODE_BITSIZE (GET_MODE (op)))
+    return false;
+
+  return true;
+}
+
+/* Check if MASK and SHIFT are valid in mask-low-and-shift-left
+   operation if MAXLEN is the maxium length of consecutive bits that
+   can make up MASK.  MODE is the mode of the operation.  See
+   mask_low_and_shift_len for the actual definition.  */
+
+bool
+mask_low_and_shift_p (enum machine_mode mode, rtx mask, rtx shift, int maxlen)
+{
+  return IN_RANGE (mask_low_and_shift_len (mode, mask, shift), 1, maxlen);
+}
+
+/* The canonical form of a mask-low-and-shift-left operation is
+   (and (ashift X SHIFT) MASK) where MASK has the lower SHIFT number of bits
+   cleared.  Thus we need to shift MASK to the right before checking if it
+   is a valid mask value.  MODE is the mode of the operation.  If true
+   return the length of the mask, otherwise return -1.  */
+
+int
+mask_low_and_shift_len (enum machine_mode mode, rtx mask, rtx shift)
+{
+  HOST_WIDE_INT shval;
+
+  shval = INTVAL (shift) & (GET_MODE_BITSIZE (mode) - 1);
+  return exact_log2 ((UINTVAL (mask) >> shval) + 1);
+}
+\f
+/* Return true if -msplit-addresses is selected and should be honored.
+
+   -msplit-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 bool
+mips_split_addresses_p (void)
+{
+  return (TARGET_SPLIT_ADDRESSES
+         && optimize
+         && !TARGET_MIPS16
+         && !flag_pic
+         && !ABI_HAS_64BIT_SYMBOLS);
+}
+
+/* (Re-)Initialize mips_split_p, mips_lo_relocs and mips_hi_relocs.  */
+
+static void
+mips_init_relocs (void)
+{
+  memset (mips_split_p, '\0', sizeof (mips_split_p));
+  memset (mips_split_hi_p, '\0', sizeof (mips_split_hi_p));
+  memset (mips_hi_relocs, '\0', sizeof (mips_hi_relocs));
+  memset (mips_lo_relocs, '\0', sizeof (mips_lo_relocs));
+
+  if (ABI_HAS_64BIT_SYMBOLS)
+    {
+      if (TARGET_EXPLICIT_RELOCS)
+       {
+         mips_split_p[SYMBOL_64_HIGH] = true;
+         mips_hi_relocs[SYMBOL_64_HIGH] = "%highest(";
+         mips_lo_relocs[SYMBOL_64_HIGH] = "%higher(";
+
+         mips_split_p[SYMBOL_64_MID] = true;
+         mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
+         mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
+
+         mips_split_p[SYMBOL_64_LOW] = true;
+         mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
+         mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
+
+         mips_split_p[SYMBOL_ABSOLUTE] = true;
+         mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
+       }
+    }
+  else
+    {
+      if (TARGET_EXPLICIT_RELOCS || mips_split_addresses_p () || TARGET_MIPS16)
+       {
+         mips_split_p[SYMBOL_ABSOLUTE] = true;
+         mips_hi_relocs[SYMBOL_ABSOLUTE] = "%hi(";
+         mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
+
+         mips_lo_relocs[SYMBOL_32_HIGH] = "%hi(";
+       }
+    }
+
+  if (TARGET_MIPS16)
+    {
+      /* The high part is provided by a pseudo copy of $gp.  */
+      mips_split_p[SYMBOL_GP_RELATIVE] = true;
+      mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gprel(";
+    }
+  else 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 (TARGET_EXPLICIT_RELOCS)
+    {
+      mips_split_p[SYMBOL_GOT_PAGE_OFST] = true;
+      if (TARGET_NEWABI)
+       {
+         mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page(";
+         mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%got_ofst(";
+       }
+      else
+       {
+         mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
+         mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%lo(";
+       }
+      if (TARGET_MIPS16)
+       /* Expose the use of $28 as soon as possible.  */
+       mips_split_hi_p[SYMBOL_GOT_PAGE_OFST] = true;
+
+      if (TARGET_XGOT)
+       {
+         /* The HIGH and LO_SUM are matched by special .md patterns.  */
+         mips_split_p[SYMBOL_GOT_DISP] = true;
+
+         mips_split_p[SYMBOL_GOTOFF_DISP] = true;
+         mips_hi_relocs[SYMBOL_GOTOFF_DISP] = "%got_hi(";
+         mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_lo(";
+
+         mips_split_p[SYMBOL_GOTOFF_CALL] = true;
+         mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi(";
+         mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo(";
+       }
+      else
+       {
+         if (TARGET_NEWABI)
+           mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_disp(";
+         else
+           mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got(";
+         mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
+         if (TARGET_MIPS16)
+           /* Expose the use of $28 as soon as possible.  */
+           mips_split_p[SYMBOL_GOT_DISP] = true;
+       }
+    }
 
-         else if (FP_REG_P (regno1))
-           {
-             delay = DELAY_LOAD;
-             if (TARGET_FLOAT64)
-               {
-                 if (!TARGET_64BIT)
-                   abort_with_insn (insn, "bad move");
+  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(";
+    }
 
-#ifdef TARGET_FP_CALL_32
-                 if (FP_CALL_GP_REG_P (regno0))
-                   ret = "dmfc1\t%0,%1\n\tmfc1\t%D0,%1\n\tdsrl\t%0,32";
-                 else
-#endif
-                   ret = "dmfc1\t%0,%1";
-               }
-             else
-               ret = "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1";
-           }
+  mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd(";
+  mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm(";
 
-         else if (MD_REG_P (regno0) && GP_REG_P (regno1) && !TARGET_MIPS16)
-           {
-             delay = DELAY_HILO;
-             if (TARGET_64BIT)
-               {
-                 if (regno0 != HILO_REGNUM)
-                   ret = "mt%0\t%1";
-                 else if (regno1 == 0)
-                   ret = "mtlo\t%.\n\tmthi\t%.";
-               }
-             else
-               ret = "mthi\t%M1\n\tmtlo\t%L1";
-           }
+  mips_split_p[SYMBOL_DTPREL] = true;
+  mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
+  mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
 
-         else if (GP_REG_P (regno0) && MD_REG_P (regno1))
-           {
-             delay = DELAY_HILO;
-             if (TARGET_64BIT)
-               {
-                 if (regno1 != HILO_REGNUM)
-                   ret = "mf%1\t%0";
-               }
-             else
-               ret = "mfhi\t%M0\n\tmflo\t%L0";
-           }
+  mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
 
-         else if (TARGET_64BIT)
-           ret = "move\t%0,%1";
+  mips_split_p[SYMBOL_TPREL] = true;
+  mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
+  mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
 
-         else if (regno0 != (regno1+1))
-           ret = "move\t%0,%1\n\tmove\t%D0,%D1";
+  mips_lo_relocs[SYMBOL_HALF] = "%half(";
+}
 
-         else
-           ret = "move\t%D0,%D1\n\tmove\t%0,%1";
-       }
+/* If OP is an UNSPEC address, return the address to which it refers,
+   otherwise return OP itself.  */
 
-      else if (code1 == CONST_DOUBLE)
-       {
-         /* Move zero from $0 unless !TARGET_64BIT and recipient
-            is 64-bit fp reg, in which case generate a constant.  */
-         if (op1 != CONST0_RTX (GET_MODE (op1))
-             || (TARGET_FLOAT64 && !TARGET_64BIT && FP_REG_P (regno0)))
-           {
-             if (GET_MODE (op1) == DFmode)
-               {
-                 delay = DELAY_LOAD;
+static rtx
+mips_strip_unspec_address (rtx op)
+{
+  rtx base, offset;
 
-#ifdef TARGET_FP_CALL_32
-                 if (FP_CALL_GP_REG_P (regno0))
-                   {
-                     if (TARGET_FLOAT64 && !TARGET_64BIT)
-                       {
-                         split_double (op1, operands + 2, operands + 3);
-                         ret = "li\t%0,%2\n\tli\t%D0,%3";
-                       }
-                     else
-                       ret = "li.d\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32";
-                   }
-                 else
-#endif
-                   /* GNU as emits 64-bit code for li.d if the ISA is 3
-                      or higher.  For !TARGET_64BIT && gp registers we
-                      need to avoid this by using two li instructions
-                      instead.  */
-                   if (ISA_HAS_64BIT_REGS
-                       && ! TARGET_64BIT
-                       && ! FP_REG_P (regno0))
-                     {
-                       split_double (op1, operands + 2, operands + 3);
-                       ret = "li\t%0,%2\n\tli\t%D0,%3";
-                     }
-                   else
-                     ret = "li.d\t%0,%1";
-               }
+  split_const (op, &base, &offset);
+  if (UNSPEC_ADDRESS_P (base))
+    op = plus_constant (UNSPEC_ADDRESS (base), INTVAL (offset));
+  return op;
+}
 
-             else if (TARGET_64BIT)
-               {
-                 if (! TARGET_MIPS16)
-                   ret = "dli\t%0,%1";
-               }
+/* Print symbolic operand OP, which is part of a HIGH or LO_SUM
+   in context CONTEXT.  RELOCS is the array of relocations to use.  */
 
-             else
-               {
-                 split_double (op1, operands + 2, operands + 3);
-                 ret = "li\t%0,%2\n\tli\t%D0,%3";
-               }
-           }
+static void
+mips_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);
+  gcc_assert (relocs[symbol_type]);
+
+  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);
+}
+
+/* Print the text for PRINT_OPERAND punctation character CH to FILE.
+   The punctuation characters are:
+
+   '(' Start a nested ".set noreorder" block.
+   ')' End a nested ".set noreorder" block.
+   '[' Start a nested ".set noat" block.
+   ']' End a nested ".set noat" block.
+   '<' Start a nested ".set nomacro" block.
+   '>' End a nested ".set nomacro" block.
+   '*' Behave like %(%< if generating a delayed-branch sequence.
+   '#' Print a nop if in a ".set noreorder" block.
+   '/' Like '#', but do nothing within a delayed-branch sequence.
+   '?' Print "l" if mips_branch_likely is true
+   '~' Print a nop if mips_branch_likely is true
+   '.' Print the name of the register with a hard-wired zero (zero or $0).
+   '@' Print the name of the assembler temporary register (at or $1).
+   '^' Print the name of the pic call-through register (t9 or $25).
+   '+' Print the name of the gp register (usually gp or $28).
+   '$' Print the name of the stack pointer register (sp or $29).
+   '|' Print ".set push; .set mips2" if !ISA_HAS_LL_SC.
+   '-' Print ".set pop" under the same conditions for '|'.
 
-         else
-           {
-             if (GP_REG_P (regno0))
-               ret = (TARGET_64BIT
-#ifdef TARGET_FP_CALL_32
-                      && ! FP_CALL_GP_REG_P (regno0)
-#endif
-                      ? "move\t%0,%."
-                      : "move\t%0,%.\n\tmove\t%D0,%.");
+   See also mips_init_print_operand_pucnt.  */
 
-             else if (FP_REG_P (regno0))
-               {
-                 delay = DELAY_LOAD;
-                 ret = (TARGET_64BIT
-                        ? "dmtc1\t%.,%0"
-                        : "mtc1\t%.,%0\n\tmtc1\t%.,%D0");
-               }
-           }
-       }
+static void
+mips_print_operand_punctuation (FILE *file, int ch)
+{
+  switch (ch)
+    {
+    case '(':
+      if (set_noreorder++ == 0)
+       fputs (".set\tnoreorder\n\t", file);
+      break;
 
-      else if (code1 == CONST_INT && INTVAL (op1) == 0 && ! TARGET_MIPS16)
-       {
-         if (GP_REG_P (regno0))
-           ret = (TARGET_64BIT
-                  ? "move\t%0,%."
-                  : "move\t%0,%.\n\tmove\t%D0,%.");
+    case ')':
+      gcc_assert (set_noreorder > 0);
+      if (--set_noreorder == 0)
+       fputs ("\n\t.set\treorder", file);
+      break;
 
-         else if (FP_REG_P (regno0))
-           {
-             delay = DELAY_LOAD;
-             ret = (TARGET_64BIT
-                    ? "dmtc1\t%.,%0"
-                    : (TARGET_FLOAT64
-                       ? "li.d\t%0,%1"
-                       : "mtc1\t%.,%0\n\tmtc1\t%.,%D0"));
-           }
-         else if (MD_REG_P (regno0))
-           {
-             delay = DELAY_HILO;
-             ret =  (regno0 == HILO_REGNUM
-                     ? "mtlo\t%.\n\tmthi\t%."
-                     : "mt%0\t%.\n");
-           }
-       }
+    case '[':
+      if (set_noat++ == 0)
+       fputs (".set\tnoat\n\t", file);
+      break;
+
+    case ']':
+      gcc_assert (set_noat > 0);
+      if (--set_noat == 0)
+       fputs ("\n\t.set\tat", file);
+      break;
+
+    case '<':
+      if (set_nomacro++ == 0)
+       fputs (".set\tnomacro\n\t", file);
+      break;
+
+    case '>':
+      gcc_assert (set_nomacro > 0);
+      if (--set_nomacro == 0)
+       fputs ("\n\t.set\tmacro", file);
+      break;
 
-      else if (code1 == CONST_INT && GET_MODE (op0) == DImode
-              && GP_REG_P (regno0))
+    case '*':
+      if (final_sequence != 0)
        {
-         if (TARGET_64BIT)
-           {
-             if (TARGET_MIPS16)
-               {
-                 if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
-                   ret = "li\t%0,%1";
-                 else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
-                   ret = "li\t%0,%n1\n\tneg\t%0";
-               }
-             else if (GET_CODE (operands[1]) == SIGN_EXTEND)
-               ret = "li\t%0,%1\t\t# %X1";
-             else if (HOST_BITS_PER_WIDE_INT < 64)
-               /* We can't use 'X' for negative numbers, because then we won't
-                  get the right value for the upper 32 bits.  */
-               ret = (INTVAL (op1) < 0
-                      ? "dli\t%0,%1\t\t\t# %X1"
-                      : "dli\t%0,%X1\t\t# %1");
-             else
-               /* We must use 'X', because otherwise LONG_MIN will print as
-                  a number that the assembler won't accept.  */
-               ret = "dli\t%0,%X1\t\t# %1";
-           }
-         else if (HOST_BITS_PER_WIDE_INT < 64)
-           {
-             operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1);
-             if (TARGET_MIPS16)
-               {
-                 if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
-                   ret = "li\t%M0,%2\n\tli\t%L0,%1";
-                 else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
-                   {
-                     operands[2] = GEN_INT (1);
-                     ret = "li\t%M0,%2\n\tneg\t%M0\n\tli\t%L0,%n1\n\tneg\t%L0";
-                   }
-               }
-             else
-               ret = "li\t%M0,%2\n\tli\t%L0,%1";
-           }
-         else
-           {
-             /* We use multiple shifts here, to avoid warnings about out
-                of range shifts on 32 bit hosts.  */
-             operands[2] = GEN_INT (INTVAL (operands[1]) >> 16 >> 16);
-             operands[1]
-               = GEN_INT (INTVAL (operands[1]) << 16 << 16 >> 16 >> 16);
-             if (TARGET_MIPS16)
-               {
-                 if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
-                   ret = "li\t%M0,%2\n\tli\t%L0,%1";
-                 else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
-                   {
-                     operands[2] = GEN_INT (1);
-                     ret = "li\t%M0,%2\n\tneg\t%M0\n\tli\t%L0,%n1\n\tneg\t%L0";
-                   }
-               }
-             else
-               ret = "li\t%M0,%2\n\tli\t%L0,%1";
-           }
+         mips_print_operand_punctuation (file, '(');
+         mips_print_operand_punctuation (file, '<');
        }
+      break;
 
-      else if (code1 == MEM)
-       {
-         delay = DELAY_LOAD;
+    case '#':
+      if (set_noreorder != 0)
+       fputs ("\n\tnop", file);
+      break;
+
+    case '/':
+      /* Print an extra newline so that the delayed insn is separated
+        from the following ones.  This looks neater and is consistent
+        with non-nop delayed sequences.  */
+      if (set_noreorder != 0 && final_sequence == 0)
+       fputs ("\n\tnop\n", file);
+      break;
 
-         if (TARGET_STATS)
-           mips_count_memory_refs (op1, 2);
+    case '?':
+      if (mips_branch_likely)
+       putc ('l', file);
+      break;
 
-         if (FP_REG_P (regno0))
-           ret = "l.d\t%0,%1";
+    case '~':
+      if (mips_branch_likely)
+       fputs ("\n\tnop", file);
+      break;
 
-         else if (TARGET_64BIT)
-           {
+    case '.':
+      fputs (reg_names[GP_REG_FIRST + 0], file);
+      break;
 
-#ifdef TARGET_FP_CALL_32
-             if (FP_CALL_GP_REG_P (regno0))
-               ret = (double_memory_operand (op1, GET_MODE (op1))
-                      ? "lwu\t%0,%1\n\tlwu\t%D0,4+%1"
-                      : "ld\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32");
-             else
-#endif
-               ret = "ld\t%0,%1";
-           }
+    case '@':
+      fputs (reg_names[GP_REG_FIRST + 1], file);
+      break;
 
-         else if (double_memory_operand (op1, GET_MODE (op1)))
-           ret = (reg_mentioned_p (op0, op1)
-                  ? "lw\t%D0,%D1\n\tlw\t%0,%1"
-                  : "lw\t%0,%1\n\tlw\t%D0,%D1");
+    case '^':
+      fputs (reg_names[PIC_FUNCTION_ADDR_REGNUM], file);
+      break;
 
-         if (ret != 0 && MEM_VOLATILE_P (op1))
-           {
-             size_t i = strlen (ret);
+    case '+':
+      fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file);
+      break;
 
-             if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
-               abort ();
+    case '$':
+      fputs (reg_names[STACK_POINTER_REGNUM], file);
+      break;
 
-             sprintf (volatile_buffer, "%%{%s%%}", ret);
-             ret = volatile_buffer;
-           }
-       }
+    case '|':
+      if (!ISA_HAS_LL_SC)
+       fputs (".set\tpush\n\t.set\tmips2\n\t", file);
+      break;
 
-      else if (code1 == LABEL_REF)
-       {
-         if (TARGET_STATS)
-           mips_count_memory_refs (op1, 2);
-
-         if (GET_CODE (operands[1]) == SIGN_EXTEND)
-           /* We deliberately remove the 'a' from '%1', so that we don't
-              have to add SIGN_EXTEND support to print_operand_address.
-              print_operand will just call print_operand_address in this
-              case, so there is no problem.  */
-           ret = "la\t%0,%1";
-         else
-           ret = "dla\t%0,%a1";
-       }
-      else if (code1 == SYMBOL_REF || code1 == CONST)
-       {
-         if (TARGET_MIPS16
-             && code1 == CONST
-             && GET_CODE (XEXP (op1, 0)) == REG
-             && REGNO (XEXP (op1, 0)) == GP_REG_FIRST + 28)
-           {
-             /* This case arises on the mips16; see
-                 mips16_gp_pseudo_reg.  */
-             ret = "move\t%0,%+";
-           }
-         else if (TARGET_MIPS16
-                  && code1 == SYMBOL_REF
-                  && SYMBOL_REF_FLAG (op1)
-                  && (XSTR (op1, 0)[0] != '*'
-                      || strncmp (XSTR (op1, 0) + 1,
-                                  LOCAL_LABEL_PREFIX,
-                                  sizeof LOCAL_LABEL_PREFIX - 1) != 0))
-           {
-             /* This can occur when reloading the address of a GP
-                 relative symbol on the mips16.  */
-             ret = "move\t%0,%+\n\taddu\t%0,%%gprel(%a1)";
-           }
-         else
-           {
-             if (TARGET_STATS)
-               mips_count_memory_refs (op1, 2);
-
-             if (GET_CODE (operands[1]) == SIGN_EXTEND)
-               /* We deliberately remove the 'a' from '%1', so that we don't
-                  have to add SIGN_EXTEND support to print_operand_address.
-                  print_operand will just call print_operand_address in this
-                  case, so there is no problem.  */
-               ret = "la\t%0,%1";
-             else
-               ret = "dla\t%0,%a1";
-           }
-       }
+    case '-':
+      if (!ISA_HAS_LL_SC)
+       fputs ("\n\t.set\tpop", file);
+      break;
+
+    default:
+      gcc_unreachable ();
+      break;
     }
+}
+
+/* Initialize mips_print_operand_punct.  */
+
+static void
+mips_init_print_operand_punct (void)
+{
+  const char *p;
+
+  for (p = "()[]<>*#/?~.@^+$|-"; *p; p++)
+    mips_print_operand_punct[(unsigned char) *p] = true;
+}
+
+/* PRINT_OPERAND prefix LETTER refers to the integer branch instruction
+   associated with condition CODE.  Print the condition part of the
+   opcode to FILE.  */
 
-  else if (code0 == MEM)
+static void
+mips_print_int_branch_condition (FILE *file, enum rtx_code code, int letter)
+{
+  switch (code)
     {
-      if (code1 == REG)
-       {
-         int regno1 = REGNO (op1) + subreg_offset1;
+    case EQ:
+    case NE:
+    case GT:
+    case GE:
+    case LT:
+    case LE:
+    case GTU:
+    case GEU:
+    case LTU:
+    case LEU:
+      /* Conveniently, the MIPS names for these conditions are the same
+        as their RTL equivalents.  */
+      fputs (GET_RTX_NAME (code), file);
+      break;
 
-         if (FP_REG_P (regno1))
-           ret = "s.d\t%1,%0";
+    default:
+      output_operand_lossage ("'%%%c' is not a valid operand prefix", letter);
+      break;
+    }
+}
 
-         else if (TARGET_64BIT)
-           {
+/* Likewise floating-point branches.  */
 
-#ifdef TARGET_FP_CALL_32
-             if (FP_CALL_GP_REG_P (regno1))
-               ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tsd\t%1,%0";
-             else
-#endif
-               ret = "sd\t%1,%0";
-           }
+static void
+mips_print_float_branch_condition (FILE *file, enum rtx_code code, int letter)
+{
+  switch (code)
+    {
+    case EQ:
+      fputs ("c1f", file);
+      break;
 
-         else if (double_memory_operand (op0, GET_MODE (op0)))
-           ret = "sw\t%1,%0\n\tsw\t%D1,%D0";
-       }
+    case NE:
+      fputs ("c1t", file);
+      break;
+
+    default:
+      output_operand_lossage ("'%%%c' is not a valid operand prefix", letter);
+      break;
+    }
+}
+
+/* Implement the PRINT_OPERAND macro.  The MIPS-specific operand codes are:
+
+   'X' Print CONST_INT OP in hexadecimal format.
+   'x' Print the low 16 bits of CONST_INT OP in hexadecimal format.
+   'd' Print CONST_INT OP in decimal.
+   'm' Print one less than CONST_INT OP in decimal.
+   'h' Print the high-part relocation associated with OP, after stripping
+         any outermost HIGH.
+   'R' Print the low-part relocation associated with OP.
+   'C' Print the integer branch condition for comparison OP.
+   'N' Print the inverse of the integer branch condition for comparison OP.
+   'F' Print the FPU branch condition for comparison OP.
+   'W' Print the inverse of the FPU branch condition for comparison OP.
+   'T' Print 'f' for (eq:CC ...), 't' for (ne:CC ...),
+             'z' for (eq:?I ...), 'n' for (ne:?I ...).
+   't' Like 'T', but with the EQ/NE cases reversed
+   'Y' Print mips_fp_conditions[INTVAL (OP)]
+   'Z' Print OP and a comma for ISA_HAS_8CC, otherwise print nothing.
+   'q' Print a DSP accumulator register.
+   'D' Print the second part of a double-word register or memory operand.
+   'L' Print the low-order register in a double-word register operand.
+   'M' Print high-order register in a double-word register operand.
+   'z' Print $0 if OP is zero, otherwise print OP normally.  */
+
+void
+mips_print_operand (FILE *file, rtx op, int letter)
+{
+  enum rtx_code code;
+
+  if (PRINT_OPERAND_PUNCT_VALID_P (letter))
+    {
+      mips_print_operand_punctuation (file, letter);
+      return;
+    }
+
+  gcc_assert (op);
+  code = GET_CODE (op);
+
+  switch (letter)
+    {
+    case 'X':
+      if (GET_CODE (op) == CONST_INT)
+       fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
+      else
+       output_operand_lossage ("invalid use of '%%%c'", letter);
+      break;
+
+    case 'x':
+      if (GET_CODE (op) == CONST_INT)
+       fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff);
+      else
+       output_operand_lossage ("invalid use of '%%%c'", letter);
+      break;
+
+    case 'd':
+      if (GET_CODE (op) == CONST_INT)
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op));
+      else
+       output_operand_lossage ("invalid use of '%%%c'", letter);
+      break;
+
+    case 'm':
+      if (GET_CODE (op) == CONST_INT)
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op) - 1);
+      else
+       output_operand_lossage ("invalid use of '%%%c'", letter);
+      break;
+
+    case 'h':
+      if (code == HIGH)
+       op = XEXP (op, 0);
+      mips_print_operand_reloc (file, op, SYMBOL_CONTEXT_LEA, mips_hi_relocs);
+      break;
+
+    case 'R':
+      mips_print_operand_reloc (file, op, SYMBOL_CONTEXT_LEA, mips_lo_relocs);
+      break;
+
+    case 'C':
+      mips_print_int_branch_condition (file, code, letter);
+      break;
+
+    case 'N':
+      mips_print_int_branch_condition (file, reverse_condition (code), letter);
+      break;
+
+    case 'F':
+      mips_print_float_branch_condition (file, code, letter);
+      break;
+
+    case 'W':
+      mips_print_float_branch_condition (file, reverse_condition (code),
+                                        letter);
+      break;
+
+    case 'T':
+    case 't':
+      {
+       int truth = (code == NE) == (letter == 'T');
+       fputc ("zfnt"[truth * 2 + (GET_MODE (op) == CCmode)], file);
+      }
+      break;
+
+    case 'Y':
+      if (code == CONST_INT && UINTVAL (op) < ARRAY_SIZE (mips_fp_conditions))
+       fputs (mips_fp_conditions[UINTVAL (op)], file);
+      else
+       output_operand_lossage ("'%%%c' is not a valid operand prefix",
+                               letter);
+      break;
 
-      else if (((code1 == CONST_INT && INTVAL (op1) == 0)
-               || (code1 == CONST_DOUBLE
-                   && op1 == CONST0_RTX (GET_MODE (op1))))
-              && (TARGET_64BIT
-                  || double_memory_operand (op0, GET_MODE (op0))))
+    case 'Z':
+      if (ISA_HAS_8CC)
        {
-         if (TARGET_64BIT)
-           ret = "sd\t%.,%0";
-         else
-           ret = "sw\t%.,%0\n\tsw\t%.,%D0";
+         mips_print_operand (file, op, 0);
+         fputc (',', file);
        }
+      break;
 
-      if (TARGET_STATS)
-       mips_count_memory_refs (op0, 2);
+    case 'q':
+      if (code == REG && MD_REG_P (REGNO (op)))
+       fprintf (file, "$ac0");
+      else if (code == REG && DSP_ACC_REG_P (REGNO (op)))
+       fprintf (file, "$ac%c", reg_names[REGNO (op)][3]);
+      else
+       output_operand_lossage ("invalid use of '%%%c'", letter);
+      break;
 
-      if (ret != 0 && MEM_VOLATILE_P (op0))
+    default:
+      switch (code)
        {
-         size_t i = strlen (ret);
+       case REG:
+         {
+           unsigned int regno = REGNO (op);
+           if ((letter == 'M' && TARGET_LITTLE_ENDIAN)
+               || (letter == 'L' && TARGET_BIG_ENDIAN)
+               || letter == 'D')
+             regno++;
+           fprintf (file, "%s", reg_names[regno]);
+         }
+         break;
 
-         if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
-           abort ();
+       case MEM:
+         if (letter == 'D')
+           output_address (plus_constant (XEXP (op, 0), 4));
+         else
+           output_address (XEXP (op, 0));
+         break;
 
-         sprintf (volatile_buffer, "%%{%s%%}", ret);
-         ret = volatile_buffer;
+       default:
+         if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
+           fputs (reg_names[GP_REG_FIRST], file);
+         else if (CONST_GP_P (op))
+           fputs (reg_names[GLOBAL_POINTER_REGNUM], file);
+         else
+           output_addr_const (file, mips_strip_unspec_address (op));
+         break;
        }
     }
+}
 
-  if (ret == 0)
-    {
-      abort_with_insn (insn, "bad move");
-      return 0;
-    }
+/* Output address operand X to FILE.  */
+
+void
+mips_print_operand_address (FILE *file, rtx x)
+{
+  struct mips_address_info addr;
+
+  if (mips_classify_address (&addr, x, word_mode, true))
+    switch (addr.type)
+      {
+      case ADDRESS_REG:
+       mips_print_operand (file, addr.offset, 0);
+       fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
+       return;
+
+      case ADDRESS_LO_SUM:
+       mips_print_operand_reloc (file, addr.offset, SYMBOL_CONTEXT_MEM,
+                                 mips_lo_relocs);
+       fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
+       return;
 
-  if (delay != DELAY_NONE)
-    return mips_fill_delay_slot (ret, delay, operands, insn);
+      case ADDRESS_CONST_INT:
+       output_addr_const (file, x);
+       fprintf (file, "(%s)", reg_names[GP_REG_FIRST]);
+       return;
 
-  return ret;
+      case ADDRESS_SYMBOLIC:
+       output_addr_const (file, mips_strip_unspec_address (x));
+       return;
+      }
+  gcc_unreachable ();
 }
 \f
-/* Provide the costs of an addressing mode that contains ADDR.
-   If ADDR is not a valid address, its cost is irrelevant.  */
+/* Implement TARGET_ENCODE_SECTION_INFO.  */
 
-int
-mips_address_cost (addr)
-     rtx addr;
+static void
+mips_encode_section_info (tree decl, rtx rtl, int first)
 {
-  switch (GET_CODE (addr))
+  default_encode_section_info (decl, rtl, first);
+
+  if (TREE_CODE (decl) == FUNCTION_DECL)
     {
-    case LO_SUM:
-      return 1;
+      rtx symbol = XEXP (rtl, 0);
+      tree type = TREE_TYPE (decl);
+
+      /* Encode whether the symbol is short or long.  */
+      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);
+}
 
-    case LABEL_REF:
-      return 2;
+/* Implement TARGET_ASM_FUNCTION_RODATA_SECTION.
 
-    case CONST:
-      {
-       rtx offset = const0_rtx;
-       addr = eliminate_constant_term (XEXP (addr, 0), &offset);
-       if (GET_CODE (addr) == LABEL_REF)
-         return 2;
+   The complication here is that, with the combination TARGET_ABICALLS
+   && !TARGET_ABSOLUTE_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 (GET_CODE (addr) != SYMBOL_REF)
-         return 4;
+static section *
+mips_function_rodata_section (tree decl)
+{
+  if (!TARGET_ABICALLS || TARGET_ABSOLUTE_ABICALLS || TARGET_GPWORD)
+    return default_function_rodata_section (decl);
 
-       if (! SMALL_INT (offset))
-         return 2;
-      }
+  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;
+}
 
-      /* ... fall through ...  */
+/* Implement TARGET_IN_SMALL_DATA_P.  */
 
-    case SYMBOL_REF:
-      return SYMBOL_REF_FLAG (addr) ? 1 : 2;
+static bool
+mips_in_small_data_p (const_tree decl)
+{
+  unsigned HOST_WIDE_INT size;
 
-    case PLUS:
-      {
-       register rtx plus0 = XEXP (addr, 0);
-       register rtx plus1 = XEXP (addr, 1);
+  if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL)
+    return false;
 
-       if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG)
-         plus0 = XEXP (addr, 1), plus1 = XEXP (addr, 0);
+  /* We don't yet generate small-data references for -mabicalls
+     or VxWorks RTP code.  See the related -G handling in
+     mips_override_options.  */
+  if (TARGET_ABICALLS || TARGET_VXWORKS_RTP)
+    return false;
 
-       if (GET_CODE (plus0) != REG)
-         break;
+  if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
+    {
+      const char *name;
 
-       switch (GET_CODE (plus1))
-         {
-         case CONST_INT:
-           return SMALL_INT (plus1) ? 1 : 2;
+      /* 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;
 
-         case CONST:
-         case SYMBOL_REF:
-         case LABEL_REF:
-         case HIGH:
-         case LO_SUM:
-           return mips_address_cost (plus1) + 1;
+      if (TREE_READONLY (decl)
+         && !TREE_SIDE_EFFECTS (decl)
+         && (!DECL_INITIAL (decl) || TREE_CONSTANT (DECL_INITIAL (decl))))
+       return false;
+    }
 
-         default:
-           break;
-         }
-      }
+  /* Enforce -mlocal-sdata.  */
+  if (!TARGET_LOCAL_SDATA && !TREE_PUBLIC (decl))
+    return false;
 
-    default:
-      break;
+  /* 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;
     }
 
-  return 4;
+  /* We have traditionally not treated zero-sized objects as small data,
+     so this is now effectively part of the ABI.  */
+  size = int_size_in_bytes (TREE_TYPE (decl));
+  return size > 0 && size <= mips_small_data_threshold;
 }
 
-/* Return nonzero if X is an address which needs a temporary register when
-   reloaded while generating PIC code.  */
+/* 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.  */
 
-int
-pic_address_needs_scratch (x)
-     rtx x;
+static bool
+mips_use_anchors_for_symbol_p (const_rtx symbol)
 {
-  /* An address which is a symbolic plus a non SMALL_INT needs a temp reg.  */
-  if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
-      && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
-      && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
-      && ! SMALL_INT (XEXP (XEXP (x, 0), 1)))
-    return 1;
+  switch (mips_classify_symbol (symbol, SYMBOL_CONTEXT_MEM))
+    {
+    case SYMBOL_PC_RELATIVE:
+    case SYMBOL_GP_RELATIVE:
+      return false;
 
-  return 0;
+    default:
+      return default_use_anchors_for_symbol_p (symbol);
+    }
 }
 \f
-/* Make normal rtx_code into something we can index from an array */
+/* 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.  */
 
-static enum internal_test
-map_test_to_internal_test (test_code)
-     enum rtx_code test_code;
+HOST_WIDE_INT
+mips_debugger_offset (rtx addr, HOST_WIDE_INT offset)
 {
-  enum internal_test test = ITEST_MAX;
+  rtx offset2 = const0_rtx;
+  rtx reg = eliminate_constant_term (addr, &offset2);
+
+  if (offset == 0)
+    offset = INTVAL (offset2);
 
-  switch (test_code)
+  if (reg == stack_pointer_rtx
+      || reg == frame_pointer_rtx
+      || reg == hard_frame_pointer_rtx)
     {
-    case EQ:  test = ITEST_EQ;  break;
-    case NE:  test = ITEST_NE;  break;
-    case GT:  test = ITEST_GT;  break;
-    case GE:  test = ITEST_GE;  break;
-    case LT:  test = ITEST_LT;  break;
-    case LE:  test = ITEST_LE;  break;
-    case GTU: test = ITEST_GTU; break;
-    case GEU: test = ITEST_GEU; break;
-    case LTU: test = ITEST_LTU; break;
-    case LEU: test = ITEST_LEU; break;
-    default:                   break;
+      offset -= cfun->machine->frame.total_size;
+      if (reg == hard_frame_pointer_rtx)
+       offset += cfun->machine->frame.hard_frame_pointer_offset;
     }
 
-  return test;
-}
+  /* 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
-/* Generate the code to compare two integer values.  The return value is:
-   (reg:SI xx)         The pseudo register the comparison is in
-   0                   No register, generate a simple branch.
-
-   ??? This is called with result nonzero by the Scond patterns in
-   mips.md.  These patterns are called with a target in the mode of
-   the Scond instruction pattern.  Since this must be a constant, we
-   must use SImode.  This means that if RESULT is non-zero, it will
-   always be an SImode register, even if TARGET_64BIT is true.  We
-   cope with this by calling convert_move rather than emit_move_insn.
-   This will sometimes lead to an unnecessary extension of the result;
-   for example:
-
-   long long
-   foo (long long i)
-   {
-     return i < 5;
-   }
-
-   */
-
-rtx
-gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
-     enum rtx_code test_code;  /* relational test (EQ, etc) */
-     rtx result;               /* result to store comp. or 0 if branch */
-     rtx cmp0;                 /* first operand to compare */
-     rtx cmp1;                 /* second operand to compare */
-     int *p_invert;            /* NULL or ptr to hold whether branch needs */
-                               /* to reverse its test */
-{
-  struct cmp_info
-  {
-    enum rtx_code test_code;   /* code to use in instruction (LT vs. LTU) */
-    int const_low;             /* low bound of constant we can accept */
-    int const_high;            /* high bound of constant we can accept */
-    int const_add;             /* constant to add (convert LE -> LT) */
-    int reverse_regs;          /* reverse registers in test */
-    int invert_const;          /* != 0 if invert value if cmp1 is constant */
-    int invert_reg;            /* != 0 if invert value if cmp1 is register */
-    int unsignedp;             /* != 0 for unsigned comparisons.  */
-  };
-
-  static struct cmp_info info[ (int)ITEST_MAX ] = {
-
-    { XOR,      0,  65535,  0,  0,  0,  0, 0 },        /* EQ  */
-    { XOR,      0,  65535,  0,  0,  1,  1, 0 },        /* NE  */
-    { LT,   -32769,  32766,  1,         1,  1,  0, 0 },        /* GT  */
-    { LT,   -32768,  32767,  0,         0,  1,  1, 0 },        /* GE  */
-    { LT,   -32768,  32767,  0,         0,  0,  0, 0 },        /* LT  */
-    { LT,   -32769,  32766,  1,         1,  0,  1, 0 },        /* LE  */
-    { LTU,  -32769,  32766,  1,         1,  1,  0, 1 },        /* GTU */
-    { LTU,  -32768,  32767,  0,         0,  1,  1, 1 },        /* GEU */
-    { LTU,  -32768,  32767,  0,         0,  0,  0, 1 },        /* LTU */
-    { LTU,  -32769,  32766,  1,         1,  0,  1, 1 },        /* LEU */
-  };
-
-  enum internal_test test;
-  enum machine_mode mode;
-  struct cmp_info *p_info;
-  int branch_p;
-  int eqne_p;
-  int invert;
-  rtx reg;
-  rtx reg2;
-
-  test = map_test_to_internal_test (test_code);
-  if (test == ITEST_MAX)
-    abort ();
+/* Implement ASM_OUTPUT_EXTERNAL.  */
 
-  p_info = &info[(int) test];
-  eqne_p = (p_info->test_code == XOR);
-
-  mode = GET_MODE (cmp0);
-  if (mode == VOIDmode)
-    mode = GET_MODE (cmp1);
+void
+mips_output_external (FILE *file, tree decl, const char *name)
+{
+  default_elf_asm_output_external (file, decl, name);
 
-  /* Eliminate simple branches */
-  branch_p = (result == 0);
-  if (branch_p)
+  /* We output the name if and only if TREE_SYMBOL_REFERENCED is
+     set in order to avoid putting out names that are never really
+     used. */
+  if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
     {
-      if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
+      if (!TARGET_EXPLICIT_RELOCS && mips_in_small_data_p (decl))
        {
-         /* Comparisons against zero are simple branches */
-         if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0
-             && (! TARGET_MIPS16 || eqne_p))
-           return 0;
-
-         /* Test for beq/bne.  */
-         if (eqne_p && ! TARGET_MIPS16)
-           return 0;
-       }
+         /* When using assembler macros, emit .extern directives for
+            all small-data externs so that the assembler knows how
+            big they are.
 
-      /* allocate a pseudo to calculate the value in.  */
-      result = gen_reg_rtx (mode);
+            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 than .sbss or .sdata.  */
+         fputs ("\t.extern\t", file);
+         assemble_name (file, name);
+         fprintf (file, ", " HOST_WIDE_INT_PRINT_DEC "\n",
+                  int_size_in_bytes (TREE_TYPE (decl)));
+       }
+      else if (TARGET_IRIX
+              && mips_abi == ABI_32
+              && TREE_CODE (decl) == FUNCTION_DECL)
+       {
+         /* In IRIX 5 or IRIX 6 for the O32 ABI, we must output a
+            `.global name .text' directive for every used but
+            undefined function.  If we don't, the linker may perform
+            an optimization (skipping over the insns that set $gp)
+            when it is unsafe.  */
+         fputs ("\t.globl ", file);
+         assemble_name (file, name);
+         fputs (" .text\n", file);
+       }
     }
+}
 
-  /* Make sure we can handle any constants given to us.  */
-  if (GET_CODE (cmp0) == CONST_INT)
-    cmp0 = force_reg (mode, cmp0);
+/* Implement ASM_OUTPUT_SOURCE_FILENAME.  */
 
-  if (GET_CODE (cmp1) == CONST_INT)
+void
+mips_output_filename (FILE *stream, const char *name)
+{
+  /* If we are emitting DWARF-2, let dwarf2out handle the ".file"
+     directives.  */
+  if (write_symbols == DWARF2_DEBUG)
+    return;
+  else if (mips_output_filename_first_time)
     {
-      HOST_WIDE_INT value = INTVAL (cmp1);
-
-      if (value < p_info->const_low
-         || value > p_info->const_high
-         /* ??? Why?  And why wasn't the similar code below modified too?  */
-         || (TARGET_64BIT
-             && HOST_BITS_PER_WIDE_INT < 64
-             && p_info->const_add != 0
-             && ((p_info->unsignedp
-                  ? ((unsigned HOST_WIDE_INT) (value + p_info->const_add)
-                     > (unsigned HOST_WIDE_INT) INTVAL (cmp1))
-                  : (value + p_info->const_add) > INTVAL (cmp1))
-                 != (p_info->const_add > 0))))
-       cmp1 = force_reg (mode, cmp1);
+      mips_output_filename_first_time = 0;
+      num_source_filenames += 1;
+      current_function_file = name;
+      fprintf (stream, "\t.file\t%d ", num_source_filenames);
+      output_quoted_string (stream, name);
+      putc ('\n', stream);
     }
-
-  /* See if we need to invert the result.  */
-  invert = (GET_CODE (cmp1) == CONST_INT
-           ? p_info->invert_const : p_info->invert_reg);
-
-  if (p_invert != (int *)0)
+  /* If we are emitting stabs, let dbxout.c handle this (except for
+     the mips_output_filename_first_time case).  */
+  else if (write_symbols == DBX_DEBUG)
+    return;
+  else if (name != current_function_file
+          && strcmp (name, current_function_file) != 0)
     {
-      *p_invert = invert;
-      invert = 0;
+      num_source_filenames += 1;
+      current_function_file = name;
+      fprintf (stream, "\t.file\t%d ", num_source_filenames);
+      output_quoted_string (stream, name);
+      putc ('\n', stream);
     }
+}
 
-  /* Comparison to constants, may involve adding 1 to change a LT into LE.
-     Comparison between two registers, may involve switching operands.  */
-  if (GET_CODE (cmp1) == CONST_INT)
-    {
-      if (p_info->const_add != 0)
-       {
-         HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add;
-
-         /* If modification of cmp1 caused overflow,
-            we would get the wrong answer if we follow the usual path;
-            thus, x > 0xffffffffU would turn into x > 0U.  */
-         if ((p_info->unsignedp
-              ? (unsigned HOST_WIDE_INT) new >
-              (unsigned HOST_WIDE_INT) INTVAL (cmp1)
-              : new > INTVAL (cmp1))
-             != (p_info->const_add > 0))
-           {
-             /* This test is always true, but if INVERT is true then
-                the result of the test needs to be inverted so 0 should
-                be returned instead.  */
-             emit_move_insn (result, invert ? const0_rtx : const_true_rtx);
-             return result;
-           }
-         else
-           cmp1 = GEN_INT (new);
-       }
-    }
+/* Implement TARGET_ASM_OUTPUT_DWARF_DTPREL.  */
 
-  else if (p_info->reverse_regs)
+static void ATTRIBUTE_UNUSED
+mips_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+  switch (size)
     {
-      rtx temp = cmp0;
-      cmp0 = cmp1;
-      cmp1 = temp;
+    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);
+}
 
-  if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
-    reg = cmp0;
-  else
+/* 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)
     {
-      reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result;
-      convert_move (reg, gen_rtx (p_info->test_code, mode, cmp0, cmp1), 0);
+      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));
     }
 
-  if (test == ITEST_NE)
+  return NULL_RTX;
+}
+
+/* Implement ASM_OUTPUT_ASCII.  */
+
+void
+mips_output_ascii (FILE *stream, const char *string, size_t len)
+{
+  size_t i;
+  int cur_pos;
+
+  cur_pos = 17;
+  fprintf (stream, "\t.ascii\t\"");
+  for (i = 0; i < len; i++)
     {
-      if (! TARGET_MIPS16)
+      int c;
+
+      c = (unsigned char) string[i];
+      if (ISPRINT (c))
        {
-         convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0);
-         if (p_invert != NULL)
-           *p_invert = 0;
-         invert = 0;
+         if (c == '\\' || c == '\"')
+           {
+             putc ('\\', stream);
+             cur_pos++;
+           }
+         putc (c, stream);
+         cur_pos++;
        }
       else
        {
-         reg2 = invert ? gen_reg_rtx (mode) : result;
-         convert_move (reg2, gen_rtx (LTU, mode, reg, const1_rtx), 0);
-         reg = reg2;
+         fprintf (stream, "\\%03o", c);
+         cur_pos += 4;
+       }
+
+      if (cur_pos > 72 && i+1 < len)
+       {
+         cur_pos = 17;
+         fprintf (stream, "\"\n\t.ascii\t\"");
        }
     }
+  fprintf (stream, "\"\n");
+}
+
+/* Emit either a label, .comm, or .lcomm directive.  When using assembler
+   macros, mark the symbol as written so that mips_asm_output_external
+   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_declare_object (FILE *stream, const char *name, const char *init_string,
+                    const char *final_string, ...)
+{
+  va_list ap;
+
+  fputs (init_string, stream);
+  assemble_name (stream, name);
+  va_start (ap, final_string);
+  vfprintf (stream, final_string, ap);
+  va_end (ap);
 
-  else if (test == ITEST_EQ)
+  if (!TARGET_EXPLICIT_RELOCS)
     {
-      reg2 = invert ? gen_reg_rtx (mode) : result;
-      convert_move (reg2, gen_rtx_LTU (mode, reg, const1_rtx), 0);
-      reg = reg2;
+      tree name_tree = get_identifier (name);
+      TREE_ASM_WRITTEN (name_tree) = 1;
     }
+}
 
-  if (invert)
-    {
-      rtx one;
+/* Declare a common object of SIZE bytes using asm directive INIT_STRING.
+   NAME is the name of the object and ALIGN is the required alignment
+   in bytes.  TAKES_ALIGNMENT_P is true if the directive takes a third
+   alignment argument.  */
 
-      if (! TARGET_MIPS16)
-       one = const1_rtx;
-      else
-       {
-         /* The value is in $24.  Copy it to another register, so
-             that reload doesn't think it needs to store the $24 and
-             the input to the XOR in the same location.  */
-         reg2 = gen_reg_rtx (mode);
-         emit_move_insn (reg2, reg);
-         reg = reg2;
-         one = force_reg (mode, const1_rtx);
-       }
-      convert_move (result, gen_rtx (XOR, mode, reg, one), 0);
+void
+mips_declare_common_object (FILE *stream, const char *name,
+                           const char *init_string,
+                           unsigned HOST_WIDE_INT size,
+                           unsigned int align, bool takes_alignment_p)
+{
+  if (!takes_alignment_p)
+    {
+      size += (align / BITS_PER_UNIT) - 1;
+      size -= size % (align / BITS_PER_UNIT);
+      mips_declare_object (stream, name, init_string,
+                          "," HOST_WIDE_INT_PRINT_UNSIGNED "\n", size);
     }
+  else
+    mips_declare_object (stream, name, init_string,
+                        "," HOST_WIDE_INT_PRINT_UNSIGNED ",%u\n",
+                        size, align / BITS_PER_UNIT);
+}
+
+/* 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.  */
 
-  return result;
+void
+mips_output_aligned_decl_common (FILE *stream, tree decl, const char *name,
+                                unsigned HOST_WIDE_INT size,
+                                unsigned int align)
+{
+  /* 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);
+
+      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);
 }
-\f
-/* Emit the common code for doing conditional branches.
-   operand[0] is the label to jump to.
-   The comparison operands are saved away by cmp{si,di,sf,df}.  */
+
+#ifdef ASM_OUTPUT_SIZE_DIRECTIVE
+extern int size_directive_output;
+
+/* Implement ASM_DECLARE_OBJECT_NAME.  This is like most of the standard ELF
+   definitions except that it uses mips_declare_object to emit the label.  */
 
 void
-gen_conditional_branch (operands, test_code)
-     rtx operands[];
-     enum rtx_code test_code;
+mips_declare_object_name (FILE *stream, const char *name,
+                         tree decl ATTRIBUTE_UNUSED)
 {
-  enum cmp_type type = branch_type;
-  rtx cmp0 = branch_cmp[0];
-  rtx cmp1 = branch_cmp[1];
-  enum machine_mode mode;
-  rtx reg;
-  int invert;
-  rtx label1, label2;
+#ifdef ASM_OUTPUT_TYPE_DIRECTIVE
+  ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "object");
+#endif
 
-  switch (type)
+  size_directive_output = 0;
+  if (!flag_inhibit_size_directive && DECL_SIZE (decl))
     {
-    case CMP_SI:
-    case CMP_DI:
-      mode = type == CMP_SI ? SImode : DImode;
-      invert = 0;
-      reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert);
+      HOST_WIDE_INT size;
 
-      if (reg)
-       {
-         cmp0 = reg;
-         cmp1 = const0_rtx;
-         test_code = NE;
-       }
-      else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
-       /* We don't want to build a comparison against a non-zero
-          constant.  */
-       cmp1 = force_reg (mode, cmp1);
+      size_directive_output = 1;
+      size = int_size_in_bytes (TREE_TYPE (decl));
+      ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+    }
 
-      break;
+  mips_declare_object (stream, name, "", ":\n");
+}
 
-    case CMP_SF:
-    case CMP_DF:
-      if (! ISA_HAS_8CC)
-       reg = gen_rtx_REG (CCmode, FPSW_REGNUM);
-      else
-       reg = gen_reg_rtx (CCmode);
-
-      /* For cmp0 != cmp1, build cmp0 == cmp1, and test for result ==
-         0 in the instruction built below.  The MIPS FPU handles
-         inequality testing by testing for equality and looking for a
-         false result.  */
-      emit_insn (gen_rtx_SET (VOIDmode, reg,
-                             gen_rtx (test_code == NE ? EQ : test_code,
-                                      CCmode, cmp0, cmp1)));
-
-      test_code = test_code == NE ? EQ : NE;
-      mode = CCmode;
-      cmp0 = reg;
-      cmp1 = const0_rtx;
-      invert = 0;
-      break;
+/* Implement ASM_FINISH_DECLARE_OBJECT.  This is generic ELF stuff.  */
+
+void
+mips_finish_declare_object (FILE *stream, tree decl, int top_level, int at_end)
+{
+  const char *name;
+
+  name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+  if (!flag_inhibit_size_directive
+      && DECL_SIZE (decl) != 0
+      && !at_end
+      && top_level
+      && DECL_INITIAL (decl) == error_mark_node
+      && !size_directive_output)
+    {
+      HOST_WIDE_INT size;
 
+      size_directive_output = 1;
+      size = int_size_in_bytes (TREE_TYPE (decl));
+      ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+    }
+}
+#endif
+\f
+/* Return the FOO in the name of the ".mdebug.FOO" section associated
+   with the current ABI.  */
+
+static const char *
+mips_mdebug_abi_name (void)
+{
+  switch (mips_abi)
+    {
+    case ABI_32:
+      return "abi32";
+    case ABI_O64:
+      return "abiO64";
+    case ABI_N32:
+      return "abiN32";
+    case ABI_64:
+      return "abi64";
+    case ABI_EABI:
+      return TARGET_64BIT ? "eabi64" : "eabi32";
     default:
-      abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test");
+      gcc_unreachable ();
     }
+}
 
-  /* Generate the branch.  */
+/* Implement TARGET_ASM_FILE_START.  */
 
-  label1 = gen_rtx_LABEL_REF (VOIDmode, operands[0]);
-  label2 = pc_rtx;
+static void
+mips_file_start (void)
+{
+  default_file_start ();
+
+  /* Generate a special section to describe the ABI switches used to
+     produce the resultant binary.  This is unnecessary on IRIX and
+     causes unwanted warnings from the native linker.  */
+  if (!TARGET_IRIX)
+    {
+      /* Record the ABI itself.  Modern versions of binutils encode
+        this information in the ELF header flags, but GDB needs the
+        information in order to correctly debug binaries produced by
+        older binutils.  See the function mips_gdbarch_init in
+        gdb/mips-tdep.c.  */
+      fprintf (asm_out_file, "\t.section .mdebug.%s\n\t.previous\n",
+              mips_mdebug_abi_name ());
+
+      /* 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
+                  ? ((!TARGET_64BIT && TARGET_FLOAT64) ? 4 : 1) : 2) : 3));
+#endif
+    }
 
-  if (invert)
+  /* If TARGET_ABICALLS, tell GAS to generate -KPIC code.  */
+  if (TARGET_ABICALLS)
     {
-      label2 = label1;
-      label1 = pc_rtx;
+      fprintf (asm_out_file, "\t.abicalls\n");
+      if (TARGET_ABICALLS_PIC0)
+       fprintf (asm_out_file, "\t.option\tpic0\n");
     }
 
-  emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
-                              gen_rtx_IF_THEN_ELSE (VOIDmode,
-                                                    gen_rtx (test_code, mode,
-                                                             cmp0, cmp1),
-                                                    label1, label2)));
+  if (flag_verbose_asm)
+    fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
+            ASM_COMMENT_START,
+            mips_small_data_threshold, mips_arch_info->name, mips_isa);
 }
+\f
+/* Make the last instruction frame-related and note that it performs
+   the operation described by FRAME_PATTERN.  */
 
-/* Emit the common code for conditional moves.  OPERANDS is the array
-   of operands passed to the conditional move defined_expand.  */
+static void
+mips_set_frame_expr (rtx frame_pattern)
+{
+  rtx insn;
 
-void
-gen_conditional_move (operands)
-     rtx *operands;
+  insn = get_last_insn ();
+  RTX_FRAME_RELATED_P (insn) = 1;
+  REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                                     frame_pattern,
+                                     REG_NOTES (insn));
+}
+
+/* Return a frame-related rtx that stores REG at MEM.
+   REG must be a single register.  */
+
+static rtx
+mips_frame_set (rtx mem, rtx reg)
 {
-  rtx op0 = branch_cmp[0];
-  rtx op1 = branch_cmp[1];
-  enum machine_mode mode = GET_MODE (branch_cmp[0]);
-  enum rtx_code cmp_code = GET_CODE (operands[1]);
-  enum rtx_code move_code = NE;
-  enum machine_mode op_mode = GET_MODE (operands[0]);
-  enum machine_mode cmp_mode;
-  rtx cmp_reg;
+  rtx set;
 
-  if (GET_MODE_CLASS (mode) != MODE_FLOAT)
-    {
-      switch (cmp_code)
-       {
-       case EQ:
-         cmp_code = XOR;
-         move_code = EQ;
-         break;
-       case NE:
-         cmp_code = XOR;
-         break;
-       case LT:
-         break;
-       case GE:
-         cmp_code = LT;
-         move_code = EQ;
-         break;
-       case GT:
-         cmp_code = LT;
-         op0 = force_reg (mode, branch_cmp[1]);
-         op1 = branch_cmp[0];
-         break;
-       case LE:
-         cmp_code = LT;
-         op0 = force_reg (mode, branch_cmp[1]);
-         op1 = branch_cmp[0];
-         move_code = EQ;
-         break;
-       case LTU:
-         break;
-       case GEU:
-         cmp_code = LTU;
-         move_code = EQ;
-         break;
-       case GTU:
-         cmp_code = LTU;
-         op0 = force_reg (mode, branch_cmp[1]);
-         op1 = branch_cmp[0];
-         break;
-       case LEU:
-         cmp_code = LTU;
-         op0 = force_reg (mode, branch_cmp[1]);
-         op1 = branch_cmp[0];
-         move_code = EQ;
-         break;
-       default:
-         abort ();
-       }
-    }
-  else if (cmp_code == NE)
-    cmp_code = EQ, move_code = EQ;
+  /* 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);
 
-  if (mode == SImode || mode == DImode)
-    cmp_mode = mode;
-  else if (mode == SFmode || mode == DFmode)
-    cmp_mode = CCmode;
-  else
-    abort ();
+  set = gen_rtx_SET (VOIDmode, mem, reg);
+  RTX_FRAME_RELATED_P (set) = 1;
+
+  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
+};
+
+/* 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
+};
 
-  cmp_reg = gen_reg_rtx (cmp_mode);
-  emit_insn (gen_rtx_SET (cmp_mode, cmp_reg,
-                         gen_rtx (cmp_code, cmp_mode, op0, op1)));
+/* 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.  */
 
-  emit_insn (gen_rtx_SET (op_mode, operands[0],
-                         gen_rtx_IF_THEN_ELSE (op_mode,
-                                               gen_rtx (move_code, VOIDmode,
-                                                        cmp_reg,
-                                                        CONST0_RTX (SImode)),
-                                               operands[2], operands[3])));
+static unsigned int
+mips16e_find_first_register (unsigned int mask, const unsigned char *regs,
+                            unsigned int size)
+{
+  unsigned int i;
+
+  for (i = 0; i < size; i++)
+    if (BITSET_P (mask, regs[i]))
+      break;
+
+  return i;
 }
 
-/* Emit the common code for conditional moves.  OPERANDS is the array
-   of operands passed to the conditional move defined_expand.  */
+/* *MASK_PTR is a mask of general-purpose registers and *NUM_REGS_PTR
+   is the number of set bits.  If *MASK_PTR contains REGS[X] for some X
+   in [0, SIZE), adjust *MASK_PTR and *NUM_REGS_PTR so that the same
+   is true for all indexes (X, SIZE).  */
 
-void
-mips_gen_conditional_trap (operands)
-     rtx operands[];
+static void
+mips16e_mask_registers (unsigned int *mask_ptr, const unsigned char *regs,
+                       unsigned int size, unsigned int *num_regs_ptr)
 {
-  rtx op0, op1;
-  enum rtx_code cmp_code = GET_CODE (operands[0]);
-  enum machine_mode mode = GET_MODE (branch_cmp[0]);
+  unsigned int i;
+
+  i = mips16e_find_first_register (*mask_ptr, regs, size);
+  for (i++; i < size; i++)
+    if (!BITSET_P (*mask_ptr, regs[i]))
+      {
+       *num_regs_ptr += 1;
+       *mask_ptr |= 1 << regs[i];
+      }
+}
+
+/* Return a simplified form of X using the register values in REG_VALUES.
+   REG_VALUES[R] is the last value assigned to hard register R, or null
+   if R has not been modified.
+
+   This function is rather limited, but is good enough for our purposes.  */
+
+static rtx
+mips16e_collect_propagate_value (rtx x, rtx *reg_values)
+{
+  x = avoid_constant_pool_reference (x);
 
-  /* MIPS conditional trap machine instructions don't have GT or LE
-     flavors, so we must invert the comparison and convert to LT and
-     GE, respectively.  */
-  switch (cmp_code)
+  if (UNARY_P (x))
     {
-    case GT: cmp_code = LT; break;
-    case LE: cmp_code = GE; break;
-    case GTU: cmp_code = LTU; break;
-    case LEU: cmp_code = GEU; break;
-    default: break;
+      rtx x0 = mips16e_collect_propagate_value (XEXP (x, 0), reg_values);
+      return simplify_gen_unary (GET_CODE (x), GET_MODE (x),
+                                x0, GET_MODE (XEXP (x, 0)));
     }
-  if (cmp_code == GET_CODE (operands[0]))
-    {
-      op0 = force_reg (mode, branch_cmp[0]);
-      op1 = branch_cmp[1];
-    }
-  else
+
+  if (ARITHMETIC_P (x))
     {
-      op0 = force_reg (mode, branch_cmp[1]);
-      op1 = branch_cmp[0];
+      rtx x0 = mips16e_collect_propagate_value (XEXP (x, 0), reg_values);
+      rtx x1 = mips16e_collect_propagate_value (XEXP (x, 1), reg_values);
+      return simplify_gen_binary (GET_CODE (x), GET_MODE (x), x0, x1);
     }
-  if (GET_CODE (op1) == CONST_INT && ! SMALL_INT (op1))
-    op1 = force_reg (mode, op1);
 
-  emit_insn (gen_rtx_TRAP_IF (VOIDmode,
-                             gen_rtx (cmp_code, GET_MODE (operands[0]), op0, op1),
-                             operands[1]));
+  if (REG_P (x)
+      && reg_values[REGNO (x)]
+      && !rtx_unstable_p (reg_values[REGNO (x)]))
+    return reg_values[REGNO (x)];
+
+  return x;
 }
 
-/* Return true if operand OP is a condition code register.
-   Only for use during or after reload.  */
+/* 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.  */
 
-int
-fcc_register_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static bool
+mips16e_collect_argument_save_p (rtx dest, rtx src, rtx *reg_values,
+                                unsigned int *regno_ptr)
 {
-  return ((mode == VOIDmode || mode == GET_MODE (op))
-         && (reload_in_progress || reload_completed)
-         && (GET_CODE (op) == REG || GET_CODE (op) == SUBREG)
-         && ST_REG_P (true_regnum (op)));
-}
+  unsigned int argno, regno;
+  HOST_WIDE_INT offset, required_offset;
+  rtx addr, base;
 
-/* Emit code to move general operand SRC into condition-code
-   register DEST.  SCRATCH is a scratch TFmode float register.
-   The sequence is:
+  /* Check that this is a word-mode store.  */
+  if (!MEM_P (dest) || !REG_P (src) || GET_MODE (dest) != word_mode)
+    return false;
 
-       FP1 = SRC
-       FP2 = 0.0f
-       DEST = FP2 < FP1
+  /* Check that the register being saved is an unmodified argument
+     register.  */
+  regno = REGNO (src);
+  if (!IN_RANGE (regno, GP_ARG_FIRST, GP_ARG_LAST) || reg_values[regno])
+    return false;
+  argno = regno - GP_ARG_FIRST;
+
+  /* Check whether the address is an appropriate stack-pointer or
+     frame-pointer access.  */
+  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.hard_frame_pointer_offset;
+  else if (base != stack_pointer_rtx)
+    return false;
+  if (offset != required_offset)
+    return false;
+
+  *regno_ptr = regno;
+  return true;
+}
+
+/* A subroutine of mips_expand_prologue, called only when generating
+   MIPS16e SAVE instructions.  Search the start of the function for any
+   instructions that save argument registers into their caller-allocated
+   save slots.  Delete such instructions and return a value N such that
+   saving [GP_ARG_FIRST, GP_ARG_FIRST + N) would make all the deleted
+   instructions redundant.  */
+
+static unsigned int
+mips16e_collect_argument_saves (void)
+{
+  rtx reg_values[FIRST_PSEUDO_REGISTER];
+  rtx insn, next, set, dest, src;
+  unsigned int nargs, regno;
+
+  push_topmost_sequence ();
+  nargs = 0;
+  memset (reg_values, 0, sizeof (reg_values));
+  for (insn = get_insns (); insn; insn = next)
+    {
+      next = NEXT_INSN (insn);
+      if (NOTE_P (insn))
+       continue;
 
-   where FP1 and FP2 are single-precision float registers
-   taken from SCRATCH.  */
+      if (!INSN_P (insn))
+       break;
 
-void
-mips_emit_fcc_reload (dest, src, scratch)
-     rtx dest, src, scratch;
-{
-  rtx fp1, fp2;
+      set = PATTERN (insn);
+      if (GET_CODE (set) != SET)
+       break;
 
-  /* Change the source to SFmode.  */
-  if (GET_CODE (src) == MEM)
-    src = adjust_address (src, SFmode, 0);
-  else if (GET_CODE (src) == REG || GET_CODE (src) == SUBREG)
-    src = gen_rtx_REG (SFmode, true_regnum (src));
+      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 ();
 
-  fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
-  fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + FP_INC);
+  return nargs;
+}
 
-  emit_move_insn (copy_rtx (fp1), src);
-  emit_move_insn (copy_rtx (fp2), CONST0_RTX (SFmode));
-  emit_insn (gen_slt_sf (dest, fp2, fp1));
+/* Return a move between register REGNO and memory location SP + OFFSET.
+   Make the move a load if RESTORE_P, otherwise make it a frame-related
+   store.  */
+
+static rtx
+mips16e_save_restore_reg (bool restore_p, HOST_WIDE_INT offset,
+                         unsigned int regno)
+{
+  rtx reg, mem;
+
+  mem = gen_frame_mem (SImode, plus_constant (stack_pointer_rtx, offset));
+  reg = gen_rtx_REG (SImode, regno);
+  return (restore_p
+         ? gen_rtx_SET (VOIDmode, reg, mem)
+         : mips_frame_set (mem, reg));
 }
-\f
-/* Write a loop to move a constant number of bytes.
-   Generate load/stores as follows:
-
-   do {
-     temp1 = src[0];
-     temp2 = src[1];
-     ...
-     temp<last> = src[MAX_MOVE_REGS-1];
-     dest[0] = temp1;
-     dest[1] = temp2;
-     ...
-     dest[MAX_MOVE_REGS-1] = temp<last>;
-     src += MAX_MOVE_REGS;
-     dest += MAX_MOVE_REGS;
-   } while (src != final);
-
-   This way, no NOP's are needed, and only MAX_MOVE_REGS+3 temp
-   registers are needed.
-
-   Aligned moves move MAX_MOVE_REGS*4 bytes every (2*MAX_MOVE_REGS)+3
-   cycles, unaligned moves move MAX_MOVE_REGS*4 bytes every
-   (4*MAX_MOVE_REGS)+3 cycles, assuming no cache misses.  */
-
-#define MAX_MOVE_REGS 4
-#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
 
-static void
-block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src)
-     rtx dest_reg;             /* register holding destination address */
-     rtx src_reg;              /* register holding source address */
-     unsigned int bytes;       /* # bytes to move */
-     int align;                        /* alignment */
-     rtx orig_dest;            /* original dest */
-     rtx orig_src;             /* original source for making a reg note */
-{
-  rtx dest_mem = replace_equiv_address (orig_dest, dest_reg);
-  rtx src_mem = replace_equiv_address (orig_src, src_reg);
-  rtx align_rtx = GEN_INT (align);
-  rtx label;
-  rtx final_src;
-  rtx bytes_rtx;
-  int leftover;
+/* Return RTL for a MIPS16e SAVE or RESTORE instruction; RESTORE_P says which.
+   The instruction must:
 
-  if (bytes < (unsigned)2 * MAX_MOVE_BYTES)
-    abort ();
+     - Allocate or deallocate SIZE bytes in total; SIZE is known
+       to be nonzero.
 
-  leftover = bytes % MAX_MOVE_BYTES;
-  bytes -= leftover;
+     - 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.
 
-  label = gen_label_rtx ();
-  final_src = gen_reg_rtx (Pmode);
-  bytes_rtx = GEN_INT (bytes);
+     - Save NARGS argument registers above the allocated area.
 
-  if (bytes > 0x7fff)
-    {
-      if (Pmode == DImode)
-       {
-         emit_insn (gen_movdi (final_src, bytes_rtx));
-         emit_insn (gen_adddi3 (final_src, final_src, src_reg));
-       }
-      else
-       {
-         emit_insn (gen_movsi (final_src, bytes_rtx));
-         emit_insn (gen_addsi3 (final_src, final_src, src_reg));
-       }
-    }
-  else
-    {
-      if (Pmode == DImode)
-       emit_insn (gen_adddi3 (final_src, src_reg, bytes_rtx));
-      else
-       emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx));
-    }
+   (NARGS is always zero if RESTORE_P.)
 
-  emit_label (label);
+   The SAVE and RESTORE instructions cannot save and restore all general
+   registers, so there may be some registers left over for the caller to
+   handle.  Destructively modify *MASK_PTR so that it contains the registers
+   that still need to be saved or restored.  The caller can save these
+   registers in the memory immediately below *OFFSET_PTR, which is a
+   byte offset from the bottom of the allocated stack area.  */
+
+static rtx
+mips16e_build_save_restore (bool restore_p, unsigned int *mask_ptr,
+                           HOST_WIDE_INT *offset_ptr, unsigned int nargs,
+                           HOST_WIDE_INT size)
+{
+  rtx pattern, set;
+  HOST_WIDE_INT offset, top_offset;
+  unsigned int i, regno;
+  int n;
+
+  gcc_assert (cfun->machine->frame.num_fp == 0);
+
+  /* Calculate the number of elements in the PARALLEL.  We need one element
+     for the stack adjustment, one for each argument register save, and one
+     for each additional register move.  */
+  n = 1 + nargs;
+  for (i = 0; i < ARRAY_SIZE (mips16e_save_restore_regs); i++)
+    if (BITSET_P (*mask_ptr, mips16e_save_restore_regs[i]))
+      n++;
+
+  /* Create the final PARALLEL.  */
+  pattern = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (n));
+  n = 0;
+
+  /* Add the stack pointer adjustment.  */
+  set = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+                    plus_constant (stack_pointer_rtx,
+                                   restore_p ? size : -size));
+  RTX_FRAME_RELATED_P (set) = 1;
+  XVECEXP (pattern, 0, n++) = set;
 
-  bytes_rtx = GEN_INT (MAX_MOVE_BYTES);
-  emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx));
+  /* Stack offsets in the PARALLEL are relative to the old stack pointer.  */
+  top_offset = restore_p ? size : 0;
 
-  if (Pmode == DImode)
+  /* Save the arguments.  */
+  for (i = 0; i < nargs; i++)
     {
-      emit_insn (gen_adddi3 (src_reg, src_reg, bytes_rtx));
-      emit_insn (gen_adddi3 (dest_reg, dest_reg, bytes_rtx));
-      emit_insn (gen_cmpdi (src_reg, final_src));
+      offset = top_offset + i * UNITS_PER_WORD;
+      set = mips16e_save_restore_reg (restore_p, offset, GP_ARG_FIRST + i);
+      XVECEXP (pattern, 0, n++) = set;
     }
-  else
+
+  /* Then fill in the other register moves.  */
+  offset = top_offset;
+  for (i = 0; i < ARRAY_SIZE (mips16e_save_restore_regs); i++)
     {
-      emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx));
-      emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx));
-      emit_insn (gen_cmpsi (src_reg, final_src));
+      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);
+       }
     }
 
-  emit_jump_insn (gen_bne (label));
+  /* Tell the caller what offset it should use for the remaining registers.  */
+  *offset_ptr = size + (offset - top_offset);
 
-  if (leftover)
-    emit_insn (gen_movstrsi_internal (dest_mem, src_mem, GEN_INT (leftover),
-                                     align_rtx));
-}
-\f
-/* Use a library function to move some bytes.  */
+  gcc_assert (n == XVECLEN (pattern, 0));
 
-static void
-block_move_call (dest_reg, src_reg, bytes_rtx)
-     rtx dest_reg;
-     rtx src_reg;
-     rtx bytes_rtx;
-{
-  /* We want to pass the size as Pmode, which will normally be SImode
-     but will be DImode if we are using 64 bit longs and pointers.  */
-  if (GET_MODE (bytes_rtx) != VOIDmode
-      && GET_MODE (bytes_rtx) != (unsigned) Pmode)
-    bytes_rtx = convert_to_mode (Pmode, bytes_rtx, 1);
-
-#ifdef TARGET_MEM_FUNCTIONS
-  emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "memcpy"), 0,
-                    VOIDmode, 3, dest_reg, Pmode, src_reg, Pmode,
-                    convert_to_mode (TYPE_MODE (sizetype), bytes_rtx,
-                                     TREE_UNSIGNED (sizetype)),
-                    TYPE_MODE (sizetype));
-#else
-  emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "bcopy"), 0,
-                    VOIDmode, 3, src_reg, Pmode, dest_reg, Pmode,
-                    convert_to_mode (TYPE_MODE (integer_type_node), bytes_rtx,
-                                     TREE_UNSIGNED (integer_type_node)),
-                    TYPE_MODE (integer_type_node));
-#endif
+  return pattern;
 }
-\f
-/* Expand string/block move operations.
-
-   operands[0] is the pointer to the destination.
-   operands[1] is the pointer to the source.
-   operands[2] is the number of bytes to move.
-   operands[3] is the alignment.  */
 
-void
-expand_block_move (operands)
-     rtx operands[];
-{
-  rtx bytes_rtx        = operands[2];
-  rtx align_rtx = operands[3];
-  int constp = GET_CODE (bytes_rtx) == CONST_INT;
-  unsigned HOST_WIDE_INT bytes = constp ? INTVAL (bytes_rtx) : 0;
-  unsigned int align = INTVAL (align_rtx);
-  rtx orig_src = operands[1];
-  rtx orig_dest        = operands[0];
-  rtx src_reg;
-  rtx dest_reg;
-
-  if (constp && bytes == 0)
-    return;
+/* 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.  */
 
-  if (align > (unsigned) UNITS_PER_WORD)
-    align = UNITS_PER_WORD;
+bool
+mips16e_save_restore_pattern_p (rtx pattern, HOST_WIDE_INT adjust,
+                               struct mips16e_save_restore_info *info)
+{
+  unsigned int i, nargs, mask, extra;
+  HOST_WIDE_INT top_offset, save_offset, offset;
+  rtx set, reg, mem, base;
+  int n;
 
-  /* Move the address into scratch registers.  */
-  dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0));
-  src_reg  = copy_addr_to_reg (XEXP (orig_src, 0));
+  if (!GENERATE_MIPS16E_SAVE_RESTORE)
+    return false;
 
-  if (TARGET_MEMCPY)
-    block_move_call (dest_reg, src_reg, bytes_rtx);
+  /* Stack offsets in the PARALLEL are relative to the old stack pointer.  */
+  top_offset = adjust > 0 ? adjust : 0;
 
-  else if (constp && bytes <= (unsigned)2 * MAX_MOVE_BYTES
-          && align == (unsigned) UNITS_PER_WORD)
-    move_by_pieces (orig_dest, orig_src, bytes, align * BITS_PER_WORD);
+  /* Interpret all other members of the PARALLEL.  */
+  save_offset = top_offset - UNITS_PER_WORD;
+  mask = 0;
+  nargs = 0;
+  i = 0;
+  for (n = 1; n < XVECLEN (pattern, 0); n++)
+    {
+      /* Check that we have a SET.  */
+      set = XVECEXP (pattern, 0, n);
+      if (GET_CODE (set) != SET)
+       return false;
+
+      /* Check that the SET is a load (if restoring) or a store
+        (if saving).  */
+      mem = adjust > 0 ? SET_SRC (set) : SET_DEST (set);
+      if (!MEM_P (mem))
+       return false;
+
+      /* Check that the address is the sum of the stack pointer and a
+        possibly-zero constant offset.  */
+      mips_split_plus (XEXP (mem, 0), &base, &offset);
+      if (base != stack_pointer_rtx)
+       return false;
+
+      /* Check that SET's other operand is a register.  */
+      reg = adjust > 0 ? SET_DEST (set) : SET_SRC (set);
+      if (!REG_P (reg))
+       return false;
+
+      /* Check for argument saves.  */
+      if (offset == top_offset + nargs * UNITS_PER_WORD
+         && REGNO (reg) == GP_ARG_FIRST + nargs)
+       nargs++;
+      else if (offset == save_offset)
+       {
+         while (mips16e_save_restore_regs[i++] != REGNO (reg))
+           if (i == ARRAY_SIZE (mips16e_save_restore_regs))
+             return false;
+
+         mask |= 1 << REGNO (reg);
+         save_offset -= UNITS_PER_WORD;
+       }
+      else
+       return false;
+    }
 
-  else if (constp && bytes <= (unsigned)2 * MAX_MOVE_BYTES)
-    emit_insn (gen_movstrsi_internal (replace_equiv_address (orig_dest,
-                                                            dest_reg),
-                                     replace_equiv_address (orig_src,
-                                                            src_reg),
-                                     bytes_rtx, align_rtx));
+  /* 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;
 
-  else if (constp && align >= (unsigned) UNITS_PER_WORD && optimize)
-    block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src);
+  /* 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;
 
-  else if (constp && optimize)
+  /* Pass back information, if requested.  */
+  if (info)
     {
-      /* If the alignment is not word aligned, generate a test at
-        runtime, to see whether things wound up aligned, and we
-        can use the faster lw/sw instead ulw/usw.  */
+      info->nargs = nargs;
+      info->mask = mask;
+      info->size = (adjust > 0 ? adjust : -adjust);
+    }
 
-      rtx temp = gen_reg_rtx (Pmode);
-      rtx aligned_label = gen_label_rtx ();
-      rtx join_label = gen_label_rtx ();
-      int leftover = bytes % MAX_MOVE_BYTES;
+  return true;
+}
 
-      bytes -= leftover;
+/* 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.  */
 
-      if (Pmode == DImode)
-       {
-         emit_insn (gen_iordi3 (temp, src_reg, dest_reg));
-         emit_insn (gen_anddi3 (temp, temp, GEN_INT (UNITS_PER_WORD - 1)));
-         emit_insn (gen_cmpdi (temp, const0_rtx));
-       }
-      else
-       {
-         emit_insn (gen_iorsi3 (temp, src_reg, dest_reg));
-         emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD - 1)));
-         emit_insn (gen_cmpsi (temp, const0_rtx));
-       }
+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;
+}
 
-      emit_jump_insn (gen_beq (aligned_label));
+/* Return the assembly instruction for a MIPS16e SAVE or RESTORE instruction.
+   PATTERN and ADJUST are as for mips16e_save_restore_pattern_p.  */
 
-      /* Unaligned loop.  */
-      block_move_loop (dest_reg, src_reg, bytes, 1, orig_dest, orig_src);
-      emit_jump_insn (gen_jump (join_label));
-      emit_barrier ();
+const char *
+mips16e_output_save_restore (rtx pattern, HOST_WIDE_INT adjust)
+{
+  static char buffer[300];
+
+  struct mips16e_save_restore_info info;
+  unsigned int i, end;
+  char *s;
+
+  /* Parse the pattern.  */
+  if (!mips16e_save_restore_pattern_p (pattern, adjust, &info))
+    gcc_unreachable ();
+
+  /* Add the mnemonic.  */
+  s = strcpy (buffer, adjust > 0 ? "restore\t" : "save\t");
+  s += strlen (s);
+
+  /* Save the arguments.  */
+  if (info.nargs > 1)
+    s += sprintf (s, "%s-%s,", reg_names[GP_ARG_FIRST],
+                 reg_names[GP_ARG_FIRST + info.nargs - 1]);
+  else if (info.nargs == 1)
+    s += sprintf (s, "%s,", reg_names[GP_ARG_FIRST]);
+
+  /* Emit the amount of stack space to allocate or deallocate.  */
+  s += sprintf (s, "%d", (int) info.size);
+
+  /* Save or restore $16.  */
+  if (BITSET_P (info.mask, 16))
+    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 16]);
+
+  /* Save or restore $17.  */
+  if (BITSET_P (info.mask, 17))
+    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 17]);
+
+  /* Save or restore registers in the range $s2...$s8, which
+     mips16e_s2_s8_regs lists in decreasing order.  Note that this
+     is a software register range; the hardware registers are not
+     numbered consecutively.  */
+  end = ARRAY_SIZE (mips16e_s2_s8_regs);
+  i = mips16e_find_first_register (info.mask, mips16e_s2_s8_regs, end);
+  if (i < end)
+    s = mips16e_add_register_range (s, mips16e_s2_s8_regs[end - 1],
+                                   mips16e_s2_s8_regs[i]);
+
+  /* Save or restore registers in the range $a0...$a3.  */
+  end = ARRAY_SIZE (mips16e_a0_a3_regs);
+  i = mips16e_find_first_register (info.mask, mips16e_a0_a3_regs, end);
+  if (i < end)
+    s = mips16e_add_register_range (s, mips16e_a0_a3_regs[i],
+                                   mips16e_a0_a3_regs[end - 1]);
+
+  /* Save or restore $31.  */
+  if (BITSET_P (info.mask, 31))
+    s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 31]);
+
+  return buffer;
+}
+\f
+/* Return true if the current function has an insn that implicitly
+   refers to $gp.  */
 
-      /* Aligned loop.  */
-      emit_label (aligned_label);
-      block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_dest,
-                      orig_src);
-      emit_label (join_label);
+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;
 
-      /* Bytes at the end of the loop.  */
-      if (leftover)
-       emit_insn (gen_movstrsi_internal (replace_equiv_address (orig_dest,
-                                                                dest_reg),
-                                         replace_equiv_address (orig_src,
-                                                                src_reg),
-                                         GEN_INT (leftover),
-                                         GEN_INT (align)));
+      push_topmost_sequence ();
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+       if (USEFUL_INSN_P (insn)
+           && (get_attr_got (insn) != GOT_UNSET
+               || mips_small_data_pattern_p (PATTERN (insn))))
+         {
+           cfun->machine->has_gp_insn_p = true;
+           break;
+         }
+      pop_topmost_sequence ();
     }
-
-  else
-    block_move_call (dest_reg, src_reg, bytes_rtx);
+  return cfun->machine->has_gp_insn_p;
 }
-\f
-/* Emit load/stores for a small constant block_move.
-
-   operands[0] is the memory address of the destination.
-   operands[1] is the memory address of the source.
-   operands[2] is the number of bytes to move.
-   operands[3] is the alignment.
-   operands[4] is a temp register.
-   operands[5] is a temp register.
-   ...
-   operands[3+num_regs] is the last temp register.
-
-   The block move type can be one of the following:
-       BLOCK_MOVE_NORMAL       Do all of the block move.
-       BLOCK_MOVE_NOT_LAST     Do all but the last store.
-       BLOCK_MOVE_LAST         Do just the last store.  */
 
-const char *
-output_block_move (insn, operands, num_regs, move_type)
-     rtx insn;
-     rtx operands[];
-     int num_regs;
-     enum block_move_type move_type;
-{
-  rtx dest_reg = XEXP (operands[0], 0);
-  rtx src_reg = XEXP (operands[1], 0);
-  HOST_WIDE_INT bytes = INTVAL (operands[2]);
-  int align = INTVAL (operands[3]);
-  int num = 0;
-  int offset = 0;
-  int use_lwl_lwr = 0;
-  int last_operand = num_regs + 4;
-  int safe_regs = 4;
-  int i;
-  rtx xoperands[10];
+/* Return true if the current function returns its value in a floating-point
+   register in MIPS16 mode.  */
 
-  struct {
-    const char *load;          /* load insn without nop */
-    const char *load_nop;      /* load insn with trailing nop */
-    const char *store;         /* store insn */
-    const char *final;         /* if last_store used: NULL or swr */
-    const char *last_store;    /* last store instruction */
-    int offset;                        /* current offset */
-    enum machine_mode mode;    /* mode to use on (MEM) */
-  } load_store[4];
-
-  /* ??? Detect a bug in GCC, where it can give us a register
-     the same as one of the addressing registers and reduce
-     the number of registers available.  */
-  for (i = 4; i < last_operand && safe_regs < (int) ARRAY_SIZE (xoperands); i++)
-    if (! reg_mentioned_p (operands[i], operands[0])
-       && ! reg_mentioned_p (operands[i], operands[1]))
-      xoperands[safe_regs++] = operands[i];
-
-  if (safe_regs < last_operand)
-    {
-      xoperands[0] = operands[0];
-      xoperands[1] = operands[1];
-      xoperands[2] = operands[2];
-      xoperands[3] = operands[3];
-      return output_block_move (insn, xoperands, safe_regs - 4, move_type);
-    }
-
-  /* If we are given global or static addresses, and we would be
-     emitting a few instructions, try to save time by using a
-     temporary register for the pointer.  */
-  /* ??? The SGI Irix6 assembler fails when a SYMBOL_REF is used in
-     an ldl/ldr instruction pair.  We play it safe, and always move
-     constant addresses into registers when generating N32/N64 code, just
-     in case we might emit an unaligned load instruction.  */
-  if (num_regs > 2 && (bytes > 2 * align || move_type != BLOCK_MOVE_NORMAL
-                      || mips_abi == ABI_MEABI
-                      || mips_abi == ABI_N32
-                      || mips_abi == ABI_64))
-    {
-      if (CONSTANT_P (src_reg))
-       {
-         if (TARGET_STATS)
-           mips_count_memory_refs (operands[1], 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)));
+}
 
-         src_reg = operands[3 + num_regs--];
-         if (move_type != BLOCK_MOVE_LAST)
-           {
-             xoperands[1] = operands[1];
-             xoperands[0] = src_reg;
-             if (Pmode == DImode)
-               output_asm_insn ("dla\t%0,%1", xoperands);
-             else
-               output_asm_insn ("la\t%0,%1", xoperands);
-           }
-       }
+/* Return the register that should be used as the global pointer
+   within this function.  Return INVALID_REGNUM if the function
+   doesn't need a global pointer.  */
 
-      if (CONSTANT_P (dest_reg))
-       {
-         if (TARGET_STATS)
-           mips_count_memory_refs (operands[0], 1);
+static unsigned int
+mips_global_pointer (void)
+{
+  unsigned int regno;
 
-         dest_reg = operands[3 + num_regs--];
-         if (move_type != BLOCK_MOVE_LAST)
-           {
-             xoperands[1] = operands[0];
-             xoperands[0] = dest_reg;
-             if (Pmode == DImode)
-               output_asm_insn ("dla\t%0,%1", xoperands);
-             else
-               output_asm_insn ("la\t%0,%1", xoperands);
-           }
-       }
-    }
+  /* $gp is always available unless we're using a GOT.  */
+  if (!TARGET_USE_GOT)
+    return GLOBAL_POINTER_REGNUM;
 
-  /* ??? We really shouldn't get any LO_SUM addresses here, because they
-     are not offsettable, however, offsettable_address_p says they are
-     offsettable. I think this is a bug in offsettable_address_p.
-     For expediency, we fix this by just loading the address into a register
-     if we happen to get one.  */
+  /* We must always provide $gp when it is used implicitly.  */
+  if (!TARGET_EXPLICIT_RELOCS)
+    return GLOBAL_POINTER_REGNUM;
 
-  if (GET_CODE (src_reg) == LO_SUM)
-    {
-      src_reg = operands[3 + num_regs--];
-      if (move_type != BLOCK_MOVE_LAST)
-       {
-         xoperands[2] = XEXP (XEXP (operands[1], 0), 1);
-         xoperands[1] = XEXP (XEXP (operands[1], 0), 0);
-         xoperands[0] = src_reg;
-         if (Pmode == DImode)
-           output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
-         else
-           output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
-       }
-    }
+  /* FUNCTION_PROFILER includes a jal macro, so we need to give it
+     a valid gp.  */
+  if (crtl->profile)
+    return GLOBAL_POINTER_REGNUM;
+
+  /* If the function has a nonlocal goto, $gp must hold the correct
+     global pointer for the target function.  */
+  if (crtl->has_nonlocal_goto)
+    return GLOBAL_POINTER_REGNUM;
 
-  if (GET_CODE (dest_reg) == LO_SUM)
+  /* There's no need to initialize $gp if it isn't referenced now,
+     and if we can be sure that no new references will be added during
+     or after reload.  */
+  if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM)
+      && !mips_function_has_gp_insn ())
     {
-      dest_reg = operands[3 + num_regs--];
-      if (move_type != BLOCK_MOVE_LAST)
-       {
-         xoperands[2] = XEXP (XEXP (operands[0], 0), 1);
-         xoperands[1] = XEXP (XEXP (operands[0], 0), 0);
-         xoperands[0] = dest_reg;
-         if (Pmode == DImode)
-           output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
-         else
-           output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
-       }
-    }
+      /* The function doesn't use $gp at the moment.  If we're generating
+        -call_nonpic code, no new uses will be introduced during or after
+        reload.  */
+      if (TARGET_ABICALLS_PIC0)
+       return INVALID_REGNUM;
 
-  if (num_regs > (int) ARRAY_SIZE (load_store))
-    num_regs = ARRAY_SIZE (load_store);
+      /* We need to handle the following implicit gp references:
 
-  else if (num_regs < 1)
-    abort_with_insn (insn,
-                    "cannot do block move, not enough scratch registers");
+        - Reload can sometimes introduce constant pool references
+          into a function that otherwise didn't need them.  For example,
+          suppose we have an instruction like:
 
-  while (bytes > 0)
-    {
-      load_store[num].offset = offset;
+              (set (reg:DF R1) (float:DF (reg:SI R2)))
 
-      if (TARGET_64BIT && bytes >= 8 && align >= 8)
-       {
-         load_store[num].load = "ld\t%0,%1";
-         load_store[num].load_nop = "ld\t%0,%1%#";
-         load_store[num].store = "sd\t%0,%1";
-         load_store[num].last_store = "sd\t%0,%1";
-         load_store[num].final = 0;
-         load_store[num].mode = DImode;
-         offset += 8;
-         bytes -= 8;
-       }
+          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.
 
-      /* ??? Fails because of a MIPS assembler bug?  */
-      else if (TARGET_64BIT && bytes >= 8 && ! TARGET_MIPS16)
-       {
-         if (BYTES_BIG_ENDIAN)
-           {
-             load_store[num].load = "ldl\t%0,%1\n\tldr\t%0,%2";
-             load_store[num].load_nop = "ldl\t%0,%1\n\tldr\t%0,%2%#";
-             load_store[num].store = "sdl\t%0,%1\n\tsdr\t%0,%2";
-             load_store[num].last_store = "sdr\t%0,%2";
-             load_store[num].final = "sdl\t%0,%1";
-           }
-         else
-           {
-             load_store[num].load = "ldl\t%0,%2\n\tldr\t%0,%1";
-             load_store[num].load_nop = "ldl\t%0,%2\n\tldr\t%0,%1%#";
-             load_store[num].store = "sdl\t%0,%2\n\tsdr\t%0,%1";
-             load_store[num].last_store = "sdr\t%0,%1";
-             load_store[num].final = "sdl\t%0,%2";
-           }
+          In cases like these, reload will have added the constant to the
+          pool but no instruction will yet refer to it.
 
-         load_store[num].mode = DImode;
-         offset += 8;
-         bytes -= 8;
-         use_lwl_lwr = 1;
-       }
+        - MIPS16 functions that return in FPRs need to call an
+          external libgcc routine.  */
+      if (!crtl->uses_const_pool
+         && !mips16_cfun_returns_in_fpr_p ())
+       return INVALID_REGNUM;
+    }
 
-      else if (bytes >= 4 && align >= 4)
-       {
-         load_store[num].load = "lw\t%0,%1";
-         load_store[num].load_nop = "lw\t%0,%1%#";
-         load_store[num].store = "sw\t%0,%1";
-         load_store[num].last_store = "sw\t%0,%1";
-         load_store[num].final = 0;
-         load_store[num].mode = SImode;
-         offset += 4;
-         bytes -= 4;
-       }
+  /* 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;
 
-      else if (bytes >= 4 && ! TARGET_MIPS16)
-       {
-         if (BYTES_BIG_ENDIAN)
-           {
-             load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2";
-             load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#";
-             load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2";
-             load_store[num].last_store = "swr\t%0,%2";
-             load_store[num].final = "swl\t%0,%1";
-           }
-         else
-           {
-             load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1";
-             load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#";
-             load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1";
-             load_store[num].last_store = "swr\t%0,%1";
-             load_store[num].final = "swl\t%0,%2";
-           }
+  return GLOBAL_POINTER_REGNUM;
+}
 
-         load_store[num].mode = SImode;
-         offset += 4;
-         bytes -= 4;
-         use_lwl_lwr = 1;
-       }
+/* Return true if the current function should treat register REGNO
+   as call-saved.  */
 
-      else if (bytes >= 2 && align >= 2)
-       {
-         load_store[num].load = "lh\t%0,%1";
-         load_store[num].load_nop = "lh\t%0,%1%#";
-         load_store[num].store = "sh\t%0,%1";
-         load_store[num].last_store = "sh\t%0,%1";
-         load_store[num].final = 0;
-         load_store[num].mode = HImode;
-         offset += 2;
-         bytes -= 2;
-       }
-      else
-       {
-         load_store[num].load = "lb\t%0,%1";
-         load_store[num].load_nop = "lb\t%0,%1%#";
-         load_store[num].store = "sb\t%0,%1";
-         load_store[num].last_store = "sb\t%0,%1";
-         load_store[num].final = 0;
-         load_store[num].mode = QImode;
-         offset++;
-         bytes--;
-       }
+static bool
+mips_cfun_call_saved_reg_p (unsigned int regno)
+{
+  /* call_insns preserve $28 unless they explicitly say otherwise,
+     so call_really_used_regs[] treats $28 as call-saved.  However,
+     we want the ABI property rather than the default call_insn
+     property here.  */
+  return (regno == GLOBAL_POINTER_REGNUM
+         ? TARGET_CALL_SAVED_GP
+         : !call_really_used_regs[regno]);
+}
 
-      if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
-       {
-         dslots_load_total++;
-         dslots_load_filled++;
+/* Return true if the function body might clobber register REGNO.
+   We know that REGNO is call-saved.  */
 
-         if (CONSTANT_P (src_reg))
-           mips_count_memory_refs (src_reg, 1);
+static bool
+mips_cfun_might_clobber_call_saved_reg_p (unsigned int regno)
+{
+  /* Some functions should be treated as clobbering all call-saved
+     registers.  */
+  if (crtl->saves_all_registers)
+    return true;
 
-         if (CONSTANT_P (dest_reg))
-           mips_count_memory_refs (dest_reg, 1);
-       }
+  /* DF handles cases where a register is explicitly referenced in
+     the rtl.  Incoming values are passed in call-clobbered registers,
+     so we can assume that any live call-saved register is set within
+     the function.  */
+  if (df_regs_ever_live_p (regno))
+    return true;
 
-      /* Emit load/stores now if we have run out of registers or are
-        at the end of the move.  */
+  /* Check for registers that are clobbered by FUNCTION_PROFILER.
+     These clobbers are not explicit in the rtl.  */
+  if (crtl->profile && MIPS_SAVE_REG_FOR_PROFILING_P (regno))
+    return true;
 
-      if (++num == num_regs || bytes == 0)
-       {
-         /* If only load/store, we need a NOP after the load.  */
-         if (num == 1)
-           {
-             load_store[0].load = load_store[0].load_nop;
-             if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
-               dslots_load_filled--;
-           }
+  /* If we're using a call-saved global pointer, the function's
+     prologue will need to set it up.  */
+  if (cfun->machine->global_pointer == regno)
+    return true;
 
-         if (move_type != BLOCK_MOVE_LAST)
-           {
-             for (i = 0; i < num; i++)
-               {
-                 int offset;
+  /* The function's prologue will need to set the frame pointer if
+     frame_pointer_needed.  */
+  if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
+    return true;
 
-                 if (!operands[i + 4])
-                   abort ();
+  /* If a MIPS16 function returns a value in FPRs, its epilogue
+     will need to call an external libgcc routine.  This yet-to-be
+     generated call_insn will clobber $31.  */
+  if (regno == GP_REG_FIRST + 31 && mips16_cfun_returns_in_fpr_p ())
+    return true;
 
-                 if (GET_MODE (operands[i + 4]) != load_store[i].mode)
-                   operands[i + 4] = gen_rtx_REG (load_store[i].mode,
-                                                  REGNO (operands[i + 4]));
+  return false;
+}
 
-                 offset = load_store[i].offset;
-                 xoperands[0] = operands[i + 4];
-                 xoperands[1] = gen_rtx_MEM (load_store[i].mode,
-                                             plus_constant (src_reg, offset));
+/* Return true if the current function must save register REGNO.  */
 
-                 if (use_lwl_lwr)
-                   {
-                     int extra_offset
-                       = GET_MODE_SIZE (load_store[i].mode) - 1;
+static bool
+mips_save_reg_p (unsigned int regno)
+{
+  if (mips_cfun_call_saved_reg_p (regno))
+    {
+      if (mips_cfun_might_clobber_call_saved_reg_p (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
+         && mips_cfun_might_clobber_call_saved_reg_p (regno + 1))
+       return true;
+    }
+
+  /* We need to save the incoming return address if __builtin_eh_return
+     is being used to set a different return address.  */
+  if (regno == GP_REG_FIRST + 31 && crtl->calls_eh_return)
+    return true;
+
+  return false;
+}
+
+/* Populate the current function's mips_frame_info structure.
+
+   MIPS stack frames look like:
+
+       +-------------------------------+
+       |                               |
+       |  incoming stack arguments     |
+       |                               |
+       +-------------------------------+
+       |                               |
+       |  caller-allocated save area   |
+      A |  for register arguments       |
+       |                               |
+       +-------------------------------+ <-- incoming stack pointer
+       |                               |
+       |  callee-allocated save area   |
+      B |  for arguments that are       |
+       |  split between registers and  |
+       |  the stack                    |
+       |                               |
+       +-------------------------------+ <-- arg_pointer_rtx
+       |                               |
+      C |  callee-allocated save area   |
+       |  for register varargs         |
+       |                               |
+       +-------------------------------+ <-- frame_pointer_rtx + fp_sp_offset
+       |                               |       + UNITS_PER_HWFPVALUE
+       |  FPR save area                |
+       |                               |
+       +-------------------------------+ <-- frame_pointer_rtx + gp_sp_offset
+       |                               |       + UNITS_PER_WORD
+       |  GPR save area                |
+       |                               |
+       +-------------------------------+
+       |                               | \
+       |  local variables              |  | var_size
+       |                               | /
+       +-------------------------------+
+       |                               | \
+       |  $gp save area                |  | cprestore_size
+       |                               | /
+      P +-------------------------------+ <-- hard_frame_pointer_rtx for
+       |                               |       MIPS16 code
+       |  outgoing stack arguments     |
+       |                               |
+       +-------------------------------+
+       |                               |
+       |  caller-allocated save area   |
+       |  for register arguments       |
+       |                               |
+       +-------------------------------+ <-- stack_pointer_rtx
+                                             frame_pointer_rtx
+                                             hard_frame_pointer_rtx for
+                                               non-MIPS16 code.
+
+   At least two of A, B and C will be empty.
+
+   Dynamic stack allocations such as alloca insert data at point P.
+   They decrease stack_pointer_rtx but leave frame_pointer_rtx and
+   hard_frame_pointer_rtx unchanged.  */
 
-                     xoperands[2] = gen_rtx_MEM (load_store[i].mode,
-                                                 plus_constant (src_reg,
-                                                                extra_offset
-                                                                + offset));
-                   }
+static void
+mips_compute_frame_info (void)
+{
+  struct mips_frame_info *frame;
+  HOST_WIDE_INT offset, size;
+  unsigned int regno, i;
+
+  frame = &cfun->machine->frame;
+  memset (frame, 0, sizeof (*frame));
+  size = get_frame_size ();
+
+  cfun->machine->global_pointer = mips_global_pointer ();
+
+  /* The first STARTING_FRAME_OFFSET bytes contain the outgoing argument
+     area and the $gp save slot.  This area isn't needed in leaf functions,
+     but if the target-independent frame size is nonzero, we're committed
+     to allocating it anyway.  */
+  if (size == 0 && current_function_is_leaf)
+    {
+      /* 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 (cfun->calls_alloca)
+       frame->args_size = REG_PARM_STACK_SPACE (cfun->decl);
+      else
+       frame->args_size = 0;
+      frame->cprestore_size = 0;
+    }
+  else
+    {
+      frame->args_size = crtl->outgoing_args_size;
+      frame->cprestore_size = STARTING_FRAME_OFFSET - frame->args_size;
+    }
+  offset = frame->args_size + frame->cprestore_size;
 
-                 output_asm_insn (load_store[i].load, xoperands);
-               }
-           }
+  /* Move above the local variables.  */
+  frame->var_size = MIPS_STACK_ALIGN (size);
+  offset += frame->var_size;
 
-         for (i = 0; i < num; i++)
-           {
-             int last_p = (i == num-1 && bytes == 0);
-             int offset = load_store[i].offset;
+  /* Find out which GPRs we need to save.  */
+  for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+    if (mips_save_reg_p (regno))
+      {
+       frame->num_gp++;
+       frame->mask |= 1 << (regno - GP_REG_FIRST);
+      }
 
-             xoperands[0] = operands[i + 4];
-             xoperands[1] = gen_rtx_MEM (load_store[i].mode,
-                                         plus_constant (dest_reg, offset));
+  /* If this function calls eh_return, we must also save and restore the
+     EH data registers.  */
+  if (crtl->calls_eh_return)
+    for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM; i++)
+      {
+       frame->num_gp++;
+       frame->mask |= 1 << (EH_RETURN_DATA_REGNO (i) - GP_REG_FIRST);
+      }
 
+  /* 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 (&frame->mask, mips16e_s2_s8_regs,
+                             ARRAY_SIZE (mips16e_s2_s8_regs), &frame->num_gp);
+      mips16e_mask_registers (&frame->mask, mips16e_a0_a3_regs,
+                             ARRAY_SIZE (mips16e_a0_a3_regs), &frame->num_gp);
+    }
 
-             if (use_lwl_lwr)
-               {
-                 int extra_offset = GET_MODE_SIZE (load_store[i].mode) - 1;
-                 xoperands[2] = gen_rtx_MEM (load_store[i].mode,
-                                             plus_constant (dest_reg,
-                                                            extra_offset
-                                                            + offset));
-               }
+  /* Move above the GPR save area.  */
+  if (frame->num_gp > 0)
+    {
+      offset += MIPS_STACK_ALIGN (frame->num_gp * UNITS_PER_WORD);
+      frame->gp_sp_offset = offset - UNITS_PER_WORD;
+    }
 
-             if (move_type == BLOCK_MOVE_NORMAL)
-               output_asm_insn (load_store[i].store, xoperands);
+  /* Find out which FPRs we need to save.  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_FIRST; regno <= FP_REG_LAST; regno += MAX_FPRS_PER_FMT)
+      if (mips_save_reg_p (regno))
+       {
+         frame->num_fp += MAX_FPRS_PER_FMT;
+         frame->fmask |= ~(~0 << MAX_FPRS_PER_FMT) << (regno - FP_REG_FIRST);
+       }
 
-             else if (move_type == BLOCK_MOVE_NOT_LAST)
-               {
-                 if (!last_p)
-                   output_asm_insn (load_store[i].store, xoperands);
+  /* Move above the FPR save area.  */
+  if (frame->num_fp > 0)
+    {
+      offset += MIPS_STACK_ALIGN (frame->num_fp * UNITS_PER_FPREG);
+      frame->fp_sp_offset = offset - UNITS_PER_HWFPVALUE;
+    }
 
-                 else if (load_store[i].final != 0)
-                   output_asm_insn (load_store[i].final, xoperands);
-               }
+  /* Move above the callee-allocated varargs save area.  */
+  offset += MIPS_STACK_ALIGN (cfun->machine->varargs_size);
+  frame->arg_pointer_offset = offset;
 
-             else if (last_p)
-               output_asm_insn (load_store[i].last_store, xoperands);
-           }
+  /* Move above the callee-allocated area for pretend stack arguments.  */
+  offset += crtl->args.pretend_args_size;
+  frame->total_size = offset;
 
-         num = 0;              /* reset load_store */
-         use_lwl_lwr = 0;
-       }
-    }
+  /* Work out the offsets of the save areas from the top of the frame.  */
+  if (frame->gp_sp_offset > 0)
+    frame->gp_save_offset = frame->gp_sp_offset - offset;
+  if (frame->fp_sp_offset > 0)
+    frame->fp_save_offset = frame->fp_sp_offset - offset;
 
-  return "";
+  /* MIPS16 code offsets the frame pointer by the size of the outgoing
+     arguments.  This tends to increase the chances of using unextended
+     instructions for local variables and incoming arguments.  */
+  if (TARGET_MIPS16)
+    frame->hard_frame_pointer_offset = frame->args_size;
 }
-\f
-/* Argument support functions.  */
 
-/* Initialize CUMULATIVE_ARGS for a function.  */
+/* Return the style of GP load sequence that is being used for the
+   current function.  */
 
-void
-init_cumulative_args (cum, fntype, libname)
-     CUMULATIVE_ARGS *cum;             /* argument info to initialize */
-     tree fntype;                      /* tree ptr for function decl */
-     rtx libname ATTRIBUTE_UNUSED;     /* SYMBOL_REF of library name or 0 */
+enum mips_loadgp_style
+mips_current_loadgp_style (void)
 {
-  static CUMULATIVE_ARGS zero_cum;
-  tree param, next_param;
+  if (!TARGET_USE_GOT || cfun->machine->global_pointer == INVALID_REGNUM)
+    return LOADGP_NONE;
 
-  if (TARGET_DEBUG_E_MODE)
-    {
-      fprintf (stderr,
-              "\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype);
+  if (TARGET_RTP_PIC)
+    return LOADGP_RTP;
 
-      if (!fntype)
-       fputc ('\n', stderr);
+  if (TARGET_ABSOLUTE_ABICALLS)
+    return LOADGP_ABSOLUTE;
 
-      else
-       {
-         tree ret_type = TREE_TYPE (fntype);
-         fprintf (stderr, ", fntype code = %s, ret code = %s\n",
-                  tree_code_name[(int)TREE_CODE (fntype)],
-                  tree_code_name[(int)TREE_CODE (ret_type)]);
-       }
-    }
+  return TARGET_NEWABI ? LOADGP_NEWABI : LOADGP_OLDABI;
+}
 
-  *cum = zero_cum;
-  cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
+/* Implement FRAME_POINTER_REQUIRED.  */
 
-  /* 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.  */
+bool
+mips_frame_pointer_required (void)
+{
+  /* If the function contains dynamic stack allocations, we need to
+     use the frame pointer to access the static parts of the frame.  */
+  if (cfun->calls_alloca)
+    return true;
 
-  for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
-       param != 0; param = next_param)
+  /* In MIPS16 mode, we need a frame pointer for a large frame; otherwise,
+     reload may be unable to compute the address of a local variable,
+     since there is no way to add a large constant to the stack pointer
+     without using a second temporary register.  */
+  if (TARGET_MIPS16)
     {
-      next_param = TREE_CHAIN (param);
-      if (next_param == 0 && TREE_VALUE (param) != void_type_node)
-       cum->gp_reg_found = 1;
+      mips_compute_frame_info ();
+      if (!SMALL_OPERAND (cfun->machine->frame.total_size))
+       return true;
     }
+
+  return false;
 }
 
-/* Advance the argument to the next argument position.  */
+/* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame pointer
+   or argument pointer.  TO is either the stack pointer or hard frame
+   pointer.  */
 
-void
-function_arg_advance (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;     /* current arg information */
-     enum machine_mode mode;   /* current arg mode */
-     tree type;                        /* type of the argument or 0 if lib support */
-     int named;                        /* whether or not the argument was named */
+HOST_WIDE_INT
+mips_initial_elimination_offset (int from, int to)
 {
-  if (TARGET_DEBUG_E_MODE)
-    {
-      fprintf (stderr,
-              "function_adv({gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
-              cum->gp_reg_found, cum->arg_number, cum->arg_words,
-              GET_MODE_NAME (mode));
-      fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
-      fprintf (stderr, ", %d )\n\n", named);
-    }
+  HOST_WIDE_INT offset;
 
-  cum->arg_number++;
-  switch (mode)
+  mips_compute_frame_info ();
+
+  /* Set OFFSET to the offset from the soft frame pointer, which is also
+     the offset from the end-of-prologue stack pointer.  */
+  switch (from)
     {
-    case VOIDmode:
+    case FRAME_POINTER_REGNUM:
+      offset = 0;
+      break;
+
+    case ARG_POINTER_REGNUM:
+      offset = cfun->machine->frame.arg_pointer_offset;
       break;
 
     default:
-      if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
-         && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
-       abort ();
+      gcc_unreachable ();
+    }
 
-      cum->gp_reg_found = 1;
-      cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
-                        / UNITS_PER_WORD);
-      break;
+  if (to == HARD_FRAME_POINTER_REGNUM)
+    offset -= cfun->machine->frame.hard_frame_pointer_offset;
 
-    case BLKmode:
-      cum->gp_reg_found = 1;
-      cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
-                        / UNITS_PER_WORD);
-      break;
+  return offset;
+}
+\f
+/* Implement TARGET_EXTRA_LIVE_ON_ENTRY.  */
 
-    case SFmode:
-      if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
-       cum->fp_arg_words++;
-      else
-       cum->arg_words++;
-      if (! cum->gp_reg_found && cum->arg_number <= 2)
-       cum->fp_code += 1 << ((cum->arg_number - 1) * 2);
-      break;
+static void
+mips_extra_live_on_entry (bitmap regs)
+{
+  if (TARGET_USE_GOT)
+    {
+      /* PIC_FUNCTION_ADDR_REGNUM is live if we need it to set up
+        the global pointer.   */
+      if (!TARGET_ABSOLUTE_ABICALLS)
+       bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
+
+      /* The prologue may set MIPS16_PIC_TEMP_REGNUM to the value of
+        the global pointer.  */
+      if (TARGET_MIPS16)
+       bitmap_set_bit (regs, MIPS16_PIC_TEMP_REGNUM);
+
+      /* See the comment above load_call<mode> for details.  */
+      bitmap_set_bit (regs, GOT_VERSION_REGNUM);
+    }
+}
+
+/* Implement RETURN_ADDR_RTX.  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);
+}
 
-    case DFmode:
-      if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
-       cum->fp_arg_words += (TARGET_64BIT ? 1 : 2);
-      else
-       cum->arg_words += (TARGET_64BIT ? 1 : 2);
-      if (! cum->gp_reg_found && ! TARGET_SINGLE_FLOAT && cum->arg_number <= 2)
-       cum->fp_code += 2 << ((cum->arg_number - 1) * 2);
-      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.  */
 
-    case DImode:
-    case TImode:
-      cum->gp_reg_found = 1;
-      cum->arg_words += (TARGET_64BIT ? 1 : 2);
-      break;
+void
+mips_set_return_address (rtx address, rtx scratch)
+{
+  rtx slot_address;
 
-    case QImode:
-    case HImode:
-    case SImode:
-      cum->gp_reg_found = 1;
-      cum->arg_words++;
-      break;
-    }
+  gcc_assert (BITSET_P (cfun->machine->frame.mask, 31));
+  slot_address = mips_add_offset (scratch, stack_pointer_rtx,
+                                 cfun->machine->frame.gp_sp_offset);
+  mips_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address);
 }
 
-/* Return an RTL expression containing the register for the given mode,
-   or 0 if the argument is to be passed on the stack.  */
+/* Return a MEM rtx for the cprestore slot, using TEMP as a temporary base
+   register if need be.  */
 
-struct rtx_def *
-function_arg (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;     /* current arg information */
-     enum machine_mode mode;   /* current arg mode */
-     tree type;                        /* type of the argument or 0 if lib support */
-     int named;                        /* != 0 for normal args, == 0 for ... args */
+static rtx
+mips_cprestore_slot (rtx temp)
 {
-  rtx ret;
-  int regbase = -1;
-  int bias = 0;
-  unsigned int *arg_words = &cum->arg_words;
-  int struct_p = (type != 0
-                 && (TREE_CODE (type) == RECORD_TYPE
-                     || TREE_CODE (type) == UNION_TYPE
-                     || TREE_CODE (type) == QUAL_UNION_TYPE));
+  const struct mips_frame_info *frame;
+  rtx base;
+  HOST_WIDE_INT offset;
 
-  if (TARGET_DEBUG_E_MODE)
+  frame = &cfun->machine->frame;
+  if (frame_pointer_needed)
+    {
+      base = hard_frame_pointer_rtx;
+      offset = frame->args_size - frame->hard_frame_pointer_offset;
+    }
+  else
     {
-      fprintf (stderr,
-              "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
-              cum->gp_reg_found, cum->arg_number, cum->arg_words,
-              GET_MODE_NAME (mode));
-      fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
-      fprintf (stderr, ", %d ) = ", named);
+      base = stack_pointer_rtx;
+      offset = frame->args_size;
     }
+  return gen_frame_mem (Pmode, mips_add_offset (temp, base, offset));
+}
 
+/* Restore $gp from its save slot, using TEMP as a temporary base register
+   if need be.  This function is for o32 and o64 abicalls only.  */
 
-  cum->last_arg_fp = 0;
-  switch (mode)
+void
+mips_restore_gp (rtx temp)
+{
+  gcc_assert (TARGET_ABICALLS && TARGET_OLDABI);
+
+  if (cfun->machine->global_pointer == INVALID_REGNUM)
+    return;
+
+  if (TARGET_MIPS16)
     {
-    case SFmode:
-      if (mips_abi == ABI_32 || mips_abi == ABI_O64)
-       {
-         if (cum->gp_reg_found || cum->arg_number >= 2 || TARGET_SOFT_FLOAT)
-           regbase = GP_ARG_FIRST;
-         else
-           {
-             regbase = FP_ARG_FIRST;
+      mips_emit_move (temp, mips_cprestore_slot (temp));
+      mips_emit_move (pic_offset_table_rtx, temp);
+    }
+  else
+    mips_emit_move (pic_offset_table_rtx, mips_cprestore_slot (temp));
+  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);
 
-             /* If the first arg was a float in a floating point register,
-                then set bias to align this float arg properly.  */
-             if (cum->arg_words == 1)
-               bias = 1;
-           }
-       }
-      else if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
-       {
-         if (! TARGET_64BIT)
-           cum->fp_arg_words += cum->fp_arg_words & 1;
-         cum->last_arg_fp = 1;
-         arg_words = &cum->fp_arg_words;
-         regbase = FP_ARG_FIRST;
-       }
-      /* The MIPS eabi says only structures containing doubles get passed in a
-         fp register, so force a structure containing a float to be passed in
-         the integer registers.  */
-      else if (mips_abi == ABI_MEABI && struct_p)
-       regbase = GP_ARG_FIRST;
-      else
-       regbase = (TARGET_SOFT_FLOAT || ! named ? GP_ARG_FIRST : FP_ARG_FIRST);
-      break;
+/* 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.  */
 
-    case DFmode:
-      if (! TARGET_64BIT)
-       {
-         if (mips_abi == ABI_EABI
-             && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
-           cum->fp_arg_words += cum->fp_arg_words & 1;
-         else
-           cum->arg_words += cum->arg_words & 1;
-       }
+static void
+mips_save_restore_reg (enum machine_mode mode, int regno,
+                      HOST_WIDE_INT offset, mips_save_restore_fn fn)
+{
+  rtx mem;
 
-      if (mips_abi == ABI_32 || mips_abi == ABI_O64)
-       regbase = ((cum->gp_reg_found
-                   || TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT
-                   || cum->arg_number >= 2)
-                  ? GP_ARG_FIRST : FP_ARG_FIRST);
-      else if (mips_abi == ABI_EABI
-              && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
-       {
-         cum->last_arg_fp = 1;
-         arg_words = &cum->fp_arg_words;
-         regbase = FP_ARG_FIRST;
-       }
-      else
-       regbase = (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT || ! named
-                  ? GP_ARG_FIRST : FP_ARG_FIRST);
-      break;
+  mem = gen_frame_mem (mode, plus_constant (stack_pointer_rtx, offset));
+  fn (gen_rtx_REG (mode, regno), mem);
+}
 
-    default:
-      if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
-         && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
-       abort ();
-
-      /* Drops through.  */
-    case BLKmode:
-      if (type != NULL_TREE && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD
-         && ! TARGET_64BIT && mips_abi != ABI_EABI)
-       cum->arg_words += (cum->arg_words & 1);
-      regbase = GP_ARG_FIRST;
-      break;
+/* 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.  */
 
-    case VOIDmode:
-    case QImode:
-    case HImode:
-    case SImode:
-      regbase = GP_ARG_FIRST;
-      break;
+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;
 
-    case DImode:
-    case TImode:
-      if (! TARGET_64BIT)
-       cum->arg_words += (cum->arg_words & 1);
-      regbase = GP_ARG_FIRST;
-    }
+  /* 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 (word_mode, regno, offset, fn);
+       offset -= UNITS_PER_WORD;
+      }
 
-  if (*arg_words >= (unsigned) MAX_ARGS_IN_REGISTERS)
-    {
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
+  /* This loop must iterate over the same space as its companion in
+     mips_compute_frame_info.  */
+  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.  */
 
-      ret = 0;
-    }
-  else
-    {
-      if (regbase == -1)
-       abort ();
+static void
+mips_output_cplocal (void)
+{
+  if (!TARGET_EXPLICIT_RELOCS
+      && cfun->machine->global_pointer != INVALID_REGNUM
+      && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM)
+    output_asm_insn (".cplocal %+", 0);
+}
 
-      if (! type || TREE_CODE (type) != RECORD_TYPE
-         || mips_abi == ABI_32  || mips_abi == ABI_EABI
-         || mips_abi == ABI_O64 || mips_abi == ABI_MEABI
-         || ! named
-         || ! TYPE_SIZE_UNIT (type)
-         || ! host_integerp (TYPE_SIZE_UNIT (type), 1))
-       {
+/* Implement TARGET_OUTPUT_FUNCTION_PROLOGUE.  */
 
-         unsigned int arg_reg = (regbase + *arg_words + bias);
-         ret = gen_rtx_REG (mode, arg_reg);
-         if (mips_abi == ABI_MEABI
-             && regbase == FP_ARG_FIRST
-             && ! cum->prototype)
-           {
-              /* To make K&R varargs work we need to pass floating
-                 point arguments in both integer and FP registers.  */
-              ret = gen_rtx_PARALLEL (mode,
-                                     gen_rtvec (2,
-                                                gen_rtx_EXPR_LIST (VOIDmode,
-                                                                   gen_rtx_REG (mode,
-                                                                                arg_reg + GP_ARG_FIRST - FP_ARG_FIRST),
-                                                                   const0_rtx),                                                gen_rtx_EXPR_LIST (VOIDmode, ret, const0_rtx)));
-            }
-       }
-      else
-       {
-         /* 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;
-
-         /* 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_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+{
+  const char *fnname;
 
-         /* If the whole struct fits a DFmode register,
-            we don't need the PARALLEL.  */
-         if (! field || mode == DFmode)
-           ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
-         else
-           {
-             /* Now handle the special case by returning a PARALLEL
-                indicating where each 64 bit chunk goes.  */
-             unsigned int chunks;
-             HOST_WIDE_INT bitpos;
-             unsigned int regno;
-             unsigned int i;
-
-             /* ??? If this is a packed structure, then the last hunk won't
-                be 64 bits.  */
-
-             chunks
-               = tree_low_cst (TYPE_SIZE_UNIT (type), 1) / UNITS_PER_WORD;
-             if (chunks + *arg_words + bias > (unsigned) MAX_ARGS_IN_REGISTERS)
-               chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias;
-
-             /* assign_parms checks the mode of ENTRY_PARM, so we must
-                use the actual mode here.  */
-             ret = gen_rtx_PARALLEL (mode, rtvec_alloc (chunks));
-
-             bitpos = 0;
-             regno = regbase + *arg_words + bias;
-             field = TYPE_FIELDS (type);
-             for (i = 0; i < chunks; i++)
-               {
-                 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,
-                                      regno + FP_ARG_FIRST - GP_ARG_FIRST);
-                 else
-                   reg = gen_rtx_REG (word_mode, regno);
-
-                 XVECEXP (ret, 0, i)
-                   = gen_rtx_EXPR_LIST (VOIDmode, reg,
-                                        GEN_INT (bitpos / BITS_PER_UNIT));
-
-                 bitpos += 64;
-                 regno++;
-               }
-           }
-       }
+#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
 
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias],
-                struct_p ? ", [struct]" : "");
-
-      /* The following is a hack in order to pass 1 byte structures
-        the same way that the MIPS compiler does (namely by passing
-        the structure in the high byte or half word of the register).
-        This also makes varargs work.  If we have such a structure,
-        we save the adjustment RTL, and the call define expands will
-        emit them.  For the VOIDmode argument (argument after the
-        last real argument), pass back a parallel vector holding each
-        of the adjustments.  */
-
-      /* ??? function_arg can be called more than once for each argument.
-        As a result, we compute more adjustments than we need here.
-        See the CUMULATIVE_ARGS definition in mips.h.  */
-
-      /* ??? This scheme requires everything smaller than the word size to
-        shifted to the left, but when TARGET_64BIT and ! TARGET_INT64,
-        that would mean every int needs to be shifted left, which is very
-        inefficient.  Let's not carry this compatibility to the 64 bit
-        calling convention for now.  */
-
-      if (struct_p && int_size_in_bytes (type) < UNITS_PER_WORD
-         && ! TARGET_64BIT
-         && mips_abi != ABI_EABI
-         && mips_abi != ABI_MEABI)
-       {
-         rtx amount = GEN_INT (BITS_PER_WORD
-                               - int_size_in_bytes (type) * BITS_PER_UNIT);
-         rtx reg = gen_rtx_REG (word_mode, regbase + *arg_words + bias);
+  /* In MIPS16 mode, we may need to generate a non-MIPS16 stub to handle
+     floating-point arguments.  */
+  if (TARGET_MIPS16
+      && TARGET_HARD_FLOAT_ABI
+      && crtl->args.info.fp_code != 0)
+    mips16_build_function_stub ();
 
-         if (TARGET_64BIT)
-           cum->adjust[cum->num_adjusts++] = gen_ashldi3 (reg, reg, amount);
-         else
-           cum->adjust[cum->num_adjusts++] = gen_ashlsi3 (reg, reg, amount);
-       }
-    }
+  /* 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);
+  mips_start_function_definition (fnname, TARGET_MIPS16);
 
-  /* 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 any shifts for small structures, return them in
-     a PARALLEL; in that case, stuff the mips16 fp_code in as the
-     mode.  Otherwise, if we have need a mips16 fp_code, return a REG
-     with the code stored as the mode.  */
-  if (mode == VOIDmode)
-    {
-      if (cum->num_adjusts > 0)
-       ret = gen_rtx (PARALLEL, (enum machine_mode) cum->fp_code,
-                      gen_rtvec_v (cum->num_adjusts, cum->adjust));
-      else if (TARGET_MIPS16 && cum->fp_code != 0)
-       ret = gen_rtx (REG, (enum machine_mode) cum->fp_code, 0);
-    }
+  /* 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;
 
-  return ret;
-}
+  /* Output MIPS-specific frame information.  */
+  if (!flag_inhibit_size_directive)
+    {
+      const struct mips_frame_info *frame;
 
-int
-function_arg_partial_nregs (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;     /* current arg information */
-     enum machine_mode mode;   /* current arg mode */
-     tree type;                        /* type of the argument or 0 if lib support */
-     int named ATTRIBUTE_UNUSED;/* != 0 for normal args, == 0 for ... args */
-{
-  if ((mode == BLKmode
-       || GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
-       || GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
-      && cum->arg_words < (unsigned) MAX_ARGS_IN_REGISTERS
-      && mips_abi != ABI_EABI)
-    {
-      int words;
-      if (mode == BLKmode)
-       words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
-                / UNITS_PER_WORD);
-      else
-       words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+      frame = &cfun->machine->frame;
 
-      if (words + cum->arg_words <= (unsigned) MAX_ARGS_IN_REGISTERS)
-       return 0;               /* structure fits in registers */
+      /* .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
+               ? frame->total_size - frame->hard_frame_pointer_offset
+               : frame->total_size),
+              reg_names[GP_REG_FIRST + 31],
+              frame->var_size,
+              frame->num_gp, frame->num_fp,
+              frame->args_size,
+              frame->cprestore_size);
 
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "function_arg_partial_nregs = %d\n",
-                MAX_ARGS_IN_REGISTERS - cum->arg_words);
+      /* .mask MASK, OFFSET.  */
+      fprintf (file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
+              frame->mask, frame->gp_save_offset);
 
-      return MAX_ARGS_IN_REGISTERS - cum->arg_words;
+      /* .fmask MASK, OFFSET.  */
+      fprintf (file, "\t.fmask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
+              frame->fmask, frame->fp_save_offset);
     }
 
-  else if (mode == DImode
-          && cum->arg_words == MAX_ARGS_IN_REGISTERS - (unsigned)1
-          && ! TARGET_64BIT && mips_abi != ABI_EABI)
+  /* Handle the initialization of $gp for SVR4 PIC, if applicable.
+     Also emit the ".set noreorder; .set nomacro" sequence for functions
+     that need it.  */
+  if (mips_current_loadgp_style () == LOADGP_OLDABI)
     {
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "function_arg_partial_nregs = 1\n");
-
-      return 1;
+      if (TARGET_MIPS16)
+       {
+         /* This is a fixed-form sequence.  The position of the
+            first two instructions is important because of the
+            way _gp_disp is defined.  */
+         output_asm_insn ("li\t$2,%%hi(_gp_disp)", 0);
+         output_asm_insn ("addiu\t$3,$pc,%%lo(_gp_disp)", 0);
+         output_asm_insn ("sll\t$2,16", 0);
+         output_asm_insn ("addu\t$2,$3", 0);
+       }
+      /* .cpload must be in a .set noreorder but not a .set nomacro block.  */
+      else 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);
 
-  return 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
-/* Create the va_list data type.
-   We keep 3 pointers, and two offsets.
-   Two pointers are to the overflow area, which starts at the CFA.
-     One of these is constant, for addressing into the GPR save area below it.
-     The other is advanced up the stack through the overflow region.
-   The third pointer is to the GPR save area.  Since the FPR save area
-     is just below it, we can address FPR slots off this pointer.
-   We also keep two one-byte offsets, which are to be subtracted from the
-     constant pointers to yield addresses in the GPR and FPR save areas.
-     These are downcounted as float or non-float arguments are used,
-     and when they get to zero, the argument must be obtained from the
-     overflow region.
-   If TARGET_SOFT_FLOAT or TARGET_SINGLE_FLOAT, then no FPR save area exists,
-     and a single pointer is enough.  It's started at the GPR save area,
-     and is advanced, period.
-   Note that the GPR save area is not constant size, due to optimization
-     in the prologue.  Hence, we can't use a design with two pointers
-     and two offsets, although we could have designed this with two pointers
-     and three offsets.  */
-
-
-tree
-mips_build_va_list ()
-{
-  if (mips_abi == ABI_EABI && !TARGET_SOFT_FLOAT && !TARGET_SINGLE_FLOAT)
-    {
-      tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff, record;
-
-      record = make_node (RECORD_TYPE);
-
-      f_ovfl = build_decl (FIELD_DECL, get_identifier ("__overflow_argptr"),
-                         ptr_type_node);
-      f_gtop = build_decl (FIELD_DECL, get_identifier ("__gpr_top"),
-                         ptr_type_node);
-      f_ftop = build_decl (FIELD_DECL, get_identifier ("__fpr_top"),
-                         ptr_type_node);
-      f_goff = build_decl (FIELD_DECL, get_identifier ("__gpr_offset"),
-                         unsigned_char_type_node);
-      f_foff = build_decl (FIELD_DECL, get_identifier ("__fpr_offset"),
-                         unsigned_char_type_node);
 
+/* Implement TARGET_OUTPUT_FUNCTION_EPILOGUE.  */
 
-      DECL_FIELD_CONTEXT (f_ovfl) = record;
-      DECL_FIELD_CONTEXT (f_gtop) = record;
-      DECL_FIELD_CONTEXT (f_ftop) = record;
-      DECL_FIELD_CONTEXT (f_goff) = record;
-      DECL_FIELD_CONTEXT (f_foff) = record;
+static void
+mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
+                              HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+{
+  const char *fnname;
 
-      TYPE_FIELDS (record) = f_ovfl;
-      TREE_CHAIN (f_ovfl) = f_gtop;
-      TREE_CHAIN (f_gtop) = f_ftop;
-      TREE_CHAIN (f_ftop) = f_goff;
-      TREE_CHAIN (f_goff) = f_foff;
+  /* Reinstate the normal $gp.  */
+  SET_REGNO (pic_offset_table_rtx, GLOBAL_POINTER_REGNUM);
+  mips_output_cplocal ();
 
-      layout_type (record);
-      return record;
+  if (cfun->machine->all_noreorder_p)
+    {
+      /* Avoid using %>%) since it adds excess whitespace.  */
+      output_asm_insn (".set\tmacro", 0);
+      output_asm_insn (".set\treorder", 0);
+      set_noreorder = set_nomacro = 0;
     }
-  else
-    return ptr_type_node;
-}
 
-/* Implement va_start.   stdarg_p is 0 if implementing
-   __builtin_varargs_va_start, 1 if implementing __builtin_stdarg_va_start.
-   Note that this routine isn't called when compiling e.g. "_vfprintf_r".
-     (It doesn't have "...", so it inherits the pointers of its caller.) */
+  /* 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);
+  mips_end_function_definition (fnname);
+}
+\f
+/* Save register REG to MEM.  Make the instruction frame-related.  */
 
-void
-mips_va_start (stdarg_p, valist, nextarg)
-     int stdarg_p;
-     tree valist;
-     rtx nextarg;
+static void
+mips_save_reg (rtx reg, rtx mem)
 {
-  int int_arg_words;
-  tree t;
-
-  /* Find out how many non-float named formals */
-  int_arg_words = current_function_args_info.arg_words;
-
-  if (mips_abi == ABI_EABI)
+  if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64)
     {
-      int gpr_save_area_size;
-      /* Note UNITS_PER_WORD is 4 bytes or 8, depending on TARGET_64BIT.  */
-      if (int_arg_words < 8 )
-       /* Adjust for the prologue's economy measure */
-       gpr_save_area_size = (8 - int_arg_words) * UNITS_PER_WORD;
-      else
-       gpr_save_area_size = 0;
+      rtx x1, x2;
 
-      if (!TARGET_SOFT_FLOAT && !TARGET_SINGLE_FLOAT)
-       {
-         tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
-         tree ovfl, gtop, ftop, goff, foff;
-         tree gprv;
-         int float_formals, fpr_offset, size_excess, floats_passed_in_regs;
-         int fpr_save_offset;
-
-         float_formals = current_function_args_info.fp_arg_words;
-         /* If mips2, the number of formals is half the reported # of words */
-         if (!TARGET_64BIT)
-           float_formals /= 2;
-         floats_passed_in_regs = (TARGET_64BIT ? 8 : 4);
-
-         f_ovfl  = TYPE_FIELDS (va_list_type_node);
-         f_gtop = TREE_CHAIN (f_ovfl);
-         f_ftop = TREE_CHAIN (f_gtop);
-         f_goff = TREE_CHAIN (f_ftop);
-         f_foff = TREE_CHAIN (f_goff);
-
-         ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
-         gtop = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
-         ftop = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
-         goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
-         foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
-
-         /* Emit code setting a pointer into the overflow (shared-stack) area.
-            If there were more than 8 non-float formals, or more than 8
-            float formals, then this pointer isn't to the base of the area.
-            In that case, it must point to where the first vararg is.  */
-         size_excess = 0;
-         if (float_formals > floats_passed_in_regs)
-           size_excess += (float_formals-floats_passed_in_regs) * 8;
-         if (int_arg_words > 8)
-           size_excess += (int_arg_words-8) * UNITS_PER_WORD;
-
-         /* FIXME: for mips2, the above size_excess can be wrong.  Because the
-            overflow stack holds mixed size items, there can be alignments,
-            so that an 8 byte double following a 4 byte int will be on an
-            8 byte boundary.  This means that the above calculation should
-            take into account the exact sequence of floats and non-floats
-            which make up the excess.  That calculation should be rolled
-            into the code which sets the current_function_args_info struct.
-            The above then reduces to a fetch from that struct.  */
-
-
-         t = make_tree (TREE_TYPE (ovfl), virtual_incoming_args_rtx);
-         if (size_excess)
-           t = build (PLUS_EXPR, TREE_TYPE (ovfl), t,
-               build_int_2 (size_excess, 0));
-         t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
-         expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
-         /* Emit code setting a ptr to the base of the overflow area.  */
-         t = make_tree (TREE_TYPE (gtop), virtual_incoming_args_rtx);
-         t = build (MODIFY_EXPR, TREE_TYPE (gtop), gtop, t);
-         expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
-         /* Emit code setting a pointer to the GPR save area.
-            More precisely, a pointer to off-the-end of the FPR save area.
-            If mips4, this is gpr_save_area_size below the overflow area.
-            If mips2, also round down to an 8-byte boundary, since the FPR
-            save area is 8-byte aligned, and GPR is 4-byte-aligned.
-            Therefore there can be a 4-byte gap between the save areas.  */
-         gprv = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
-         fpr_save_offset = gpr_save_area_size;
-         if (!TARGET_64BIT)
-           {
-             if (fpr_save_offset & 7)
-               fpr_save_offset += 4;
-           }
-         if (fpr_save_offset)
-           gprv = build (PLUS_EXPR, TREE_TYPE (ftop), gprv,
-               build_int_2 (-fpr_save_offset,-1));
-         t = build (MODIFY_EXPR, TREE_TYPE (ftop), ftop, gprv);
-         expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
-         /* Emit code initting an offset to the size of the GPR save area */
-         t = build (MODIFY_EXPR, TREE_TYPE (goff), goff,
-               build_int_2 (gpr_save_area_size,0));
-         expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
-         /* Emit code initting an offset from ftop to the first float
-            vararg.  This varies in size, since any float
-            varargs are put in the FPR save area after the formals.
-            Note it's 8 bytes/formal regardless of TARGET_64BIT.
-            However, mips2 stores 4 GPRs, mips4 stores 8 GPRs.
-            If there are 8 or more float formals, init to zero.
-            (In fact, the formals aren't stored in the bottom of the
-            FPR save area: they are elsewhere, and the size of the FPR
-            save area is economized by the prologue.  But this code doesn't
-            care.  This design is unaffected by that fact.) */
-         if (float_formals >= floats_passed_in_regs)
-           fpr_offset = 0;
-         else
-           fpr_offset = (floats_passed_in_regs - float_formals) * 8;
-         t = build (MODIFY_EXPR, TREE_TYPE (foff), foff,
-                    build_int_2 (fpr_offset,0));
-         expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-       }
+      if (mips_split_64bit_move_p (mem, reg))
+       mips_split_doubleword_move (mem, reg);
       else
-       {
-         /* TARGET_SOFT_FLOAT or TARGET_SINGLE_FLOAT */
-
-         /* Everything is in the GPR save area, or in the overflow
-            area which is contiguous with it.  */
+       mips_emit_move (mem, reg);
 
-         int offset = -gpr_save_area_size;
-         if (gpr_save_area_size == 0)
-           offset = (stdarg_p ? 0 : -UNITS_PER_WORD);
-         nextarg = plus_constant (nextarg, offset);
-         std_expand_builtin_va_start (1, valist, nextarg);
-       }
+      x1 = mips_frame_set (mips_subword (mem, false),
+                          mips_subword (reg, false));
+      x2 = mips_frame_set (mips_subword (mem, true),
+                          mips_subword (reg, true));
+      mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
     }
   else
     {
-      /* not EABI */
-      int ofs;
-
-      if (stdarg_p)
-       ofs = 0;
-      else
+      if (TARGET_MIPS16
+         && REGNO (reg) != GP_REG_FIRST + 31
+         && !M16_REG_P (REGNO (reg)))
        {
-         /* ??? This had been conditional on
-              _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
-            and both iris5.h and iris6.h define _MIPS_SIM.  */
-         if (mips_abi == ABI_N32 || mips_abi == ABI_64)
-           ofs = (int_arg_words >= 8 ? -UNITS_PER_WORD : 0);
-         else if (mips_abi == ABI_MEABI)
-           ofs = (int_arg_words >= 8 ? -UNITS_PER_WORD : 0);
-         else
-           ofs = -UNITS_PER_WORD;
+         /* 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);
 
-      nextarg = plus_constant (nextarg, ofs);
-      std_expand_builtin_va_start (1, valist, nextarg);
+      mips_set_frame_expr (mips_frame_set (mem, reg));
     }
 }
 
-/* Implement va_arg.  */
+/* The __gnu_local_gp symbol.  */
 
-rtx
-mips_va_arg (valist, type)
-     tree valist, type;
-{
-  HOST_WIDE_INT size, rsize;
-  rtx addr_rtx;
-  tree t;
+static GTY(()) rtx mips_gnu_local_gp;
 
-  size = int_size_in_bytes (type);
-  rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
+/* If we're generating n32 or n64 abicalls, emit instructions
+   to set up the global pointer.  */
 
-  if (mips_abi == ABI_EABI)
-    {
-      int indirect;
-      rtx r, lab_over = NULL_RTX, lab_false;
-      tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
-      tree ovfl, gtop, ftop, goff, foff;
+static void
+mips_emit_loadgp (void)
+{
+  rtx addr, offset, incoming_address, base, index, pic_reg;
 
-      indirect
-       = function_arg_pass_by_reference (NULL, TYPE_MODE (type), type, 0);
-      if (indirect)
+  pic_reg = TARGET_MIPS16 ? MIPS16_PIC_TEMP : pic_offset_table_rtx;
+  switch (mips_current_loadgp_style ())
+    {
+    case LOADGP_ABSOLUTE:
+      if (mips_gnu_local_gp == NULL)
        {
-         size = POINTER_SIZE / BITS_PER_UNIT;
-         rsize = UNITS_PER_WORD;
+         mips_gnu_local_gp = gen_rtx_SYMBOL_REF (Pmode, "__gnu_local_gp");
+         SYMBOL_REF_FLAGS (mips_gnu_local_gp) |= SYMBOL_FLAG_LOCAL;
        }
+      emit_insn (Pmode == SImode
+                ? gen_loadgp_absolute_si (pic_reg, mips_gnu_local_gp)
+                : gen_loadgp_absolute_di (pic_reg, mips_gnu_local_gp));
+      break;
 
-      addr_rtx = gen_reg_rtx (Pmode);
-
-      if (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT)
-       {
-         /* Case of all args in a merged stack. No need to check bounds,
-            just advance valist along the stack.  */
-
-         tree gpr = valist;
-         if (! indirect
-             && ! TARGET_64BIT
-             && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD)
-           {
-             t = build (PLUS_EXPR, TREE_TYPE (gpr), gpr,
-                    build_int_2 (2*UNITS_PER_WORD - 1, 0));
-             t = build (BIT_AND_EXPR, TREE_TYPE (t), t,
-                    build_int_2 (-2*UNITS_PER_WORD, -1));
-             t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, t);
-             expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-           }
-
-         t = build (POSTINCREMENT_EXPR, TREE_TYPE (gpr), gpr,
-               size_int (rsize));
-         r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-         if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
-
-         /* flush the POSTINCREMENT */
-         emit_queue();
+    case LOADGP_OLDABI:
+      /* Added by mips_output_function_prologue.  */
+      break;
 
-         if (indirect)
-           {
-             r = gen_rtx_MEM (Pmode, addr_rtx);
-             set_mem_alias_set (r, get_varargs_alias_set ());
-             emit_move_insn (addr_rtx, r);
-           }
-         else
-           {
-             if (BYTES_BIG_ENDIAN && rsize != size)
-             addr_rtx = plus_constant (addr_rtx, rsize - size);
-           }
-         return addr_rtx;
-       }
+    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 (Pmode == SImode
+                ? gen_loadgp_newabi_si (pic_reg, offset, incoming_address)
+                : gen_loadgp_newabi_di (pic_reg, offset, incoming_address));
+      break;
 
-      /* Not a simple merged stack.  Need ptrs and indexes left by va_start.  */
+    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 (Pmode == SImode
+                ? gen_loadgp_rtp_si (pic_reg, base, index)
+                : gen_loadgp_rtp_di (pic_reg, base, index));
+      break;
 
-      f_ovfl  = TYPE_FIELDS (va_list_type_node);
-      f_gtop = TREE_CHAIN (f_ovfl);
-      f_ftop = TREE_CHAIN (f_gtop);
-      f_goff = TREE_CHAIN (f_ftop);
-      f_foff = TREE_CHAIN (f_goff);
+    default:
+      return;
+    }
 
-      ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
-      gtop = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
-      ftop = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
-      goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
-      foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
+  if (TARGET_MIPS16)
+    emit_insn (gen_copygp_mips16 (pic_offset_table_rtx, pic_reg));
 
-      lab_false = gen_label_rtx ();
-      lab_over = gen_label_rtx ();
+  /* Emit a blockage if there are implicit uses of the GP register.
+     This includes profiled functions, because FUNCTION_PROFILE uses
+     a jal macro.  */
+  if (!TARGET_EXPLICIT_RELOCS || crtl->profile)
+    emit_insn (gen_loadgp_blockage ());
+}
 
-      if (TREE_CODE (type) == REAL_TYPE)
-        {
+/* Expand the "prologue" pattern.  */
 
-         /* Emit code to branch if foff == 0.  */
-          r = expand_expr (foff, NULL_RTX, TYPE_MODE (TREE_TYPE (foff)),
-               EXPAND_NORMAL);
-          emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
-                                  1, lab_false);
-
-          /* Emit code for addr_rtx = ftop - foff */
-          t = build (MINUS_EXPR, TREE_TYPE (ftop), ftop, foff );
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
-
-          /* Emit code for foff-=8.
-            Advances the offset up FPR save area by one double */
-          t = build (MINUS_EXPR, TREE_TYPE (foff), foff, build_int_2 (8, 0));
-          t = build (MODIFY_EXPR, TREE_TYPE (foff), foff, t);
-          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
-          emit_queue();
-          emit_jump (lab_over);
-          emit_barrier ();
-          emit_label (lab_false);
-
-         if (!TARGET_64BIT)
-           {
-             /* For mips2, the overflow area contains mixed size items.
-                If a 4-byte int is followed by an 8-byte float, then
-                natural alignment causes a 4 byte gap.
-                So, dynamically adjust ovfl up to a multiple of 8.  */
-             t = build (BIT_AND_EXPR, TREE_TYPE (ovfl), ovfl,
-                       build_int_2 (7, 0));
-             t = build (PLUS_EXPR, TREE_TYPE (ovfl), ovfl, t);
-             t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
-             expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-           }
+void
+mips_expand_prologue (void)
+{
+  const struct mips_frame_info *frame;
+  HOST_WIDE_INT size;
+  unsigned int nargs;
+  rtx insn;
 
-          /* Emit code for addr_rtx = the ovfl pointer into overflow area.
-            Regardless of mips2, postincrement the ovfl pointer by 8.  */
-          t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
-               size_int (8));
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
-
-          emit_queue();
-          emit_label (lab_over);
-                 return addr_rtx;
-        }
+  if (cfun->machine->global_pointer != INVALID_REGNUM)
+    SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+
+  frame = &cfun->machine->frame;
+  size = frame->total_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 ((frame->mask | 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 = 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 -= UNITS_PER_WORD;
+               mips_save_restore_reg (word_mode, regno,
+                                      offset, mips_save_reg);
+             }
+       }
       else
-        {
-          /* not REAL_TYPE */
-         int step_size;
+       {
+         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);
+       }
+    }
 
-         if (! TARGET_64BIT
-             && TREE_CODE (type) == INTEGER_TYPE
-             && TYPE_PRECISION (type) == 64)
+  /* 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)
            {
-             /* In mips2, int takes 32 bits of the GPR save area, but
-                longlong takes an aligned 64 bits.  So, emit code
-                to zero the low order bits of goff, thus aligning
-                the later calculation of (gtop-goff) upwards.  */
-              t = build (BIT_AND_EXPR, TREE_TYPE (goff), goff,
-                       build_int_2 (-8, -1));
-              t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
-              expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+             /* 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);
            }
-
-         /* Emit code to branch if goff == 0.  */
-          r = expand_expr (goff, NULL_RTX, TYPE_MODE (TREE_TYPE (goff)),
-               EXPAND_NORMAL);
-          emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
-                                  1, lab_false);
-
-          /* Emit code for addr_rtx = gtop - goff.  */
-          t = build (MINUS_EXPR, TREE_TYPE (gtop), gtop, goff);
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
-
-         /* Note that mips2 int is 32 bit, but mips2 longlong is 64.  */
-         if (! TARGET_64BIT && TYPE_PRECISION (type) == 64)
-           step_size = 8;
          else
-           step_size = UNITS_PER_WORD;
-
-          /* Emit code for goff = goff - step_size.
-            Advances the offset up GPR save area over the item.  */
-          t = build (MINUS_EXPR, TREE_TYPE (goff), goff,
-               build_int_2 (step_size, 0));
-          t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
-          expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
-          emit_queue();
-          emit_jump (lab_over);
-          emit_barrier ();
-          emit_label (lab_false);
-
-          /* Emit code for addr_rtx -> overflow area, postinc by step_size */
-          t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
-               size_int (step_size));
-          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
-          if (r != addr_rtx)
-           emit_move_insn (addr_rtx, r);
-
-          emit_queue();
-          emit_label (lab_over);
-
-         if (BYTES_BIG_ENDIAN && rsize != size)
-           addr_rtx = plus_constant (addr_rtx, rsize - size);
-
-          if (indirect)
-           {
-             addr_rtx = force_reg (Pmode, addr_rtx);
-             r = gen_rtx_MEM (Pmode, addr_rtx);
-             set_mem_alias_set (r, get_varargs_alias_set ());
-             emit_move_insn (addr_rtx, r);
-           }
+           emit_insn (gen_sub3_insn (stack_pointer_rtx,
+                                     stack_pointer_rtx,
+                                     MIPS_PROLOGUE_TEMP (Pmode)));
 
-         return addr_rtx;
+         /* 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)));
        }
     }
-  else
-    {
-      /* Not EABI.  */
-      int align;
 
-      /* ??? The original va-mips.h did always align, despite the fact
-        that alignments <= UNITS_PER_WORD are preserved by the va_arg
-        increment mechanism.  */
+  /* Set up the frame pointer, if we're using one.  */
+  if (frame_pointer_needed)
+    {
+      HOST_WIDE_INT offset;
 
-      if (TARGET_64BIT)
-       align = 8;
-      else if (TYPE_ALIGN (type) > 32)
-       align = 8;
+      offset = frame->hard_frame_pointer_offset;
+      if (offset == 0)
+       {
+         insn = mips_emit_move (hard_frame_pointer_rtx, stack_pointer_rtx);
+         RTX_FRAME_RELATED_P (insn) = 1;
+       }
+      else if (SMALL_OPERAND (offset))
+       {
+         insn = gen_add3_insn (hard_frame_pointer_rtx,
+                               stack_pointer_rtx, GEN_INT (offset));
+         RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+       }
       else
-       align = 4;
+       {
+         mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (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, offset)));
+       }
+    }
 
-      t = build (PLUS_EXPR, TREE_TYPE (valist), valist,
-                build_int_2 (align - 1, 0));
-      t = build (BIT_AND_EXPR, TREE_TYPE (t), t, build_int_2 (-align, -1));
-      t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
-      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  mips_emit_loadgp ();
 
-      /* Everything past the alignment is standard.  */
-      return std_expand_builtin_va_arg (valist, type);
+  /* Initialize the $gp save slot.  */
+  if (frame->cprestore_size > 0
+      && cfun->machine->global_pointer != INVALID_REGNUM)
+    {
+      if (TARGET_MIPS16)
+       mips_emit_move (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)),
+                       MIPS16_PIC_TEMP);
+      else if (TARGET_ABICALLS_PIC2)
+       emit_insn (gen_cprestore (GEN_INT (frame->args_size)));
+      else
+       emit_move_insn (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)),
+                       pic_offset_table_rtx);
     }
+
+  /* If we are profiling, make sure no instructions are scheduled before
+     the call to mcount.  */
+  if (crtl->profile)
+    emit_insn (gen_blockage ());
 }
 \f
-/* Abort after printing out a specific insn.  */
+/* Emit instructions to restore register REG from slot MEM.  */
 
 static void
-abort_with_insn (insn, reason)
-     rtx insn;
-     const char *reason;
+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), GP_REG_FIRST + 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);
+}
+
+/* Emit any instructions needed before a return.  */
+
+void
+mips_expand_before_return (void)
 {
-  error (reason);
-  debug_rtx (insn);
-  abort ();
+  /* When using a call-clobbered gp, we start out with unified call
+     insns that include instructions to restore the gp.  We then split
+     these unified calls after reload.  These split calls explicitly
+     clobber gp, so there is no need to define
+     PIC_OFFSET_TABLE_REG_CALL_CLOBBERED.
+
+     For consistency, we should also insert an explicit clobber of $28
+     before return insns, so that the post-reload optimizers know that
+     the register is not live on exit.  */
+  if (TARGET_CALL_CLOBBERED_GP)
+    emit_clobber (pic_offset_table_rtx);
 }
-\f
-/* 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 ()
-{
-  register int i, start;
-  register int regno;
-  register enum machine_mode mode;
-  register enum processor_type mips_cpu;
-
-  mips_section_threshold = g_switch_set ? g_switch_value : MIPS_DEFAULT_GVALUE;
-
-  if (mips_section_threshold <= 0)
-    target_flags &= ~MASK_GPOPT;
-  else if (optimize)
-    target_flags |= MASK_GPOPT;
-
-  /* If both single-float and soft-float are set, then clear the one that
-     was set by TARGET_DEFAULT, leaving the one that was set by the
-     user.  We assume here that the specs prevent both being set by the
-     user.  */
-#ifdef TARGET_DEFAULT
-  if (TARGET_SINGLE_FLOAT && TARGET_SOFT_FLOAT)
-    target_flags &= ~((TARGET_DEFAULT) & (MASK_SOFT_FLOAT | MASK_SINGLE_FLOAT));
-#endif
+/* Expand an "epilogue" or "sibcall_epilogue" pattern; SIBCALL_P
+   says which.  */
+
+void
+mips_expand_epilogue (bool sibcall_p)
+{
+  const struct mips_frame_info *frame;
+  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 ())
+    mips16_copy_fpr_return_value ();
 
-  /* Get the architectural level.  */
-  if (mips_isa_string == 0)
-    mips_isa = MIPS_ISA_DEFAULT;
+  /* Split the frame into two.  STEP1 is the amount of stack we should
+     deallocate before restoring the registers.  STEP2 is the amount we
+     should deallocate afterwards.
 
-  else if (mips_isa_string != 0
-          && mips_arch_string != 0)
-      warning ("The -march option is incompatible to -mipsN and therefore ignored.");
+     Start off by assuming that no registers need to be restored.  */
+  frame = &cfun->machine->frame;
+  step1 = frame->total_size;
+  step2 = 0;
 
-  else if (ISDIGIT (*mips_isa_string))
+  /* Work out which register holds the frame address.  */
+  if (!frame_pointer_needed)
+    base = stack_pointer_rtx;
+  else
     {
-      mips_isa = atoi (mips_isa_string);
-      if (mips_isa == 16)
-       {
-         /* -mno-mips16 overrides -mips16.  */
-         if (mips_no_mips16_string == NULL)
-           {
-             target_flags |= MASK_MIPS16;
-             if (TARGET_64BIT)
-               mips_isa = 3;
-             else
-               mips_isa = MIPS_ISA_DEFAULT;
-           }
-         else
-           {
-             mips_isa = MIPS_ISA_DEFAULT;
-           }
-       }
-      else if (mips_isa < 1
-              || (mips_isa > 4
-                  && mips_isa != 32
-                  && mips_isa != 64))
-       {
-         error ("-mips%d not supported", mips_isa);
-         mips_isa = 1;
-       }
+      base = hard_frame_pointer_rtx;
+      step1 -= frame->hard_frame_pointer_offset;
     }
 
-  else
+  /* If we need to restore registers, deallocate as much stack as
+     possible in the second step without going out of range.  */
+  if ((frame->mask | frame->fmask) != 0)
     {
-      error ("bad value (%s) for -mips switch", mips_isa_string);
-      mips_isa = 1;
-    }
-
-#ifdef MIPS_ABI_DEFAULT
-  /* Get the ABI to use.  */
-  if (mips_abi_string == (char *) 0)
-    mips_abi = MIPS_ABI_DEFAULT;
-  else if (! strcmp (mips_abi_string, "32"))
-    mips_abi = ABI_32;
-  else if (! strcmp (mips_abi_string, "o64"))
-    mips_abi = ABI_O64;
-  else if (! strcmp (mips_abi_string, "n32"))
-    mips_abi = ABI_N32;
-  else if (! strcmp (mips_abi_string, "64"))
-    mips_abi = ABI_64;
-  else if (! strcmp (mips_abi_string, "eabi"))
-    mips_abi = ABI_EABI;
-  else if (! strcmp (mips_abi_string, "meabi"))
-    mips_abi = ABI_MEABI;
-  else
-    error ("bad value (%s) for -mabi= switch", mips_abi_string);
+      step2 = MIN (step1, MIPS_MAX_FIRST_STACK_STEP);
+      step1 -= step2;
+    }
 
-  /* A specified ISA defaults the ABI if it was not specified.  */
-  if (mips_abi_string == 0 && mips_isa_string
-      && mips_abi != ABI_EABI
-      && mips_abi != ABI_O64
-      && mips_abi != ABI_MEABI)
+  /* Set TARGET to BASE + STEP1.  */
+  target = base;
+  if (step1 > 0)
     {
-      if (mips_isa == 64)
-       mips_abi = ABI_O64;
-      else
+      rtx adjust;
+
+      /* Get an rtx for STEP1 that we can add to BASE.  */
+      adjust = GEN_INT (step1);
+      if (!SMALL_OPERAND (step1))
        {
-         if (! ISA_HAS_64BIT_REGS)
-           mips_abi = ABI_32;
-         else if (mips_abi != ABI_N32)
-           mips_abi = ABI_64;
+         mips_emit_move (MIPS_EPILOGUE_TEMP (Pmode), adjust);
+         adjust = MIPS_EPILOGUE_TEMP (Pmode);
        }
-    }
 
-#ifdef MIPS_CPU_STRING_DEFAULT
-  /* A specified ABI defaults the ISA if it was not specified.  */
-  else if (mips_isa_string == 0 && mips_abi_string
-          && mips_abi != ABI_EABI && mips_abi != ABI_O64)
-    {
-      if (mips_abi == ABI_32)
-       mips_isa = 1;
-      else if (mips_abi == ABI_N32)
-       mips_isa = 3;
-      else
-       mips_isa = 4;
-    }
-#endif
+      /* Normal mode code can copy the result straight into $sp.  */
+      if (!TARGET_MIPS16)
+       target = stack_pointer_rtx;
 
-  /* If both ABI and ISA were specified, check for conflicts.  */
-  else if (mips_isa_string && mips_abi_string)
-    {
-      if (! ISA_HAS_64BIT_REGS && (mips_abi == ABI_N32 || mips_abi == ABI_64
-                            || mips_abi == ABI_O64))
-       error ("-mabi=%s does not support -mips%d", mips_abi_string, mips_isa);
+      emit_insn (gen_add3_insn (target, base, adjust));
     }
 
-  /* Override TARGET_DEFAULT if necessary.  */
-  if (mips_abi == ABI_32)
-    target_flags &= ~ (MASK_FLOAT64|MASK_64BIT);
-
-  /* If no type size setting options (-mlong64,-mint64,-mlong32) were used
-     then set the type sizes.  In the EABI in 64 bit mode, longs and
-     pointers are 64 bits.  Likewise for the SGI Irix6 N64 ABI.  */
-  if (mips_explicit_type_size_string == NULL
-      && ((mips_abi == ABI_EABI && TARGET_64BIT)
-         || mips_abi == ABI_64))
-    target_flags |= MASK_LONG64;
+  /* Copy TARGET into the stack pointer.  */
+  if (target != stack_pointer_rtx)
+    mips_emit_move (stack_pointer_rtx, target);
 
-#else
-  if (mips_abi_string)
-    error ("this target does not support the -mabi switch");
-#endif
+  /* If we're using addressing macros, $gp is implicitly used by all
+     SYMBOL_REFs.  We must emit a blockage insn before restoring $gp
+     from the stack.  */
+  if (TARGET_CALL_SAVED_GP && !TARGET_EXPLICIT_RELOCS)
+    emit_insn (gen_blockage ());
 
-#ifdef MIPS_CPU_STRING_DEFAULT
-  /* ??? There is a minor inconsistency here.  If the user specifies an ISA
-     greater than that supported by the default processor, then the user gets
-     an error.  Normally, the compiler will just default to the base level cpu
-     for the indicated isa.  */
-  if (mips_arch_string == 0)
-    mips_arch_string = MIPS_CPU_STRING_DEFAULT;
-  if (mips_tune_string == 0)
-    mips_tune_string = MIPS_CPU_STRING_DEFAULT;
-#endif
+  if (GENERATE_MIPS16E_SAVE_RESTORE && frame->mask != 0)
+    {
+      unsigned int regno, mask;
+      HOST_WIDE_INT offset;
+      rtx restore;
 
-  /* Identify the processor type.  */
+      /* Generate the restore instruction.  */
+      mask = frame->mask;
+      restore = mips16e_build_save_restore (true, &mask, &offset, 0, step2);
 
-  if (mips_cpu_string != 0)
-    {
-      mips_cpu = mips_parse_cpu (mips_cpu_string);
-      if (mips_cpu == PROCESSOR_DEFAULT)
-       {
-         error ("bad value (%s) for -mcpu= switch", mips_cpu_string);
-         mips_cpu_string = "default";
-       }
-      mips_arch = mips_cpu;
-      mips_tune = mips_cpu;
-    }
+      /* Restore any other registers manually.  */
+      for (regno = GP_REG_FIRST; regno < GP_REG_LAST; regno++)
+       if (BITSET_P (mask, regno - GP_REG_FIRST))
+         {
+           offset -= UNITS_PER_WORD;
+           mips_save_restore_reg (word_mode, regno, offset, mips_restore_reg);
+         }
 
-  if (mips_arch_string == 0
-      || ! strcmp (mips_arch_string, "default")
-      || ! strcmp (mips_arch_string, "DEFAULT"))
-    {
-      switch (mips_isa)
-       {
-       default:
-         mips_arch_string = "3000";
-         mips_arch = PROCESSOR_R3000;
-         break;
-       case 2:
-         mips_arch_string = "6000";
-         mips_arch = PROCESSOR_R6000;
-         break;
-       case 3:
-         mips_arch_string = "4000";
-         mips_arch = PROCESSOR_R4000;
-         break;
-       case 4:
-         mips_arch_string = "8000";
-         mips_arch = PROCESSOR_R8000;
-         break;
-       case 32:
-          mips_arch_string = "4kc";
-          mips_arch = PROCESSOR_R4KC;
-          break;
-        case 64:
-          mips_arch_string = "5kc";
-          mips_arch = PROCESSOR_R5KC;
-          break;
-       }
+      /* Restore the remaining registers and deallocate the final bit
+        of the frame.  */
+      emit_insn (restore);
     }
   else
     {
-      mips_arch = mips_parse_cpu (mips_arch_string);
-      if (mips_arch == PROCESSOR_DEFAULT)
-       {
-         error ("bad value (%s) for -march= switch", mips_arch_string);
-         mips_arch_string = "default";
-       }
-    }
-  if (mips_tune_string == 0
-      || ! strcmp (mips_tune_string, "default")
-      || ! strcmp (mips_tune_string, "DEFAULT"))
-    {
-      if (mips_arch != PROCESSOR_DEFAULT)
-       mips_tune = mips_arch;
-      else
-      switch (mips_isa)
-       {
-       default:
-         mips_tune_string = "3000";
-         mips_tune = PROCESSOR_R3000;
-         break;
-       case 2:
-         mips_tune_string = "6000";
-         mips_tune = PROCESSOR_R6000;
-         break;
-       case 3:
-         mips_tune_string = "4000";
-         mips_tune = PROCESSOR_R4000;
-         break;
-       case 4:
-         mips_tune_string = "8000";
-         mips_tune = PROCESSOR_R8000;
-         break;
-       case 32:
-         mips_tune_string = "4kc";
-         mips_tune = PROCESSOR_R4KC;
-         break;
-       case 64:
-         mips_tune_string = "5kc";
-         mips_tune = PROCESSOR_R5KC;
-         break;
-       }
+      /* Restore the registers.  */
+      mips_for_each_saved_reg (frame->total_size - step2, mips_restore_reg);
 
+      /* Deallocate the final bit of the frame.  */
+      if (step2 > 0)
+       emit_insn (gen_add3_insn (stack_pointer_rtx,
+                                 stack_pointer_rtx,
+                                 GEN_INT (step2)));
     }
-  else
+
+  /* Add in the __builtin_eh_return stack adjustment.  We need to
+     use a temporary in MIPS16 code.  */
+  if (crtl->calls_eh_return)
     {
-       mips_tune = mips_parse_cpu (mips_tune_string);
-      if (mips_tune == PROCESSOR_DEFAULT)
+      if (TARGET_MIPS16)
        {
-         error ("bad value (%s) for -mtune= switch", mips_tune_string);
-         mips_tune_string = "default";
+         mips_emit_move (MIPS_EPILOGUE_TEMP (Pmode), stack_pointer_rtx);
+         emit_insn (gen_add3_insn (MIPS_EPILOGUE_TEMP (Pmode),
+                                   MIPS_EPILOGUE_TEMP (Pmode),
+                                   EH_RETURN_STACKADJ_RTX));
+         mips_emit_move (stack_pointer_rtx, MIPS_EPILOGUE_TEMP (Pmode));
        }
+      else
+       emit_insn (gen_add3_insn (stack_pointer_rtx,
+                                 stack_pointer_rtx,
+                                 EH_RETURN_STACKADJ_RTX));
     }
 
-  /* make sure sizes of ints/longs/etc. are ok */
-  if (! ISA_HAS_64BIT_REGS)
+  if (!sibcall_p)
     {
-      if (TARGET_FLOAT64)
-       {
-         error ("-mips%d does not support 64 bit fp registers", mips_isa);
-         target_flags &= ~ MASK_FLOAT64;
-       }
+      unsigned int regno;
 
-      else if (TARGET_64BIT)
-       {
-         error ("-mips%d does not support 64 bit gp registers", mips_isa);
-         target_flags &= ~MASK_64BIT;
-       }
+      /* When generating MIPS16 code, the normal mips_for_each_saved_reg
+        path will restore the return address into $7 rather than $31.  */
+      if (TARGET_MIPS16
+         && !GENERATE_MIPS16E_SAVE_RESTORE
+         && BITSET_P (frame->mask, 31))
+       regno = GP_REG_FIRST + 7;
+      else
+       regno = GP_REG_FIRST + 31;
+      mips_expand_before_return ();
+      emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, regno)));
     }
+}
+\f
+/* Return nonzero if this function is known to have a null epilogue.
+   This allows the optimizer to omit jumps to jumps if no stack
+   was created.  */
 
-  if (mips_abi != ABI_32 && mips_abi != ABI_O64)
-    flag_pcc_struct_return = 0;
+bool
+mips_can_use_return_insn (void)
+{
+  if (!reload_completed)
+    return false;
 
-  /* Tell halfpic.c that we have half-pic code if we do.  */
-  if (TARGET_HALF_PIC)
-    HALF_PIC_INIT ();
+  if (crtl->profile)
+    return false;
 
-  /* -fpic (-KPIC) is the default when TARGET_ABICALLS is defined.  We need
-     to set flag_pic so that the LEGITIMATE_PIC_OPERAND_P macro will work.  */
-  /* ??? -non_shared turns off pic code generation, but this is not
-     implemented.  */
-  if (TARGET_ABICALLS)
-    {
-      mips_abicalls = MIPS_ABICALLS_YES;
-      flag_pic = 1;
-      if (mips_section_threshold > 0)
-       warning ("-G is incompatible with PIC code which is the default");
-    }
-  else
-    mips_abicalls = MIPS_ABICALLS_NO;
-
-  /* -membedded-pic is a form of PIC code suitable for embedded
-     systems.  All calls are made using PC relative addressing, and
-     all data is addressed using the $gp register.  This requires gas,
-     which does most of the work, and GNU ld, which automatically
-     expands PC relative calls which are out of range into a longer
-     instruction sequence.  All gcc really does differently is
-     generate a different sequence for a switch.  */
-  if (TARGET_EMBEDDED_PIC)
-    {
-      flag_pic = 1;
-      if (TARGET_ABICALLS)
-       warning ("-membedded-pic and -mabicalls are incompatible");
-
-      if (g_switch_set)
-       warning ("-G and -membedded-pic are incompatible");
-
-      /* Setting mips_section_threshold is not required, because gas
-        will force everything to be GP addressable anyhow, but
-        setting it will cause gcc to make better estimates of the
-        number of instructions required to access a particular data
-        item.  */
-      mips_section_threshold = 0x7fffffff;
-    }
-
-  /* This optimization requires a linker that can support a R_MIPS_LO16
-     relocation which is not immediately preceded by a R_MIPS_HI16 relocation.
-     GNU ld has this support, but not all other MIPS linkers do, so we enable
-     this optimization only if the user requests it, or if GNU ld is the
-     standard linker for this configuration.  */
-  /* ??? This does not work when target addresses are DImode.
-     This is because we are missing DImode high/lo_sum patterns.  */
-  if (TARGET_GAS && ! TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES && optimize && ! flag_pic
-      && Pmode == SImode)
-    mips_split_addresses = 1;
-  else
-    mips_split_addresses = 0;
+  /* In MIPS16 mode, a function that returns a floating-point value
+     needs to arrange to copy the return value into the floating-point
+     registers.  */
+  if (mips16_cfun_returns_in_fpr_p ())
+    return false;
 
-  /* -mrnames says to use the MIPS software convention for register
-     names instead of the hardware names (ie, $a0 instead of $4).
-     We do this by switching the names in mips_reg_names, which the
-     reg_names points into via the REGISTER_NAMES macro.  */
+  return cfun->machine->frame.total_size == 0;
+}
+\f
+/* Return true if register REGNO can store a value of mode MODE.
+   The result of this function is cached in mips_hard_regno_mode_ok.  */
 
-  if (TARGET_NAME_REGS)
-    memcpy (mips_reg_names, mips_sw_reg_names, sizeof (mips_reg_names));
+static bool
+mips_hard_regno_mode_ok_p (unsigned int regno, enum machine_mode mode)
+{
+  unsigned int size;
+  enum mode_class mclass;
 
-  /* When compiling for the mips16, we can not use floating point.  We
-     record the original hard float value in mips16_hard_float.  */
-  if (TARGET_MIPS16)
-    {
-      if (TARGET_SOFT_FLOAT)
-       mips16_hard_float = 0;
-      else
-       mips16_hard_float = 1;
-      target_flags |= MASK_SOFT_FLOAT;
+  if (mode == CCV2mode)
+    return (ISA_HAS_8CC
+           && ST_REG_P (regno)
+           && (regno - ST_REG_FIRST) % 2 == 0);
 
-      /* Don't run the scheduler before reload, since it tends to
-         increase register pressure.  */
-      flag_schedule_insns = 0;
-    }
+  if (mode == CCV4mode)
+    return (ISA_HAS_8CC
+           && ST_REG_P (regno)
+           && (regno - ST_REG_FIRST) % 4 == 0);
 
-  /* We put -mentry in TARGET_OPTIONS rather than TARGET_SWITCHES only
-     to avoid using up another bit in target_flags.  */
-  if (mips_entry_string != NULL)
+  if (mode == CCmode)
     {
-      if (*mips_entry_string != '\0')
-       error ("invalid option `entry%s'", mips_entry_string);
+      if (!ISA_HAS_8CC)
+       return regno == FPSW_REGNUM;
 
-      if (! TARGET_MIPS16)
-       warning ("-mentry is only meaningful with -mips-16");
-      else
-       mips_entry = 1;
+      return (ST_REG_P (regno)
+             || GP_REG_P (regno)
+             || FP_REG_P (regno));
     }
 
-  /* We copy TARGET_MIPS16 into the mips16 global variable, so that
-     attributes can access it.  */
-  if (TARGET_MIPS16)
-    mips16 = 1;
-  else
-    mips16 = 0;
-
-  /* Initialize the high and low values for legitimate floating point
-     constants.  Rather than trying to get the accuracy down to the
-     last bit, just use approximate ranges.  */
-  dfhigh = REAL_VALUE_ATOF ("1.0e300", DFmode);
-  dflow = REAL_VALUE_ATOF ("1.0e-300", DFmode);
-  sfhigh = REAL_VALUE_ATOF ("1.0e38", SFmode);
-  sflow = REAL_VALUE_ATOF ("1.0e-38", SFmode);
-
-  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_char_to_class['d'] = TARGET_MIPS16 ? M16_REGS : GR_REGS;
-  mips_char_to_class['e'] = M16_NA_REGS;
-  mips_char_to_class['t'] = T_REG;
-  mips_char_to_class['f'] = (TARGET_HARD_FLOAT ? FP_REGS : NO_REGS);
-  mips_char_to_class['h'] = HI_REG;
-  mips_char_to_class['l'] = LO_REG;
-  mips_char_to_class['a'] = HILO_REG;
-  mips_char_to_class['x'] = MD_REGS;
-  mips_char_to_class['b'] = ALL_REGS;
-  mips_char_to_class['y'] = GR_REGS;
-  mips_char_to_class['z'] = ST_REGS;
+  size = GET_MODE_SIZE (mode);
+  mclass = GET_MODE_CLASS (mode);
 
-  /* Set up array to map GCC register number to debug register number.
-     Ignore the special purpose register numbers.  */
+  if (GP_REG_P (regno))
+    return ((regno - GP_REG_FIRST) & 1) == 0 || size <= UNITS_PER_WORD;
 
-  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    mips_dbx_regno[i] = -1;
+  if (FP_REG_P (regno)
+      && (((regno - FP_REG_FIRST) % MAX_FPRS_PER_FMT) == 0
+         || (MIN_FPRS_PER_FMT == 1 && size <= UNITS_PER_FPREG)))
+    {
+      /* Allow TFmode for CCmode reloads.  */
+      if (mode == TFmode && ISA_HAS_8CC)
+       return true;
 
-  start = GP_DBX_FIRST - GP_REG_FIRST;
-  for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
-    mips_dbx_regno[i] = i + start;
+      /* Allow 64-bit vector modes for Loongson-2E/2F.  */
+      if (TARGET_LOONGSON_VECTORS
+         && (mode == V2SImode
+             || mode == V4HImode
+             || mode == V8QImode
+             || mode == DImode))
+       return true;
 
-  start = FP_DBX_FIRST - FP_REG_FIRST;
-  for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
-    mips_dbx_regno[i] = i + start;
+      if (mclass == MODE_FLOAT
+         || mclass == MODE_COMPLEX_FLOAT
+         || mclass == MODE_VECTOR_FLOAT)
+       return size <= UNITS_PER_FPVALUE;
 
-  /* Set up array giving whether a given register can hold a given mode.
-     At present, restrict ints from being in FP registers, because reload
-     is a little enthusiastic about storing extra values in FP registers,
-     and this is not good for things like OS kernels.  Also, due to the
-     mandatory delay, it is as fast to load from cached memory as to move
-     from the FP register.  */
+      /* Allow integer modes that fit into a single register.  We need
+        to put integers into FPRs when using instructions like CVT
+        and TRUNC.  There's no point allowing sizes smaller than a word,
+        because the FPU has no appropriate load/store instructions.  */
+      if (mclass == MODE_INT)
+       return size >= MIN_UNITS_PER_WORD && size <= UNITS_PER_FPREG;
+    }
 
-  for (mode = VOIDmode;
-       mode != MAX_MACHINE_MODE;
-       mode = (enum machine_mode) ((int)mode + 1))
+  if (ACC_REG_P (regno)
+      && (INTEGRAL_MODE_P (mode) || ALL_FIXED_POINT_MODE_P (mode)))
     {
-      register int size                     = GET_MODE_SIZE (mode);
-      register enum mode_class class = GET_MODE_CLASS (mode);
-
-      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+      if (MD_REG_P (regno))
        {
-         register int temp;
-
-         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));
-           }
-
-         else if (GP_REG_P (regno))
-           temp = ((regno & 1) == 0 || size <= UNITS_PER_WORD);
-
-         else if (FP_REG_P (regno))
-           temp = ((TARGET_FLOAT64 || ((regno & 1) == 0)
-                     /* I think this change is OK regardless of abi, but
-                        I'm being cautions untill I can test this more.
-                        HARD_REGNO_MODE_OK is about whether or not you
-                        can move to and from a register without changing
-                        the value, not about whether math works on the
-                        register.  */
-                     || (mips_abi == ABI_MEABI && size <= 4))
-                   && (class == MODE_FLOAT
-                       || class == MODE_COMPLEX_FLOAT
-                       || (TARGET_DEBUG_H_MODE && class == MODE_INT))
-                   && (! TARGET_SINGLE_FLOAT || size <= 4));
-
-         else if (MD_REG_P (regno))
-           temp = (class == MODE_INT
-                   && (size <= UNITS_PER_WORD
-                       || (regno == MD_REG_FIRST
-                           && size == 2 * UNITS_PER_WORD)));
+         /* After a multiplication or division, clobbering HI makes
+            the value of LO unpredictable, and vice versa.  This means
+            that, for all interesting cases, HI and LO are effectively
+            a single register.
 
-         else
-           temp = 0;
+            We model this by requiring that any value that uses HI
+            also uses LO.  */
+         if (size <= UNITS_PER_WORD * 2)
+           return regno == (size <= UNITS_PER_WORD ? LO_REGNUM : MD_REG_FIRST);
+       }
+      else
+       {
+         /* DSP accumulators do not have the same restrictions as
+            HI and LO, so we can treat them as normal doubleword
+            registers.  */
+         if (size <= UNITS_PER_WORD)
+           return true;
 
-         mips_hard_regno_mode_ok[(int)mode][regno] = temp;
+         if (size <= UNITS_PER_WORD * 2
+             && ((regno - DSP_ACC_REG_FIRST) & 1) == 0)
+           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;
+  if (ALL_COP_REG_P (regno))
+    return mclass == MODE_INT && size <= UNITS_PER_WORD;
 
-  /* Provide default values for align_* for 64-bit targets.  */
-  if (TARGET_64BIT && !TARGET_MIPS16)
-    {
-      if (align_loops == 0)
-       align_loops = 8;
-      if (align_jumps == 0)
-       align_jumps = 8;
-      if (align_functions == 0)
-       align_functions = 8;
-    }
+  if (regno == GOT_VERSION_REGNUM)
+    return mode == SImode;
 
-  /* Register global variables with the garbage collector.  */
-  mips_add_gc_roots ();
+  return false;
 }
 
-/* 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.  */
+/* Implement HARD_REGNO_NREGS.  */
 
-void
-mips_order_regs_for_local_alloc ()
+unsigned int
+mips_hard_regno_nregs (int regno, enum machine_mode mode)
 {
-  register int i;
+  if (ST_REG_P (regno))
+    /* The size of FP status registers is always 4, because they only hold
+       CCmode values, and CCmode is always considered to be 4 bytes wide.  */
+    return (GET_MODE_SIZE (mode) + 3) / 4;
 
-  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-    reg_alloc_order[i] = i;
+  if (FP_REG_P (regno))
+    return (GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG;
 
-  if (TARGET_MIPS16)
+  /* All other registers are word-sized.  */
+  return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+}
+
+/* Implement CLASS_MAX_NREGS, taking the maximum of the cases
+   in mips_hard_regno_nregs.  */
+
+int
+mips_class_max_nregs (enum reg_class rclass, enum machine_mode mode)
+{
+  int size;
+  HARD_REG_SET left;
+
+  size = 0x8000;
+  COPY_HARD_REG_SET (left, reg_class_contents[(int) rclass]);
+  if (hard_reg_set_intersect_p (left, reg_class_contents[(int) ST_REGS]))
     {
-      /* 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;
+      size = MIN (size, 4);
+      AND_COMPL_HARD_REG_SET (left, reg_class_contents[(int) ST_REGS]);
+    }
+  if (hard_reg_set_intersect_p (left, reg_class_contents[(int) FP_REGS]))
+    {
+      size = MIN (size, UNITS_PER_FPREG);
+      AND_COMPL_HARD_REG_SET (left, reg_class_contents[(int) FP_REGS]);
     }
+  if (!hard_reg_set_empty_p (left))
+    size = MIN (size, UNITS_PER_WORD);
+  return (GET_MODE_SIZE (mode) + size - 1) / size;
 }
 
-\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.  */
+/* Implement CANNOT_CHANGE_MODE_CLASS.  */
 
-HOST_WIDE_INT
-mips_debugger_offset (addr, offset)
-     rtx addr;
-     HOST_WIDE_INT offset;
+bool
+mips_cannot_change_mode_class (enum machine_mode from ATTRIBUTE_UNUSED,
+                              enum machine_mode to ATTRIBUTE_UNUSED,
+                              enum reg_class rclass)
 {
-  rtx offset2 = const0_rtx;
-  rtx reg = eliminate_constant_term (addr, &offset2);
-
-  if (offset == 0)
-    offset = INTVAL (offset2);
+  /* There are several problems with changing the modes of values
+     in floating-point registers:
 
-  if (reg == stack_pointer_rtx || reg == frame_pointer_rtx
-      || reg == hard_frame_pointer_rtx)
-    {
-      HOST_WIDE_INT frame_size = (!current_frame_info.initialized)
-                                 ? compute_frame_size (get_frame_size ())
-                                 : current_frame_info.total_size;
+     - 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.
 
-      /* MIPS16 frame is smaller */
-      if (frame_pointer_needed && TARGET_MIPS16)
-       frame_size -= current_function_outgoing_args_size;
+     - 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.
 
-      offset = offset - frame_size;
-    }
+     - 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.
 
-  /* sdbout_parms does not want this to crash for unrecognized cases.  */
-#if 0
-  else if (reg != arg_pointer_rtx)
-    abort_with_insn (addr, "mips_debugger_offset called with non stack/frame/arg pointer");
-#endif
+     - 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.
 
-  return offset;
+     We therefore disallow all mode changes involving FPRs.  */
+  return reg_classes_intersect_p (FP_REGS, rclass);
 }
-\f
-/* A C compound statement to output to stdio stream STREAM the
-   assembler syntax for an instruction operand X.  X is an RTL
-   expression.
-
-   CODE is a value that can be used to specify one of several ways
-   of printing the operand.  It is used when identical operands
-   must be printed differently depending on the context.  CODE
-   comes from the `%' specification that was used to request
-   printing of the operand.  If the specification was just `%DIGIT'
-   then CODE is 0; if the specification was `%LTR DIGIT' then CODE
-   is the ASCII code for LTR.
-
-   If X is a register, this macro should print the register's name.
-   The names can be found in an array `reg_names' whose type is
-   `char *[]'.  `reg_names' is initialized from `REGISTER_NAMES'.
-
-   When the machine description has a specification `%PUNCT' (a `%'
-   followed by a punctuation character), this macro is called with
-   a null pointer for X and the punctuation character for CODE.
-
-   The MIPS specific codes are:
-
-   'X'  X is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x",
-   'x'  X is CONST_INT, prints 16 bits in hexadecimal format = "0x%04x",
-   'd'  output integer constant in decimal,
-   'z' if the operand is 0, use $0 instead of normal operand.
-   'D'  print second part of double-word register or memory operand.
-   'L'  print low-order register of double-word register operand.
-   'M'  print high-order register of double-word register operand.
-   'C'  print part of opcode for a branch condition.
-   'F'  print part of opcode for a floating-point branch condition.
-   'N'  print part of opcode for a branch condition, inverted.
-   'W'  print part of opcode for a floating-point branch condition, inverted.
-   'S'  X is CODE_LABEL, print with prefix of "LS" (for embedded switch).
-   'B'  print 'z' for EQ, 'n' for NE
-   'b'  print 'n' for EQ, 'z' for NE
-   'T'  print 'f' for EQ, 't' for NE
-   't'  print 't' for EQ, 'f' for NE
-   'Z'  print register and a comma, but print nothing for $fcc0
-   '(' Turn on .set noreorder
-   ')' Turn on .set reorder
-   '[' Turn on .set noat
-   ']' Turn on .set at
-   '<' Turn on .set nomacro
-   '>' Turn on .set macro
-   '{' Turn on .set volatile (not GAS)
-   '}' Turn on .set novolatile (not GAS)
-   '&' Turn on .set noreorder if filling delay slots
-   '*' Turn on both .set noreorder and .set nomacro if filling delay slots
-   '!' Turn on .set nomacro if filling delay slots
-   '#' Print nop if in a .set noreorder section.
-   '?' Print 'l' if we are to use a branch likely instead of normal branch.
-   '@' Print the name of the assembler temporary register (at or $1).
-   '.' Print the name of the register with a hard-wired zero (zero or $0).
-   '^' Print the name of the pic call-through register (t9 or $25).
-   '$' Print the name of the stack pointer register (sp or $29).
-   '+' Print the name of the gp register (gp or $28).
-   '~' Output an branch alignment to LABEL_ALIGN(NULL).  */
 
-void
-print_operand (file, op, letter)
-     FILE *file;               /* file to write to */
-     rtx op;                   /* operand to print */
-     int letter;               /* %<letter> or 0 */
-{
-  register enum rtx_code code;
+/* Return true if moves in mode MODE can use the FPU's mov.fmt instruction.  */
 
-  if (PRINT_OPERAND_PUNCT_VALID_P (letter))
+static bool
+mips_mode_ok_for_mov_fmt_p (enum machine_mode mode)
+{
+  switch (mode)
     {
-      switch (letter)
-       {
-       case '?':
-         if (mips_branch_likely)
-           putc ('l', file);
-         break;
+    case SFmode:
+      return TARGET_HARD_FLOAT;
 
-       case '@':
-         fputs (reg_names [GP_REG_FIRST + 1], file);
-         break;
+    case DFmode:
+      return TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT;
 
-       case '^':
-         fputs (reg_names [PIC_FUNCTION_ADDR_REGNUM], file);
-         break;
+    case V2SFmode:
+      return TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT;
 
-       case '.':
-         fputs (reg_names [GP_REG_FIRST + 0], file);
-         break;
+    default:
+      return false;
+    }
+}
 
-       case '$':
-         fputs (reg_names[STACK_POINTER_REGNUM], file);
-         break;
+/* Implement MODES_TIEABLE_P.  */
 
-       case '+':
-         fputs (reg_names[GP_REG_FIRST + 28], file);
-         break;
+bool
+mips_modes_tieable_p (enum machine_mode mode1, enum machine_mode mode2)
+{
+  /* FPRs allow no mode punning, so it's not worth tying modes if we'd
+     prefer to put one of them in FPRs.  */
+  return (mode1 == mode2
+         || (!mips_mode_ok_for_mov_fmt_p (mode1)
+             && !mips_mode_ok_for_mov_fmt_p (mode2)));
+}
 
-       case '&':
-         if (final_sequence != 0 && set_noreorder++ == 0)
-           fputs (".set\tnoreorder\n\t", file);
-         break;
+/* Implement PREFERRED_RELOAD_CLASS.  */
 
-       case '*':
-         if (final_sequence != 0)
-           {
-             if (set_noreorder++ == 0)
-               fputs (".set\tnoreorder\n\t", file);
+enum reg_class
+mips_preferred_reload_class (rtx x, enum reg_class rclass)
+{
+  if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, rclass))
+    return LEA_REGS;
 
-             if (set_nomacro++ == 0)
-               fputs (".set\tnomacro\n\t", file);
-           }
-         break;
+  if (reg_class_subset_p (FP_REGS, rclass)
+      && mips_mode_ok_for_mov_fmt_p (GET_MODE (x)))
+    return FP_REGS;
 
-       case '!':
-         if (final_sequence != 0 && set_nomacro++ == 0)
-           fputs ("\n\t.set\tnomacro", file);
-         break;
+  if (reg_class_subset_p (GR_REGS, rclass))
+    rclass = GR_REGS;
 
-       case '#':
-         if (set_noreorder != 0)
-           fputs ("\n\tnop", file);
-         else if (TARGET_STATS)
-           fputs ("\n\t#nop", file);
+  if (TARGET_MIPS16 && reg_class_subset_p (M16_REGS, rclass))
+    rclass = M16_REGS;
 
-         break;
+  return rclass;
+}
 
-       case '(':
-         if (set_noreorder++ == 0)
-           fputs (".set\tnoreorder\n\t", file);
-         break;
+/* RCLASS is a class involved in a REGISTER_MOVE_COST calculation.
+   Return a "canonical" class to represent it in later calculations.  */
 
-       case ')':
-         if (set_noreorder == 0)
-           error ("internal error: %%) found without a %%( in assembler pattern");
+static enum reg_class
+mips_canonicalize_move_class (enum reg_class rclass)
+{
+  /* All moves involving accumulator registers have the same cost.  */
+  if (reg_class_subset_p (rclass, ACC_REGS))
+    rclass = ACC_REGS;
 
-         else if (--set_noreorder == 0)
-           fputs ("\n\t.set\treorder", file);
+  /* Likewise promote subclasses of general registers to the most
+     interesting containing class.  */
+  if (TARGET_MIPS16 && reg_class_subset_p (rclass, M16_REGS))
+    rclass = M16_REGS;
+  else if (reg_class_subset_p (rclass, GENERAL_REGS))
+    rclass = GENERAL_REGS;
 
-         break;
+  return rclass;
+}
 
-       case '[':
-         if (set_noat++ == 0)
-           fputs (".set\tnoat\n\t", file);
-         break;
+/* Return the cost of moving a value of mode MODE from a register of
+   class FROM to a GPR.  Return 0 for classes that are unions of other
+   classes handled by this function.  */
 
-       case ']':
-         if (set_noat == 0)
-           error ("internal error: %%] found without a %%[ in assembler pattern");
-         else if (--set_noat == 0)
-           fputs ("\n\t.set\tat", file);
+static int
+mips_move_to_gpr_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+                      enum reg_class from)
+{
+  switch (from)
+    {
+    case GENERAL_REGS:
+      /* A MIPS16 MOVE instruction, or a non-MIPS16 MOVE macro.  */
+      return 2;
 
-         break;
+    case ACC_REGS:
+      /* MFLO and MFHI.  */
+      return 6;
 
-       case '<':
-         if (set_nomacro++ == 0)
-           fputs (".set\tnomacro\n\t", file);
-         break;
+    case FP_REGS:
+      /* MFC1, etc.  */
+      return 4;
 
-       case '>':
-         if (set_nomacro == 0)
-           error ("internal error: %%> found without a %%< in assembler pattern");
-         else if (--set_nomacro == 0)
-           fputs ("\n\t.set\tmacro", file);
+    case ST_REGS:
+      /* LUI followed by MOVF.  */
+      return 4;
 
-         break;
+    case COP0_REGS:
+    case COP2_REGS:
+    case COP3_REGS:
+      /* This choice of value is historical.  */
+      return 5;
 
-       case '{':
-         if (set_volatile++ == 0)
-           fprintf (file, "%s.set\tvolatile\n\t", TARGET_MIPS_AS ? "" : "#");
-         break;
+    default:
+      return 0;
+    }
+}
 
-       case '}':
-         if (set_volatile == 0)
-           error ("internal error: %%} found without a %%{ in assembler pattern");
-         else if (--set_volatile == 0)
-           fprintf (file, "\n\t%s.set\tnovolatile", (TARGET_MIPS_AS) ? "" : "#");
+/* Return the cost of moving a value of mode MODE from a GPR to a
+   register of class TO.  Return 0 for classes that are unions of
+   other classes handled by this function.  */
 
-         break;
+static int
+mips_move_from_gpr_cost (enum machine_mode mode, enum reg_class to)
+{
+  switch (to)
+    {
+    case GENERAL_REGS:
+      /* A MIPS16 MOVE instruction, or a non-MIPS16 MOVE macro.  */
+      return 2;
 
-       case '~':
-         {
-           if (align_labels_log > 0)
-             ASM_OUTPUT_ALIGN (file, align_labels_log);
-         }
-       break;
+    case ACC_REGS:
+      /* MTLO and MTHI.  */
+      return 6;
 
-       default:
-         error ("PRINT_OPERAND: unknown punctuation '%c'", letter);
-         break;
-       }
+    case FP_REGS:
+      /* MTC1, etc.  */
+      return 4;
 
-      return;
-    }
+    case ST_REGS:
+      /* A secondary reload through an FPR scratch.  */
+      return (mips_register_move_cost (mode, GENERAL_REGS, FP_REGS)
+             + mips_register_move_cost (mode, FP_REGS, ST_REGS));
 
-  if (! op)
-    {
-      error ("PRINT_OPERAND null pointer");
-      return;
-    }
+    case COP0_REGS:
+    case COP2_REGS:
+    case COP3_REGS:
+      /* This choice of value is historical.  */
+      return 5;
 
-  code = GET_CODE (op);
+    default:
+      return 0;
+    }
+}
 
-  if (code == SIGN_EXTEND)
-    op = XEXP (op, 0), code = GET_CODE (op);
+/* Implement REGISTER_MOVE_COST.  Return 0 for classes that are the
+   maximum of the move costs for subclasses; regclass will work out
+   the maximum for us.  */
 
-  if (letter == 'C')
-    switch (code)
-      {
-      case EQ: fputs ("eq",  file); break;
-      case NE: fputs ("ne",  file); break;
-      case GT: fputs ("gt",  file); break;
-      case GE: fputs ("ge",  file); break;
-      case LT: fputs ("lt",  file); break;
-      case LE: fputs ("le",  file); break;
-      case GTU: fputs ("gtu", file); break;
-      case GEU: fputs ("geu", file); break;
-      case LTU: fputs ("ltu", file); break;
-      case LEU: fputs ("leu", file); break;
-      default:
-       abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%C");
-      }
+int
+mips_register_move_cost (enum machine_mode mode,
+                        enum reg_class from, enum reg_class to)
+{
+  enum reg_class dregs;
+  int cost1, cost2;
 
-  else if (letter == 'N')
-    switch (code)
-      {
-      case EQ: fputs ("ne",  file); break;
-      case NE: fputs ("eq",  file); break;
-      case GT: fputs ("le",  file); break;
-      case GE: fputs ("lt",  file); break;
-      case LT: fputs ("ge",  file); break;
-      case LE: fputs ("gt",  file); break;
-      case GTU: fputs ("leu", file); break;
-      case GEU: fputs ("ltu", file); break;
-      case LTU: fputs ("geu", file); break;
-      case LEU: fputs ("gtu", file); break;
-      default:
-       abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%N");
-      }
+  from = mips_canonicalize_move_class (from);
+  to = mips_canonicalize_move_class (to);
 
-  else if (letter == 'F')
-    switch (code)
-      {
-      case EQ: fputs ("c1f", file); break;
-      case NE: fputs ("c1t", file); break;
-      default:
-       abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%F");
-      }
+  /* Handle moves that can be done without using general-purpose registers.  */
+  if (from == FP_REGS)
+    {
+      if (to == FP_REGS && mips_mode_ok_for_mov_fmt_p (mode))
+       /* MOV.FMT.  */
+       return 4;
+      if (to == ST_REGS)
+       /* The sequence generated by mips_expand_fcc_reload.  */
+       return 8;
+    }
 
-  else if (letter == 'W')
-    switch (code)
-      {
-      case EQ: fputs ("c1t", file); break;
-      case NE: fputs ("c1f", file); break;
-      default:
-       abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%W");
-      }
+  /* Handle cases in which only one class deviates from the ideal.  */
+  dregs = TARGET_MIPS16 ? M16_REGS : GENERAL_REGS;
+  if (from == dregs)
+    return mips_move_from_gpr_cost (mode, to);
+  if (to == dregs)
+    return mips_move_to_gpr_cost (mode, from);
 
-  else if (letter == 'S')
+  /* Handles cases that require a GPR temporary.  */
+  cost1 = mips_move_to_gpr_cost (mode, from);
+  if (cost1 != 0)
     {
-      char buffer[100];
-
-      ASM_GENERATE_INTERNAL_LABEL (buffer, "LS", CODE_LABEL_NUMBER (op));
-      assemble_name (file, buffer);
+      cost2 = mips_move_from_gpr_cost (mode, to);
+      if (cost2 != 0)
+       return cost1 + cost2;
     }
 
-  else if (letter == 'Z')
-    {
-      register int regnum;
+  return 0;
+}
 
-      if (code != REG)
-       abort ();
+/* Implement TARGET_IRA_COVER_CLASSES.  */
 
-      regnum = REGNO (op);
-      if (! ST_REG_P (regnum))
-       abort ();
+static const enum reg_class *
+mips_ira_cover_classes (void)
+{
+  static const enum reg_class acc_classes[] = {
+    GR_AND_ACC_REGS, FP_REGS, COP0_REGS, COP2_REGS, COP3_REGS,
+    ST_REGS, LIM_REG_CLASSES
+  };
+  static const enum reg_class no_acc_classes[] = {
+    GR_REGS, FP_REGS, COP0_REGS, COP2_REGS, COP3_REGS,
+    ST_REGS, LIM_REG_CLASSES
+  };
 
-      if (regnum != ST_REG_FIRST)
-       fprintf (file, "%s,", reg_names[regnum]);
-    }
+  /* Don't allow the register allocators to use LO and HI in MIPS16 mode,
+     which has no MTLO or MTHI instructions.  Also, using GR_AND_ACC_REGS
+     as a cover class only works well when we keep per-register costs.
+     Using it when not optimizing can cause us to think accumulators
+     have the same cost as GPRs in cases where GPRs are actually much
+     cheaper.  */
+  return TARGET_MIPS16 || !optimize ? no_acc_classes : acc_classes;
+}
 
-  else if (code == REG || code == SUBREG)
-    {
-      register int regnum;
+/* Return the register class required for a secondary register when
+   copying between one of the registers in RCLASS and value X, which
+   has mode MODE.  X is the source of the move if IN_P, otherwise it
+   is the destination.  Return NO_REGS if no secondary register is
+   needed.  */
 
-      if (code == REG)
-       regnum = REGNO (op);
-      else
-       regnum = true_regnum (op);
+enum reg_class
+mips_secondary_reload_class (enum reg_class rclass,
+                            enum machine_mode mode, rtx x, bool in_p)
+{
+  int regno;
 
-      if ((letter == 'M' && ! WORDS_BIG_ENDIAN)
-         || (letter == 'L' && WORDS_BIG_ENDIAN)
-         || letter == 'D')
-       regnum++;
+  /* 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 (rclass, LEA_REGS) ? NO_REGS : LEA_REGS;
+
+  regno = true_regnum (x);
+  if (TARGET_MIPS16)
+    {
+      /* In MIPS16 mode, every move must involve a member of M16_REGS.  */
+      if (!reg_class_subset_p (rclass, M16_REGS) && !M16_REG_P (regno))
+       return M16_REGS;
 
-      fprintf (file, "%s", reg_names[regnum]);
+      return NO_REGS;
     }
 
-  else if (code == MEM)
+  /* Copying from accumulator registers to anywhere other than a general
+     register requires a temporary general register.  */
+  if (reg_class_subset_p (rclass, ACC_REGS))
+    return GP_REG_P (regno) ? NO_REGS : GR_REGS;
+  if (ACC_REG_P (regno))
+    return reg_class_subset_p (rclass, 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 (rclass, ST_REGS))
     {
-      if (letter == 'D')
-       output_address (plus_constant (XEXP (op, 0), 4));
-      else
-       output_address (XEXP (op, 0));
+      if (in_p)
+       return FP_REGS;
+      return GP_REG_P (regno) ? NO_REGS : GR_REGS;
     }
-
-  else if (code == CONST_DOUBLE
-          && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT)
+  if (ST_REG_P (regno))
     {
-      REAL_VALUE_TYPE d;
-      char s[30];
-
-      REAL_VALUE_FROM_CONST_DOUBLE (d, op);
-      REAL_VALUE_TO_DECIMAL (d, "%.20e", s);
-      fprintf (file, s);
+      if (!in_p)
+       return FP_REGS;
+      return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
     }
 
-  else if (letter == 'x' && GET_CODE (op) == CONST_INT)
-    fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL(op));
+  if (reg_class_subset_p (rclass, 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;
 
-  else if (letter == 'X' && GET_CODE(op) == CONST_INT)
-    fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
+      if (GP_REG_P (regno) || x == CONST0_RTX (mode))
+       /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1.  */
+       return NO_REGS;
 
-  else if (letter == 'd' && GET_CODE(op) == CONST_INT)
-    fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL(op)));
+      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;
 
-  else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
-    fputs (reg_names[GP_REG_FIRST], file);
+      if (FP_REG_P (regno) && mips_mode_ok_for_mov_fmt_p (mode))
+       /* In this case we can use mov.fmt.  */
+       return NO_REGS;
 
-  else if (letter == 'd' || letter == 'x' || letter == 'X')
-    output_operand_lossage ("invalid use of %%d, %%x, or %%X");
+      /* Otherwise, we need to reload through an integer register.  */
+      return GR_REGS;
+    }
+  if (FP_REG_P (regno))
+    return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
 
-  else if (letter == 'B')
-    fputs (code == EQ ? "z" : "n", file);
-  else if (letter == 'b')
-    fputs (code == EQ ? "n" : "z", file);
-  else if (letter == 'T')
-    fputs (code == EQ ? "f" : "t", file);
-  else if (letter == 't')
-    fputs (code == EQ ? "t" : "f", file);
+  return NO_REGS;
+}
 
-  else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG)
-    {
-      /* This case arises on the mips16; see mips16_gp_pseudo_reg.  */
-      print_operand (file, XEXP (op, 0), letter);
-    }
+/* Implement TARGET_MODE_REP_EXTENDED.  */
 
-  else if (TARGET_MIPS16 && code == CONST && mips16_gp_offset_p (op))
-    {
-      fputs ("%gprel(", file);
-      mips16_output_gp_offset (file, op);
-      fputs (")", file);
-    }
+static int
+mips_mode_rep_extended (enum machine_mode mode, enum machine_mode mode_rep)
+{
+  /* On 64-bit targets, SImode register values are sign-extended to DImode.  */
+  if (TARGET_64BIT && mode == SImode && mode_rep == DImode)
+    return SIGN_EXTEND;
 
-  else
-    output_addr_const (file, op);
+  return UNKNOWN;
 }
 \f
-/* A C compound statement to output to stdio stream STREAM the
-   assembler syntax for an instruction operand that is a memory
-   reference whose address is ADDR.  ADDR is an RTL expression.
+/* Implement TARGET_VALID_POINTER_MODE.  */
+
+static bool
+mips_valid_pointer_mode (enum machine_mode mode)
+{
+  return mode == SImode || (TARGET_64BIT && mode == DImode);
+}
 
-   On some machines, the syntax for a symbolic address depends on
-   the section that the address refers to.  On these machines,
-   define the macro `ENCODE_SECTION_INFO' to store the information
-   into the `symbol_ref', and then check for it here.  */
+/* Implement TARGET_VECTOR_MODE_SUPPORTED_P.  */
 
-void
-print_operand_address (file, addr)
-     FILE *file;
-     rtx addr;
+static bool
+mips_vector_mode_supported_p (enum machine_mode mode)
 {
-  if (!addr)
-    error ("PRINT_OPERAND_ADDRESS, null pointer");
+  switch (mode)
+    {
+    case V2SFmode:
+      return TARGET_PAIRED_SINGLE_FLOAT;
 
-  else
-    switch (GET_CODE (addr))
-      {
-      case REG:
-       if (! TARGET_MIPS16 && REGNO (addr) == ARG_POINTER_REGNUM)
-         abort_with_insn (addr, "arg pointer not eliminated");
+    case V2HImode:
+    case V4QImode:
+    case V2HQmode:
+    case V2UHQmode:
+    case V2HAmode:
+    case V2UHAmode:
+    case V4QQmode:
+    case V4UQQmode:
+      return TARGET_DSP;
 
-       fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
-       break;
+    case V2SImode:
+    case V4HImode:
+    case V8QImode:
+      return TARGET_LOONGSON_VECTORS;
 
-      case LO_SUM:
-       {
-         register rtx arg0 = XEXP (addr, 0);
-         register rtx arg1 = XEXP (addr, 1);
+    default:
+      return false;
+    }
+}
 
-         if (! mips_split_addresses)
-           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, Spurious LO_SUM");
+/* Implement TARGET_SCALAR_MODE_SUPPORTED_P.  */
 
-         if (GET_CODE (arg0) != REG)
-           abort_with_insn (addr,
-                            "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG");
+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;
 
-         fprintf (file, "%%lo(");
-         print_operand_address (file, arg1);
-         fprintf (file, ")(%s)", reg_names [REGNO (arg0)]);
-       }
-       break;
+  return default_scalar_mode_supported_p (mode);
+}
+\f
+/* Implement TARGET_INIT_LIBFUNCS.  */
 
-      case PLUS:
-       {
-         register rtx reg = 0;
-         register rtx offset = 0;
-         register rtx arg0 = XEXP (addr, 0);
-         register rtx arg1 = XEXP (addr, 1);
+#include "config/gofast.h"
 
-         if (GET_CODE (arg0) == REG)
-           {
-             reg = arg0;
-             offset = arg1;
-             if (GET_CODE (offset) == REG)
-               abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs");
-           }
+static void
+mips_init_libfuncs (void)
+{
+  if (TARGET_FIX_VR4120)
+    {
+      /* Register the special divsi3 and modsi3 functions needed to work
+        around VR4120 division errata.  */
+      set_optab_libfunc (sdiv_optab, SImode, "__vr4120_divsi3");
+      set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
+    }
+
+  if (TARGET_MIPS16 && TARGET_HARD_FLOAT_ABI)
+    {
+      /* Register the MIPS16 -mhard-float stubs.  */
+      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");
+
+      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");
+
+         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");
+
+         set_conv_libfunc (sext_optab, DFmode, SFmode,
+                           "__mips16_extendsfdf2");
+         set_conv_libfunc (trunc_optab, SFmode, DFmode,
+                           "__mips16_truncdfsf2");
+         set_conv_libfunc (sfix_optab, SImode, DFmode,
+                           "__mips16_fix_truncdfsi");
+         set_conv_libfunc (sfloat_optab, DFmode, SImode,
+                           "__mips16_floatsidf");
+         set_conv_libfunc (ufloat_optab, DFmode, SImode,
+                           "__mips16_floatunsidf");
+       }
+    }
+  else
+    /* Register the gofast functions if selected using --enable-gofast.  */
+    gofast_maybe_init_libfuncs ();
 
-         else if (GET_CODE (arg1) == REG)
-             reg = arg1, offset = arg0;
-         else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
-           {
-             output_addr_const (file, addr);
-             break;
-           }
-         else
-           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs");
+  /* The MIPS16 ISA does not have an encoding for "sync", so we rely
+     on an external non-MIPS16 routine to implement __sync_synchronize.  */
+  if (TARGET_MIPS16)
+    synchronize_libfunc = init_one_libfunc ("__sync_synchronize");
+}
 
-         if (! CONSTANT_P (offset))
-           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2");
+/* Return the length of INSN.  LENGTH is the initial length computed by
+   attributes in the machine-description file.  */
 
-         if (REGNO (reg) == ARG_POINTER_REGNUM)
-           abort_with_insn (addr, "arg pointer not eliminated");
+int
+mips_adjust_insn_length (rtx insn, int length)
+{
+  /* 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;
 
-         if (TARGET_MIPS16
-             && GET_CODE (offset) == CONST
-             && mips16_gp_offset_p (offset))
-           {
-             fputs ("%gprel(", file);
-             mips16_output_gp_offset (file, offset);
-             fputs (")", file);
-           }
-         else
-           output_addr_const (file, offset);
-         fprintf (file, "(%s)", reg_names [REGNO (reg)]);
-       }
+  /* 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;
 
-      case LABEL_REF:
-      case SYMBOL_REF:
-      case CONST_INT:
-      case CONST:
-       output_addr_const (file, addr);
+      case HAZARD_DELAY:
+       length += 4;
        break;
 
-      default:
-       abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1");
+      case HAZARD_HILO:
+       length += 8;
        break;
-    }
+      }
+
+  /* In order to make it easier to share MIPS16 and non-MIPS16 patterns,
+     the .md file length attributes are 4-based for both modes.
+     Adjust the MIPS16 ones here.  */
+  if (TARGET_MIPS16)
+    length /= 2;
+
+  return length;
 }
-\f
-/* Target hook for assembling integer objects.  It appears that the Irix
-   6 assembler can't handle 64-bit decimal integers, so avoid printing
-   such an integer here.  */
 
-static bool
-mips_assemble_integer (x, size, aligned_p)
-     rtx x;
-     unsigned int size;
-     int aligned_p;
+/* 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)
 {
-  if ((TARGET_64BIT || TARGET_GAS) && size == 8 && aligned_p)
+  if (TARGET_EXPLICIT_RELOCS)
+    switch (mips_abi)
+      {
+      case ABI_N32:
+       return "%[lw\t%@,%%got_page(%0)(%+)\n\taddiu\t%@,%@,%%got_ofst(%0)";
+
+      case ABI_64:
+       return "%[ld\t%@,%%got_page(%0)(%+)\n\tdaddiu\t%@,%@,%%got_ofst(%0)";
+
+      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
     {
-      fputs ("\t.dword\t", asm_out_file);
-      if (HOST_BITS_PER_WIDE_INT < 64 || GET_CODE (x) != CONST_INT)
-       output_addr_const (asm_out_file, x);
+      if (Pmode == DImode)
+       return "%[dla\t%@,%0";
       else
-       print_operand (asm_out_file, x, 'X');
-      fputc ('\n', asm_out_file);
-      return true;
+       return "%[la\t%@,%0";
     }
-  return default_assemble_integer (x, size, aligned_p);
 }
-\f
-/* If optimizing for the global pointer, keep track of all of the externs, so
-   that at the end of the file, we can emit the appropriate .extern
-   declaration for them, before writing out the text section.  We assume all
-   names passed to us are in the permanent obstack, so they will be valid at
-   the end of the compilation.
-
-   If we have -G 0, or the extern size is unknown, or the object is in a user
-   specified section that is not .sbss/.sdata, don't bother emitting the
-   .externs.  In the case of user specified sections this behaviour is
-   required as otherwise GAS will think the object lives in .sbss/.sdata.  */
-
-int
-mips_output_external (file, decl, name)
-     FILE *file ATTRIBUTE_UNUSED;
-     tree decl;
-     const char *name;
-{
-  register struct extern_list *p;
-  int len;
-  tree section_name;
-
-  if (TARGET_GP_OPT
-      && TREE_CODE (decl) != FUNCTION_DECL
-      && !DECL_COMDAT (decl)
-      && (len = int_size_in_bytes (TREE_TYPE (decl))) > 0
-      && ((section_name = DECL_SECTION_NAME (decl)) == NULL
-         || strcmp (TREE_STRING_POINTER (section_name), ".sbss") == 0
-         || strcmp (TREE_STRING_POINTER (section_name), ".sdata") == 0))
-    {
-      p = (struct extern_list *) permalloc (sizeof (struct extern_list));
-      p->next = extern_head;
-      p->name = name;
-      p->size = len;
-      extern_head = p;
-    }
-
-#ifdef ASM_OUTPUT_UNDEF_FUNCTION
-  if (TREE_CODE (decl) == FUNCTION_DECL
-      /* ??? Don't include alloca, since gcc will always expand it
-        inline.  If we don't do this, the C++ library fails to build.  */
-      && strcmp (name, "alloca")
-      /* ??? Don't include __builtin_next_arg, because then gcc will not
-        bootstrap under Irix 5.1.  */
-      && strcmp (name, "__builtin_next_arg"))
-    {
-      p = (struct extern_list *) permalloc (sizeof (struct extern_list));
-      p->next = extern_head;
-      p->name = name;
-      p->size = -1;
-      extern_head = p;
-    }
-#endif
 
-  return 0;
-}
+/* 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.  */
 
-#ifdef ASM_OUTPUT_UNDEF_FUNCTION
-int
-mips_output_external_libcall (file, name)
-     FILE *file ATTRIBUTE_UNUSED;
-     const char *name;
+const char *
+mips_output_conditional_branch (rtx insn, rtx *operands,
+                               const char *branch_if_true,
+                               const char *branch_if_false)
 {
-  register struct extern_list *p;
+  unsigned int length;
+  rtx taken, not_taken;
 
-  p = (struct extern_list *) permalloc (sizeof (struct extern_list));
-  p->next = extern_head;
-  p->name = name;
-  p->size = -1;
-  extern_head = p;
+  gcc_assert (LABEL_P (operands[1]));  
 
-  return 0;
-}
-#endif
-\f
-/* Emit a new filename to a stream.  If this is MIPS ECOFF, watch out
-   for .file's that start within a function.  If we are smuggling stabs, try to
-   put out a MIPS ECOFF file and a stab.  */
+  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;
+    }
 
-void
-mips_output_filename (stream, name)
-     FILE *stream;
-     const char *name;
-{
-  static int first_time = 1;
-  char ltext_label_name[100];
+  /* 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 we are emitting DWARF-2, let dwarf2out handle the ".file"
-     directives.  */
-  if (write_symbols == DWARF2_DEBUG)
-    return;
-  else if (first_time)
+  /* Generate the reversed branch to NOT_TAKEN.  */
+  operands[1] = not_taken;
+  output_asm_insn (branch_if_false, operands);
+
+  /* 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)
     {
-      first_time = 0;
-      SET_FILE_NUMBER ();
-      current_function_file = name;
-      ASM_OUTPUT_FILENAME (stream, num_source_filenames, name);
-      /* This tells mips-tfile that stabs will follow.  */
-      if (!TARGET_GAS && write_symbols == DBX_DEBUG)
-       fprintf (stream, "\t#@stabs\n");
+      /* 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");
     }
 
-  else if (write_symbols == DBX_DEBUG)
+  /* Output the unconditional branch to TAKEN.  */
+  if (length <= 16)
+    output_asm_insn ("j\t%0%/", &taken);
+  else
     {
-      ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0);
-      fprintf (stream, "%s", ASM_STABS_OP);
-      output_quoted_string (stream, name);
-      fprintf (stream, ",%d,0,0,%s\n", N_SOL, &ltext_label_name[1]);
+      output_asm_insn (mips_output_load_label (), &taken);
+      output_asm_insn ("jr\t%@%]%/", 0);
     }
 
-  else if (name != current_function_file
-      && strcmp (name, current_function_file) != 0)
+  /* Now deal with its delay slot; see above.  */
+  if (final_sequence)
     {
-      if (inside_function && !TARGET_GAS)
+      /* 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 (!file_in_function_warning)
-           {
-             file_in_function_warning = 1;
-             ignore_line_number = 1;
-             warning ("MIPS ECOFF format does not allow changing filenames within functions with #line");
-           }
+         final_scan_insn (XVECEXP (final_sequence, 0, 1),
+                          asm_out_file, optimize, 1, NULL);
+         INSN_DELETED_P (XVECEXP (final_sequence, 0, 1)) = 1;
        }
       else
-       {
-         SET_FILE_NUMBER ();
-         current_function_file = name;
-         ASM_OUTPUT_FILENAME (stream, num_source_filenames, name);
-       }
+       output_asm_insn ("nop", 0);
+      fprintf (asm_out_file, "\n");
     }
+
+  /* Output NOT_TAKEN.  */
+  targetm.asm_out.internal_label (asm_out_file, "L",
+                                 CODE_LABEL_NUMBER (not_taken));
+  return "";
 }
-\f
-/* Emit a linenumber.  For encapsulated stabs, we need to put out a stab
-   as well as a .loc, since it is possible that MIPS ECOFF might not be
-   able to represent the location for inlines that come from a different
-   file.  */
 
-void
-mips_output_lineno (stream, line)
-     FILE *stream;
-     int line;
+/* Return the assembly code for INSN, which branches to OPERANDS[1]
+   if some ordering 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.  */
+
+const char *
+mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p)
 {
-  if (write_symbols == DBX_DEBUG)
-    {
-      ++sym_lineno;
-      fprintf (stream, "%sLM%d:\n%s%d,0,%d,%sLM%d\n",
-              LOCAL_LABEL_PREFIX, sym_lineno, ASM_STABN_OP, N_SLINE, line,
-              LOCAL_LABEL_PREFIX, sym_lineno);
-    }
-  else
+  const char *branch[2];
+
+  /* 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]))
     {
-      fprintf (stream, "\n\t%s.loc\t%d %d\n",
-              (ignore_line_number) ? "#" : "",
-              num_source_filenames, line);
+      /* 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;
+
+      /* 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;
 
-      LABEL_AFTER_LOC (stream);
+    default:
+      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
-/* Output an ASCII string, in a space-saving way.  */
+/* Return the assembly code for __sync_*() loop LOOP.  The loop should support
+   both normal and likely branches, using %? and %~ where appropriate.  */
 
-void
-mips_output_ascii (stream, string_param, len)
-     FILE *stream;
-     const char *string_param;
-     size_t len;
+const char *
+mips_output_sync_loop (const char *loop)
 {
-  size_t i;
-  int cur_pos = 17;
-  register const unsigned char *string =
-    (const unsigned char *)string_param;
+  /* Use branch-likely instructions to work around the LL/SC R10000 errata.  */
+  mips_branch_likely = TARGET_FIX_R10000;
+  return loop;
+}
+\f
+/* Return the assembly code for DIV or DDIV instruction DIVISION, which has
+   the operands given by OPERANDS.  Add in a divide-by-zero check if needed.
+
+   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.
+
+   [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.
+
+   [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":
+
+       "19. In this following sequence:
+
+                   ddiv                (or ddivu or div or divu)
+                   dsll32              (or dsrl32, dsra32)
+
+           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.
+
+           Workaround: The compiler needs to avoid generating any
+           sequence with divide followed by extended double shift."
+
+       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.
+
+   [3] From "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0"
+       (also valid for MIPS R4000MC processors):
+
+       "52. R4000SC: This bug does not apply for the R4000PC.
+
+           There are two flavors of this bug:
+
+           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.
+
+           ##1
+                   nop
+                   div r8, r9
+                   -------------------         # end-of page. -tlb-refill
+                   nop
+           ##2
+                   nop
+                   div r8, r9
+                   -------------------         # end-of page. -tlb-invalid
+                   nop
+
+           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.
+
+           ##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
+
+           ##2
+                   beq r0, r0, NextPage        # to Next page
+                   div r8,r9
+                   nop
+
+           This bug is present for div, divu, ddiv, and ddivu
+           instructions.
+
+           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.  */
 
-  fprintf (stream, "\t.ascii\t\"");
-  for (i = 0; i < len; i++)
-    {
-      register int c = string[i];
+const char *
+mips_output_division (const char *division, rtx *operands)
+{
+  const char *s;
 
-      switch (c)
+  s = division;
+  if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
+    {
+      output_asm_insn (s, operands);
+      s = "nop";
+    }
+  if (TARGET_CHECK_ZERO_DIV)
+    {
+      if (TARGET_MIPS16)
        {
-       case '\"':
-       case '\\':
-         putc ('\\', stream);
-         putc (c, stream);
-         cur_pos += 2;
-         break;
-
-       case TARGET_NEWLINE:
-         fputs ("\\n", stream);
-         if (i+1 < len
-             && (((c = string[i+1]) >= '\040' && c <= '~')
-                 || c == TARGET_TAB))
-           cur_pos = 32767;            /* break right here */
-         else
-           cur_pos += 2;
-         break;
-
-       case TARGET_TAB:
-         fputs ("\\t", stream);
-         cur_pos += 2;
-         break;
-
-       case TARGET_FF:
-         fputs ("\\f", stream);
-         cur_pos += 2;
-         break;
-
-       case TARGET_BS:
-         fputs ("\\b", stream);
-         cur_pos += 2;
-         break;
-
-       case TARGET_CR:
-         fputs ("\\r", stream);
-         cur_pos += 2;
-         break;
-
-       default:
-         if (c >= ' ' && c < 0177)
-           {
-             putc (c, stream);
-             cur_pos++;
-           }
-         else
-           {
-             fprintf (stream, "\\%03o", c);
-             cur_pos += 4;
-           }
+         output_asm_insn (s, operands);
+         s = "bnez\t%2,1f\n\tbreak\t7\n1:";
        }
-
-      if (cur_pos > 72 && i+1 < len)
+      else if (GENERATE_DIVIDE_TRAPS)
+        {
+         output_asm_insn (s, operands);
+         s = "teq\t%2,%.,7";
+        }
+      else
        {
-         cur_pos = 17;
-         fprintf (stream, "\"\n\t.ascii\t\"");
+         output_asm_insn ("%(bne\t%2,%.,1f", operands);
+         output_asm_insn (s, operands);
+         s = "break\t7%)\n1:";
        }
     }
-  fprintf (stream, "\"\n");
+  return s;
 }
 \f
-/* If defined, a C statement to be executed just prior to the output of
-   assembler code for INSN, to modify the extracted operands so they will be
-   output differently.
-
-   Here the argument OPVEC is the vector containing the operands extracted
-   from INSN, and NOPERANDS is the number of elements of the vector which
-   contain meaningful data for this insn.  The contents of this vector are
-   what will be used to convert the insn template into assembler code, so you
-   can change the assembler output by changing the contents of the vector.
+/* Return true if IN_INSN is a multiply-add or multiply-subtract
+   instruction and if OUT_INSN assigns to the accumulator operand.  */
 
-   We use it to check if the current insn needs a nop in front of it because
-   of load delays, and also to update the delay slot statistics.  */
-
-/* ??? There is no real need for this function, because it never actually
-   emits a NOP anymore.  */
-
-void
-final_prescan_insn (insn, opvec, noperands)
-     rtx insn;
-     rtx opvec[] ATTRIBUTE_UNUSED;
-     int noperands ATTRIBUTE_UNUSED;
+bool
+mips_linked_madd_p (rtx out_insn, rtx in_insn)
 {
-  if (dslots_number_nops > 0)
-    {
-      rtx pattern = PATTERN (insn);
-      int length = get_attr_length (insn);
+  rtx x;
 
-      /* Do we need to emit a NOP? */
-      if (length == 0
-         || (mips_load_reg != 0 && reg_mentioned_p (mips_load_reg,  pattern))
-         || (mips_load_reg2 != 0 && reg_mentioned_p (mips_load_reg2, pattern))
-         || (mips_load_reg3 != 0 && reg_mentioned_p (mips_load_reg3, pattern))
-         || (mips_load_reg4 != 0
-             && reg_mentioned_p (mips_load_reg4, pattern)))
-       fputs ("\t#nop\n", asm_out_file);
+  x = single_set (in_insn);
+  if (x == 0)
+    return false;
 
-      else
-       dslots_load_filled++;
+  x = SET_SRC (x);
 
-      while (--dslots_number_nops > 0)
-       fputs ("\t#nop\n", asm_out_file);
+  if (GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 0)) == MULT
+      && reg_set_p (XEXP (x, 1), out_insn))
+    return true;
 
-      mips_load_reg = 0;
-      mips_load_reg2 = 0;
-      mips_load_reg3 = 0;
-      mips_load_reg4 = 0;
-    }
+  if (GET_CODE (x) == MINUS
+      && GET_CODE (XEXP (x, 1)) == MULT
+      && reg_set_p (XEXP (x, 0), out_insn))
+    return true;
 
-  if (TARGET_STATS
-      && (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN))
-    dslots_jump_total++;
+  return false;
 }
-\f
-/* Output at beginning of assembler file.
 
-   If we are optimizing to use the global pointer, create a temporary file to
-   hold all of the text stuff, and write it out to the end. This is needed
-   because the MIPS assembler is evidently one pass, and if it hasn't seen the
-   relevant .comm/.lcomm/.extern/.sdata declaration when the code is
-   processed, it generates a two instruction sequence.  */
+/* True if the dependency between OUT_INSN and IN_INSN is on the store
+   data rather than the address.  We need this because the cprestore
+   pattern is type "store", but is defined using an UNSPEC_VOLATILE,
+   which causes the default routine to abort.  We just return false
+   for that case.  */
 
-void
-mips_asm_file_start (stream)
-     FILE *stream;
+bool
+mips_store_data_bypass_p (rtx out_insn, rtx in_insn)
 {
-  ASM_OUTPUT_SOURCE_FILENAME (stream, main_input_filename);
-
-  /* Versions of the MIPS assembler before 2.20 generate errors if a branch
-     inside of a .set noreorder section jumps to a label outside of the .set
-     noreorder section.  Revision 2.20 just set nobopt silently rather than
-     fixing the bug.  */
+  if (GET_CODE (PATTERN (in_insn)) == UNSPEC_VOLATILE)
+    return false;
 
-  if (TARGET_MIPS_AS && optimize && flag_delayed_branch)
-    fprintf (stream, "\t.set\tnobopt\n");
-
-  if (TARGET_GAS)
-    {
-#if defined(OBJECT_FORMAT_ELF)
-      /* 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.  */
-      const char * abi_string = NULL;
+  return !store_data_bypass_p (out_insn, in_insn);
+}
+\f
 
-      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;
-       case ABI_MEABI:abi_string = TARGET_64BIT ? "meabi64" : "meabi32"; break;
-       default:
-         abort ();
-       }
-      /* Note - we use fprintf directly rather than called named_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 (stream, "\t.section .mdebug.%s\n", abi_string);
-
-      /* Restore the default section.  */
-      fprintf (stream, "\t.previous\n");
-#endif
-    }
+/* Variables and flags used in scheduler hooks when tuning for
+   Loongson 2E/2F.  */
+static struct
+{
+  /* Variables to support Loongson 2E/2F round-robin [F]ALU1/2 dispatch
+     strategy.  */
 
+  /* If true, then next ALU1/2 instruction will go to ALU1.  */
+  bool alu1_turn_p;
 
+  /* If true, then next FALU1/2 unstruction will go to FALU1.  */
+  bool falu1_turn_p;
 
-  /* Generate the pseudo ops that System V.4 wants.  */
-#ifndef ABICALLS_ASM_OP
-#define ABICALLS_ASM_OP "\t.abicalls"
-#endif
-  if (TARGET_ABICALLS)
-    /* ??? but do not want this (or want pic0) if -non-shared? */
-    fprintf (stream, "%s\n", ABICALLS_ASM_OP);
+  /* Codes to query if [f]alu{1,2}_core units are subscribed or not.  */
+  int alu1_core_unit_code;
+  int alu2_core_unit_code;
+  int falu1_core_unit_code;
+  int falu2_core_unit_code;
 
-  if (TARGET_MIPS16)
-    fprintf (stream, "\t.set\tmips16\n");
+  /* True if current cycle has a multi instruction.
+     This flag is used in mips_ls2_dfa_post_advance_cycle.  */
+  bool cycle_has_multi_p;
 
-  /* This code exists so that we can put all externs before all symbol
-     references.  This is necessary for the MIPS assembler's global pointer
-     optimizations to work.  */
-  if (TARGET_FILE_SWITCHING)
-    {
-      asm_out_data_file = stream;
-      asm_out_text_file = tmpfile ();
-    }
-  else
-    asm_out_data_file = asm_out_text_file = stream;
+  /* Instructions to subscribe ls2_[f]alu{1,2}_turn_enabled units.
+     These are used in mips_ls2_dfa_post_advance_cycle to initialize
+     DFA state.
+     E.g., when alu1_turn_enabled_insn is issued it makes next ALU1/2
+     instruction to go ALU1.  */
+  rtx alu1_turn_enabled_insn;
+  rtx alu2_turn_enabled_insn;
+  rtx falu1_turn_enabled_insn;
+  rtx falu2_turn_enabled_insn;
+} mips_ls2;
 
-  if (flag_verbose_asm)
-    fprintf (stream, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
-            ASM_COMMENT_START,
-            mips_section_threshold, mips_arch_string, mips_isa);
-}
-\f
-/* If we are optimizing the global pointer, emit the text section now and any
-   small externs which did not have .comm, etc that are needed.  Also, give a
-   warning if the data area is more than 32K and -pic because 3 instructions
-   are needed to reference the data pointers.  */
+/* 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.  */
 
-void
-mips_asm_file_end (file)
-     FILE *file;
+static int
+mips_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link,
+                 rtx dep ATTRIBUTE_UNUSED, int cost)
 {
-  tree name_tree;
-  struct extern_list *p;
-
-  if (HALF_PIC_P ())
-    {
-      HALF_PIC_FINISH (file);
-    }
+  if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT
+      && TUNE_20KC)
+    return cost;
+  if (REG_NOTE_KIND (link) != 0)
+    return 0;
+  return cost;
+}
 
-  if (extern_head)
-    {
-      fputs ("\n", file);
+/* Return the number of instructions that can be issued per cycle.  */
 
-      for (p = extern_head; p != 0; p = p->next)
-       {
-         name_tree = get_identifier (p->name);
+static int
+mips_issue_rate (void)
+{
+  switch (mips_tune)
+    {
+    case PROCESSOR_74KC:
+    case PROCESSOR_74KF2_1:
+    case PROCESSOR_74KF1_1:
+    case PROCESSOR_74KF3_2:
+      /* The 74k is not strictly quad-issue cpu, but can be seen as one
+        by the scheduler.  It can issue 1 ALU, 1 AGEN and 2 FPU insns,
+        but in reality only a maximum of 3 insns can be issued as
+        floating-point loads and stores also require a slot in the
+        AGEN pipe.  */
+    case PROCESSOR_R10000:
+      /* All R10K Processors are quad-issue (being the first MIPS
+         processors to support this feature). */
+      return 4;
+
+    case PROCESSOR_20KC:
+    case PROCESSOR_R4130:
+    case PROCESSOR_R5400:
+    case PROCESSOR_R5500:
+    case PROCESSOR_R7000:
+    case PROCESSOR_R9000:
+    case PROCESSOR_OCTEON:
+      return 2;
 
-         /* Positively ensure only one .extern for any given symbol.  */
-         if (! TREE_ASM_WRITTEN (name_tree))
-           {
-             TREE_ASM_WRITTEN (name_tree) = 1;
-#ifdef ASM_OUTPUT_UNDEF_FUNCTION
-             if (p->size == -1)
-               ASM_OUTPUT_UNDEF_FUNCTION (file, p->name);
-             else
-#endif
-               {
-                 fputs ("\t.extern\t", file);
-                 assemble_name (file, p->name);
-                 fprintf (file, ", %d\n", p->size);
-               }
-           }
-       }
-    }
+    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;
 
-  if (TARGET_FILE_SWITCHING)
-    {
-      fprintf (file, "\n\t.text\n");
-      copy_file_data (file, asm_out_text_file);
+    case PROCESSOR_LOONGSON_2E:
+    case PROCESSOR_LOONGSON_2F:
+      return 4;
+
+    default:
+      return 1;
     }
 }
 
+/* Implement TARGET_SCHED_INIT_DFA_POST_CYCLE_INSN hook for Loongson2.  */
+
 static void
-copy_file_data (to, from)
-     FILE *to, *from;
+mips_ls2_init_dfa_post_cycle_insn (void)
 {
-  char buffer[8192];
-  size_t len;
-  rewind (from);
-  if (ferror (from))
-    fatal_io_error ("can't rewind temp file");
-
-  while ((len = fread (buffer, 1, sizeof (buffer), from)) > 0)
-    if (fwrite (buffer, 1, len, to) != len)
-      fatal_io_error ("can't write to output file");
+  start_sequence ();
+  emit_insn (gen_ls2_alu1_turn_enabled_insn ());
+  mips_ls2.alu1_turn_enabled_insn = get_insns ();
+  end_sequence ();
 
-  if (ferror (from))
-    fatal_io_error ("can't read from temp file");
-
-  if (fclose (from))
-    fatal_io_error ("can't close temp file");
-}
+  start_sequence ();
+  emit_insn (gen_ls2_alu2_turn_enabled_insn ());
+  mips_ls2.alu2_turn_enabled_insn = get_insns ();
+  end_sequence ();
 
-/* Emit either a label, .comm, or .lcomm directive, and mark that the symbol
-   is used, so that we don't emit an .extern for it in mips_asm_file_end.  */
+  start_sequence ();
+  emit_insn (gen_ls2_falu1_turn_enabled_insn ());
+  mips_ls2.falu1_turn_enabled_insn = get_insns ();
+  end_sequence ();
 
-void
-mips_declare_object (stream, name, init_string, final_string, size)
-     FILE *stream;
-     const char *name;
-     const char *init_string;
-     const char *final_string;
-     int size;
-{
-  fputs (init_string, stream);         /* "", "\t.comm\t", or "\t.lcomm\t" */
-  assemble_name (stream, name);
-  fprintf (stream, final_string, size);        /* ":\n", ",%u\n", ",%u\n" */
+  start_sequence ();
+  emit_insn (gen_ls2_falu2_turn_enabled_insn ());
+  mips_ls2.falu2_turn_enabled_insn = get_insns ();
+  end_sequence ();
 
-  if (TARGET_GP_OPT)
-    {
-      tree name_tree = get_identifier (name);
-      TREE_ASM_WRITTEN (name_tree) = 1;
-    }
+  mips_ls2.alu1_core_unit_code = get_cpu_unit_code ("ls2_alu1_core");
+  mips_ls2.alu2_core_unit_code = get_cpu_unit_code ("ls2_alu2_core");
+  mips_ls2.falu1_core_unit_code = get_cpu_unit_code ("ls2_falu1_core");
+  mips_ls2.falu2_core_unit_code = get_cpu_unit_code ("ls2_falu2_core");
 }
-\f
-/* Return the bytes needed to compute the frame pointer from the current
-   stack pointer.
-
-   Mips stack frames look like:
-
-             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   |
-    SP->|  if not passed.       |  VFP->|  if not passed.      |
-       +-----------------------+       +-----------------------+
-                                       |                       |
-                                        |  fp register save     |
-                                       |                       |
-                                       +-----------------------+
-                                       |                       |
-                                        |  gp register save     |
-                                        |                      |
-                                       +-----------------------+
-                                       |                       |
-                                       |  local variables      |
-                                       |                       |
-                                       +-----------------------+
-                                       |                       |
-                                        |  alloca allocations   |
-                                       |                       |
-                                       +-----------------------+
-                                       |                       |
-                                       |  GP save for V.4 abi  |
-                                       |                       |
-                                       +-----------------------+
-                                       |                       |
-                                        |  arguments on stack   |
-                                       |                       |
-                                       +-----------------------+
-                                        |  4 words to save      |
-                                       |  arguments passed     |
-                                        |  in registers, even   |
-   low                              SP->|  if not passed.       |
-   memory                              +-----------------------+
-
-*/
-
-HOST_WIDE_INT
-compute_frame_size (size)
-     HOST_WIDE_INT size;       /* # of var. bytes allocated */
-{
-  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 extra_size;    /* # extra bytes */
-  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 */
-  long mask;                   /* mask of saved gp registers */
-  long fmask;                  /* mask of saved fp registers */
-  int  fp_inc;                 /* 1 or 2 depending on the size of fp regs */
-  long fp_bits;                        /* bitmask to use for each fp register */
-
-  gp_reg_size = 0;
-  fp_reg_size = 0;
-  mask = 0;
-  fmask        = 0;
-  extra_size = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0));
-  var_size = MIPS_STACK_ALIGN (size);
-  args_size = MIPS_STACK_ALIGN (current_function_outgoing_args_size);
 
-  /* 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.  */
+/* Implement TARGET_SCHED_INIT_DFA_POST_CYCLE_INSN hook.
+   Init data used in mips_dfa_post_advance_cycle.  */
 
-  if (args_size == 0 && current_function_calls_alloca)
-    args_size = 4 * UNITS_PER_WORD;
+static void
+mips_init_dfa_post_cycle_insn (void)
+{
+  if (TUNE_LOONGSON_2EF)
+    mips_ls2_init_dfa_post_cycle_insn ();
+}
 
-  total_size = var_size + args_size + extra_size;
+/* Initialize STATE when scheduling for Loongson 2E/2F.
+   Support round-robin dispatch scheme by enabling only one of
+   ALU1/ALU2 and one of FALU1/FALU2 units for ALU1/2 and FALU1/2 instructions
+   respectively.  */
 
-  /* Calculate space needed for gp registers.  */
-  for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+static void
+mips_ls2_dfa_post_advance_cycle (state_t state)
+{
+  if (cpu_unit_reservation_p (state, mips_ls2.alu1_core_unit_code))
     {
-      /* $18 is a special case on the mips16.  It may be used to call
-         a function which returns a floating point value, but it is
-         marked in call_used_regs.  $31 is also a special case.  When
-         not using -mentry, it will be used to copy a return value
-         into the floating point registers if the return value is
-         floating point.  */
-      if (MUST_SAVE_REGISTER (regno)
-         || (TARGET_MIPS16
-             && regno == GP_REG_FIRST + 18
-             && regs_ever_live[regno])
-         || (TARGET_MIPS16
-             && regno == GP_REG_FIRST + 31
-             && mips16_hard_float
-             && ! mips_entry
-             && ! aggregate_value_p (DECL_RESULT (current_function_decl))
-             && (GET_MODE_CLASS (DECL_MODE (DECL_RESULT (current_function_decl)))
-                 == MODE_FLOAT)
-             && (! TARGET_SINGLE_FLOAT
-                 || (GET_MODE_SIZE (DECL_MODE (DECL_RESULT (current_function_decl)))
-                     <= 4))))
-       {
-         gp_reg_size += GET_MODE_SIZE (gpr_mode);
-         mask |= 1L << (regno - GP_REG_FIRST);
-
-         /* The entry and exit pseudo instructions can not save $17
-            without also saving $16.  */
-         if (mips_entry
-             && regno == GP_REG_FIRST + 17
-             && ! MUST_SAVE_REGISTER (GP_REG_FIRST + 16))
-           {
-             gp_reg_size += UNITS_PER_WORD;
-             mask |= 1L << 16;
-           }
-       }
+      /* Though there are no non-pipelined ALU1 insns,
+        we can get an instruction of type 'multi' before reload.  */
+      gcc_assert (mips_ls2.cycle_has_multi_p);
+      mips_ls2.alu1_turn_p = false;
     }
 
-  /* 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 |= 1L << (regno - GP_REG_FIRST);
-       }
-    }
+  mips_ls2.cycle_has_multi_p = false;
+
+  if (cpu_unit_reservation_p (state, mips_ls2.alu2_core_unit_code))
+    /* We have a non-pipelined alu instruction in the core,
+       adjust round-robin counter.  */
+    mips_ls2.alu1_turn_p = true;
 
-  /* Calculate space needed for fp registers.  */
-  if (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT)
+  if (mips_ls2.alu1_turn_p)
     {
-      fp_inc = 1;
-      fp_bits = 1;
+      if (state_transition (state, mips_ls2.alu1_turn_enabled_insn) >= 0)
+       gcc_unreachable ();
     }
   else
     {
-      fp_inc = 2;
-      fp_bits = 3;
+      if (state_transition (state, mips_ls2.alu2_turn_enabled_insn) >= 0)
+       gcc_unreachable ();
     }
 
-  /* This loop must iterate over the same space as its companion in
-     save_restore_insns.  */
-  for (regno = (FP_REG_LAST - fp_inc + 1);
-       regno >= FP_REG_FIRST;
-       regno -= fp_inc)
+  if (cpu_unit_reservation_p (state, mips_ls2.falu1_core_unit_code))
     {
-      if (regs_ever_live[regno] && !call_used_regs[regno])
-       {
-         fp_reg_size += fp_inc * UNITS_PER_FPREG;
-         fmask |= fp_bits << (regno - FP_REG_FIRST);
-       }
+      /* There are no non-pipelined FALU1 insns.  */
+      gcc_unreachable ();
+      mips_ls2.falu1_turn_p = false;
     }
 
-  gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
-  total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size);
-
-  /* The gp reg is caller saved in the 32 bit ABI, so there is no need
-     for leaf routines (total_size == extra_size) to save the gp reg.
-     The gp reg is callee saved in the 64 bit ABI, so all routines must
-     save the gp reg.  This is not a leaf routine if -p, because of the
-     call to mcount.  */
-  if (total_size == extra_size
-      && (mips_abi == ABI_32 || mips_abi == ABI_O64 || mips_abi == ABI_EABI)
-      && ! current_function_profile)
-    total_size = extra_size = 0;
-  else if (TARGET_ABICALLS)
-    {
-      /* Add the context-pointer to the saved registers.  */
-      gp_reg_size += UNITS_PER_WORD;
-      mask |= 1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST);
-      total_size -= gp_reg_rounded;
-      gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
-      total_size += gp_reg_rounded;
-    }
-
-  /* Add in space reserved on the stack by the callee for storing arguments
-     passed in registers.  */
-  if (mips_abi != ABI_32 && mips_abi != ABI_O64)
-    total_size += MIPS_STACK_ALIGN (current_function_pretend_args_size);
-
-  /* The entry pseudo instruction will allocate 32 bytes on the stack.  */
-  if (mips_entry && total_size > 0 && total_size < 32)
-    total_size = 32;
-
-  /* Save other computed information.  */
-  current_frame_info.total_size = total_size;
-  current_frame_info.var_size = var_size;
-  current_frame_info.args_size = args_size;
-  current_frame_info.extra_size = extra_size;
-  current_frame_info.gp_reg_size = gp_reg_size;
-  current_frame_info.fp_reg_size = fp_reg_size;
-  current_frame_info.mask = mask;
-  current_frame_info.fmask = fmask;
-  current_frame_info.initialized = reload_completed;
-  current_frame_info.num_gp = gp_reg_size / UNITS_PER_WORD;
-  current_frame_info.num_fp = fp_reg_size / (fp_inc * UNITS_PER_FPREG);
-
-  if (mask)
-    {
-      unsigned long offset;
-
-      /* When using mips_entry, the registers are always saved at the
-         top of the stack.  */
-      if (! mips_entry)
-       offset = (args_size + extra_size + var_size
-                 + gp_reg_size - GET_MODE_SIZE (gpr_mode));
-      else
-       offset = total_size - GET_MODE_SIZE (gpr_mode);
-
-      current_frame_info.gp_sp_offset = offset;
-      current_frame_info.gp_save_offset = offset - total_size;
-    }
-  else
-    {
-      current_frame_info.gp_sp_offset = 0;
-      current_frame_info.gp_save_offset = 0;
-    }
+  if (cpu_unit_reservation_p (state, mips_ls2.falu2_core_unit_code))
+    /* We have a non-pipelined falu instruction in the core,
+       adjust round-robin counter.  */
+    mips_ls2.falu1_turn_p = true;
 
-  if (fmask)
+  if (mips_ls2.falu1_turn_p)
     {
-      unsigned long offset = (args_size + extra_size + var_size
-                             + gp_reg_rounded + fp_reg_size
-                             - fp_inc * UNITS_PER_FPREG);
-      current_frame_info.fp_sp_offset = offset;
-      current_frame_info.fp_save_offset = offset - total_size;
+      if (state_transition (state, mips_ls2.falu1_turn_enabled_insn) >= 0)
+       gcc_unreachable ();
     }
   else
     {
-      current_frame_info.fp_sp_offset = 0;
-      current_frame_info.fp_save_offset = 0;
+      if (state_transition (state, mips_ls2.falu2_turn_enabled_insn) >= 0)
+       gcc_unreachable ();
     }
-
-  /* Ok, we're done.  */
-  return total_size;
 }
-\f
-/* Common code to emit the insns (or to write the instructions to a file)
-   to save/restore registers.
 
-   Other parts of the code assume that MIPS_TEMP1_REGNUM (aka large_reg)
-   is not modified within save_restore_insns.  */
+/* Implement TARGET_SCHED_DFA_POST_ADVANCE_CYCLE.
+   This hook is being called at the start of each cycle.  */
 
-#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
-
-/* Emit instructions to load the value (SP + OFFSET) into MIPS_TEMP2_REGNUM
-   and return an rtl expression for the register.  Write the assembly
-   instructions directly to FILE if it is not null, otherwise emit them as
-   rtl.
+static void
+mips_dfa_post_advance_cycle (void)
+{
+  if (TUNE_LOONGSON_2EF)
+    mips_ls2_dfa_post_advance_cycle (curr_state);
+}
 
-   This function is a subroutine of save_restore_insns.  It is used when
-   OFFSET is too large to add in a single instruction.  */
+/* Implement TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD.  This should
+   be as wide as the scheduling freedom in the DFA.  */
 
-static rtx
-mips_add_large_offset_to_sp (offset, file)
-     HOST_WIDE_INT offset;
-     FILE *file;
+static int
+mips_multipass_dfa_lookahead (void)
 {
-  rtx reg = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
-  if (file == 0)
-    {
-      rtx offset_rtx = GEN_INT (offset);
+  /* Can schedule up to 4 of the 6 function units in any one cycle.  */
+  if (TUNE_SB1)
+    return 4;
 
-      emit_move_insn (reg, offset_rtx);
-      if (Pmode == DImode)
-       emit_insn (gen_adddi3 (reg, reg, stack_pointer_rtx));
-      else
-       emit_insn (gen_addsi3 (reg, reg, stack_pointer_rtx));
-    }
-  else
-    {
-      fprintf (file, "\tli\t%s,0x%.08lx\t# ",
-              reg_names[MIPS_TEMP2_REGNUM], (long) offset);
-      fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset);
-      fprintf (file, "\n\t%s\t%s,%s,%s\n",
-              Pmode == DImode ? "daddu" : "addu",
-              reg_names[MIPS_TEMP2_REGNUM],
-              reg_names[MIPS_TEMP2_REGNUM],
-              reg_names[STACK_POINTER_REGNUM]);
-    }
-  return reg;
-}
+  if (TUNE_LOONGSON_2EF)
+    return 4;
 
-/* Make INSN frame related and note that it performs the frame-related
-   operation DWARF_PATTERN.  */
+  if (TUNE_OCTEON)
+    return 2;
 
-static void
-mips_annotate_frame_insn (insn, dwarf_pattern)
-     rtx insn, dwarf_pattern;
-{
-  RTX_FRAME_RELATED_P (insn) = 1;
-  REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                                     dwarf_pattern,
-                                     REG_NOTES (insn));
+  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.  */
 
-/* Return a frame-related rtx that stores register REGNO at (SP + OFFSET).
-   The expression should only be used to store single registers.  */
+static void
+mips_promote_ready (rtx *ready, int lower, int higher)
+{
+  rtx new_head;
+  int i;
 
-static rtx
-mips_frame_set (mode, regno, offset)
-     enum machine_mode mode;
-     int regno;
-     int offset;
-{
-  rtx address = plus_constant (stack_pointer_rtx, offset);
-  rtx set = gen_rtx_SET (mode,
-                        gen_rtx_MEM (mode, address),
-                        gen_rtx_REG (mode, regno));
-  RTX_FRAME_RELATED_P (set) = 1;
-  return set;
+  new_head = ready[lower];
+  for (i = lower; i < higher; i++)
+    ready[i] = ready[i + 1];
+  ready[i] = new_head;
 }
 
-
-/* Emit a move instruction that stores REG in MEM.  Make the instruction
-   frame related and note that it stores REG at (SP + OFFSET).  This
-   function may be asked to store an FPR pair.  */
+/* If the priority of the instruction at POS2 in the ready queue READY
+   is within LIMIT units of that of the instruction at POS1, swap the
+   instructions if POS2 is not already less than POS1.  */
 
 static void
-mips_emit_frame_related_store (mem, reg, offset)
-     rtx mem;
-     rtx reg;
-     HOST_WIDE_INT offset;
+mips_maybe_swap_ready (rtx *ready, int pos1, int pos2, int limit)
 {
-  rtx dwarf_expr;
-
-  if (GET_MODE (reg) == DFmode && ! TARGET_FLOAT64)
+  if (pos1 < pos2
+      && INSN_PRIORITY (ready[pos1]) + limit >= INSN_PRIORITY (ready[pos2]))
     {
-      /* Two registers are being stored, so the frame-related expression
-        must be a PARALLEL rtx with one SET for each register.  The
-        higher numbered register is stored in the lower address on
-        big-endian targets.  */
-      int regno1 = TARGET_BIG_ENDIAN ? REGNO (reg) + 1 : REGNO (reg);
-      int regno2 = TARGET_BIG_ENDIAN ? REGNO (reg) : REGNO (reg) + 1;
-      rtx set1 = mips_frame_set (SFmode, regno1, offset);
-      rtx set2 = mips_frame_set (SFmode, regno2, offset + UNITS_PER_FPREG);
-      dwarf_expr = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set1, set2));
-    }
-  else
-    dwarf_expr = mips_frame_set (GET_MODE (reg), REGNO (reg), offset);
+      rtx temp;
 
-  mips_annotate_frame_insn (emit_move_insn (mem, reg), dwarf_expr);
+      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.  */
+static rtx mips_macc_chains_last_hilo;
+
+/* A TUNE_MACC_CHAINS helper function.  Record that instruction INSN has
+   been scheduled, updating mips_macc_chains_last_hilo appropriately.  */
 
 static void
-save_restore_insns (store_p, large_reg, large_offset, file)
-     int store_p;      /* true if this is prologue */
-     rtx large_reg;    /* register holding large offset constant or NULL */
-     long large_offset;        /* large constant offset value */
-     FILE *file;       /* file to write instructions instead of making RTL */
-{
-  long mask = current_frame_info.mask;
-  long fmask = current_frame_info.fmask;
-  long real_mask = mask;
-  int regno;
-  rtx base_reg_rtx;
-  HOST_WIDE_INT base_offset;
-  HOST_WIDE_INT gp_offset;
-  HOST_WIDE_INT fp_offset;
-  HOST_WIDE_INT end_offset;
-  rtx insn;
+mips_macc_chains_record (rtx insn)
+{
+  if (get_attr_may_clobber_hilo (insn))
+    mips_macc_chains_last_hilo = insn;
+}
 
-  if (frame_pointer_needed
-      && ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
-    abort ();
+/* 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.  */
 
-  /* Do not restore GP under certain conditions.  */
-  if (! store_p
-      && TARGET_ABICALLS
-      && (mips_abi == ABI_32 || mips_abi == ABI_O64))
-    mask &= ~(1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST));
+static void
+mips_macc_chains_reorder (rtx *ready, int nready)
+{
+  int i, j;
 
-  if (mask == 0 && fmask == 0)
-    return;
+  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.  */
+static rtx vr4130_last_insn;
 
-  /* 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 epilog if at least one register is reloaded in
-     addition to return address.  */
+/* 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.  */
 
-  /* Save GP registers if needed.  */
-  if (mask)
-    {
-      /* Pick which pointer to use as a base register.  For small frames, just
-        use the stack pointer.  Otherwise, use a temporary register.  Save 2
-        cycles if the save area is near the end of a large frame, by reusing
-        the constant created in the prologue/epilogue to adjust the stack
-        frame.  */
+static void
+vr4130_true_reg_dependence_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED,
+                               void *data)
+{
+  rtx *insn_ptr;
 
-      gp_offset = current_frame_info.gp_sp_offset;
-      end_offset
-       = gp_offset - (current_frame_info.gp_reg_size
-                      - GET_MODE_SIZE (gpr_mode));
+  insn_ptr = (rtx *) data;
+  if (REG_P (x)
+      && *insn_ptr != 0
+      && reg_referenced_p (x, PATTERN (*insn_ptr)))
+    *insn_ptr = 0;
+}
 
-      if (gp_offset < 0 || end_offset < 0)
-       internal_error
-         ("gp_offset (%ld) or end_offset (%ld) is less than zero",
-          (long) gp_offset, (long) end_offset);
+/* Return true if there is true register dependence between vr4130_last_insn
+   and INSN.  */
 
-      /* If we see a large frame in mips16 mode, we save the registers
-         before adjusting the stack pointer, and load them afterward.  */
-      else if (TARGET_MIPS16 && large_offset > 32767)
-       base_reg_rtx = stack_pointer_rtx, base_offset = large_offset;
+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;
+}
 
-      else if (gp_offset < 32768)
-       base_reg_rtx = stack_pointer_rtx, base_offset  = 0;
+/* 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.  */
 
-      else if (large_reg != 0
-              && (unsigned HOST_WIDE_INT) (large_offset - gp_offset) < 32768
-              && (unsigned HOST_WIDE_INT) (large_offset - end_offset) < 32768)
-       {
-         base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
-         base_offset = large_offset;
-         if (file == 0)
-           {
-             if (Pmode == DImode)
-               insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
-                                             stack_pointer_rtx));
-             else
-               insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg,
-                                             stack_pointer_rtx));
-           }
-         else
-           fprintf (file, "\t%s\t%s,%s,%s\n",
-                    Pmode == DImode ? "daddu" : "addu",
-                    reg_names[MIPS_TEMP2_REGNUM],
-                    reg_names[REGNO (large_reg)],
-                    reg_names[STACK_POINTER_REGNUM]);
-       }
-      else
-       {
-         base_offset = gp_offset;
-         base_reg_rtx = mips_add_large_offset_to_sp (base_offset, file);
-       }
+static bool
+vr4130_swap_insns_p (rtx insn1, rtx insn2)
+{
+  sd_iterator_def sd_it;
+  dep_t dep;
 
-      /* When we restore the registers in MIPS16 mode, then if we are
-         using a frame pointer, and this is not a large frame, the
-         current stack pointer will be offset by
-         current_function_outgoing_args_size.  Doing it this way lets
-         us avoid offsetting the frame pointer before copying it into
-         the stack pointer; there is no instruction to set the stack
-         pointer to the sum of a register and a constant.  */
-      if (TARGET_MIPS16
-         && ! store_p
-         && frame_pointer_needed
-         && large_offset <= 32767)
-       base_offset += current_function_outgoing_args_size;
+  /* Check for the following case:
 
-      for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
-       if (BITSET_P (mask, regno - GP_REG_FIRST))
-         {
-           if (file == 0)
-             {
-               rtx reg_rtx;
-               rtx mem_rtx
-                 = gen_rtx (MEM, gpr_mode,
-                            gen_rtx (PLUS, Pmode, base_reg_rtx,
-                                     GEN_INT (gp_offset - base_offset)));
-
-               if (! current_function_calls_eh_return)
-                 RTX_UNCHANGING_P (mem_rtx) = 1;
-
-               /* The mips16 does not have an instruction to load
-                   $31, so we load $7 instead, and work things out
-                   in mips_expand_epilogue.  */
-               if (TARGET_MIPS16 && ! store_p && regno == GP_REG_FIRST + 31)
-                 reg_rtx = gen_rtx (REG, gpr_mode, GP_REG_FIRST + 7);
-               /* The mips16 sometimes needs to save $18.  */
-               else if (TARGET_MIPS16
-                        && regno != GP_REG_FIRST + 31
-                        && ! M16_REG_P (regno))
-                 {
-                   if (! store_p)
-                     reg_rtx = gen_rtx (REG, gpr_mode, 6);
-                   else
-                     {
-                       reg_rtx = gen_rtx (REG, gpr_mode, 3);
-                       emit_move_insn (reg_rtx,
-                                       gen_rtx (REG, gpr_mode, regno));
-                     }
-                 }
-               else
-                 reg_rtx = gen_rtx (REG, gpr_mode, regno);
+     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).
 
-               if (store_p)
-                 mips_emit_frame_related_store (mem_rtx, reg_rtx, gp_offset);
-               else
-                 {
-                   emit_move_insn (reg_rtx, mem_rtx);
-                   if (TARGET_MIPS16
-                       && regno != GP_REG_FIRST + 31
-                       && ! M16_REG_P (regno))
-                     emit_move_insn (gen_rtx (REG, gpr_mode, regno),
-                                     reg_rtx);
-                 }
-             }
-           else
-             {
-               int r = regno;
-
-               /* The mips16 does not have an instruction to
-                  load $31, so we load $7 instead, and work
-                  things out in the caller.  */
-               if (TARGET_MIPS16 && ! store_p && r == GP_REG_FIRST + 31)
-                 r = GP_REG_FIRST + 7;
-               /* The mips16 sometimes needs to save $18.  */
-               if (TARGET_MIPS16
-                   && regno != GP_REG_FIRST + 31
-                   && ! M16_REG_P (regno))
-                 {
-                   if (! store_p)
-                     r = GP_REG_FIRST + 6;
-                   else
-                     {
-                       r = GP_REG_FIRST + 3;
-                       fprintf (file, "\tmove\t%s,%s\n",
-                                reg_names[r], reg_names[regno]);
-                     }
-                 }
-               fprintf (file, "\t%s\t%s,",
-                        (TARGET_64BIT
-                         ? (store_p) ? "sd" : "ld"
-                         : (store_p) ? "sw" : "lw"),
-                        reg_names[r]);
-               fprintf (file, HOST_WIDE_INT_PRINT_DEC,
-                        gp_offset - base_offset);
-               fprintf (file, "(%s)\n", reg_names[REGNO(base_reg_rtx)]);
-               if (! store_p
-                   && TARGET_MIPS16
-                   && regno != GP_REG_FIRST + 31
-                   && ! M16_REG_P (regno))
-                 fprintf (file, "\tmove\t%s,%s\n",
-                          reg_names[regno], reg_names[r]);
-             }
-           gp_offset -= GET_MODE_SIZE (gpr_mode);
-         }
-        /* If the restore is being supressed, still take into account
-          the offset at which it is stored.  */
-       else if (BITSET_P (real_mask, regno - GP_REG_FIRST))
-         {
-           gp_offset -= GET_MODE_SIZE (gpr_mode);
-         }
-    }
-  else
-    base_reg_rtx = 0, base_offset  = 0;
+     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;
 
-  /* Save floating point registers if needed.  */
-  if (fmask)
+  if (vr4130_last_insn != 0
+      && recog_memoized (insn1) >= 0
+      && recog_memoized (insn2) >= 0)
     {
-      int fp_inc = (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT) ? 1 : 2;
-      int fp_size = fp_inc * UNITS_PER_FPREG;
-
-      /* Pick which pointer to use as a base register.  */
-      fp_offset = current_frame_info.fp_sp_offset;
-      end_offset = fp_offset - (current_frame_info.fp_reg_size - fp_size);
-
-      if (fp_offset < 0 || end_offset < 0)
-       internal_error
-         ("fp_offset (%ld) or end_offset (%ld) is less than zero",
-          (long) fp_offset, (long) end_offset);
-
-      else if (fp_offset < 32768)
-       base_reg_rtx = stack_pointer_rtx, base_offset  = 0;
-
-      else if (base_reg_rtx != 0
-              && (unsigned HOST_WIDE_INT) (base_offset - fp_offset) < 32768
-              && (unsigned HOST_WIDE_INT) (base_offset - end_offset) < 32768)
-       ;                       /* already set up for gp registers above */
-
-      else if (large_reg != 0
-              && (unsigned HOST_WIDE_INT) (large_offset - fp_offset) < 32768
-              && (unsigned HOST_WIDE_INT) (large_offset - end_offset) < 32768)
+      /* 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)
        {
-         base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
-         base_offset = large_offset;
-         if (file == 0)
-           {
-             if (Pmode == DImode)
-               insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
-                                             stack_pointer_rtx));
-             else
-               insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg,
-                                             stack_pointer_rtx));
-           }
+         /* If only one of the instructions has a dependence on
+            vr4130_last_insn, prefer to schedule the other one first.  */
+         bool dep1_p = vr4130_true_reg_dependence_p (insn1);
+         bool dep2_p = vr4130_true_reg_dependence_p (insn2);
+         if (dep1_p != dep2_p)
+           return dep1_p;
 
-         else
-           fprintf (file, "\t%s\t%s,%s,%s\n",
-                    Pmode == DImode ? "daddu" : "addu",
-                    reg_names[MIPS_TEMP2_REGNUM],
-                    reg_names[REGNO (large_reg)],
-                    reg_names[STACK_POINTER_REGNUM]);
+         /* 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;
        }
-      else
-       {
-         base_offset = fp_offset;
-         base_reg_rtx = mips_add_large_offset_to_sp (fp_offset, file);
-       }
-
-      /* This loop must iterate over the same space as its companion in
-        compute_frame_size.  */
-      for (regno = (FP_REG_LAST - fp_inc + 1);
-          regno >= FP_REG_FIRST;
-          regno -= fp_inc)
-       if (BITSET_P (fmask, regno - FP_REG_FIRST))
-         {
-           if (file == 0)
-             {
-               enum machine_mode sz
-                 = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
-               rtx reg_rtx = gen_rtx (REG, sz, regno);
-               rtx mem_rtx = gen_rtx (MEM, sz,
-                                      gen_rtx (PLUS, Pmode, base_reg_rtx,
-                                               GEN_INT (fp_offset
-                                                        - base_offset)));
-               if (! current_function_calls_eh_return)
-                 RTX_UNCHANGING_P (mem_rtx) = 1;
-
-               if (store_p)
-                 mips_emit_frame_related_store (mem_rtx, reg_rtx, fp_offset);
-               else
-                 emit_move_insn (reg_rtx, mem_rtx);
-             }
-           else
-             {
-               fprintf (file, "\t%s\t%s,",
-                        (TARGET_SINGLE_FLOAT
-                         ? (store_p ? "s.s" : "l.s")
-                         : (store_p ? "s.d" : "l.d")),
-                        reg_names[regno]);
-               fprintf (file, HOST_WIDE_INT_PRINT_DEC,
-                        fp_offset - base_offset);
-               fprintf (file, "(%s)\n", reg_names[REGNO(base_reg_rtx)]);
-             }
-
-           fp_offset -= fp_size;
-         }
     }
+  return false;
 }
-\f
-/* Set up the stack and frame (if desired) for the function.  */
+
+/* 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.  */
 
 static void
-mips_output_function_prologue (file, size)
-     FILE *file;
-     HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+vr4130_reorder (rtx *ready, int nready)
 {
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
-  const char *fnname;
-#endif
-  HOST_WIDE_INT tsize = current_frame_info.total_size;
-
-  /* ??? When is this really needed?  At least the GNU assembler does not
-     need the source filename more than once in the file, beyond what is
-     emitted by the debug information.  */
-  if (!TARGET_GAS)
-    ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl));
-
-#ifdef SDB_DEBUGGING_INFO
-  if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG)
-    ASM_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_SOFT_FLOAT
-      && current_function_args_info.fp_code != 0)
-    build_mips16_function_stub (file);
-
-  inside_function = 1;
+  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.  */
+static enum attr_type mips_last_74k_agen_insn = TYPE_UNKNOWN;
 
-#ifndef 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);
+/* Initialize mips_last_74k_agen_insn from INSN.  A null argument
+   resets to TYPE_UNKNOWN state.  */
 
-  if (!flag_inhibit_size_directive)
+static void
+mips_74k_agen_init (rtx insn)
+{
+  if (!insn || !NONJUMP_INSN_P (insn))
+    mips_last_74k_agen_insn = TYPE_UNKNOWN;
+  else
     {
-      fputs ("\t.ent\t", file);
-      assemble_name (file, fnname);
-      fputs ("\n", file);
+      enum attr_type type = get_attr_type (insn);
+      if (type == TYPE_LOAD || type == TYPE_STORE)
+       mips_last_74k_agen_insn = type;
     }
+}
 
-  assemble_name (file, fnname);
-  fputs (":\n", file);
-#endif
+/* 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.  */
 
-  if (!flag_inhibit_size_directive)
-    {
-      /* .frame FRAMEREG, FRAMESIZE, RETREG */
-      fprintf (file,
-              "\t.frame\t%s,%ld,%s\t\t# vars= %ld, regs= %d/%d, args= %d, extra= %ld\n",
-              (reg_names[(frame_pointer_needed)
-                         ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
-              ((frame_pointer_needed && TARGET_MIPS16)
-               ? ((long) tsize - current_function_outgoing_args_size)
-               : (long) tsize),
-              reg_names[GP_REG_FIRST + 31],
-              current_frame_info.var_size,
-              current_frame_info.num_gp,
-              current_frame_info.num_fp,
-              current_function_outgoing_args_size,
-              current_frame_info.extra_size);
-
-      /* .mask MASK, GPOFFSET; .fmask FPOFFSET */
-      fprintf (file, "\t.mask\t0x%08lx,%ld\n\t.fmask\t0x%08lx,%ld\n",
-              current_frame_info.mask,
-              current_frame_info.gp_save_offset,
-              current_frame_info.fmask,
-              current_frame_info.fp_save_offset);
-
-      /* 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_entry && ! mips_can_use_return_insn ())
-    {
-      int save16 = BITSET_P (current_frame_info.mask, 16);
-      int save17 = BITSET_P (current_frame_info.mask, 17);
-      int save31 = BITSET_P (current_frame_info.mask, 31);
-      int savearg = 0;
-      rtx insn;
+static void
+mips_74k_agen_reorder (rtx *ready, int nready)
+{
+  int i;
+  int store_pos, load_pos;
 
-      /* Look through the initial insns to see if any of them store
-        the function parameters into the incoming parameter storage
-        area.  If they do, we delete the insn, and save the register
-        using the entry pseudo-instruction instead.  We don't try to
-        look past a label, jump, or call.  */
-      for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
-       {
-         rtx note, set, src, dest, base, offset;
-         int hireg;
+  store_pos = -1;
+  load_pos = -1;
 
-         if (GET_CODE (insn) == CODE_LABEL
-             || GET_CODE (insn) == JUMP_INSN
-             || GET_CODE (insn) == CALL_INSN)
+  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;
-         if (GET_CODE (insn) != INSN)
-           continue;
-         set = PATTERN (insn);
-         if (GET_CODE (set) != SET)
-           continue;
-
-         /* An insn storing a function parameter will still have a
-             REG_EQUIV note on it mentioning the argument pointer.  */
-         note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
-         if (note == NULL_RTX)
-           continue;
-         if (! reg_mentioned_p (arg_pointer_rtx, XEXP (note, 0)))
-           continue;
-
-         src = SET_SRC (set);
-         if (GET_CODE (src) != REG
-             || REGNO (src) < GP_REG_FIRST + 4
-             || REGNO (src) > GP_REG_FIRST + 7)
-           continue;
-
-         dest = SET_DEST (set);
-         if (GET_CODE (dest) != MEM)
-           continue;
-         if (GET_MODE_SIZE (GET_MODE (dest)) == (unsigned) UNITS_PER_WORD)
-           ;
-         else if (GET_MODE_SIZE (GET_MODE (dest)) == (unsigned)2 * UNITS_PER_WORD
-                  && REGNO (src) < GP_REG_FIRST + 7)
-           ;
-         else
-           continue;
-         offset = const0_rtx;
-         base = eliminate_constant_term (XEXP (dest, 0), &offset);
-         if (GET_CODE (base) != REG
-             || GET_CODE (offset) != CONST_INT)
-           continue;
-         if (REGNO (base) == (unsigned) STACK_POINTER_REGNUM
-             && INTVAL (offset) == tsize + (REGNO (src) - 4) * UNITS_PER_WORD)
-           ;
-         else if (REGNO (base) == (unsigned) HARD_FRAME_POINTER_REGNUM
-                  && (INTVAL (offset)
-                      == (tsize
-                          + (REGNO (src) - 4) * UNITS_PER_WORD
-                          - current_function_outgoing_args_size)))
-           ;
-         else
-           continue;
-
-         /* This insn stores a parameter onto the stack, in the same
-             location where the entry pseudo-instruction will put it.
-             Delete the insn, and arrange to tell the entry
-             instruction to save the register.  */
-         PUT_CODE (insn, NOTE);
-         NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
-         NOTE_SOURCE_FILE (insn) = 0;
-
-         hireg = (REGNO (src)
-                  + HARD_REGNO_NREGS (REGNO (src), GET_MODE (dest))
-                  - 1);
-         if (hireg > savearg)
-           savearg = hireg;
-       }
 
-      /* If this is a varargs function, we need to save all the
-         registers onto the stack anyhow.  */
-      if (current_function_stdarg || current_function_varargs)
-       savearg = GP_REG_FIRST + 7;
+         case TYPE_LOAD:
+           if (load_pos == -1)
+             load_pos = i;
+           break;
 
-      fprintf (file, "\tentry\t");
-      if (savearg > 0)
-       {
-         if (savearg == GP_REG_FIRST + 4)
-           fprintf (file, "%s", reg_names[savearg]);
-         else
-           fprintf (file, "%s-%s", reg_names[GP_REG_FIRST + 4],
-                    reg_names[savearg]);
-       }
-      if (save16 || save17)
-       {
-         if (savearg > 0)
-           fprintf (file, ",");
-         fprintf (file, "%s", reg_names[GP_REG_FIRST + 16]);
-         if (save17)
-           fprintf (file, "-%s", reg_names[GP_REG_FIRST + 17]);
-       }
-      if (save31)
-       {
-         if (savearg > 0 || save16 || save17)
-           fprintf (file, ",");
-         fprintf (file, "%s", reg_names[GP_REG_FIRST + 31]);
-       }
-      fprintf (file, "\n");
+         default:
+           break;
+         }
     }
 
-  if (TARGET_ABICALLS && (mips_abi == ABI_32 || mips_abi == ABI_O64))
-    {
-      const char *const sp_str = reg_names[STACK_POINTER_REGNUM];
-
-      fprintf (file, "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n",
-              reg_names[PIC_FUNCTION_ADDR_REGNUM]);
-      if (tsize > 0)
-       {
-         fprintf (file, "\t%s\t%s,%s,%ld\n",
-                  (Pmode == DImode ? "dsubu" : "subu"),
-                  sp_str, sp_str, (long) tsize);
-         fprintf (file, "\t.cprestore %ld\n", current_frame_info.args_size);
-       }
+  if (load_pos == -1 || store_pos == -1)
+    return;
 
-      if (dwarf2out_do_frame ())
-       dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, tsize);
+  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;
     }
 }
 \f
-/* Expand the prologue into a bunch of separate insns.  */
+/* Implement TARGET_SCHED_INIT.  */
 
-void
-mips_expand_prologue ()
+static void
+mips_sched_init (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                int max_ready ATTRIBUTE_UNUSED)
 {
-  int regno;
-  HOST_WIDE_INT tsize;
-  rtx tmp_rtx = 0;
-  int last_arg_is_vararg_marker = 0;
-  tree fndecl = current_function_decl;
-  tree fntype = TREE_TYPE (fndecl);
-  tree fnargs = DECL_ARGUMENTS (fndecl);
-  rtx next_arg_reg;
-  int i;
-  tree next_arg;
-  tree cur_arg;
-  CUMULATIVE_ARGS args_so_far;
-  rtx reg_18_save = NULL_RTX;
-  int store_args_on_stack = (mips_abi == ABI_32 || mips_abi == ABI_O64)
-                            && (! mips_entry || mips_can_use_return_insn ());
-
-  /* If struct value address is treated as the first argument, make it so.  */
-  if (aggregate_value_p (DECL_RESULT (fndecl))
-      && ! current_function_returns_pcc_struct
-      && struct_value_incoming_rtx == 0)
-    {
-      tree type = build_pointer_type (fntype);
-      tree function_result_decl = build_decl (PARM_DECL, NULL_TREE, type);
-
-      DECL_ARG_TYPE (function_result_decl) = type;
-      TREE_CHAIN (function_result_decl) = fnargs;
-      fnargs = function_result_decl;
-    }
-
-  /* For arguments passed in registers, find the register number
-     of the first argument in the variable part of the argument list,
-     otherwise GP_ARG_LAST+1.  Note also if the last argument is
-     the varargs special argument, and treat it as part of the
-     variable arguments.
+  mips_macc_chains_last_hilo = 0;
+  vr4130_last_insn = 0;
+  mips_74k_agen_init (NULL_RTX);
 
-     This is only needed if store_args_on_stack is true.  */
-
-  INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0);
-  regno = GP_ARG_FIRST;
+  /* When scheduling for Loongson2, branch instructions go to ALU1,
+     therefore basic block is most likely to start with round-robin counter
+     pointed to ALU2.  */
+  mips_ls2.alu1_turn_p = false;
+  mips_ls2.falu1_turn_p = true;
+}
 
-  for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg)
-    {
-      tree passed_type = DECL_ARG_TYPE (cur_arg);
-      enum machine_mode passed_mode = TYPE_MODE (passed_type);
-      rtx entry_parm;
+/* Implement TARGET_SCHED_REORDER and TARGET_SCHED_REORDER2.  */
 
-      if (TREE_ADDRESSABLE (passed_type))
-       {
-         passed_type = build_pointer_type (passed_type);
-         passed_mode = Pmode;
-       }
+static int
+mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                   rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+{
+  if (!reload_completed
+      && TUNE_MACC_CHAINS
+      && *nreadyp > 0)
+    mips_macc_chains_reorder (ready, *nreadyp);
 
-      entry_parm = FUNCTION_ARG (args_so_far, passed_mode, passed_type, 1);
+  if (reload_completed
+      && TUNE_MIPS4130
+      && !TARGET_VR4130_ALIGN
+      && *nreadyp > 1)
+    vr4130_reorder (ready, *nreadyp);
 
-      FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1);
-      next_arg = TREE_CHAIN (cur_arg);
+  if (TUNE_74K)
+    mips_74k_agen_reorder (ready, *nreadyp);
 
-      if (entry_parm && store_args_on_stack)
-       {
-         if (next_arg == 0
-             && DECL_NAME (cur_arg)
-             && ((0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
-                               "__builtin_va_alist"))
-                 || (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
-                                  "va_alist"))))
-           {
-             last_arg_is_vararg_marker = 1;
-             break;
-           }
-         else
-           {
-             int words;
+  return mips_issue_rate ();
+}
 
-             if (GET_CODE (entry_parm) != REG)
-               abort ();
+/* Update round-robin counters for ALU1/2 and FALU1/2.  */
 
-             /* passed in a register, so will get homed automatically */
-             if (GET_MODE (entry_parm) == BLKmode)
-               words = (int_size_in_bytes (passed_type) + 3) / 4;
-             else
-               words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
+static void
+mips_ls2_variable_issue (rtx insn)
+{
+  if (mips_ls2.alu1_turn_p)
+    {
+      if (cpu_unit_reservation_p (curr_state, mips_ls2.alu1_core_unit_code))
+       mips_ls2.alu1_turn_p = false;
+    }
+  else
+    {
+      if (cpu_unit_reservation_p (curr_state, mips_ls2.alu2_core_unit_code))
+       mips_ls2.alu1_turn_p = true;
+    }
 
-             regno = REGNO (entry_parm) + words - 1;
-           }
-       }
-      else
-       {
-         regno = GP_ARG_LAST+1;
-         break;
-       }
+  if (mips_ls2.falu1_turn_p)
+    {
+      if (cpu_unit_reservation_p (curr_state, mips_ls2.falu1_core_unit_code))
+       mips_ls2.falu1_turn_p = false;
+    }
+  else
+    {
+      if (cpu_unit_reservation_p (curr_state, mips_ls2.falu2_core_unit_code))
+       mips_ls2.falu1_turn_p = true;
     }
 
-  /* In order to pass small structures by value in registers compatibly with
-     the MIPS compiler, we need to shift the value into the high part of the
-     register.  Function_arg has encoded a PARALLEL rtx, holding a vector of
-     adjustments to be made as the next_arg_reg variable, so we split up the
-     insns, and emit them separately.  */
+  if (recog_memoized (insn) >= 0)
+    mips_ls2.cycle_has_multi_p |= (get_attr_type (insn) == TYPE_MULTI);
+}
 
-  next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1);
-  if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
-    {
-      rtvec adjust = XVEC (next_arg_reg, 0);
-      int num = GET_NUM_ELEM (adjust);
+/* Implement TARGET_SCHED_VARIABLE_ISSUE.  */
 
-      for (i = 0; i < num; i++)
-       {
-         rtx insn, pattern;
-
-         pattern = RTVEC_ELT (adjust, i);
-         if (GET_CODE (pattern) != SET
-             || GET_CODE (SET_SRC (pattern)) != ASHIFT)
-           abort_with_insn (pattern, "insn is not a shift");
-         PUT_CODE (SET_SRC (pattern), ASHIFTRT);
-
-         insn = emit_insn (pattern);
-
-         /* Global life information isn't valid at this point, so we
-            can't check whether these shifts are actually used.  Mark
-            them MAYBE_DEAD so that flow2 will remove them, and not
-            complain about dead code in the prologue.  */
-         REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
-                                              REG_NOTES (insn));
-       }
+static int
+mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                    rtx insn, int more)
+{
+  /* Ignore USEs and CLOBBERs; don't count them against the issue rate.  */
+  if (USEFUL_INSN_P (insn))
+    {
+      more--;
+      if (!reload_completed && TUNE_MACC_CHAINS)
+       mips_macc_chains_record (insn);
+      vr4130_last_insn = insn;
+      if (TUNE_74K)
+       mips_74k_agen_init (insn);
+      else if (TUNE_LOONGSON_2EF)
+       mips_ls2_variable_issue (insn);
     }
 
-  tsize = compute_frame_size (get_frame_size ());
+  /* Instructions of type 'multi' should all be split before
+     the second scheduling pass.  */
+  gcc_assert (!reload_completed
+             || recog_memoized (insn) < 0
+             || get_attr_type (insn) != TYPE_MULTI);
 
-  /* If this function is a varargs function, store any registers that
-     would normally hold arguments ($4 - $7) on the stack.  */
-  if (store_args_on_stack
-      && ((TYPE_ARG_TYPES (fntype) != 0
-          && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
-              != void_type_node))
-         || last_arg_is_vararg_marker))
-    {
-      int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD;
-      rtx ptr = stack_pointer_rtx;
+  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.  */
 
-      /* If we are doing svr4-abi, sp has already been decremented by tsize.  */
-      if (TARGET_ABICALLS)
-       offset += tsize;
+rtx
+mips_prefetch_cookie (rtx write, rtx locality)
+{
+  /* store_streamed / load_streamed.  */
+  if (INTVAL (locality) <= 0)
+    return GEN_INT (INTVAL (write) + 4);
 
-      for (; regno <= GP_ARG_LAST; regno++)
-       {
-         if (offset != 0)
-           ptr = gen_rtx (PLUS, Pmode, stack_pointer_rtx, GEN_INT (offset));
-         emit_move_insn (gen_rtx (MEM, gpr_mode, ptr),
-                         gen_rtx (REG, gpr_mode, regno));
+  /* store / load.  */
+  if (INTVAL (locality) <= 2)
+    return write;
 
-         offset += GET_MODE_SIZE (gpr_mode);
-       }
-    }
+  /* store_retained / load_retained.  */
+  return GEN_INT (INTVAL (write) + 6);
+}
+\f
+/* Flags that indicate when a built-in function is available.
+
+   BUILTIN_AVAIL_NON_MIPS16
+       The function is available on the current target, but only
+       in non-MIPS16 mode.  */
+#define BUILTIN_AVAIL_NON_MIPS16 1
+
+/* Declare an availability predicate for built-in functions that
+   require non-MIPS16 mode and also require COND to be true.
+   NAME is the main part of the predicate's name.  */
+#define AVAIL_NON_MIPS16(NAME, COND)                                   \
+ static unsigned int                                                   \
+ mips_builtin_avail_##NAME (void)                                      \
+ {                                                                     \
+   return (COND) ? BUILTIN_AVAIL_NON_MIPS16 : 0;                       \
+ }
+
+/* This structure describes a single built-in function.  */
+struct mips_builtin_description {
+  /* The code of the main .md file instruction.  See mips_builtin_type
+     for more information.  */
+  enum insn_code icode;
+
+  /* The floating-point comparison code to use with ICODE, if any.  */
+  enum mips_fp_condition cond;
+
+  /* The name of the built-in function.  */
+  const char *name;
 
-  /* If we are using the entry pseudo instruction, it will
-     automatically subtract 32 from the stack pointer, so we don't
-     need to.  The entry pseudo instruction is emitted by
-     function_prologue.  */
-  if (mips_entry && ! mips_can_use_return_insn ())
-    {
-      if (tsize > 0 && tsize <= 32 && frame_pointer_needed)
-       {
-          rtx insn;
+  /* Specifies how the function should be expanded.  */
+  enum mips_builtin_type builtin_type;
 
-         /* If we are using a frame pointer with a small stack frame,
-             we need to initialize it here since it won't be done
-             below.  */
-         if (TARGET_MIPS16 && current_function_outgoing_args_size != 0)
-           {
-             rtx incr = GEN_INT (current_function_outgoing_args_size);
-             if (Pmode == DImode)
-               insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
-                                              stack_pointer_rtx,
-                                              incr));
-             else
-               insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
-                                              stack_pointer_rtx,
-                                              incr));
-           }
-         else if (Pmode == DImode)
-           insn = emit_insn (gen_movdi (hard_frame_pointer_rtx,
-                                        stack_pointer_rtx));
-         else
-           insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
-                                        stack_pointer_rtx));
+  /* The function's prototype.  */
+  enum mips_function_type function_type;
 
-          RTX_FRAME_RELATED_P (insn) = 1;
-       }
+  /* Whether the function is available.  */
+  unsigned int (*avail) (void);
+};
 
-      /* We may need to save $18, if it is used to call a function
-        which may return a floating point value.  Set up a sequence
-        of instructions to do so.  Later on we emit them at the right
-        moment.  */
-      if (TARGET_MIPS16 && BITSET_P (current_frame_info.mask, 18))
-       {
-         rtx reg_rtx = gen_rtx (REG, gpr_mode, GP_REG_FIRST + 3);
-         long gp_offset, base_offset;
-
-         gp_offset = current_frame_info.gp_sp_offset;
-         if (BITSET_P (current_frame_info.mask, 16))
-           gp_offset -= UNITS_PER_WORD;
-         if (BITSET_P (current_frame_info.mask, 17))
-           gp_offset -= UNITS_PER_WORD;
-         if (BITSET_P (current_frame_info.mask, 31))
-           gp_offset -= UNITS_PER_WORD;
-         if (tsize > 32767)
-           base_offset = tsize;
-         else
-           base_offset = 0;
-         start_sequence ();
-         emit_move_insn (reg_rtx,
-                         gen_rtx (REG, gpr_mode, GP_REG_FIRST + 18));
-         emit_move_insn (gen_rtx (MEM, gpr_mode,
-                                  gen_rtx (PLUS, Pmode, stack_pointer_rtx,
-                                           GEN_INT (gp_offset
-                                                    - base_offset))),
-                         reg_rtx);
-         reg_18_save = gen_sequence ();
-         end_sequence ();
-       }
+AVAIL_NON_MIPS16 (paired_single, TARGET_PAIRED_SINGLE_FLOAT)
+AVAIL_NON_MIPS16 (sb1_paired_single, TARGET_SB1 && TARGET_PAIRED_SINGLE_FLOAT)
+AVAIL_NON_MIPS16 (mips3d, TARGET_MIPS3D)
+AVAIL_NON_MIPS16 (dsp, TARGET_DSP)
+AVAIL_NON_MIPS16 (dspr2, TARGET_DSPR2)
+AVAIL_NON_MIPS16 (dsp_32, !TARGET_64BIT && TARGET_DSP)
+AVAIL_NON_MIPS16 (dspr2_32, !TARGET_64BIT && TARGET_DSPR2)
+AVAIL_NON_MIPS16 (loongson, TARGET_LOONGSON_VECTORS)
+AVAIL_NON_MIPS16 (cache, TARGET_CACHE_BUILTIN)
+
+/* Construct a mips_builtin_description from the given arguments.
+
+   INSN is the name of the associated instruction pattern, without the
+   leading CODE_FOR_mips_.
+
+   CODE is the floating-point condition code associated with the
+   function.  It can be 'f' if the field is not applicable.
+
+   NAME is the name of the function itself, without the leading
+   "__builtin_mips_".
+
+   BUILTIN_TYPE and FUNCTION_TYPE are mips_builtin_description fields.
+
+   AVAIL is the name of the availability predicate, without the leading
+   mips_builtin_avail_.  */
+#define MIPS_BUILTIN(INSN, COND, NAME, BUILTIN_TYPE,                   \
+                    FUNCTION_TYPE, AVAIL)                              \
+  { CODE_FOR_mips_ ## INSN, MIPS_FP_COND_ ## COND,                     \
+    "__builtin_mips_" NAME, BUILTIN_TYPE, FUNCTION_TYPE,               \
+    mips_builtin_avail_ ## AVAIL }
+
+/* Define __builtin_mips_<INSN>, which is a MIPS_BUILTIN_DIRECT function
+   mapped to instruction CODE_FOR_mips_<INSN>,  FUNCTION_TYPE and AVAIL
+   are as for MIPS_BUILTIN.  */
+#define DIRECT_BUILTIN(INSN, FUNCTION_TYPE, AVAIL)                     \
+  MIPS_BUILTIN (INSN, f, #INSN, MIPS_BUILTIN_DIRECT, FUNCTION_TYPE, AVAIL)
+
+/* Define __builtin_mips_<INSN>_<COND>_{s,d} functions, both of which
+   are subject to mips_builtin_avail_<AVAIL>.  */
+#define CMP_SCALAR_BUILTINS(INSN, COND, AVAIL)                         \
+  MIPS_BUILTIN (INSN ## _cond_s, COND, #INSN "_" #COND "_s",           \
+               MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_SF_SF, AVAIL),  \
+  MIPS_BUILTIN (INSN ## _cond_d, COND, #INSN "_" #COND "_d",           \
+               MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_DF_DF, AVAIL)
+
+/* Define __builtin_mips_{any,all,upper,lower}_<INSN>_<COND>_ps.
+   The lower and upper forms are subject to mips_builtin_avail_<AVAIL>
+   while the any and all forms are subject to mips_builtin_avail_mips3d.  */
+#define CMP_PS_BUILTINS(INSN, COND, AVAIL)                             \
+  MIPS_BUILTIN (INSN ## _cond_ps, COND, "any_" #INSN "_" #COND "_ps",  \
+               MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF,         \
+               mips3d),                                                \
+  MIPS_BUILTIN (INSN ## _cond_ps, COND, "all_" #INSN "_" #COND "_ps",  \
+               MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF,         \
+               mips3d),                                                \
+  MIPS_BUILTIN (INSN ## _cond_ps, COND, "lower_" #INSN "_" #COND "_ps",        \
+               MIPS_BUILTIN_CMP_LOWER, MIPS_INT_FTYPE_V2SF_V2SF,       \
+               AVAIL),                                                 \
+  MIPS_BUILTIN (INSN ## _cond_ps, COND, "upper_" #INSN "_" #COND "_ps",        \
+               MIPS_BUILTIN_CMP_UPPER, MIPS_INT_FTYPE_V2SF_V2SF,       \
+               AVAIL)
+
+/* Define __builtin_mips_{any,all}_<INSN>_<COND>_4s.  The functions
+   are subject to mips_builtin_avail_mips3d.  */
+#define CMP_4S_BUILTINS(INSN, COND)                                    \
+  MIPS_BUILTIN (INSN ## _cond_4s, COND, "any_" #INSN "_" #COND "_4s",  \
+               MIPS_BUILTIN_CMP_ANY,                                   \
+               MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, mips3d),            \
+  MIPS_BUILTIN (INSN ## _cond_4s, COND, "all_" #INSN "_" #COND "_4s",  \
+               MIPS_BUILTIN_CMP_ALL,                                   \
+               MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, mips3d)
+
+/* Define __builtin_mips_mov{t,f}_<INSN>_<COND>_ps.  The comparison
+   instruction requires mips_builtin_avail_<AVAIL>.  */
+#define MOVTF_BUILTINS(INSN, COND, AVAIL)                              \
+  MIPS_BUILTIN (INSN ## _cond_ps, COND, "movt_" #INSN "_" #COND "_ps", \
+               MIPS_BUILTIN_MOVT, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \
+               AVAIL),                                                 \
+  MIPS_BUILTIN (INSN ## _cond_ps, COND, "movf_" #INSN "_" #COND "_ps", \
+               MIPS_BUILTIN_MOVF, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \
+               AVAIL)
+
+/* Define all the built-in functions related to C.cond.fmt condition COND.  */
+#define CMP_BUILTINS(COND)                                             \
+  MOVTF_BUILTINS (c, COND, paired_single),                             \
+  MOVTF_BUILTINS (cabs, COND, mips3d),                                 \
+  CMP_SCALAR_BUILTINS (cabs, COND, mips3d),                            \
+  CMP_PS_BUILTINS (c, COND, paired_single),                            \
+  CMP_PS_BUILTINS (cabs, COND, mips3d),                                        \
+  CMP_4S_BUILTINS (c, COND),                                           \
+  CMP_4S_BUILTINS (cabs, COND)
+
+/* Define __builtin_mips_<INSN>, which is a MIPS_BUILTIN_DIRECT_NO_TARGET
+   function mapped to instruction CODE_FOR_mips_<INSN>,  FUNCTION_TYPE
+   and AVAIL are as for MIPS_BUILTIN.  */
+#define DIRECT_NO_TARGET_BUILTIN(INSN, FUNCTION_TYPE, AVAIL)           \
+  MIPS_BUILTIN (INSN, f, #INSN,        MIPS_BUILTIN_DIRECT_NO_TARGET,          \
+               FUNCTION_TYPE, AVAIL)
+
+/* Define __builtin_mips_bposge<VALUE>.  <VALUE> is 32 for the MIPS32 DSP
+   branch instruction.  AVAIL is as for MIPS_BUILTIN.  */
+#define BPOSGE_BUILTIN(VALUE, AVAIL)                                   \
+  MIPS_BUILTIN (bposge, f, "bposge" #VALUE,                            \
+               MIPS_BUILTIN_BPOSGE ## VALUE, MIPS_SI_FTYPE_VOID, AVAIL)
+
+/* Define a Loongson MIPS_BUILTIN_DIRECT function __builtin_loongson_<FN_NAME>
+   for instruction CODE_FOR_loongson_<INSN>.  FUNCTION_TYPE is a
+   builtin_description field.  */
+#define LOONGSON_BUILTIN_ALIAS(INSN, FN_NAME, FUNCTION_TYPE)           \
+  { CODE_FOR_loongson_ ## INSN, 0, "__builtin_loongson_" #FN_NAME,     \
+    MIPS_BUILTIN_DIRECT, FUNCTION_TYPE, mips_builtin_avail_loongson }
+
+/* Define a Loongson MIPS_BUILTIN_DIRECT function __builtin_loongson_<INSN>
+   for instruction CODE_FOR_loongson_<INSN>.  FUNCTION_TYPE is a
+   builtin_description field.  */
+#define LOONGSON_BUILTIN(INSN, FUNCTION_TYPE)                          \
+  LOONGSON_BUILTIN_ALIAS (INSN, INSN, FUNCTION_TYPE)
+
+/* Like LOONGSON_BUILTIN, but add _<SUFFIX> to the end of the function name.
+   We use functions of this form when the same insn can be usefully applied
+   to more than one datatype.  */
+#define LOONGSON_BUILTIN_SUFFIX(INSN, SUFFIX, FUNCTION_TYPE)           \
+  LOONGSON_BUILTIN_ALIAS (INSN, INSN ## _ ## SUFFIX, FUNCTION_TYPE)
+
+#define CODE_FOR_mips_sqrt_ps CODE_FOR_sqrtv2sf2
+#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
+
+#define CODE_FOR_loongson_packsswh CODE_FOR_vec_pack_ssat_v2si
+#define CODE_FOR_loongson_packsshb CODE_FOR_vec_pack_ssat_v4hi
+#define CODE_FOR_loongson_packushb CODE_FOR_vec_pack_usat_v4hi
+#define CODE_FOR_loongson_paddw CODE_FOR_addv2si3
+#define CODE_FOR_loongson_paddh CODE_FOR_addv4hi3
+#define CODE_FOR_loongson_paddb CODE_FOR_addv8qi3
+#define CODE_FOR_loongson_paddsh CODE_FOR_ssaddv4hi3
+#define CODE_FOR_loongson_paddsb CODE_FOR_ssaddv8qi3
+#define CODE_FOR_loongson_paddush CODE_FOR_usaddv4hi3
+#define CODE_FOR_loongson_paddusb CODE_FOR_usaddv8qi3
+#define CODE_FOR_loongson_pmaxsh CODE_FOR_smaxv4hi3
+#define CODE_FOR_loongson_pmaxub CODE_FOR_umaxv8qi3
+#define CODE_FOR_loongson_pminsh CODE_FOR_sminv4hi3
+#define CODE_FOR_loongson_pminub CODE_FOR_uminv8qi3
+#define CODE_FOR_loongson_pmulhuh CODE_FOR_umulv4hi3_highpart
+#define CODE_FOR_loongson_pmulhh CODE_FOR_smulv4hi3_highpart
+#define CODE_FOR_loongson_psubw CODE_FOR_subv2si3
+#define CODE_FOR_loongson_psubh CODE_FOR_subv4hi3
+#define CODE_FOR_loongson_psubb CODE_FOR_subv8qi3
+#define CODE_FOR_loongson_psubsh CODE_FOR_sssubv4hi3
+#define CODE_FOR_loongson_psubsb CODE_FOR_sssubv8qi3
+#define CODE_FOR_loongson_psubush CODE_FOR_ussubv4hi3
+#define CODE_FOR_loongson_psubusb CODE_FOR_ussubv8qi3
+#define CODE_FOR_loongson_punpckhbh CODE_FOR_vec_interleave_highv8qi
+#define CODE_FOR_loongson_punpckhhw CODE_FOR_vec_interleave_highv4hi
+#define CODE_FOR_loongson_punpckhwd CODE_FOR_vec_interleave_highv2si
+#define CODE_FOR_loongson_punpcklbh CODE_FOR_vec_interleave_lowv8qi
+#define CODE_FOR_loongson_punpcklhw CODE_FOR_vec_interleave_lowv4hi
+#define CODE_FOR_loongson_punpcklwd CODE_FOR_vec_interleave_lowv2si
+
+static const struct mips_builtin_description mips_builtins[] = {
+  DIRECT_BUILTIN (pll_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+  DIRECT_BUILTIN (pul_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+  DIRECT_BUILTIN (plu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+  DIRECT_BUILTIN (puu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+  DIRECT_BUILTIN (cvt_ps_s, MIPS_V2SF_FTYPE_SF_SF, paired_single),
+  DIRECT_BUILTIN (cvt_s_pl, MIPS_SF_FTYPE_V2SF, paired_single),
+  DIRECT_BUILTIN (cvt_s_pu, MIPS_SF_FTYPE_V2SF, paired_single),
+  DIRECT_BUILTIN (abs_ps, MIPS_V2SF_FTYPE_V2SF, paired_single),
+
+  DIRECT_BUILTIN (alnv_ps, MIPS_V2SF_FTYPE_V2SF_V2SF_INT, paired_single),
+  DIRECT_BUILTIN (addr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+  DIRECT_BUILTIN (mulr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+  DIRECT_BUILTIN (cvt_pw_ps, MIPS_V2SF_FTYPE_V2SF, mips3d),
+  DIRECT_BUILTIN (cvt_ps_pw, MIPS_V2SF_FTYPE_V2SF, mips3d),
+
+  DIRECT_BUILTIN (recip1_s, MIPS_SF_FTYPE_SF, mips3d),
+  DIRECT_BUILTIN (recip1_d, MIPS_DF_FTYPE_DF, mips3d),
+  DIRECT_BUILTIN (recip1_ps, MIPS_V2SF_FTYPE_V2SF, mips3d),
+  DIRECT_BUILTIN (recip2_s, MIPS_SF_FTYPE_SF_SF, mips3d),
+  DIRECT_BUILTIN (recip2_d, MIPS_DF_FTYPE_DF_DF, mips3d),
+  DIRECT_BUILTIN (recip2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+
+  DIRECT_BUILTIN (rsqrt1_s, MIPS_SF_FTYPE_SF, mips3d),
+  DIRECT_BUILTIN (rsqrt1_d, MIPS_DF_FTYPE_DF, mips3d),
+  DIRECT_BUILTIN (rsqrt1_ps, MIPS_V2SF_FTYPE_V2SF, mips3d),
+  DIRECT_BUILTIN (rsqrt2_s, MIPS_SF_FTYPE_SF_SF, mips3d),
+  DIRECT_BUILTIN (rsqrt2_d, MIPS_DF_FTYPE_DF_DF, mips3d),
+  DIRECT_BUILTIN (rsqrt2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+
+  MIPS_FP_CONDITIONS (CMP_BUILTINS),
+
+  /* Built-in functions for the SB-1 processor.  */
+  DIRECT_BUILTIN (sqrt_ps, MIPS_V2SF_FTYPE_V2SF, sb1_paired_single),
+
+  /* Built-in functions for the DSP ASE (32-bit and 64-bit).  */
+  DIRECT_BUILTIN (addq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_BUILTIN (addq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_BUILTIN (addq_s_w, MIPS_SI_FTYPE_SI_SI, dsp),
+  DIRECT_BUILTIN (addu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+  DIRECT_BUILTIN (addu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+  DIRECT_BUILTIN (subq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_BUILTIN (subq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_BUILTIN (subq_s_w, MIPS_SI_FTYPE_SI_SI, dsp),
+  DIRECT_BUILTIN (subu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+  DIRECT_BUILTIN (subu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+  DIRECT_BUILTIN (addsc, MIPS_SI_FTYPE_SI_SI, dsp),
+  DIRECT_BUILTIN (addwc, MIPS_SI_FTYPE_SI_SI, dsp),
+  DIRECT_BUILTIN (modsub, MIPS_SI_FTYPE_SI_SI, dsp),
+  DIRECT_BUILTIN (raddu_w_qb, MIPS_SI_FTYPE_V4QI, dsp),
+  DIRECT_BUILTIN (absq_s_ph, MIPS_V2HI_FTYPE_V2HI, dsp),
+  DIRECT_BUILTIN (absq_s_w, MIPS_SI_FTYPE_SI, dsp),
+  DIRECT_BUILTIN (precrq_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_BUILTIN (precrq_ph_w, MIPS_V2HI_FTYPE_SI_SI, dsp),
+  DIRECT_BUILTIN (precrq_rs_ph_w, MIPS_V2HI_FTYPE_SI_SI, dsp),
+  DIRECT_BUILTIN (precrqu_s_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_BUILTIN (preceq_w_phl, MIPS_SI_FTYPE_V2HI, dsp),
+  DIRECT_BUILTIN (preceq_w_phr, MIPS_SI_FTYPE_V2HI, dsp),
+  DIRECT_BUILTIN (precequ_ph_qbl, MIPS_V2HI_FTYPE_V4QI, dsp),
+  DIRECT_BUILTIN (precequ_ph_qbr, MIPS_V2HI_FTYPE_V4QI, dsp),
+  DIRECT_BUILTIN (precequ_ph_qbla, MIPS_V2HI_FTYPE_V4QI, dsp),
+  DIRECT_BUILTIN (precequ_ph_qbra, MIPS_V2HI_FTYPE_V4QI, dsp),
+  DIRECT_BUILTIN (preceu_ph_qbl, MIPS_V2HI_FTYPE_V4QI, dsp),
+  DIRECT_BUILTIN (preceu_ph_qbr, MIPS_V2HI_FTYPE_V4QI, dsp),
+  DIRECT_BUILTIN (preceu_ph_qbla, MIPS_V2HI_FTYPE_V4QI, dsp),
+  DIRECT_BUILTIN (preceu_ph_qbra, MIPS_V2HI_FTYPE_V4QI, dsp),
+  DIRECT_BUILTIN (shll_qb, MIPS_V4QI_FTYPE_V4QI_SI, dsp),
+  DIRECT_BUILTIN (shll_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+  DIRECT_BUILTIN (shll_s_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+  DIRECT_BUILTIN (shll_s_w, MIPS_SI_FTYPE_SI_SI, dsp),
+  DIRECT_BUILTIN (shrl_qb, MIPS_V4QI_FTYPE_V4QI_SI, dsp),
+  DIRECT_BUILTIN (shra_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+  DIRECT_BUILTIN (shra_r_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+  DIRECT_BUILTIN (shra_r_w, MIPS_SI_FTYPE_SI_SI, dsp),
+  DIRECT_BUILTIN (muleu_s_ph_qbl, MIPS_V2HI_FTYPE_V4QI_V2HI, dsp),
+  DIRECT_BUILTIN (muleu_s_ph_qbr, MIPS_V2HI_FTYPE_V4QI_V2HI, dsp),
+  DIRECT_BUILTIN (mulq_rs_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_BUILTIN (muleq_s_w_phl, MIPS_SI_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_BUILTIN (muleq_s_w_phr, MIPS_SI_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_BUILTIN (bitrev, MIPS_SI_FTYPE_SI, dsp),
+  DIRECT_BUILTIN (insv, MIPS_SI_FTYPE_SI_SI, dsp),
+  DIRECT_BUILTIN (repl_qb, MIPS_V4QI_FTYPE_SI, dsp),
+  DIRECT_BUILTIN (repl_ph, MIPS_V2HI_FTYPE_SI, dsp),
+  DIRECT_NO_TARGET_BUILTIN (cmpu_eq_qb, MIPS_VOID_FTYPE_V4QI_V4QI, dsp),
+  DIRECT_NO_TARGET_BUILTIN (cmpu_lt_qb, MIPS_VOID_FTYPE_V4QI_V4QI, dsp),
+  DIRECT_NO_TARGET_BUILTIN (cmpu_le_qb, MIPS_VOID_FTYPE_V4QI_V4QI, dsp),
+  DIRECT_BUILTIN (cmpgu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, dsp),
+  DIRECT_BUILTIN (cmpgu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, dsp),
+  DIRECT_BUILTIN (cmpgu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, dsp),
+  DIRECT_NO_TARGET_BUILTIN (cmp_eq_ph, MIPS_VOID_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_NO_TARGET_BUILTIN (cmp_lt_ph, MIPS_VOID_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_NO_TARGET_BUILTIN (cmp_le_ph, MIPS_VOID_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_BUILTIN (pick_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+  DIRECT_BUILTIN (pick_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_BUILTIN (packrl_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+  DIRECT_NO_TARGET_BUILTIN (wrdsp, MIPS_VOID_FTYPE_SI_SI, dsp),
+  DIRECT_BUILTIN (rddsp, MIPS_SI_FTYPE_SI, dsp),
+  DIRECT_BUILTIN (lbux, MIPS_SI_FTYPE_POINTER_SI, dsp),
+  DIRECT_BUILTIN (lhx, MIPS_SI_FTYPE_POINTER_SI, dsp),
+  DIRECT_BUILTIN (lwx, MIPS_SI_FTYPE_POINTER_SI, dsp),
+  BPOSGE_BUILTIN (32, dsp),
+
+  /* The following are for the MIPS DSP ASE REV 2 (32-bit and 64-bit).  */
+  DIRECT_BUILTIN (absq_s_qb, MIPS_V4QI_FTYPE_V4QI, dspr2),
+  DIRECT_BUILTIN (addu_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (addu_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (adduh_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+  DIRECT_BUILTIN (adduh_r_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+  DIRECT_BUILTIN (append, MIPS_SI_FTYPE_SI_SI_SI, dspr2),
+  DIRECT_BUILTIN (balign, MIPS_SI_FTYPE_SI_SI_SI, dspr2),
+  DIRECT_BUILTIN (cmpgdu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, dspr2),
+  DIRECT_BUILTIN (cmpgdu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, dspr2),
+  DIRECT_BUILTIN (cmpgdu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, dspr2),
+  DIRECT_BUILTIN (mul_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (mul_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (mulq_rs_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+  DIRECT_BUILTIN (mulq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (mulq_s_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+  DIRECT_BUILTIN (precr_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (precr_sra_ph_w, MIPS_V2HI_FTYPE_SI_SI_SI, dspr2),
+  DIRECT_BUILTIN (precr_sra_r_ph_w, MIPS_V2HI_FTYPE_SI_SI_SI, dspr2),
+  DIRECT_BUILTIN (prepend, MIPS_SI_FTYPE_SI_SI_SI, dspr2),
+  DIRECT_BUILTIN (shra_qb, MIPS_V4QI_FTYPE_V4QI_SI, dspr2),
+  DIRECT_BUILTIN (shra_r_qb, MIPS_V4QI_FTYPE_V4QI_SI, dspr2),
+  DIRECT_BUILTIN (shrl_ph, MIPS_V2HI_FTYPE_V2HI_SI, dspr2),
+  DIRECT_BUILTIN (subu_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (subu_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (subuh_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+  DIRECT_BUILTIN (subuh_r_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+  DIRECT_BUILTIN (addqh_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (addqh_r_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (addqh_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+  DIRECT_BUILTIN (addqh_r_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+  DIRECT_BUILTIN (subqh_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (subqh_r_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+  DIRECT_BUILTIN (subqh_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+  DIRECT_BUILTIN (subqh_r_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+
+  /* Built-in functions for the DSP ASE (32-bit only).  */
+  DIRECT_BUILTIN (dpau_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+  DIRECT_BUILTIN (dpau_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+  DIRECT_BUILTIN (dpsu_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+  DIRECT_BUILTIN (dpsu_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+  DIRECT_BUILTIN (dpaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+  DIRECT_BUILTIN (dpsq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+  DIRECT_BUILTIN (mulsaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+  DIRECT_BUILTIN (dpaq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+  DIRECT_BUILTIN (dpsq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+  DIRECT_BUILTIN (maq_s_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+  DIRECT_BUILTIN (maq_s_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+  DIRECT_BUILTIN (maq_sa_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+  DIRECT_BUILTIN (maq_sa_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+  DIRECT_BUILTIN (extr_w, MIPS_SI_FTYPE_DI_SI, dsp_32),
+  DIRECT_BUILTIN (extr_r_w, MIPS_SI_FTYPE_DI_SI, dsp_32),
+  DIRECT_BUILTIN (extr_rs_w, MIPS_SI_FTYPE_DI_SI, dsp_32),
+  DIRECT_BUILTIN (extr_s_h, MIPS_SI_FTYPE_DI_SI, dsp_32),
+  DIRECT_BUILTIN (extp, MIPS_SI_FTYPE_DI_SI, dsp_32),
+  DIRECT_BUILTIN (extpdp, MIPS_SI_FTYPE_DI_SI, dsp_32),
+  DIRECT_BUILTIN (shilo, MIPS_DI_FTYPE_DI_SI, dsp_32),
+  DIRECT_BUILTIN (mthlip, MIPS_DI_FTYPE_DI_SI, dsp_32),
+
+  /* The following are for the MIPS DSP ASE REV 2 (32-bit only).  */
+  DIRECT_BUILTIN (dpa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+  DIRECT_BUILTIN (dps_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+  DIRECT_BUILTIN (madd, MIPS_DI_FTYPE_DI_SI_SI, dspr2_32),
+  DIRECT_BUILTIN (maddu, MIPS_DI_FTYPE_DI_USI_USI, dspr2_32),
+  DIRECT_BUILTIN (msub, MIPS_DI_FTYPE_DI_SI_SI, dspr2_32),
+  DIRECT_BUILTIN (msubu, MIPS_DI_FTYPE_DI_USI_USI, dspr2_32),
+  DIRECT_BUILTIN (mulsa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+  DIRECT_BUILTIN (mult, MIPS_DI_FTYPE_SI_SI, dspr2_32),
+  DIRECT_BUILTIN (multu, MIPS_DI_FTYPE_USI_USI, dspr2_32),
+  DIRECT_BUILTIN (dpax_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+  DIRECT_BUILTIN (dpsx_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+  DIRECT_BUILTIN (dpaqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+  DIRECT_BUILTIN (dpaqx_sa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+  DIRECT_BUILTIN (dpsqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+  DIRECT_BUILTIN (dpsqx_sa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+
+  /* Builtin functions for ST Microelectronics Loongson-2E/2F cores.  */
+  LOONGSON_BUILTIN (packsswh, MIPS_V4HI_FTYPE_V2SI_V2SI),
+  LOONGSON_BUILTIN (packsshb, MIPS_V8QI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN (packushb, MIPS_UV8QI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_SUFFIX (paddw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+  LOONGSON_BUILTIN_SUFFIX (paddh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_SUFFIX (paddb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_SUFFIX (paddw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+  LOONGSON_BUILTIN_SUFFIX (paddh, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN_SUFFIX (paddb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+  LOONGSON_BUILTIN_SUFFIX (paddd, u, MIPS_UDI_FTYPE_UDI_UDI),
+  LOONGSON_BUILTIN_SUFFIX (paddd, s, MIPS_DI_FTYPE_DI_DI),
+  LOONGSON_BUILTIN (paddsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN (paddsb, MIPS_V8QI_FTYPE_V8QI_V8QI),
+  LOONGSON_BUILTIN (paddush, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN (paddusb, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_ALIAS (pandn_d, pandn_ud, MIPS_UDI_FTYPE_UDI_UDI),
+  LOONGSON_BUILTIN_ALIAS (pandn_w, pandn_uw, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+  LOONGSON_BUILTIN_ALIAS (pandn_h, pandn_uh, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_ALIAS (pandn_b, pandn_ub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_ALIAS (pandn_d, pandn_sd, MIPS_DI_FTYPE_DI_DI),
+  LOONGSON_BUILTIN_ALIAS (pandn_w, pandn_sw, MIPS_V2SI_FTYPE_V2SI_V2SI),
+  LOONGSON_BUILTIN_ALIAS (pandn_h, pandn_sh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN_ALIAS (pandn_b, pandn_sb, MIPS_V8QI_FTYPE_V8QI_V8QI),
+  LOONGSON_BUILTIN (pavgh, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN (pavgb, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpeqw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpeqh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpeqb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpeqw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpeqh, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpeqb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpgtw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpgth, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpgtb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpgtw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpgth, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN_SUFFIX (pcmpgtb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+  LOONGSON_BUILTIN_SUFFIX (pextrh, u, MIPS_UV4HI_FTYPE_UV4HI_USI),
+  LOONGSON_BUILTIN_SUFFIX (pextrh, s, MIPS_V4HI_FTYPE_V4HI_USI),
+  LOONGSON_BUILTIN_SUFFIX (pinsrh_0, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_SUFFIX (pinsrh_1, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_SUFFIX (pinsrh_2, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_SUFFIX (pinsrh_3, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_SUFFIX (pinsrh_0, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN_SUFFIX (pinsrh_1, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN_SUFFIX (pinsrh_2, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN_SUFFIX (pinsrh_3, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN (pmaddhw, MIPS_V2SI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN (pmaxsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN (pmaxub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN (pminsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN (pminub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_SUFFIX (pmovmskb, u, MIPS_UV8QI_FTYPE_UV8QI),
+  LOONGSON_BUILTIN_SUFFIX (pmovmskb, s, MIPS_V8QI_FTYPE_V8QI),
+  LOONGSON_BUILTIN (pmulhuh, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN (pmulhh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN (pmullh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN (pmuluw, MIPS_UDI_FTYPE_UV2SI_UV2SI),
+  LOONGSON_BUILTIN (pasubub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN (biadd, MIPS_UV4HI_FTYPE_UV8QI),
+  LOONGSON_BUILTIN (psadbh, MIPS_UV4HI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_SUFFIX (pshufh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (pshufh, s, MIPS_V4HI_FTYPE_V4HI_V4HI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psllh, u, MIPS_UV4HI_FTYPE_UV4HI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psllh, s, MIPS_V4HI_FTYPE_V4HI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psllw, u, MIPS_UV2SI_FTYPE_UV2SI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psllw, s, MIPS_V2SI_FTYPE_V2SI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psrah, u, MIPS_UV4HI_FTYPE_UV4HI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psrah, s, MIPS_V4HI_FTYPE_V4HI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psraw, u, MIPS_UV2SI_FTYPE_UV2SI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psraw, s, MIPS_V2SI_FTYPE_V2SI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psrlh, u, MIPS_UV4HI_FTYPE_UV4HI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psrlh, s, MIPS_V4HI_FTYPE_V4HI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psrlw, u, MIPS_UV2SI_FTYPE_UV2SI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psrlw, s, MIPS_V2SI_FTYPE_V2SI_UQI),
+  LOONGSON_BUILTIN_SUFFIX (psubw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+  LOONGSON_BUILTIN_SUFFIX (psubh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_SUFFIX (psubb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_SUFFIX (psubw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+  LOONGSON_BUILTIN_SUFFIX (psubh, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN_SUFFIX (psubb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+  LOONGSON_BUILTIN_SUFFIX (psubd, u, MIPS_UDI_FTYPE_UDI_UDI),
+  LOONGSON_BUILTIN_SUFFIX (psubd, s, MIPS_DI_FTYPE_DI_DI),
+  LOONGSON_BUILTIN (psubsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN (psubsb, MIPS_V8QI_FTYPE_V8QI_V8QI),
+  LOONGSON_BUILTIN (psubush, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN (psubusb, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_SUFFIX (punpckhbh, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_SUFFIX (punpckhhw, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_SUFFIX (punpckhwd, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+  LOONGSON_BUILTIN_SUFFIX (punpckhbh, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+  LOONGSON_BUILTIN_SUFFIX (punpckhhw, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN_SUFFIX (punpckhwd, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+  LOONGSON_BUILTIN_SUFFIX (punpcklbh, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+  LOONGSON_BUILTIN_SUFFIX (punpcklhw, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+  LOONGSON_BUILTIN_SUFFIX (punpcklwd, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+  LOONGSON_BUILTIN_SUFFIX (punpcklbh, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+  LOONGSON_BUILTIN_SUFFIX (punpcklhw, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+  LOONGSON_BUILTIN_SUFFIX (punpcklwd, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+
+  /* Sundry other built-in functions.  */
+  DIRECT_NO_TARGET_BUILTIN (cache, MIPS_VOID_FTYPE_SI_CVPOINTER, cache)
+};
 
-      if (tsize > 32)
-       tsize -= 32;
-      else
-       {
-         tsize = 0;
-         if (reg_18_save != NULL_RTX)
-           emit_insn (reg_18_save);
-       }
-    }
+/* MODE is a vector mode whose elements have type TYPE.  Return the type
+   of the vector itself.  */
 
-  if (tsize > 0)
-    {
-      rtx tsize_rtx = GEN_INT (tsize);
+static tree
+mips_builtin_vector_type (tree type, enum machine_mode mode)
+{
+  static tree types[2 * (int) MAX_MACHINE_MODE];
+  int mode_index;
 
-      /* If we are doing svr4-abi, sp move is done by
-         function_prologue.  In mips16 mode with a large frame, we
-         save the registers before adjusting the stack.  */
-      if ((!TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
-         && (!TARGET_MIPS16 || tsize <= 32767))
-       {
-         rtx adjustment_rtx, insn, dwarf_pattern;
+  mode_index = (int) mode;
 
-         if (tsize > 32767)
-           {
-             adjustment_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
-             emit_move_insn (adjustment_rtx, tsize_rtx);
-           }
-         else
-           adjustment_rtx = tsize_rtx;
+  if (TREE_CODE (type) == INTEGER_TYPE && TYPE_UNSIGNED (type))
+    mode_index += MAX_MACHINE_MODE;
 
-         if (Pmode == DImode)
-           insn = emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                         adjustment_rtx));
-         else
-           insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                         adjustment_rtx));
+  if (types[mode_index] == NULL_TREE)
+    types[mode_index] = build_vector_type_for_mode (type, mode);
+  return types[mode_index];
+}
 
-         dwarf_pattern = gen_rtx_SET (Pmode, stack_pointer_rtx,
-                                      plus_constant (stack_pointer_rtx,
-                                                     -tsize));
+/* Return a type for 'const volatile void *'.  */
 
-         mips_annotate_frame_insn (insn, dwarf_pattern);
-       }
+static tree
+mips_build_cvpointer_type (void)
+{
+  static tree cache;
 
-      if (! mips_entry)
-       save_restore_insns (1, tmp_rtx, tsize, (FILE *)0);
-      else if (reg_18_save != NULL_RTX)
-       emit_insn (reg_18_save);
+  if (cache == NULL_TREE)
+    cache = build_pointer_type (build_qualified_type
+                               (void_type_node,
+                                TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE));
+  return cache;
+}
 
-      if ((!TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
-         && TARGET_MIPS16
-         && tsize > 32767)
-       {
-         rtx reg_rtx;
-
-         if (!frame_pointer_needed)
-           abort ();
-
-         reg_rtx = gen_rtx (REG, Pmode, 3);
-         emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
-         emit_move_insn (reg_rtx, tsize_rtx);
-         if (Pmode == DImode)
-           emit_insn (gen_subdi3 (hard_frame_pointer_rtx,
-                                  hard_frame_pointer_rtx,
-                                  reg_rtx));
-         else
-           emit_insn (gen_subsi3 (hard_frame_pointer_rtx,
-                                  hard_frame_pointer_rtx,
-                                  reg_rtx));
-         emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
-       }
+/* Source-level argument types.  */
+#define MIPS_ATYPE_VOID void_type_node
+#define MIPS_ATYPE_INT integer_type_node
+#define MIPS_ATYPE_POINTER ptr_type_node
+#define MIPS_ATYPE_CVPOINTER mips_build_cvpointer_type ()
 
-      if (frame_pointer_needed)
-       {
-          rtx insn = 0;
-
-         /* On the mips16, we encourage the use of unextended
-             instructions when using the frame pointer by pointing the
-             frame pointer ahead of the argument space allocated on
-             the stack.  */
-         if ((! TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
-             && TARGET_MIPS16
-             && tsize > 32767)
-           {
-             /* In this case, we have already copied the stack
-                 pointer into the frame pointer, above.  We need only
-                 adjust for the outgoing argument size.  */
-             if (current_function_outgoing_args_size != 0)
-               {
-                 rtx incr = GEN_INT (current_function_outgoing_args_size);
-                 if (Pmode == DImode)
-                   insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
-                                                  hard_frame_pointer_rtx,
-                                                  incr));
-                 else
-                   insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
-                                                  hard_frame_pointer_rtx,
-                                                  incr));
-               }
-           }
-         else if (TARGET_MIPS16 && current_function_outgoing_args_size != 0)
-           {
-             rtx incr = GEN_INT (current_function_outgoing_args_size);
-             if (Pmode == DImode)
-               insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
-                                              stack_pointer_rtx,
-                                              incr));
-             else
-               insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
-                                              stack_pointer_rtx,
-                                              incr));
-           }
-         else if (Pmode == DImode)
-           insn = emit_insn (gen_movdi (hard_frame_pointer_rtx,
-                                        stack_pointer_rtx));
-         else
-           insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
-                                        stack_pointer_rtx));
+/* Standard mode-based argument types.  */
+#define MIPS_ATYPE_UQI unsigned_intQI_type_node
+#define MIPS_ATYPE_SI intSI_type_node
+#define MIPS_ATYPE_USI unsigned_intSI_type_node
+#define MIPS_ATYPE_DI intDI_type_node
+#define MIPS_ATYPE_UDI unsigned_intDI_type_node
+#define MIPS_ATYPE_SF float_type_node
+#define MIPS_ATYPE_DF double_type_node
 
-         if (insn)
-           RTX_FRAME_RELATED_P (insn) = 1;
-       }
+/* Vector argument types.  */
+#define MIPS_ATYPE_V2SF mips_builtin_vector_type (float_type_node, V2SFmode)
+#define MIPS_ATYPE_V2HI mips_builtin_vector_type (intHI_type_node, V2HImode)
+#define MIPS_ATYPE_V2SI mips_builtin_vector_type (intSI_type_node, V2SImode)
+#define MIPS_ATYPE_V4QI mips_builtin_vector_type (intQI_type_node, V4QImode)
+#define MIPS_ATYPE_V4HI mips_builtin_vector_type (intHI_type_node, V4HImode)
+#define MIPS_ATYPE_V8QI mips_builtin_vector_type (intQI_type_node, V8QImode)
+#define MIPS_ATYPE_UV2SI                                       \
+  mips_builtin_vector_type (unsigned_intSI_type_node, V2SImode)
+#define MIPS_ATYPE_UV4HI                                       \
+  mips_builtin_vector_type (unsigned_intHI_type_node, V4HImode)
+#define MIPS_ATYPE_UV8QI                                       \
+  mips_builtin_vector_type (unsigned_intQI_type_node, V8QImode)
 
-      if (TARGET_ABICALLS && (mips_abi != ABI_32 && mips_abi != ABI_O64))
-       emit_insn (gen_loadgp (XEXP (DECL_RTL (current_function_decl), 0),
-                              gen_rtx_REG (DImode, 25)));
-    }
+/* MIPS_FTYPE_ATYPESN takes N MIPS_FTYPES-like type codes and lists
+   their associated MIPS_ATYPEs.  */
+#define MIPS_FTYPE_ATYPES1(A, B) \
+  MIPS_ATYPE_##A, MIPS_ATYPE_##B
 
-  /* If we are profiling, make sure no instructions are scheduled before
-     the call to mcount.  */
+#define MIPS_FTYPE_ATYPES2(A, B, C) \
+  MIPS_ATYPE_##A, MIPS_ATYPE_##B, MIPS_ATYPE_##C
 
-  if (current_function_profile)
-    emit_insn (gen_blockage ());
+#define MIPS_FTYPE_ATYPES3(A, B, C, D) \
+  MIPS_ATYPE_##A, MIPS_ATYPE_##B, MIPS_ATYPE_##C, MIPS_ATYPE_##D
+
+#define MIPS_FTYPE_ATYPES4(A, B, C, D, E) \
+  MIPS_ATYPE_##A, MIPS_ATYPE_##B, MIPS_ATYPE_##C, MIPS_ATYPE_##D, \
+  MIPS_ATYPE_##E
+
+/* Return the function type associated with function prototype TYPE.  */
+
+static tree
+mips_build_function_type (enum mips_function_type type)
+{
+  static tree types[(int) MIPS_MAX_FTYPE_MAX];
+
+  if (types[(int) type] == NULL_TREE)
+    switch (type)
+      {
+#define DEF_MIPS_FTYPE(NUM, ARGS)                                      \
+  case MIPS_FTYPE_NAME##NUM ARGS:                                      \
+    types[(int) type]                                                  \
+      = build_function_type_list (MIPS_FTYPE_ATYPES##NUM ARGS,         \
+                                 NULL_TREE);                           \
+    break;
+#include "config/mips/mips-ftypes.def"
+#undef DEF_MIPS_FTYPE
+      default:
+       gcc_unreachable ();
+      }
+
+  return types[(int) type];
 }
-\f
-/* Do any necessary cleanup after a function to restore stack, frame,
-   and regs.  */
 
-#define RA_MASK BITMASK_HIGH   /* 1 << 31 */
-#define PIC_OFFSET_TABLE_MASK (1 << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
+/* Implement TARGET_INIT_BUILTINS.  */
 
 static void
-mips_output_function_epilogue (file, size)
-     FILE *file ATTRIBUTE_UNUSED;
-     HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+mips_init_builtins (void)
 {
-  const char *fnname = "";     /* FIXME: Correct initialisation?  */
-
-#ifndef 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);
+  const struct mips_builtin_description *d;
+  unsigned int i;
 
-  if (!flag_inhibit_size_directive)
+  /* Iterate through all of the bdesc arrays, initializing all of the
+     builtin functions.  */
+  for (i = 0; i < ARRAY_SIZE (mips_builtins); i++)
     {
-      fputs ("\t.end\t", file);
-      assemble_name (file, fnname);
-      fputs ("\n", file);
+      d = &mips_builtins[i];
+      if (d->avail ())
+       add_builtin_function (d->name,
+                             mips_build_function_type (d->function_type),
+                             i, BUILT_IN_MD, NULL, NULL);
     }
-#endif
+}
 
-  if (TARGET_STATS)
-    {
-      int num_gp_regs = current_frame_info.gp_reg_size / 4;
-      int num_fp_regs = current_frame_info.fp_reg_size / 8;
-      int num_regs = num_gp_regs + num_fp_regs;
-      const char *name = fnname;
+/* Take argument ARGNO from EXP's argument list and convert it into a
+   form suitable for input operand OPNO of instruction ICODE.  Return the
+   value.  */
 
-      if (name[0] == '*')
-       name++;
+static rtx
+mips_prepare_builtin_arg (enum insn_code icode,
+                         unsigned int opno, tree exp, unsigned int argno)
+{
+  tree arg;
+  rtx value;
+  enum machine_mode mode;
 
-      dslots_load_total += num_regs;
+  arg = CALL_EXPR_ARG (exp, argno);
+  value = expand_normal (arg);
+  mode = insn_data[icode].operand[opno].mode;
+  if (!insn_data[icode].operand[opno].predicate (value, mode))
+    {
+      /* We need to get the mode from ARG for two reasons:
 
-      fprintf (stderr,
-              "%-20s fp=%c leaf=%c alloca=%c setjmp=%c stack=%4ld arg=%3d reg=%2d/%d delay=%3d/%3dL %3d/%3dJ refs=%3d/%3d/%3d",
-              name, frame_pointer_needed ? 'y' : 'n',
-              (current_frame_info.mask & RA_MASK) != 0 ? 'n' : 'y',
-              current_function_calls_alloca ? 'y' : 'n',
-              current_function_calls_setjmp ? 'y' : 'n',
-              current_frame_info.total_size,
-              current_function_outgoing_args_size, num_gp_regs, num_fp_regs,
-              dslots_load_total, dslots_load_filled,
-              dslots_jump_total, dslots_jump_filled,
-              num_refs[0], num_refs[1], num_refs[2]);
+          - to cope with address operands, where MODE is the mode of the
+            memory, rather than of VALUE itself.
 
-      if (HALF_PIC_NUMBER_PTRS > prev_half_pic_ptrs)
-       {
-         fprintf (stderr,
-                  " half-pic=%3d", HALF_PIC_NUMBER_PTRS - prev_half_pic_ptrs);
-         prev_half_pic_ptrs = HALF_PIC_NUMBER_PTRS;
-       }
+          - to cope with special predicates like pmode_register_operand,
+            where MODE is VOIDmode.  */
+      value = copy_to_mode_reg (TYPE_MODE (TREE_TYPE (arg)), value);
 
-      if (HALF_PIC_NUMBER_REFS > prev_half_pic_refs)
+      /* Check the predicate again.  */
+      if (!insn_data[icode].operand[opno].predicate (value, mode))
        {
-         fprintf (stderr,
-                  " pic-ref=%3d", HALF_PIC_NUMBER_REFS - prev_half_pic_refs);
-         prev_half_pic_refs = HALF_PIC_NUMBER_REFS;
+         error ("invalid argument to built-in function");
+         return const0_rtx;
        }
-
-      fputc ('\n', stderr);
     }
 
-  /* Reset state info for each function.  */
-  inside_function = 0;
-  ignore_line_number = 0;
-  dslots_load_total = 0;
-  dslots_jump_total = 0;
-  dslots_load_filled = 0;
-  dslots_jump_filled = 0;
-  num_refs[0] = 0;
-  num_refs[1] = 0;
-  num_refs[2] = 0;
-  mips_load_reg = 0;
-  mips_load_reg2 = 0;
-  current_frame_info = zero_frame_info;
+  return value;
+}
 
-  while (string_constants != NULL)
-    {
-      struct string_constant *next;
+/* Return an rtx suitable for output operand OP of instruction ICODE.
+   If TARGET is non-null, try to use it where possible.  */
 
-      next = string_constants->next;
-      free (string_constants);
-      string_constants = next;
-    }
+static rtx
+mips_prepare_builtin_target (enum insn_code icode, unsigned int op, rtx target)
+{
+  enum machine_mode mode;
 
-  /* Restore the output file if optimizing the GP (optimizing the GP causes
-     the text to be diverted to a tempfile, so that data decls come before
-     references to the data).  */
-  if (TARGET_FILE_SWITCHING)
-    {
-      asm_out_file = asm_out_data_file;
-      data_section ();
-    }
+  mode = insn_data[icode].operand[op].mode;
+  if (target == 0 || !insn_data[icode].operand[op].predicate (target, mode))
+    target = gen_reg_rtx (mode);
+
+  return target;
 }
-\f
-/* Expand the epilogue into a bunch of separate insns.  */
 
-void
-mips_expand_epilogue ()
+/* Expand a MIPS_BUILTIN_DIRECT or MIPS_BUILTIN_DIRECT_NO_TARGET function;
+   HAS_TARGET_P says which.  EXP is the CALL_EXPR that calls the function
+   and ICODE is the code of the associated .md pattern.  TARGET, if nonnull,
+   suggests a good place to put the result.  */
+
+static rtx
+mips_expand_builtin_direct (enum insn_code icode, rtx target, tree exp,
+                           bool has_target_p)
 {
-  HOST_WIDE_INT tsize = current_frame_info.total_size;
-  rtx tsize_rtx = GEN_INT (tsize);
-  rtx tmp_rtx = (rtx)0;
+  rtx ops[MAX_RECOG_OPERANDS];
+  int opno, argno;
 
-  if (mips_can_use_return_insn ())
+  /* Map any target to operand 0.  */
+  opno = 0;
+  if (has_target_p)
     {
-      emit_insn (gen_return ());
-      return;
+      target = mips_prepare_builtin_target (icode, opno, target);
+      ops[opno] = target;
+      opno++;
     }
 
-  if (mips_entry && ! mips_can_use_return_insn ())
-    tsize -= 32;
+  /* Map the arguments to the other operands.  The n_operands value
+     for an expander includes match_dups and match_scratches as well as
+     match_operands, so n_operands is only an upper bound on the number
+     of arguments to the expander function.  */
+  gcc_assert (opno + call_expr_nargs (exp) <= insn_data[icode].n_operands);
+  for (argno = 0; argno < call_expr_nargs (exp); argno++, opno++)
+    ops[opno] = mips_prepare_builtin_arg (icode, opno, exp, argno);
 
-  if (tsize > 32767 && ! TARGET_MIPS16)
+  switch (opno)
     {
-      tmp_rtx = gen_rtx_REG (Pmode, MIPS_TEMP1_REGNUM);
-      emit_move_insn (tmp_rtx, tsize_rtx);
-      tsize_rtx = tmp_rtx;
+    case 2:
+      emit_insn (GEN_FCN (icode) (ops[0], ops[1]));
+      break;
+
+    case 3:
+      emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2]));
+      break;
+
+    case 4:
+      emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3]));
+      break;
+
+    default:
+      gcc_unreachable ();
     }
+  return target;
+}
 
-  if (tsize > 0)
-    {
-      long orig_tsize = tsize;
+/* Expand a __builtin_mips_movt_*_ps or __builtin_mips_movf_*_ps
+   function; TYPE says which.  EXP is the CALL_EXPR that calls the
+   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 (frame_pointer_needed)
-       {
-         emit_insn (gen_blockage ());
+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;
 
-         /* On the mips16, the frame pointer is offset from the stack
-             pointer by current_function_outgoing_args_size.  We
-             account for that by changing tsize.  Note that this can
-             actually make tsize negative.  */
-         if (TARGET_MIPS16)
-           {
-             tsize -= current_function_outgoing_args_size;
+  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)));
 
-             /* If we have a large frame, it's easier to add to $6
-                 than to $sp, since the mips16 has no instruction to
-                 add a register to $sp.  */
-             if (orig_tsize > 32767)
-               {
-                 rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6);
-
-                 emit_move_insn (g6_rtx, GEN_INT (tsize));
-                 if (Pmode == DImode)
-                   emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
-                                          hard_frame_pointer_rtx,
-                                          g6_rtx));
-                 else
-                   emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
-                                          hard_frame_pointer_rtx,
-                                          g6_rtx));
-                 tsize = 0;
-               }
+  icode = CODE_FOR_mips_cond_move_tf_ps;
+  target = mips_prepare_builtin_target (icode, 0, target);
+  if (type == MIPS_BUILTIN_MOVT)
+    {
+      op1 = mips_prepare_builtin_arg (icode, 2, exp, 2);
+      op0 = mips_prepare_builtin_arg (icode, 1, exp, 3);
+    }
+  else
+    {
+      op0 = mips_prepare_builtin_arg (icode, 1, exp, 2);
+      op1 = mips_prepare_builtin_arg (icode, 2, exp, 3);
+    }
+  emit_insn (gen_mips_cond_move_tf_ps (target, op0, op1, cmp_result));
+  return target;
+}
 
-             if (tsize && tsize != orig_tsize)
-               tsize_rtx = GEN_INT (tsize);
-           }
+/* Move VALUE_IF_TRUE into TARGET if CONDITION is true; move VALUE_IF_FALSE
+   into TARGET otherwise.  Return TARGET.  */
 
-         if (Pmode == DImode)
-           emit_insn (gen_movdi (stack_pointer_rtx, hard_frame_pointer_rtx));
-         else
-           emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
-       }
+static rtx
+mips_builtin_branch_and_move (rtx condition, rtx target,
+                             rtx value_if_true, rtx value_if_false)
+{
+  rtx true_label, done_label;
 
-      /* The GP/PIC register is implicitly used by all SYMBOL_REFs, so if we
-        are going to restore it, then we must emit a blockage insn to
-        prevent the scheduler from moving the restore out of the epilogue.  */
-      else if (TARGET_ABICALLS && mips_abi != ABI_32 && mips_abi != ABI_O64
-              && (current_frame_info.mask
-                  & (1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))))
-       emit_insn (gen_blockage ());
+  true_label = gen_label_rtx ();
+  done_label = gen_label_rtx ();
 
-      save_restore_insns (0, tmp_rtx, orig_tsize, (FILE *)0);
+  /* First assume that CONDITION is false.  */
+  mips_emit_move (target, value_if_false);
 
-      /* In mips16 mode with a large frame, we adjust the stack
-         pointer before restoring the registers.  In this case, we
-         should always be using a frame pointer, so everything should
-         have been handled above.  */
-      if (tsize > 32767 && TARGET_MIPS16)
-       abort ();
+  /* 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 ();
 
-      if (current_function_calls_eh_return)
-       {
-         rtx eh_ofs = EH_RETURN_STACKADJ_RTX;
-         if (Pmode == DImode)
-           emit_insn (gen_adddi3 (eh_ofs, eh_ofs, tsize_rtx));
-         else
-           emit_insn (gen_addsi3 (eh_ofs, eh_ofs, tsize_rtx));
-         tsize_rtx = eh_ofs;
-       }
+  /* Fix TARGET if CONDITION is true.  */
+  emit_label (true_label);
+  mips_emit_move (target, value_if_true);
 
-      emit_insn (gen_blockage ());
+  emit_label (done_label);
+  return target;
+}
 
-      if (tsize != 0 || current_function_calls_eh_return)
-       {
-         if (!TARGET_MIPS16)
-           {
-             if (Pmode == DImode)
-               emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                      tsize_rtx));
-             else
-               emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                      tsize_rtx));
-           }
-         else
-           {
-             /* We need to work around not being able to add a register
-                to the stack pointer directly. Use register $6 as an
-                intermediate step.  */
+/* Expand a comparison built-in function of type BUILTIN_TYPE.  EXP is
+   the CALL_EXPR that calls the function, ICODE is the code of the
+   comparison instruction, and COND is the condition it should test.
+   TARGET, if nonnull, suggests a good place to put the boolean result.  */
 
-             rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6);
+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, args[MAX_RECOG_OPERANDS];
+  int argno;
 
-             if (Pmode == DImode)
-               {
-                 emit_insn (gen_movdi (g6_rtx, stack_pointer_rtx));
-                 emit_insn (gen_adddi3 (g6_rtx, g6_rtx, tsize_rtx));
-                 emit_insn (gen_movdi (stack_pointer_rtx, g6_rtx));
-               }
-             else
-               {
-                 emit_insn (gen_movsi (g6_rtx, stack_pointer_rtx));
-                 emit_insn (gen_addsi3 (g6_rtx, g6_rtx, tsize_rtx));
-                 emit_insn (gen_movsi (stack_pointer_rtx, g6_rtx));
-               }
-           }
+  if (target == 0 || GET_MODE (target) != SImode)
+    target = gen_reg_rtx (SImode);
 
-       }
-    }
+  /* The instruction should have a target operand, an operand for each
+     argument, and an operand for COND.  */
+  gcc_assert (call_expr_nargs (exp) + 2 == insn_data[icode].n_operands);
 
-  /* The mips16 loads the return address into $7, not $31.  */
-  if (TARGET_MIPS16 && (current_frame_info.mask & RA_MASK) != 0)
-    emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode,
-                                                 GP_REG_FIRST + 7)));
-  else
-    emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode,
-                                                 GP_REG_FIRST + 31)));
-}
-\f
-/* Return nonzero if this function is known to have a null epilogue.
-   This allows the optimizer to omit jumps to jumps if no stack
-   was created.  */
+  /* Prepare the operands to the comparison.  */
+  cmp_result = mips_prepare_builtin_target (icode, 0, 0);
+  for (argno = 0; argno < call_expr_nargs (exp); argno++)
+    args[argno] = mips_prepare_builtin_arg (icode, argno + 1, exp, argno);
 
-int
-mips_can_use_return_insn ()
-{
-  if (! reload_completed)
-    return 0;
+  switch (insn_data[icode].n_operands)
+    {
+    case 4:
+      emit_insn (GEN_FCN (icode) (cmp_result, args[0], args[1],
+                                 GEN_INT (cond)));
+      break;
 
-  if (regs_ever_live[31] || current_function_profile)
-    return 0;
+    case 6:
+      emit_insn (GEN_FCN (icode) (cmp_result, args[0], args[1],
+                                 args[2], args[3], GEN_INT (cond)));
+      break;
 
-  /* In mips16 mode, a function which returns a floating point value
-     needs to arrange to copy the return value into the floating point
-     registers.  */
-  if (TARGET_MIPS16
-      && mips16_hard_float
-      && ! aggregate_value_p (DECL_RESULT (current_function_decl))
-      && (GET_MODE_CLASS (DECL_MODE (DECL_RESULT (current_function_decl)))
-         == MODE_FLOAT)
-      && (! TARGET_SINGLE_FLOAT
-         || (GET_MODE_SIZE (DECL_MODE (DECL_RESULT (current_function_decl)))
-             <= 4)))
-    return 0;
+    default:
+      gcc_unreachable ();
+    }
+
+  /* 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);
 
-  if (current_frame_info.initialized)
-    return current_frame_info.total_size == 0;
+    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);
 
-  return compute_frame_size (get_frame_size ()) == 0;
+    default:
+      condition = gen_rtx_NE (VOIDmode, cmp_result, const0_rtx);
+      return mips_builtin_branch_and_move (condition, target,
+                                          const1_rtx, const0_rtx);
+    }
 }
-\f
-/* Returns non-zero if X contains a SYMBOL_REF.  */
 
-static int
-symbolic_expression_p (x)
-     rtx x;
+/* Expand a bposge built-in function of type BUILTIN_TYPE.  TARGET,
+   if nonnull, suggests a good place to put the boolean result.  */
+
+static rtx
+mips_expand_builtin_bposge (enum mips_builtin_type builtin_type, rtx target)
 {
-  if (GET_CODE (x) == SYMBOL_REF)
-    return 1;
+  rtx condition, cmp_result;
+  int cmp_value;
 
-  if (GET_CODE (x) == CONST)
-    return symbolic_expression_p (XEXP (x, 0));
+  if (target == 0 || GET_MODE (target) != SImode)
+    target = gen_reg_rtx (SImode);
 
-  if (GET_RTX_CLASS (GET_CODE (x)) == '1')
-    return symbolic_expression_p (XEXP (x, 0));
+  cmp_result = gen_rtx_REG (CCDSPmode, CCDSP_PO_REGNUM);
 
-  if (GET_RTX_CLASS (GET_CODE (x)) == 'c'
-      || GET_RTX_CLASS (GET_CODE (x)) == '2')
-    return (symbolic_expression_p (XEXP (x, 0))
-           || symbolic_expression_p (XEXP (x, 1)));
+  if (builtin_type == MIPS_BUILTIN_BPOSGE32)
+    cmp_value = 32;
+  else
+    gcc_assert (0);
 
-  return 0;
+  condition = gen_rtx_GE (VOIDmode, cmp_result, GEN_INT (cmp_value));
+  return mips_builtin_branch_and_move (condition, target,
+                                      const1_rtx, const0_rtx);
 }
 
-/* Choose the section to use for the constant rtx expression X that has
-   mode MODE.  */
+/* Implement TARGET_EXPAND_BUILTIN.  */
 
-void
-mips_select_rtx_section (mode, x)
-     enum machine_mode mode;
-     rtx x ATTRIBUTE_UNUSED;
-{
+static rtx
+mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
+                    enum machine_mode mode, int ignore)
+{
+  tree fndecl;
+  unsigned int fcode, avail;
+  const struct mips_builtin_description *d;
+
+  fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+  fcode = DECL_FUNCTION_CODE (fndecl);
+  gcc_assert (fcode < ARRAY_SIZE (mips_builtins));
+  d = &mips_builtins[fcode];
+  avail = d->avail ();
+  gcc_assert (avail != 0);
   if (TARGET_MIPS16)
     {
-      /* In mips16 mode, the constant table always goes in the same section
-         as the function, so that constants can be loaded using PC relative
-         addressing.  */
-      function_section (current_function_decl);
-    }
-  else if (TARGET_EMBEDDED_DATA)
-    {
-      /* For embedded applications, always put constants in read-only data,
-        in order to reduce RAM usage.  */
-      READONLY_DATA_SECTION ();
+      error ("built-in function %qs not supported for MIPS16",
+            IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+      return ignore ? const0_rtx : CONST0_RTX (mode);
     }
-  else
+  switch (d->builtin_type)
     {
-      /* For hosted applications, always put constants in small data if
-        possible, as this gives the best performance.  */
-
-      if (GET_MODE_SIZE (mode) <= (unsigned) mips_section_threshold
-         && mips_section_threshold > 0)
-       SMALL_DATA_SECTION ();
-      else if (flag_pic && symbolic_expression_p (x))
-       /* Any expression involving a SYMBOL_REF might need a run-time
-          relocation.  (The symbol might be defined in a shared
-          library loaded at an unexpected base address.)  So, we must
-          put such expressions in the data segment (which is
-          writable), rather than the text segment (which is
-          read-only).  */
-       data_section ();
-      else
-       READONLY_DATA_SECTION ();
-    }
-}
+    case MIPS_BUILTIN_DIRECT:
+      return mips_expand_builtin_direct (d->icode, target, exp, true);
 
-/* Choose the section to use for DECL.  RELOC is true if its value contains
-   any relocatable expression.
+    case MIPS_BUILTIN_DIRECT_NO_TARGET:
+      return mips_expand_builtin_direct (d->icode, target, exp, false);
 
-   Some of the logic used here needs to be replicated in
-   ENCODE_SECTION_INFO in mips.h so that references to these symbols
-   are done correctly.  Specifically, at least all symbols assigned
-   here to rom (.text and/or .rodata) must not be referenced via
-   ENCODE_SECTION_INFO with %gprel, as the rom might be too far away.
+    case MIPS_BUILTIN_MOVT:
+    case MIPS_BUILTIN_MOVF:
+      return mips_expand_builtin_movtf (d->builtin_type, d->icode,
+                                       d->cond, target, exp);
 
-   If you need to make a change here, you probably should check
-   ENCODE_SECTION_INFO to see if it needs a similar change.  */
+    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 (d->builtin_type, d->icode,
+                                         d->cond, target, exp);
 
-void
-mips_select_section (decl, reloc)
-     tree decl;
-     int reloc;
-{
-  int size = int_size_in_bytes (TREE_TYPE (decl));
-
-  if ((TARGET_EMBEDDED_PIC || TARGET_MIPS16)
-      && TREE_CODE (decl) == STRING_CST
-      && !flag_writable_strings)
-    /* For embedded position independent code, put constant strings in the
-       text section, because the data section is limited to 64K in size.
-       For mips16 code, put strings in the text section so that a PC
-       relative load instruction can be used to get their address.  */
-    text_section ();
-  else if (TARGET_EMBEDDED_DATA)
-    {
-      /* For embedded applications, always put an object in read-only data
-        if possible, in order to reduce RAM usage.  */
-
-      if (((TREE_CODE (decl) == VAR_DECL
-           && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
-           && DECL_INITIAL (decl)
-           && (DECL_INITIAL (decl) == error_mark_node
-               || TREE_CONSTANT (DECL_INITIAL (decl))))
-          /* Deal with calls from output_constant_def_contents.  */
-          || (TREE_CODE (decl) != VAR_DECL
-              && (TREE_CODE (decl) != STRING_CST
-                  || !flag_writable_strings)))
-         && ! (flag_pic && reloc))
-       READONLY_DATA_SECTION ();
-      else if (size > 0 && size <= mips_section_threshold)
-       SMALL_DATA_SECTION ();
-      else
-       data_section ();
-    }
-  else
-    {
-      /* For hosted applications, always put an object in small data if
-        possible, as this gives the best performance.  */
-
-      if (size > 0 && size <= mips_section_threshold)
-       SMALL_DATA_SECTION ();
-      else if (((TREE_CODE (decl) == VAR_DECL
-                && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
-                && DECL_INITIAL (decl)
-                && (DECL_INITIAL (decl) == error_mark_node
-                    || TREE_CONSTANT (DECL_INITIAL (decl))))
-               /* Deal with calls from output_constant_def_contents.  */
-               || (TREE_CODE (decl) != VAR_DECL
-                   && (TREE_CODE (decl) != STRING_CST
-                       || !flag_writable_strings)))
-              && ! (flag_pic && reloc))
-       READONLY_DATA_SECTION ();
-      else
-       data_section ();
+    case MIPS_BUILTIN_BPOSGE32:
+      return mips_expand_builtin_bposge (d->builtin_type, target);
     }
+  gcc_unreachable ();
 }
 \f
-/* Return register to use for a function return value with VALTYPE for
-   function FUNC.  MODE is used instead of VALTYPE for LIBCALLs.  */
+/* 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;
+};
 
-rtx
-mips_function_value (valtype, func, mode)
-     tree valtype;
-     tree func ATTRIBUTE_UNUSED;
-     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
+mips16_add_constant (struct mips16_constant_pool *pool,
+                    rtx value, enum machine_mode mode)
 {
-  int reg = GP_RETURN;
-  enum mode_class mclass;
-  int unsignedp = 1;
+  struct mips16_constant **p, *c;
+  bool first_of_size_p;
 
-  if (valtype)
+  /* 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.
+
+     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)
     {
-      mode = TYPE_MODE (valtype);
-      unsignedp = TREE_UNSIGNED (valtype);
+      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;
+    }
+
+  /* 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 LWPC, ADDIUPC and DADDIUPC, the base PC value is the address
+       of the instruction with the lowest two bits clear.  The base PC
+       value for LDPC has the lowest three bits clear.  Assume the worst
+       case here; namely that the PC-relative instruction occupies the
+       last 2 bytes in an aligned word.  */
+    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 = XNEW (struct mips16_constant);
+  c->value = value;
+  c->mode = mode;
+  c->label = gen_label_rtx ();
+  c->next = *p;
+  *p = c;
+
+  return c->label;
+}
+
+/* Output constant VALUE after instruction INSN and return the last
+   instruction emitted.  MODE is the mode of the constant.  */
 
-      /* Since we define PROMOTE_FUNCTION_RETURN, we must promote
-        the mode just as PROMOTE_MODE does.  */
-      mode = promote_mode (valtype, mode, &unsignedp, 1);
+static rtx
+mips16_emit_constants_1 (enum machine_mode mode, rtx value, rtx insn)
+{
+  if (SCALAR_INT_MODE_P (mode) || ALL_SCALAR_FIXED_POINT_MODE_P (mode))
+    {
+      rtx size = GEN_INT (GET_MODE_SIZE (mode));
+      return emit_insn_after (gen_consttable_int (value, size), insn);
     }
-  mclass = GET_MODE_CLASS (mode);
 
-  if (mclass == MODE_FLOAT)
+  if (SCALAR_FLOAT_MODE_P (mode))
+    return emit_insn_after (gen_consttable_float (value), insn);
+
+  if (VECTOR_MODE_P (mode))
     {
-      if (TARGET_SINGLE_FLOAT
-         && (mclass == MODE_FLOAT
-             ? GET_MODE_SIZE (mode) > 4 : GET_MODE_SIZE (mode) / 2 > 4))
-       reg = GP_RETURN;
-      else
-       reg = FP_RETURN;
+      int i;
+
+      for (i = 0; i < CONST_VECTOR_NUNITS (value); i++)
+       insn = mips16_emit_constants_1 (GET_MODE_INNER (mode),
+                                       CONST_VECTOR_ELT (value, i), insn);
+      return insn;
     }
 
-  else if (mclass == MODE_COMPLEX_FLOAT)
+  gcc_unreachable ();
+}
+
+/* Dump out the constants in CONSTANTS after INSN.  */
+
+static void
+mips16_emit_constants (struct mips16_constant *constants, rtx insn)
+{
+  struct mips16_constant *c, *next;
+  int align;
+
+  align = 0;
+  for (c = constants; c != NULL; c = next)
     {
-      if (TARGET_FLOAT64)
-       reg = FP_RETURN;
-      else if (mode == SCmode)
+      /* If necessary, increase the alignment of PC.  */
+      if (align < GET_MODE_SIZE (c->mode))
        {
-         /* When FP registers are 32 bits, we can't directly reference
-            the odd numbered ones, so let's make a pair of evens.  */
-
-         enum machine_mode cmode = GET_MODE_INNER (mode);
-
-         return gen_rtx_PARALLEL
-           (VOIDmode,
-            gen_rtvec (2,
-                       gen_rtx_EXPR_LIST (VOIDmode,
-                                          gen_rtx_REG (cmode,
-                                                       FP_RETURN),
-                                          GEN_INT (0)),
-                       gen_rtx_EXPR_LIST (VOIDmode,
-                                          gen_rtx_REG (cmode,
-                                                       FP_RETURN + 2),
-                                          GEN_INT (4))));
+         int align_log = floor_log2 (GET_MODE_SIZE (c->mode));
+         insn = emit_insn_after (gen_align (GEN_INT (align_log)), insn);
        }
-      else
-       reg = FP_RETURN;
+      align = GET_MODE_SIZE (c->mode);
+
+      insn = emit_label_after (c->label, insn);
+      insn = mips16_emit_constants_1 (c->mode, c->value, insn);
+
+      next = c->next;
+      free (c);
     }
 
-  else if (valtype && TREE_CODE (valtype) == RECORD_TYPE
-          && mips_abi != ABI_32
-          && mips_abi != ABI_O64
-          && mips_abi != ABI_EABI)
-    {
-      /* A struct with only one or two floating point fields is returned in
-        the floating point registers.  */
-      tree field, fields[2];
-      int i;
+  emit_barrier_after (insn);
+}
 
-      for (i = 0, field = TYPE_FIELDS (valtype); field;
-          field = TREE_CHAIN (field))
-       {
-         if (TREE_CODE (field) != FIELD_DECL)
-           continue;
+/* Return the length of instruction INSN.  */
 
-         if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE || i >= 2)
-           break;
+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);
+}
 
-         fields[i++] = field;
-       }
+/* 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.  */
 
-      /* Must check i, so that we reject structures with no elements.  */
-      if (! field)
-       {
-         if (i == 1)
-           {
-             /* The structure has DImode, but we don't allow DImode values
-                in FP registers, so we use a PARALLEL even though it isn't
-                strictly necessary.  */
-             enum machine_mode field_mode = TYPE_MODE (TREE_TYPE (fields[0]));
-
-             return gen_rtx_PARALLEL
-               (mode,
-                gen_rtvec (1,
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (field_mode,
-                                                           FP_RETURN),
-                                              const0_rtx)));
-           }
+static void
+mips16_rewrite_pool_constant (struct mips16_constant_pool *pool, rtx *x)
+{
+  rtx base, offset, label;
 
-         else if (i == 2)
-           {
-             enum machine_mode first_mode
-               = TYPE_MODE (TREE_TYPE (fields[0]));
-             enum machine_mode second_mode
-               = TYPE_MODE (TREE_TYPE (fields[1]));
-             HOST_WIDE_INT first_offset = int_byte_position (fields[0]);
-             HOST_WIDE_INT second_offset = int_byte_position (fields[1]);
-
-             return gen_rtx_PARALLEL
-               (mode,
-                gen_rtvec (2,
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (first_mode,
-                                                           FP_RETURN),
-                                              GEN_INT (first_offset)),
-                           gen_rtx_EXPR_LIST (VOIDmode,
-                                              gen_rtx_REG (second_mode,
-                                                           FP_RETURN + 2),
-                                              GEN_INT (second_offset))));
-           }
-       }
+  split_const (*x, &base, &offset);
+  if (GET_CODE (base) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (base))
+    {
+      label = mips16_add_constant (pool, get_pool_constant (base),
+                                  get_pool_mode (base));
+      base = gen_rtx_LABEL_REF (Pmode, label);
+      *x = mips_unspec_address_offset (base, offset, SYMBOL_PC_RELATIVE);
     }
-
-  return gen_rtx_REG (mode, reg);
 }
 
-/* The implementation of FUNCTION_ARG_PASS_BY_REFERENCE.  Return
-   nonzero when an argument must be passed by reference.  */
+/* 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.  */
 
-int
-function_arg_pass_by_reference (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
-     enum machine_mode mode;
-     tree type;
-     int named ATTRIBUTE_UNUSED;
+static int
+mips16_rewrite_pool_refs (rtx *x, void *data)
 {
-  int size;
+  struct mips16_rewrite_pool_refs_info *info =
+    (struct mips16_rewrite_pool_refs_info *) data;
 
-  if (mips_abi == ABI_32 || mips_abi == ABI_O64)
-    return 0;
+  if (force_to_mem_operand (*x, Pmode))
+    {
+      rtx mem = force_const_mem (GET_MODE (*x), *x);
+      validate_change (info->insn, x, mem, false);
+    }
 
-  /* We must pass by reference if we would be both passing in registers
-     and the stack.  This is because any subsequent partial arg would be
-     handled incorrectly in this case.
-
-     ??? This is really a kludge.  We should either fix GCC so that such
-     a situation causes an abort and then do something in the MIPS port
-     to prevent it, or add code to function.c to properly handle the case.  */
-  /* ??? cum can be NULL when called from mips_va_arg.  The problem handled
-     here hopefully is not relevant to mips_va_arg.  */
-  if (cum && MUST_PASS_IN_STACK (mode, type)
-      && mips_abi != ABI_MEABI)
-     {
-       /* Don't pass the actual CUM to FUNCTION_ARG, because we would
-         get double copies of any offsets generated for small structs
-         passed in registers.  */
-       CUMULATIVE_ARGS temp;
-       temp = *cum;
-       if (FUNCTION_ARG (temp, mode, type, named) != 0)
-        return 1;
-     }
-
-  /* Otherwise, we only do this if EABI is selected.  */
-  if (mips_abi != ABI_EABI)
-    return 0;
+  if (MEM_P (*x))
+    {
+      mips16_rewrite_pool_constant (info->pool, &XEXP (*x, 0));
+      return -1;
+    }
 
-  /* ??? How should SCmode be handled?  */
-  if (type == NULL_TREE || mode == DImode || mode == DFmode)
-    return 0;
+  if (TARGET_MIPS16_TEXT_LOADS)
+    mips16_rewrite_pool_constant (info->pool, x);
 
-  size = int_size_in_bytes (type);
-  return size == -1 || size > UNITS_PER_WORD;
+  return GET_CODE (*x) == CONST ? -1 : 0;
 }
 
-/* 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.  */
+/* Build MIPS16 constant pools.  */
 
-enum reg_class
-mips_secondary_reload_class (class, mode, x, in_p)
-     enum reg_class class;
-     enum machine_mode mode;
-     rtx x;
-     int in_p;
+static void
+mips16_lay_out_constants (void)
 {
-  enum reg_class gr_regs = TARGET_MIPS16 ? M16_REGS : GR_REGS;
-  int regno = -1;
-  int gp_reg_p;
+  struct mips16_constant_pool pool;
+  struct mips16_rewrite_pool_refs_info info;
+  rtx insn, barrier;
+
+  if (!TARGET_MIPS16_PCREL_LOADS)
+    return;
 
-  if (GET_CODE (x) == SIGN_EXTEND)
+  split_all_insns_noflow ();
+  barrier = 0;
+  memset (&pool, 0, sizeof (pool));
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
-      int off = 0;
+      /* 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);
+       }
 
-      x = XEXP (x, 0);
+      pool.insn_address += mips16_insn_length (insn);
 
-      /* We may be called with reg_renumber NULL from regclass.
-        ??? This is probably a bug.  */
-      if (reg_renumber)
-       regno = true_regnum (x);
-      else
+      if (pool.first != NULL)
        {
-         while (GET_CODE (x) == SUBREG)
+         /* 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.
+
+            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)
            {
-             off += subreg_regno_offset (REGNO (SUBREG_REG (x)),
-                                         GET_MODE (SUBREG_REG (x)),
-                                         SUBREG_BYTE (x),
-                                         GET_MODE (x));
-             x = SUBREG_REG (x);
+             rtx label, jump;
+
+             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;
            }
 
-         if (GET_CODE (x) == REG)
-           regno = REGNO (x) + off;
+         /* 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)
+           {
+             mips16_emit_constants (pool.first, barrier);
+             pool.first = NULL;
+             barrier = 0;
+           }
+         else if (BARRIER_P (insn))
+           barrier = insn;
        }
     }
+  mips16_emit_constants (pool.first, get_last_insn ());
+}
+\f
+/* Return true if it is worth r10k_simplify_address's while replacing
+   an address with X.  We are looking for constants, and for addresses
+   at a known offset from the incoming stack pointer.  */
 
-  else if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
-    regno = true_regnum (x);
+static bool
+r10k_simplified_address_p (rtx x)
+{
+  if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)))
+    x = XEXP (x, 0);
+  return x == virtual_incoming_args_rtx || CONSTANT_P (x);
+}
 
-  gp_reg_p = TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
+/* X is an expression that appears in INSN.  Try to use the UD chains
+   to simplify it, returning the simplified form on success and the
+   original form otherwise.  Replace the incoming value of $sp with
+   virtual_incoming_args_rtx (which should never occur in X otherwise).  */
 
-  /* We always require a general register when copying anything to
-     HILO_REGNUM, except when copying an SImode value from HILO_REGNUM
-     to a general register, or when copying from register 0.  */
-  if (class == HILO_REG && regno != GP_REG_FIRST + 0)
-    return ((! in_p
-            && gp_reg_p
-            && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode))
-           ? NO_REGS : gr_regs);
-  else if (regno == HILO_REGNUM)
-    return ((in_p
-            && class == gr_regs
-            && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode))
-           ? NO_REGS : gr_regs);
+static rtx
+r10k_simplify_address (rtx x, rtx insn)
+{
+  rtx newx, op0, op1, set, def_insn, note;
+  df_ref use, def;
+  struct df_link *defs;
 
-  /* Copying from HI or LO to anywhere other than a general register
-     requires a general register.  */
-  if (class == HI_REG || class == LO_REG || class == MD_REGS)
+  newx = NULL_RTX;
+  if (UNARY_P (x))
     {
-      if (TARGET_MIPS16 && in_p)
-       {
-         /* We can't really copy to HI or LO at all in mips16 mode.  */
-         return M16_REGS;
-       }
-      return gp_reg_p ? NO_REGS : gr_regs;
+      op0 = r10k_simplify_address (XEXP (x, 0), insn);
+      if (op0 != XEXP (x, 0))
+       newx = simplify_gen_unary (GET_CODE (x), GET_MODE (x),
+                                  op0, GET_MODE (XEXP (x, 0)));
     }
-  if (MD_REG_P (regno))
+  else if (BINARY_P (x))
     {
-      if (TARGET_MIPS16 && ! in_p)
-       {
-         /* We can't really copy to HI or LO at all in mips16 mode.  */
-         return M16_REGS;
-       }
-      return class == gr_regs ? NO_REGS : gr_regs;
+      op0 = r10k_simplify_address (XEXP (x, 0), insn);
+      op1 = r10k_simplify_address (XEXP (x, 1), insn);
+      if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
+       newx = simplify_gen_binary (GET_CODE (x), GET_MODE (x), op0, op1);
     }
-
-  /* We can only copy a value to a condition code register from a
-     floating point register, and even then we require a scratch
-     floating point register.  We can only copy a value out of a
-     condition code register into a general register.  */
-  if (class == ST_REGS)
+  else if (GET_CODE (x) == LO_SUM)
     {
-      if (in_p)
-       return FP_REGS;
-      return GP_REG_P (regno) ? NO_REGS : GR_REGS;
+      /* LO_SUMs can be offset from HIGHs, if we know they won't
+        overflow.  See mips_classify_address for the rationale behind
+        the lax check.  */
+      op0 = r10k_simplify_address (XEXP (x, 0), insn);
+      if (GET_CODE (op0) == HIGH)
+       newx = XEXP (x, 1);
     }
-  if (ST_REG_P (regno))
+  else if (REG_P (x))
     {
-      if (! in_p)
-       return FP_REGS;
-      return class == GR_REGS ? NO_REGS : GR_REGS;
-    }
+      /* Uses are recorded by regno_reg_rtx, not X itself.  */
+      use = df_find_use (insn, regno_reg_rtx[REGNO (x)]);
+      gcc_assert (use);
+      defs = DF_REF_CHAIN (use);
 
-  /* In mips16 mode, going between memory and anything but M16_REGS
-     requires an M16_REG.  */
-  if (TARGET_MIPS16)
-    {
-      if (class != M16_REGS && class != M16_NA_REGS)
+      /* Require a single definition.  */
+      if (defs && defs->next == NULL)
        {
-         if (gp_reg_p)
-           return NO_REGS;
-         return M16_REGS;
-       }
-      if (! gp_reg_p)
-       {
-         /* The stack pointer isn't a valid operand to an add instruction,
-            so we need to load it into M16_REGS first.  This can happen as
-            a result of register elimination and form_sum converting
-            (plus reg (plus SP CONST)) to (plus (plus reg SP) CONST).  We
-            need an extra register if the dest is the same as the other
-            register.  In that case, we can't fix the problem by loading SP
-            into the dest first.  */
-         if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == REG
-             && GET_CODE (XEXP (x, 1)) == REG
-             && (XEXP (x, 0) == stack_pointer_rtx
-                 || XEXP (x, 1) == stack_pointer_rtx))
-           return (class == M16_REGS ? M16_NA_REGS : M16_REGS);
-
-         if (class == M16_REGS || class == M16_NA_REGS)
-           return NO_REGS;
-         return M16_REGS;
+         def = defs->ref;
+         if (DF_REF_IS_ARTIFICIAL (def))
+           {
+             /* Replace the incoming value of $sp with
+                virtual_incoming_args_rtx.  */
+             if (x == stack_pointer_rtx
+                 && DF_REF_BB (def) == ENTRY_BLOCK_PTR)
+               newx = virtual_incoming_args_rtx;
+           }
+         else if (dominated_by_p (CDI_DOMINATORS, DF_REF_BB (use),
+                                  DF_REF_BB (def)))
+           {
+             /* Make sure that DEF_INSN is a single set of REG.  */
+             def_insn = DF_REF_INSN (def);
+             if (NONJUMP_INSN_P (def_insn))
+               {
+                 set = single_set (def_insn);
+                 if (set && rtx_equal_p (SET_DEST (set), x))
+                   {
+                     /* Prefer to use notes, since the def-use chains
+                        are often shorter.  */
+                     note = find_reg_equal_equiv_note (def_insn);
+                     if (note)
+                       newx = XEXP (note, 0);
+                     else
+                       newx = SET_SRC (set);
+                     newx = r10k_simplify_address (newx, def_insn);
+                   }
+               }
+           }
        }
     }
-
-  return NO_REGS;
+  if (newx && r10k_simplified_address_p (newx))
+    return newx;
+  return x;
 }
-\f
-/* 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.  */
-
-rtx
-mips16_gp_pseudo_reg ()
-{
-  if (mips16_gp_pseudo_rtx == NULL_RTX)
-    {
-      rtx const_gp;
-      rtx insn, scan;
 
-      mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
-      RTX_UNCHANGING_P (mips16_gp_pseudo_rtx) = 1;
+/* Return true if ADDRESS is known to be an uncached address
+   on R10K systems.  */
 
-      /* We want to initialize this to a value which gcc will believe
-         is constant.  */
-      const_gp = gen_rtx (CONST, Pmode,
-                         gen_rtx (REG, Pmode, GP_REG_FIRST + 28));
+static bool
+r10k_uncached_address_p (unsigned HOST_WIDE_INT address)
+{
+  unsigned HOST_WIDE_INT upper;
 
-      start_sequence ();
-      emit_move_insn (mips16_gp_pseudo_rtx, const_gp);
-      insn = gen_sequence ();
-      end_sequence ();
+  /* Check for KSEG1.  */
+  if (address + 0x60000000 < 0x20000000)
+    return true;
 
-      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 (GET_CODE (scan) == NOTE
-           && NOTE_LINE_NUMBER (scan) == NOTE_INSN_FUNCTION_BEG)
-         break;
-      if (scan == NULL_RTX)
-       scan = get_insns ();
-      insn = emit_insn_after (insn, scan);
-      pop_topmost_sequence ();
+  /* Check for uncached XKPHYS addresses.  */
+  if (Pmode == DImode)
+    {
+      upper = (address >> 40) & 0xf9ffff;
+      if (upper == 0x900000 || upper == 0xb80000)
+       return true;
     }
-
-  return mips16_gp_pseudo_rtx;
+  return false;
 }
 
-/* Return an RTX which represents the signed 16 bit offset from the
-   $gp register for the given symbol.  This is only used on the
-   mips16.  */
+/* Return true if we can prove that an access to address X in instruction
+   INSN would be safe from R10K speculation.  This X is a general
+   expression; it might not be a legitimate address.  */
 
-rtx
-mips16_gp_offset (sym)
-     rtx sym;
+static bool
+r10k_safe_address_p (rtx x, rtx insn)
 {
-  tree gp;
-
-  if (GET_CODE (sym) != SYMBOL_REF
-      || ! SYMBOL_REF_FLAG (sym))
-    abort ();
-
-  /* We use a special identifier to represent the value of the gp
-     register.  */
-  gp = get_identifier ("__mips16_gp_value");
+  rtx base, offset;
+  HOST_WIDE_INT offset_val;
 
-  return gen_rtx (CONST, Pmode,
-                 gen_rtx (MINUS, Pmode, sym,
-                          gen_rtx (SYMBOL_REF, Pmode,
-                                   IDENTIFIER_POINTER (gp))));
-}
-
-/* Return nonzero if the given RTX represents a signed 16 bit offset
-   from the $gp register.  */
+  x = r10k_simplify_address (x, insn);
 
-int
-mips16_gp_offset_p (x)
-     rtx x;
-{
-  if (GET_CODE (x) == CONST)
-    x = XEXP (x, 0);
+  /* Check for references to the stack frame.  It doesn't really matter
+     how much of the frame has been allocated at INSN; -mr10k-cache-barrier
+     allows us to assume that accesses to any part of the eventual frame
+     is safe from speculation at any point in the function.  */
+  mips_split_plus (x, &base, &offset_val);
+  if (base == virtual_incoming_args_rtx
+      && offset_val >= -cfun->machine->frame.total_size
+      && offset_val < cfun->machine->frame.args_size)
+    return true;
 
-  /* It's OK to add a small integer value to a gp offset.  */
-  if (GET_CODE (x) == PLUS)
-    {
-      if (GET_CODE (XEXP (x, 1)) == CONST_INT
-         && SMALL_INT (XEXP (x, 1)))
-       return mips16_gp_offset_p (XEXP (x, 0));
-      if (GET_CODE (XEXP (x, 0)) == CONST_INT
-         && SMALL_INT (XEXP (x, 0)))
-       return mips16_gp_offset_p (XEXP (x, 1));
-      return 0;
-    }
+  /* Check for uncached addresses.  */
+  if (CONST_INT_P (x))
+    return r10k_uncached_address_p (INTVAL (x));
 
-  /* Make sure it is in the form SYM - __mips16_gp_value.  */
-  return (GET_CODE (x) == MINUS
-         && GET_CODE (XEXP (x, 0)) == SYMBOL_REF
-         && SYMBOL_REF_FLAG (XEXP (x, 0))
-         && GET_CODE (XEXP (x, 1)) == SYMBOL_REF
-         && strcmp (XSTR (XEXP (x, 1), 0), "__mips16_gp_value") == 0);
+  /* Check for accesses to a static object.  */
+  split_const (x, &base, &offset);
+  return offset_within_block_p (base, INTVAL (offset));
 }
 
-/* Output a GP offset.  We don't want to print the subtraction of
-   __mips16_gp_value; it is implicitly represented by the %gprel which
-   should have been printed by the caller.  */
+/* Return true if a MEM with MEM_EXPR EXPR and MEM_OFFSET OFFSET is
+   an in-range access to an automatic variable, or to an object with
+   a link-time-constant address.  */
 
-static void
-mips16_output_gp_offset (file, x)
-     FILE *file;
-     rtx x;
+static bool
+r10k_safe_mem_expr_p (tree expr, rtx offset)
 {
-  if (GET_CODE (x) == CONST)
-    x = XEXP (x, 0);
-
-  if (GET_CODE (x) == PLUS)
-    {
-      mips16_output_gp_offset (file, XEXP (x, 0));
-      fputs ("+", file);
-      mips16_output_gp_offset (file, XEXP (x, 1));
-      return;
-    }
+  if (expr == NULL_TREE
+      || offset == NULL_RTX
+      || !CONST_INT_P (offset)
+      || INTVAL (offset) < 0
+      || INTVAL (offset) >= int_size_in_bytes (TREE_TYPE (expr)))
+    return false;
 
-  if (GET_CODE (x) == MINUS
-      && GET_CODE (XEXP (x, 1)) == SYMBOL_REF
-      && strcmp (XSTR (XEXP (x, 1), 0), "__mips16_gp_value") == 0)
+  while (TREE_CODE (expr) == COMPONENT_REF)
     {
-      mips16_output_gp_offset (file, XEXP (x, 0));
-      return;
+      expr = TREE_OPERAND (expr, 0);
+      if (expr == NULL_TREE)
+       return false;
     }
 
-  output_addr_const (file, x);
+  return DECL_P (expr);
 }
 
-/* Return nonzero if a constant should not be output until after the
-   function.  This is true of most string constants, so that we can
-   use a more efficient PC relative reference.  However, a static
-   inline function may never call assemble_function_end to write out
-   the constant pool, so don't try to postpone the constant in that
-   case.
+/* A for_each_rtx callback for which DATA points to the instruction
+   containing *X.  Stop the search if we find a MEM that is not safe
+   from R10K speculation.  */
 
-   ??? It's really a bug that a static inline function can put stuff
-   in the constant pool even if the function itself is not output.
+static int
+r10k_needs_protection_p_1 (rtx *loc, void *data)
+{
+  rtx mem;
 
-   We record which string constants we've seen, so that we know which
-   ones might use the more efficient reference.  */
+  mem = *loc;
+  if (!MEM_P (mem))
+    return 0;
 
-int
-mips16_constant_after_function_p (x)
-     tree x;
-{
-  if (TREE_CODE (x) == STRING_CST
-      && ! flag_writable_strings
-      && current_function_decl != 0
-      && ! DECL_DEFER_OUTPUT (current_function_decl)
-      && ! (DECL_INLINE (current_function_decl)
-           && ((! TREE_PUBLIC (current_function_decl)
-                && ! TREE_ADDRESSABLE (current_function_decl)
-                && ! flag_keep_inline_functions)
-               || DECL_EXTERNAL (current_function_decl))))
-    {
-      struct string_constant *n;
-
-      n = (struct string_constant *) xmalloc (sizeof *n);
-      n->label = XSTR (XEXP (TREE_CST_RTL (x), 0), 0);
-      n->next = string_constants;
-      string_constants = n;
+  if (r10k_safe_mem_expr_p (MEM_EXPR (mem), MEM_OFFSET (mem)))
+    return -1;
 
-      return 1;
-    }
+  if (r10k_safe_address_p (XEXP (mem, 0), (rtx) data))
+    return -1;
 
-  return 0;
+  return 1;
 }
 
-/* Validate a constant for the mips16.  This rejects general symbolic
-   addresses, which must be loaded from memory.  If ADDR is nonzero,
-   this should reject anything which is not a legal address.  If
-   ADDEND is nonzero, this is being added to something else.  */
+/* A note_stores callback for which DATA points to an instruction pointer.
+   If *DATA is nonnull, make it null if it X contains a MEM that is not
+   safe from R10K speculation.  */
 
-int
-mips16_constant (x, mode, addr, addend)
-     rtx x;
-     enum machine_mode mode;
-     int addr;
-     int addend;
+static void
+r10k_needs_protection_p_store (rtx x, const_rtx pat ATTRIBUTE_UNUSED,
+                              void *data)
 {
-  while (GET_CODE (x) == CONST)
-    x = XEXP (x, 0);
-
-  switch (GET_CODE (x))
-    {
-    default:
-      return 0;
+  rtx *insn_ptr;
 
-    case PLUS:
-      return (mips16_constant (XEXP (x, 0), mode, addr, 1)
-             && mips16_constant (XEXP (x, 1), mode, addr, 1));
+  insn_ptr = (rtx *) data;
+  if (*insn_ptr && for_each_rtx (&x, r10k_needs_protection_p_1, *insn_ptr))
+    *insn_ptr = NULL_RTX;
+}
 
-    case SYMBOL_REF:
-      if (addr && GET_MODE_SIZE (mode) != 4 && GET_MODE_SIZE (mode) != 8)
-       return 0;
-      if (CONSTANT_POOL_ADDRESS_P (x))
-       return 1;
+/* A for_each_rtx callback that iterates over the pattern of a CALL_INSN.
+   Return nonzero if the call is not to a declared function.  */
 
-      /* If we aren't looking for a memory address, we can accept a GP
-         relative symbol, which will have SYMBOL_REF_FLAG set; movsi
-         knows how to handle this.  We can always accept a string
-         constant, which is the other case in which SYMBOL_REF_FLAG
-         will be set.  */
-      if (! addr
-         && ! addend
-         && SYMBOL_REF_FLAG (x)
-         && mode == (enum machine_mode) Pmode)
-       return 1;
+static int
+r10k_needs_protection_p_call (rtx *loc, void *data ATTRIBUTE_UNUSED)
+{
+  rtx x;
 
-      /* We can accept a string constant, which will have
-         SYMBOL_REF_FLAG set but must be recognized by name to
-         distinguish from a GP accessible symbol.  The name of a
-         string constant will have been generated by
-         ASM_GENERATE_INTERNAL_LABEL as called by output_constant_def.  */
-      if (SYMBOL_REF_FLAG (x))
-       {
-         const char *name = XSTR (x, 0);
+  x = *loc;
+  if (!MEM_P (x))
+    return 0;
 
-         return (name[0] == '*'
-                 && strncmp (name + 1, LOCAL_LABEL_PREFIX,
-                             sizeof LOCAL_LABEL_PREFIX - 1) == 0);
-       }
+  x = XEXP (x, 0);
+  if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_DECL (x))
+    return -1;
 
-      return 0;
+  return 1;
+}
 
-    case LABEL_REF:
-      if (addr && GET_MODE_SIZE (mode) != 4 && GET_MODE_SIZE (mode) != 8)
-       return 0;
-      return 1;
+/* Return true if instruction INSN needs to be protected by an R10K
+   cache barrier.  */
 
-    case CONST_INT:
-      if (addr && ! addend)
-       return 0;
-      return INTVAL (x) > - 0x10000 && INTVAL (x) <= 0xffff;
+static bool
+r10k_needs_protection_p (rtx insn)
+{
+  if (CALL_P (insn))
+    return for_each_rtx (&PATTERN (insn), r10k_needs_protection_p_call, NULL);
 
-    case REG:
-      /* We need to treat $gp as a legitimate constant, because
-         mips16_gp_pseudo_reg assumes that.  */
-      return REGNO (x) == GP_REG_FIRST + 28;
+  if (mips_r10k_cache_barrier == R10K_CACHE_BARRIER_STORE)
+    {
+      note_stores (PATTERN (insn), r10k_needs_protection_p_store, &insn);
+      return insn == NULL_RTX;
     }
+
+  return for_each_rtx (&PATTERN (insn), r10k_needs_protection_p_1, insn);
 }
 
-/* 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 non-zero if
-   we are copying from the floating point registers.  */
+/* Return true if BB is only reached by blocks in PROTECTED_BBS and if every
+   edge is unconditional.  */
 
-static void
-mips16_fp_args (file, fp_code, from_fp_p)
-     FILE *file;
-     int fp_code;
-     int from_fp_p;
+static bool
+r10k_protected_bb_p (basic_block bb, sbitmap protected_bbs)
 {
-  const char *s;
-  int gparg, fparg;
-  unsigned int f;
-
-  /* This code only works for the original 32 bit ABI and the O64 ABI.  */
-  if (mips_abi != ABI_32 && mips_abi != ABI_O64)
-    abort ();
-
-  if (from_fp_p)
-    s = "mfc1";
-  else
-    s = "mtc1";
-  gparg = GP_ARG_FIRST;
-  fparg = FP_ARG_FIRST;
-  for (f = (unsigned int) fp_code; f != 0; f >>= 2)
-    {
-      if ((f & 3) == 1)
-       {
-         if ((fparg & 1) != 0)
-           ++fparg;
-         fprintf (file, "\t%s\t%s,%s\n", s,
-                  reg_names[gparg], reg_names[fparg]);
-       }
-      else if ((f & 3) == 2)
-       {
-         if (TARGET_64BIT)
-           fprintf (file, "\td%s\t%s,%s\n", s,
-                    reg_names[gparg], reg_names[fparg]);
-         else
-           {
-             if ((fparg & 1) != 0)
-               ++fparg;
-             if (TARGET_BIG_ENDIAN)
-               fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
-                        reg_names[gparg], reg_names[fparg + 1], s,
-                        reg_names[gparg + 1], reg_names[fparg]);
-             else
-               fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
-                        reg_names[gparg], reg_names[fparg], s,
-                        reg_names[gparg + 1], reg_names[fparg + 1]);
-             ++gparg;
-             ++fparg;
-           }
-       }
-      else
-       abort ();
+  edge_iterator ei;
+  edge e;
 
-      ++gparg;
-      ++fparg;
-    }
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    if (!single_succ_p (e->src)
+       || !TEST_BIT (protected_bbs, e->src->index)
+       || (e->flags & EDGE_COMPLEX) != 0)
+      return false;
+  return true;
 }
 
-/* Build a mips16 function stub.  This is used for functions which
-   take aruments 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.  */
+/* Implement -mr10k-cache-barrier= for the current function.  */
 
 static void
-build_mips16_function_stub (file)
-     FILE *file;
+r10k_insert_cache_barriers (void)
 {
-  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);
-  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);
+  int *rev_post_order;
+  unsigned int i, n;
+  basic_block bb;
+  sbitmap protected_bbs;
+  rtx insn, end, unprotected_region;
 
-  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)
+  if (TARGET_MIPS16)
     {
-      fprintf (file, "%s%s",
-              need_comma ? ", " : "",
-              (f & 3) == 1 ? "float" : "double");
-      need_comma = 1;
+      sorry ("%qs does not support MIPS16 code", "-mr10k-cache-barrier");
+      return;
     }
-  fprintf (file, ")\n");
-
-  fprintf (file, "\t.set\tnomips16\n");
-  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 can not emit another .ent.  */
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
-  fputs ("\t.ent\t", file);
-  assemble_name (file, stubname);
-  fputs ("\n", file);
-#endif
-
-  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);
-
-  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");
-
-  /* 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");
-
-  fprintf (file, "\t.set\treorder\n");
-
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
-  fputs ("\t.end\t", file);
-  assemble_name (file, stubname);
-  fputs ("\n", file);
-#endif
 
-  fprintf (file, "\t.set\tmips16\n");
+  /* Restore the BLOCK_FOR_INSN pointers, which are needed by DF.  */
+  compute_bb_for_insn ();
 
-  function_section (current_function_decl);
-}
-
-/* We keep a list of functions for which we have already built stubs
-   in build_mips16_call_stub.  */
+  /* Create def-use chains.  */
+  df_set_flags (DF_EQ_NOTES);
+  df_chain_add_problem (DF_UD_CHAIN);
+  df_analyze ();
 
-struct mips16_stub
-{
-  struct mips16_stub *next;
-  char *name;
-  int fpret;
-};
+  /* Calculate dominators.  */
+  calculate_dominance_info (CDI_DOMINATORS);
 
-static struct mips16_stub *mips16_stubs;
+  /* Bit X of PROTECTED_BBS is set if the last operation in basic block
+     X is protected by a cache barrier.  */
+  protected_bbs = sbitmap_alloc (last_basic_block);
+  sbitmap_zero (protected_bbs);
 
-/* 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.
+  /* Iterate over the basic blocks in reverse post-order.  */
+  rev_post_order = XNEWVEC (int, last_basic_block);
+  n = pre_and_rev_post_order_compute (NULL, rev_post_order, false);
+  for (i = 0; i < n; i++)
+    {
+      bb = BASIC_BLOCK (rev_post_order[i]);
 
-   Similarly, we need a stub if the return value might come back in a
-   floating point register.
+      /* If this block is only reached by unconditional edges, and if the
+        source of every edge is protected, the beginning of the block is
+        also protected.  */
+      if (r10k_protected_bb_p (bb, protected_bbs))
+       unprotected_region = NULL_RTX;
+      else
+       unprotected_region = pc_rtx;
+      end = NEXT_INSN (BB_END (bb));
 
-   RETVAL, FNMEM, and ARG_SIZE are the values passed to the call insn
-   (RETVAL is NULL if this is call rather than call_value).  FP_CODE
-   is the code built by function_arg.  This function returns a nonzero
-   value if it builds the call instruction itself.  */
+      /* UNPROTECTED_REGION is:
 
-int
-build_mips16_call_stub (retval, fnmem, arg_size, fp_code)
-     rtx retval;
-     rtx fnmem;
-     rtx arg_size;
-     int fp_code;
-{
-  int fpret;
-  rtx fn;
-  const char *fnname;
-  char *secname, *stubname;
-  struct mips16_stub *l;
-  tree stubid, stubdecl;
-  int need_comma;
-  unsigned int f;
+        - null if we are processing a protected region,
+        - pc_rtx if we are processing an unprotected region but have
+          not yet found the first instruction in it
+        - the first instruction in an unprotected region otherwise.  */
+      for (insn = BB_HEAD (bb); insn != end; insn = NEXT_INSN (insn))
+       {
+         if (unprotected_region && INSN_P (insn))
+           {
+             if (recog_memoized (insn) == CODE_FOR_mips_cache)
+               /* This CACHE instruction protects the following code.  */
+               unprotected_region = NULL_RTX;
+             else
+               {
+                 /* See if INSN is the first instruction in this
+                    unprotected region.  */
+                 if (unprotected_region == pc_rtx)
+                   unprotected_region = insn;
+
+                 /* See if INSN needs to be protected.  If so,
+                    we must insert a cache barrier somewhere between
+                    PREV_INSN (UNPROTECTED_REGION) and INSN.  It isn't
+                    clear which position is better performance-wise,
+                    but as a tie-breaker, we assume that it is better
+                    to allow delay slots to be back-filled where
+                    possible, and that it is better not to insert
+                    barriers in the middle of already-scheduled code.
+                    We therefore insert the barrier at the beginning
+                    of the region.  */
+                 if (r10k_needs_protection_p (insn))
+                   {
+                     emit_insn_before (gen_r10k_cache_barrier (),
+                                       unprotected_region);
+                     unprotected_region = NULL_RTX;
+                   }
+               }
+           }
 
-  /* We don't need to do anything if we aren't in mips16 mode, or if
-     we were invoked with the -msoft-float option.  */
-  if (! TARGET_MIPS16 || ! mips16_hard_float)
-    return 0;
+         if (CALL_P (insn))
+           /* The called function is not required to protect the exit path.
+              The code that follows a call is therefore unprotected.  */
+           unprotected_region = pc_rtx;
+       }
 
-  /* Figure out whether the value might come back in a floating point
-     register.  */
-  fpret = (retval != 0
-          && GET_MODE_CLASS (GET_MODE (retval)) == MODE_FLOAT
-          && (! TARGET_SINGLE_FLOAT
-              || GET_MODE_SIZE (GET_MODE (retval)) <= 4));
+      /* Record whether the end of this block is protected.  */
+      if (unprotected_region == NULL_RTX)
+       SET_BIT (protected_bbs, bb->index);
+    }
+  XDELETEVEC (rev_post_order);
 
-  /* 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;
+  sbitmap_free (protected_bbs);
 
-  if (GET_CODE (fnmem) != MEM)
-    abort ();
-  fn = XEXP (fnmem, 0);
+  free_dominance_info (CDI_DOMINATORS);
 
-  /* 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;
+  df_finish_pass (false);
 
-  /* This code will only work for o32 and o64 abis.  The other ABI's
-     require more sophisticated support.  */
-  if (mips_abi != ABI_32 && mips_abi != ABI_O64)
-    abort ();
+  free_bb_for_insn ();
+}
+\f
+/* A temporary variable used by for_each_rtx callbacks, etc.  */
+static rtx mips_sim_insn;
 
-  /* We can only handle SFmode and DFmode floating point return
-     values.  */
-  if (fpret && GET_MODE (retval) != SFmode && GET_MODE (retval) != DFmode)
-    abort ();
+/* 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;
 
-  /* 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.  */
+  /* The current simulation time.  */
+  unsigned int time;
 
-  if (GET_CODE (fn) != SYMBOL_REF)
-    {
-      char buf[30];
-      tree id;
-      rtx stub_fn, stub_mem, insn;
+  /* How many more instructions can be issued in the current cycle.  */
+  unsigned int insns_left;
 
-      /* ??? If this code is modified to support other ABI's, we need
-         to handle PARALLEL return values here.  */
+  /* 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];
 
-      sprintf (buf, "__mips16_call_stub_%s%d",
-              (fpret
-               ? (GET_MODE (retval) == SFmode ? "sf_" : "df_")
-               : ""),
-              fp_code);
-      id = get_identifier (buf);
-      stub_fn = gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (id));
-      stub_mem = gen_rtx (MEM, Pmode, stub_fn);
+  /* The pipeline's current DFA state.  */
+  state_t dfa_state;
+};
 
-      emit_move_insn (gen_rtx (REG, Pmode, 2), fn);
+/* Reset STATE to the initial simulation state.  */
 
-      if (retval == NULL_RTX)
-       insn = gen_call_internal0 (stub_mem, arg_size,
-                                  gen_rtx (REG, SImode,
-                                           GP_REG_FIRST + 31));
-      else
-       insn = gen_call_value_internal0 (retval, stub_mem, arg_size,
-                                        gen_rtx (REG, SImode,
-                                                 GP_REG_FIRST + 31));
-      insn = emit_call_insn (insn);
-
-      /* Put the register usage information on the CALL.  */
-      if (GET_CODE (insn) != CALL_INSN)
-       abort ();
-      CALL_INSN_FUNCTION_USAGE (insn) =
-       gen_rtx (EXPR_LIST, VOIDmode,
-                gen_rtx (USE, VOIDmode, gen_rtx (REG, Pmode, 2)),
-                CALL_INSN_FUNCTION_USAGE (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 regs_ever_live[$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));
+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);
+}
 
-      /* Return 1 to tell the caller that we've generated the call
-         insn.  */
-      return 1;
-    }
+/* Initialize STATE before its first use.  DFA_STATE points to an
+   allocated but uninitialized DFA state.  */
 
-  /* 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 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);
+}
 
-  fnname = XSTR (fn, 0);
-  for (l = mips16_stubs; l != NULL; l = l->next)
-    if (strcmp (l->name, fnname) == 0)
-      break;
+/* Advance STATE by one clock cycle.  */
 
-  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.
-
-        If the function does not return a floating point value, the
-        special stub section is named
-            .mips16.call.FNNAME
+static void
+mips_sim_next_cycle (struct mips_sim *state)
+{
+  state->time++;
+  state->insns_left = state->issue_rate;
+  state_transition (state->dfa_state, 0);
+}
 
-        If the function does return a floating point value, the stub
-        section is named
-            .mips16.call.fp.FNNAME
-        */
+/* Advance simulation state STATE until instruction INSN can read
+   register REG.  */
 
-      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);
+static void
+mips_sim_wait_reg (struct mips_sim *state, rtx insn, rtx reg)
+{
+  unsigned int regno, end_regno;
 
-      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");
+  end_regno = END_REGNO (reg);
+  for (regno = REGNO (reg); regno < end_regno; regno++)
+    if (state->last_set[regno].insn != 0)
+      {
+       unsigned int t;
 
-      fprintf (asm_out_file, "\t.set\tnomips16\n");
-      assemble_start_function (stubdecl, stubname);
+       t = (state->last_set[regno].time
+            + insn_latency (state->last_set[regno].insn, insn));
+       while (state->time < t)
+         mips_sim_next_cycle (state);
+    }
+}
 
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
-      fputs ("\t.ent\t", asm_out_file);
-      assemble_name (asm_out_file, stubname);
-      fputs ("\n", asm_out_file);
+/* A for_each_rtx callback.  If *X is a register, advance simulation state
+   DATA until mips_sim_insn can read the register's value.  */
 
-      assemble_name (asm_out_file, stubname);
-      fputs (":\n", asm_out_file);
-#endif
+static int
+mips_sim_wait_regs_2 (rtx *x, void *data)
+{
+  if (REG_P (*x))
+    mips_sim_wait_reg ((struct mips_sim *) data, mips_sim_insn, *x);
+  return 0;
+}
 
-      /* 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.  */
+/* Call mips_sim_wait_regs_2 (R, DATA) for each register R mentioned in *X.  */
 
-      /* We don't want the assembler to insert any nops here.  */
-      fprintf (asm_out_file, "\t.set\tnoreorder\n");
+static void
+mips_sim_wait_regs_1 (rtx *x, void *data)
+{
+  for_each_rtx (x, mips_sim_wait_regs_2, data);
+}
 
-      mips16_fp_args (asm_out_file, fp_code, 0);
+/* Advance simulation state STATE until all of INSN's register
+   dependencies are satisfied.  */
 
-      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");
-         if (GET_MODE (retval) == SFmode)
-           fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                    reg_names[GP_REG_FIRST + 2], reg_names[FP_REG_FIRST + 0]);
-         else
-           {
-             if (TARGET_BIG_ENDIAN)
-               {
-                 fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                          reg_names[GP_REG_FIRST + 2],
-                          reg_names[FP_REG_FIRST + 1]);
-                 fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                          reg_names[GP_REG_FIRST + 3],
-                          reg_names[FP_REG_FIRST + 0]);
-               }
-             else
-               {
-                 fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                          reg_names[GP_REG_FIRST + 2],
-                          reg_names[FP_REG_FIRST + 0]);
-                 fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
-                          reg_names[GP_REG_FIRST + 3],
-                          reg_names[FP_REG_FIRST + 1]);
-               }
-           }
-         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");
-       }
+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);
+}
 
-      fprintf (asm_out_file, "\t.set\treorder\n");
+/* Advance simulation state STATE until the units required by
+   instruction INSN are available.  */
 
-#ifdef ASM_DECLARE_FUNCTION_SIZE
-      ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
-#endif
+static void
+mips_sim_wait_units (struct mips_sim *state, rtx insn)
+{
+  state_t tmp_state;
 
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
-      fputs ("\t.end\t", asm_out_file);
-      assemble_name (asm_out_file, stubname);
-      fputs ("\n", asm_out_file);
-#endif
+  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);
+}
 
-      fprintf (asm_out_file, "\t.set\tmips16\n");
+/* Advance simulation state STATE until INSN is ready to issue.  */
 
-      /* 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 void
+mips_sim_wait_insn (struct mips_sim *state, rtx insn)
+{
+  mips_sim_wait_regs (state, insn);
+  mips_sim_wait_units (state, insn);
+}
 
-  /* 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 ("can not handle inconsistent calls to `%s'", fnname);
+/* mips_sim_insn has just set X.  Update the LAST_SET array
+   in simulation state DATA.  */
 
-  /* 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.  */
+static void
+mips_sim_record_set (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+  struct mips_sim *state;
 
-  if (l->fpret)
+  state = (struct mips_sim *) data;
+  if (REG_P (x))
     {
-      rtx insn;
+      unsigned int regno, end_regno;
 
-      if (retval == NULL_RTX)
-       insn = gen_call_internal0 (fnmem, arg_size,
-                                  gen_rtx (REG, SImode,
-                                           GP_REG_FIRST + 31));
-      else
-       insn = gen_call_value_internal0 (retval, fnmem, arg_size,
-                                        gen_rtx (REG, SImode,
-                                                 GP_REG_FIRST + 31));
-      insn = emit_call_insn (insn);
-
-      if (GET_CODE (insn) != CALL_INSN)
-       abort ();
+      end_regno = END_REGNO (x);
+      for (regno = REGNO (x); regno < end_regno; regno++)
+       {
+         state->last_set[regno].insn = mips_sim_insn;
+         state->last_set[regno].time = state->time;
+       }
+    }
+}
 
-      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));
+/* Issue instruction INSN in scheduler state STATE.  Assume that INSN
+   can issue immediately (i.e., that mips_sim_wait_insn has already
+   been called).  */
 
-      /* Return 1 to tell the caller that we've generated the call
-         insn.  */
-      return 1;
-    }
+static void
+mips_sim_issue_insn (struct mips_sim *state, rtx insn)
+{
+  state_transition (state->dfa_state, insn);
+  state->insns_left--;
 
-  /* Return 0 to let the caller generate the call insn.  */
-  return 0;
+  mips_sim_insn = insn;
+  note_stores (PATTERN (insn), mips_sim_record_set, state);
 }
 
-/* This function looks through the code for a function, and tries to
-   optimize the usage of the $gp register.  We arrange to copy $gp
-   into a pseudo-register, and then let gcc's normal reload handling
-   deal with the pseudo-register.  Unfortunately, if reload choose to
-   put the pseudo-register into a call-clobbered register, it will
-   emit saves and restores for that register around any function
-   calls.  We don't need the saves, and it's faster to copy $gp than
-   to do an actual restore.  ??? This still means that we waste a
-   stack slot.
-
-   This is an optimization, and the code which gcc has actually
-   generated is correct, so we do not need to catch all cases.  */
+/* Simulate issuing a NOP in state STATE.  */
 
 static void
-mips16_optimize_gp (first)
-     rtx first;
+mips_sim_issue_nop (struct mips_sim *state)
 {
-  rtx gpcopy, slot, insn;
+  if (state->insns_left == 0)
+    mips_sim_next_cycle (state);
+  state->insns_left--;
+}
+
+/* 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.  */
 
-  /* Look through the instructions.  Set GPCOPY to the register which
-     holds a copy of $gp.  Set SLOT to the stack slot where it is
-     saved.  If we find an instruction which sets GPCOPY to anything
-     other than $gp or SLOT, then we can't use it.  If we find an
-     instruction which sets SLOT to anything other than GPCOPY, we
-     can't use it.  */
+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);
 
-  gpcopy = NULL_RTX;
-  slot = NULL_RTX;
-  for (insn = first; insn != NULL_RTX; insn = next_active_insn (insn))
+  switch (GET_CODE (SEQ_BEGIN (insn)))
     {
-      rtx set;
+    case CODE_LABEL:
+    case CALL_INSN:
+      /* We can't predict the processor state after a call or label.  */
+      mips_sim_reset (state);
+      break;
 
-      if (! INSN_P (insn))
-       continue;
+    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;
 
-      set = PATTERN (insn);
+    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).
+
+   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.  */
 
-      /* We know that all references to memory will be inside a SET,
-         because there is no other way to access memory on the mips16.
-         We don't have to worry about a PARALLEL here, because the
-         mips.md file will never generate them for memory references.  */
-      if (GET_CODE (set) != SET)
-       continue;
+static void
+vr4130_avoid_branch_rt_conflict (rtx insn)
+{
+  rtx first, second;
 
-      if (gpcopy == NULL_RTX
-         && GET_CODE (SET_SRC (set)) == CONST
-         && GET_CODE (XEXP (SET_SRC (set), 0)) == REG
-         && REGNO (XEXP (SET_SRC (set), 0)) == GP_REG_FIRST + 28
-         && GET_CODE (SET_DEST (set)) == REG
-         && GET_MODE (SET_DEST (set)) == (unsigned) Pmode)
-       gpcopy = SET_DEST (set);
-      else if (slot == NULL_RTX
-              && gpcopy != NULL_RTX
-              && GET_CODE (SET_DEST (set)) == MEM
-              && GET_CODE (SET_SRC (set)) == REG
-              && REGNO (SET_SRC (set)) == REGNO (gpcopy)
-              && GET_MODE (SET_DEST (set)) == (unsigned) Pmode)
+  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)))
        {
-         rtx base, offset;
-
-         offset = const0_rtx;
-         base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
-         if (GET_CODE (base) == REG
-             && (REGNO (base) == STACK_POINTER_REGNUM
-                 || REGNO (base) == FRAME_POINTER_REGNUM))
-           slot = SET_DEST (set);
+         /* 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;
        }
-      else if (gpcopy != NULL_RTX
-              && (GET_CODE (SET_DEST (set)) == REG
-                  || GET_CODE (SET_DEST (set)) == SUBREG)
-              && reg_overlap_mentioned_p (SET_DEST (set), gpcopy)
-              && (GET_CODE (SET_DEST (set)) != REG
-                  || REGNO (SET_DEST (set)) != REGNO (gpcopy)
-                  || GET_MODE (SET_DEST (set)) != (unsigned) Pmode
-                  || ((GET_CODE (SET_SRC (set)) != CONST
-                       || GET_CODE (XEXP (SET_SRC (set), 0)) != REG
-                       || (REGNO (XEXP (SET_SRC (set), 0))
-                           != GP_REG_FIRST + 28))
-                      && ! rtx_equal_p (SET_SRC (set), slot))))
-       break;
-      else if (slot != NULL_RTX
-              && GET_CODE (SET_DEST (set)) == MEM
-              && rtx_equal_p (SET_DEST (set), slot)
-              && (GET_CODE (SET_SRC (set)) != REG
-                  || REGNO (SET_SRC (set)) != REGNO (gpcopy)))
-       break;
     }
+}
 
-  /* If we couldn't find a unique value for GPCOPY or SLOT, then try a
-     different optimization.  Any time we find a copy of $28 into a
-     register, followed by an add of a symbol_ref to that register, we
-     convert it to load the value from the constant table instead.
-     The copy and add will take six bytes, just as the load and
-     constant table entry will take six bytes.  However, it is
-     possible that the constant table entry will be shared.
+/* 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 instructions is not 8-byte aligned,
+   insert a nop to make it aligned.  */
 
-     This could be a peephole optimization, but I don't know if the
-     peephole code can call force_const_mem.
+static void
+vr4130_align_insns (void)
+{
+  struct mips_sim state;
+  rtx insn, subinsn, last, last2, next;
+  bool aligned_p;
 
-     Using the same register for the copy of $28 and the add of the
-     symbol_ref is actually pretty likely, since the add instruction
-     requires the destination and the first addend to be the same
-     register.  */
+  dfa_start ();
 
-  if (insn != NULL_RTX || gpcopy == NULL_RTX || slot == NULL_RTX)
+  /* LAST is the last instruction before INSN to have a nonzero length.
+     LAST2 is the last such instruction before LAST.  */
+  last = 0;
+  last2 = 0;
+
+  /* ALIGNED_P is true if INSN is known to be at an aligned address.  */
+  aligned_p = true;
+
+  mips_sim_init (&state, alloca (state_size ()));
+  for (insn = get_insns (); insn != 0; insn = next)
     {
-      rtx next;
+      unsigned int length;
 
-      /* This optimization is only reasonable if the constant table
-         entries are only 4 bytes.  */
-      if (Pmode != SImode)
-       return;
+      next = NEXT_INSN (insn);
 
-      for (insn = first; insn != NULL_RTX; insn = next)
-       {
-         rtx set1, set2;
+      /* 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);
 
-         next = insn;
-         do
+      if (USEFUL_INSN_P (insn))
+       FOR_EACH_SUBINSN (subinsn, insn)
+         {
+           mips_sim_wait_insn (&state, subinsn);
+
+           /* 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:
+
+                 - 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.
+
+              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.
+
+                      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);
+
+      /* 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;
            }
-         while (next != NULL_RTX
-                && (GET_CODE (next) == NOTE
-                    || (GET_CODE (next) == INSN
-                        && (GET_CODE (PATTERN (next)) == USE
-                            || GET_CODE (PATTERN (next)) == CLOBBER))));
+         else if (length & 4)
+           aligned_p = !aligned_p;
+         last2 = last;
+         last = insn;
+       }
 
-         if (next == NULL_RTX)
-           break;
+      /* See whether INSN is an aligned label.  */
+      if (LABEL_P (insn) && label_to_alignment (insn) >= 3)
+       aligned_p = true;
+    }
+  dfa_finish ();
+}
+\f
+/* This structure records that the current function has a LO_SUM
+   involving SYMBOL_REF or LABEL_REF BASE and that MAX_OFFSET is
+   the largest offset applied to BASE by all such LO_SUMs.  */
+struct mips_lo_sum_offset {
+  rtx base;
+  HOST_WIDE_INT offset;
+};
 
-         if (! INSN_P (insn))
-           continue;
-
-         if (! INSN_P (next))
-           continue;
-
-         set1 = PATTERN (insn);
-         if (GET_CODE (set1) != SET)
-           continue;
-         set2 = PATTERN (next);
-         if (GET_CODE (set2) != SET)
-           continue;
-
-         if (GET_CODE (SET_DEST (set1)) == REG
-             && GET_CODE (SET_SRC (set1)) == CONST
-             && GET_CODE (XEXP (SET_SRC (set1), 0)) == REG
-             && REGNO (XEXP (SET_SRC (set1), 0)) == GP_REG_FIRST + 28
-             && rtx_equal_p (SET_DEST (set1), SET_DEST (set2))
-             && GET_CODE (SET_SRC (set2)) == PLUS
-             && rtx_equal_p (SET_DEST (set1), XEXP (SET_SRC (set2), 0))
-             && mips16_gp_offset_p (XEXP (SET_SRC (set2), 1))
-             && GET_CODE (XEXP (XEXP (SET_SRC (set2), 1), 0)) == MINUS)
-           {
-             rtx sym;
+/* Return a hash value for SYMBOL_REF or LABEL_REF BASE.  */
 
-             /* We've found a case we can change to load from the
-                 constant table.  */
+static hashval_t
+mips_hash_base (rtx base)
+{
+  int do_not_record_p;
 
-             sym = XEXP (XEXP (XEXP (SET_SRC (set2), 1), 0), 0);
-             if (GET_CODE (sym) != SYMBOL_REF)
-               abort ();
-             emit_insn_after (gen_rtx (SET, VOIDmode, SET_DEST (set1),
-                                       force_const_mem (Pmode, sym)),
-                              next);
+  return hash_rtx (base, GET_MODE (base), &do_not_record_p, NULL, false);
+}
 
-             PUT_CODE (insn, NOTE);
-             NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
-             NOTE_SOURCE_FILE (insn) = 0;
+/* Hash-table callbacks for mips_lo_sum_offsets.  */
 
-             PUT_CODE (next, NOTE);
-             NOTE_LINE_NUMBER (next) = NOTE_INSN_DELETED;
-             NOTE_SOURCE_FILE (next) = 0;
-           }
-       }
+static hashval_t
+mips_lo_sum_offset_hash (const void *entry)
+{
+  return mips_hash_base (((const struct mips_lo_sum_offset *) entry)->base);
+}
 
-      return;
-    }
+static int
+mips_lo_sum_offset_eq (const void *entry, const void *value)
+{
+  return rtx_equal_p (((const struct mips_lo_sum_offset *) entry)->base,
+                     (const_rtx) value);
+}
 
-  /* We can safely remove all assignments to SLOT from GPCOPY, and
-     replace all assignments from SLOT to GPCOPY with assignments from
-     $28.  */
+/* Look up symbolic constant X in HTAB, which is a hash table of
+   mips_lo_sum_offsets.  If OPTION is NO_INSERT, return true if X can be
+   paired with a recorded LO_SUM, otherwise record X in the table.  */
 
-  for (insn = first; insn != NULL_RTX; insn = next_active_insn (insn))
-    {
-      rtx set;
+static bool
+mips_lo_sum_offset_lookup (htab_t htab, rtx x, enum insert_option option)
+{
+  rtx base, offset;
+  void **slot;
+  struct mips_lo_sum_offset *entry;
 
-      if (! INSN_P (insn))
-       continue;
+  /* Split X into a base and offset.  */
+  split_const (x, &base, &offset);
+  if (UNSPEC_ADDRESS_P (base))
+    base = UNSPEC_ADDRESS (base);
 
-      set = PATTERN (insn);
-      if (GET_CODE (set) != SET
-         || GET_MODE (SET_DEST (set)) != (unsigned) Pmode)
-       continue;
+  /* Look up the base in the hash table.  */
+  slot = htab_find_slot_with_hash (htab, base, mips_hash_base (base), option);
+  if (slot == NULL)
+    return false;
 
-      if (GET_CODE (SET_DEST (set)) == MEM
-         && rtx_equal_p (SET_DEST (set), slot)
-         && GET_CODE (SET_SRC (set)) == REG
-         && REGNO (SET_SRC (set)) == REGNO (gpcopy))
+  entry = (struct mips_lo_sum_offset *) *slot;
+  if (option == INSERT)
+    {
+      if (entry == NULL)
        {
-         PUT_CODE (insn, NOTE);
-         NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
-         NOTE_SOURCE_FILE (insn) = 0;
+         entry = XNEW (struct mips_lo_sum_offset);
+         entry->base = base;
+         entry->offset = INTVAL (offset);
+         *slot = entry;
        }
-      else if (GET_CODE (SET_DEST (set)) == REG
-              && REGNO (SET_DEST (set)) == REGNO (gpcopy)
-              && GET_CODE (SET_SRC (set)) == MEM
-              && rtx_equal_p (SET_SRC (set), slot))
+      else
        {
-         emit_insn_after (gen_rtx (SET, Pmode, SET_DEST (set),
-                                   gen_rtx (CONST, Pmode,
-                                            gen_rtx (REG, Pmode,
-                                                     GP_REG_FIRST + 28))),
-                          insn);
-         PUT_CODE (insn, NOTE);
-         NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
-         NOTE_SOURCE_FILE (insn) = 0;
+         if (INTVAL (offset) > entry->offset)
+           entry->offset = INTVAL (offset);
        }
     }
+  return INTVAL (offset) <= entry->offset;
 }
 
-/* We keep a list of constants we which we have to add to internal
-   constant tables in the middle of large functions.  */
+/* A for_each_rtx callback for which DATA is a mips_lo_sum_offset hash table.
+   Record every LO_SUM in *LOC.  */
 
-struct constant
+static int
+mips_record_lo_sum (rtx *loc, void *data)
 {
-  struct constant *next;
-  rtx value;
-  rtx label;
-  enum machine_mode mode;
-};
+  if (GET_CODE (*loc) == LO_SUM)
+    mips_lo_sum_offset_lookup ((htab_t) data, XEXP (*loc, 1), INSERT);
+  return 0;
+}
 
-/* Add a constant to the list in *PCONSTANTS.  */
+/* Return true if INSN is a SET of an orphaned high-part relocation.
+   HTAB is a hash table of mips_lo_sum_offsets that describes all the
+   LO_SUMs in the current function.  */
 
-static rtx
-add_constant (pconstants, val, mode)
-     struct constant **pconstants;
-     rtx val;
-     enum machine_mode mode;
+static bool
+mips_orphaned_high_part_p (htab_t htab, rtx insn)
 {
-  struct constant *c;
+  enum mips_symbol_type type;
+  rtx x, set;
 
-  for (c = *pconstants; c != NULL; c = c->next)
-    if (mode == c->mode && rtx_equal_p (val, c->value))
-      return c->label;
+  set = single_set (insn);
+  if (set)
+    {
+      /* Check for %his.  */
+      x = SET_SRC (set);
+      if (GET_CODE (x) == HIGH
+         && absolute_symbolic_operand (XEXP (x, 0), VOIDmode))
+       return !mips_lo_sum_offset_lookup (htab, XEXP (x, 0), NO_INSERT);
 
-  c = (struct constant *) xmalloc (sizeof *c);
-  c->value = val;
-  c->mode = mode;
-  c->label = gen_label_rtx ();
-  c->next = *pconstants;
-  *pconstants = c;
-  return c->label;
+      /* Check for local %gots (and %got_pages, which is redundant but OK).  */
+      if (GET_CODE (x) == UNSPEC
+         && XINT (x, 1) == UNSPEC_LOAD_GOT
+         && mips_symbolic_constant_p (XVECEXP (x, 0, 1),
+                                      SYMBOL_CONTEXT_LEA, &type)
+         && type == SYMBOL_GOTOFF_PAGE)
+       return !mips_lo_sum_offset_lookup (htab, XVECEXP (x, 0, 1), NO_INSERT);
+    }
+  return false;
 }
 
-/* Dump out the constants in CONSTANTS after INSN.  */
+/* Subroutine of mips_reorg_process_insns.  If there is a hazard between
+   INSN and a previous instruction, avoid it by inserting nops after
+   instruction AFTER.
+
+   *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).
+
+   After inserting nops for INSN, update *DELAYED_REG and *HILO_DELAY
+   for the next instruction.
+
+   LO_REG is an rtx for the LO register, used in dependence checking.  */
 
 static void
-dump_constants (constants, insn)
-     struct constant *constants;
-     rtx insn;
+mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
+                  rtx *delayed_reg, rtx lo_reg)
 {
-  struct constant *c;
-  int align;
-
-  c = constants;
-  align = 0;
-  while (c != NULL)
-    {
-      rtx r;
-      struct constant *next;
+  rtx pattern, set;
+  int nops, ninsns;
 
-      switch (GET_MODE_SIZE (c->mode))
-       {
-       case 1:
-         align = 0;
-         break;
-       case 2:
-         if (align < 1)
-           insn = emit_insn_after (gen_align_2 (), insn);
-         align = 1;
-         break;
-       case 4:
-         if (align < 2)
-           insn = emit_insn_after (gen_align_4 (), insn);
-         align = 2;
-         break;
-       default:
-         if (align < 3)
-           insn = emit_insn_after (gen_align_8 (), insn);
-         align = 3;
-         break;
-       }
+  pattern = PATTERN (insn);
 
-      insn = emit_label_after (c->label, insn);
+  /* 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;
 
-      switch (c->mode)
-       {
-       case QImode:
-         r = gen_consttable_qi (c->value);
-         break;
-       case HImode:
-         r = gen_consttable_hi (c->value);
-         break;
-       case SImode:
-         r = gen_consttable_si (c->value);
-         break;
-       case SFmode:
-         r = gen_consttable_sf (c->value);
-         break;
-       case DImode:
-         r = gen_consttable_di (c->value);
-         break;
-       case DFmode:
-         r = gen_consttable_df (c->value);
-         break;
-       default:
-         abort ();
-       }
+  /* Ignore zero-length instructions (barriers and the like).  */
+  ninsns = get_attr_length (insn) / 4;
+  if (ninsns == 0)
+    return;
 
-      insn = emit_insn_after (r, insn);
+  /* 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;
+
+  /* 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);
+
+  /* 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;
 
-      next = c->next;
-      free (c);
-      c = next;
-    }
+      case HAZARD_HILO:
+       *hilo_delay = 0;
+       break;
 
-  emit_barrier_after (insn);
+      case HAZARD_DELAY:
+       set = single_set (insn);
+       gcc_assert (set);
+       *delayed_reg = SET_DEST (set);
+       break;
+      }
 }
 
-/* Find the symbol in an address expression.  */
+/* Go through the instruction stream and insert nops where necessary.
+   Also delete any high-part relocations whose partnering low parts
+   are now all dead.  See if the whole function can then be put into
+   .set noreorder and .set nomacro.  */
 
-static rtx
-mips_find_symbol (addr)
-     rtx addr;
-{
-  if (GET_CODE (addr) == MEM)
-    addr = XEXP (addr, 0);
-  while (GET_CODE (addr) == CONST)
-    addr = XEXP (addr, 0);
-  if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
-    return addr;
-  if (GET_CODE (addr) == PLUS)
-    {
-      rtx l1, l2;
-
-      l1 = mips_find_symbol (XEXP (addr, 0));
-      l2 = mips_find_symbol (XEXP (addr, 1));
-      if (l1 != NULL_RTX && l2 == NULL_RTX)
-       return l1;
-      else if (l1 == NULL_RTX && l2 != NULL_RTX)
-       return l2;
+static void
+mips_reorg_process_insns (void)
+{
+  rtx insn, last_insn, subinsn, next_insn, lo_reg, delayed_reg;
+  int hilo_delay;
+  htab_t htab;
+
+  /* Force all instructions to be split into their final form.  */
+  split_all_insns_noflow ();
+
+  /* Recalculate instruction lengths without taking nops into account.  */
+  cfun->machine->ignore_hazard_length_p = true;
+  shorten_branches (get_insns ());
+
+  cfun->machine->all_noreorder_p = true;
+
+  /* We don't track MIPS16 PC-relative offsets closely enough to make
+     a good job of "set .noreorder" code in MIPS16 mode.  */
+  if (TARGET_MIPS16)
+    cfun->machine->all_noreorder_p = false;
+
+  /* Code that doesn't use explicit relocs can't be ".set nomacro".  */
+  if (!TARGET_EXPLICIT_RELOCS)
+    cfun->machine->all_noreorder_p = false;
+
+  /* Profiled functions can't be all noreorder because the profiler
+     support uses assembler macros.  */
+  if (crtl->profile)
+    cfun->machine->all_noreorder_p = false;
+
+  /* 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;
+
+  /* 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;
+
+  htab = htab_create (37, mips_lo_sum_offset_hash,
+                     mips_lo_sum_offset_eq, free);
+
+  /* Make a first pass over the instructions, recording all the LO_SUMs.  */
+  for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn))
+    FOR_EACH_SUBINSN (subinsn, insn)
+      if (INSN_P (subinsn))
+       for_each_rtx (&PATTERN (subinsn), mips_record_lo_sum, htab);
+
+  last_insn = 0;
+  hilo_delay = 2;
+  delayed_reg = 0;
+  lo_reg = gen_rtx_REG (SImode, LO_REGNUM);
+
+  /* Make a second pass over the instructions.  Delete orphaned
+     high-part relocations or turn them into NOPs.  Avoid hazards
+     by inserting NOPs.  */
+  for (insn = get_insns (); insn != 0; insn = next_insn)
+    {
+      next_insn = NEXT_INSN (insn);
+      if (INSN_P (insn))
+       {
+         if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+           {
+             /* If we find an orphaned high-part relocation in a delay
+                slot, it's easier to turn that instruction into a NOP than
+                to delete it.  The delay slot will be a NOP either way.  */
+             FOR_EACH_SUBINSN (subinsn, insn)
+               if (INSN_P (subinsn))
+                 {
+                   if (mips_orphaned_high_part_p (htab, subinsn))
+                     {
+                       PATTERN (subinsn) = gen_nop ();
+                       INSN_CODE (subinsn) = CODE_FOR_nop;
+                     }
+                   mips_avoid_hazard (last_insn, subinsn, &hilo_delay,
+                                      &delayed_reg, lo_reg);
+                 }
+             last_insn = insn;
+           }
+         else
+           {
+             /* INSN is a single instruction.  Delete it if it's an
+                orphaned high-part relocation.  */
+             if (mips_orphaned_high_part_p (htab, insn))
+               delete_insn (insn);
+             /* Also delete cache barriers if the last instruction
+                was an annulled branch.  INSN will not be speculatively
+                executed.  */
+             else if (recog_memoized (insn) == CODE_FOR_r10k_cache_barrier
+                      && last_insn
+                      && INSN_ANNULLED_BRANCH_P (SEQ_BEGIN (last_insn)))
+               delete_insn (insn);
+             else
+               {
+                 mips_avoid_hazard (last_insn, insn, &hilo_delay,
+                                    &delayed_reg, lo_reg);
+                 last_insn = insn;
+               }
+           }
+       }
     }
-  return NULL_RTX;
+
+  htab_delete (htab);
 }
 
-/* Exported to toplev.c.
+/* Implement TARGET_MACHINE_DEPENDENT_REORG.  */
 
-   Do a final pass over the function, just before delayed branch
-   scheduling.  */
+static void
+mips_reorg (void)
+{
+  mips16_lay_out_constants ();
+  if (mips_r10k_cache_barrier != R10K_CACHE_BARRIER_NONE)
+    r10k_insert_cache_barriers ();
+  if (optimize > 0 && flag_delayed_branch)
+    dbr_schedule (get_insns ());
+  mips_reorg_process_insns ();
+  if (!TARGET_MIPS16
+      && TARGET_EXPLICIT_RELOCS
+      && 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.  */
 
-void
-machine_dependent_reorg (first)
-     rtx first;
+static void
+mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
+                     HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+                     tree function)
 {
-  int insns_len, max_internal_pool_size, pool_size, addr, first_constant_ref;
-  rtx insn;
-  struct constant *constants;
+  rtx this_rtx, temp1, temp2, insn, fnaddr;
+  bool use_sibcall_p;
 
-  if (! TARGET_MIPS16)
-    return;
+  /* Pretend to be a post-reload pass while generating rtl.  */
+  reload_completed = 1;
 
-  /* If $gp is used, try to remove stores, and replace loads with
-     copies from $gp.  */
-  if (optimize)
-    mips16_optimize_gp (first);
+  /* Mark the end of the (empty) prologue.  */
+  emit_note (NOTE_INSN_PROLOGUE_END);
 
-  /* Scan the function looking for PC relative loads which may be out
-     of range.  All such loads will either be from the constant table,
-     or be getting the address of a constant string.  If the size of
-     the function plus the size of the constant table is less than
-     0x8000, then all loads are in range.  */
+  /* 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));
 
-  insns_len = 0;
-  for (insn = first; insn; insn = NEXT_INSN (insn))
+  /* Determine if we need to load FNADDR from the GOT.  */
+  if (!use_sibcall_p
+      && (mips_got_symbol_type_p
+         (mips_classify_symbol (fnaddr, SYMBOL_CONTEXT_LEA))))
     {
-      insns_len += get_attr_length (insn);
+      /* 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);
 
-      /* ??? We put switch tables in .text, but we don't define
-         JUMP_TABLES_IN_TEXT_SECTION, so get_attr_length will not
-         compute their lengths correctly.  */
-      if (GET_CODE (insn) == JUMP_INSN)
-       {
-         rtx body;
+      /* Set up the global pointer for n32 or n64 abicalls.  */
+      mips_emit_loadgp ();
+    }
 
-         body = PATTERN (insn);
-         if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
-           insns_len += (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC)
-                         * GET_MODE_SIZE (GET_MODE (body)));
-         insns_len += GET_MODE_SIZE (GET_MODE (body)) - 1;
+  /* 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_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1);
+  else
+    this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST);
+
+  /* Add DELTA to THIS_RTX.  */
+  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_rtx, this_rtx, offset));
+    }
+
+  /* If needed, add *(*THIS_RTX + VCALL_OFFSET) to THIS_RTX.  */
+  if (vcall_offset != 0)
+    {
+      rtx addr;
+
+      /* Set TEMP1 to *THIS_RTX.  */
+      mips_emit_move (temp1, gen_rtx_MEM (Pmode, this_rtx));
+
+      /* Set ADDR to a legitimate address for *THIS_RTX + VCALL_OFFSET.  */
+      addr = mips_add_offset (temp2, temp1, vcall_offset);
+
+      /* Load the offset and add it to THIS_RTX.  */
+      mips_emit_move (temp1, gen_rtx_MEM (Pmode, addr));
+      emit_insn (gen_add3_insn (this_rtx, this_rtx, 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.
+
+        We can use the same technique for MIPS16 code, where $25
+        is not a valid JR register.  */
+      if (TARGET_USE_PIC_FN_ADDR_REG
+         && !TARGET_MIPS16
+         && !mips_dangerous_for_la25_p (fnaddr))
+       temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+      mips_load_call_address (MIPS_CALL_SIBCALL, temp1, fnaddr);
+
+      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 ();
+  free_after_compilation (cfun);
+
+  /* Clean up the vars set above.  Note that final_end_function resets
+     the global pointer for us.  */
+  reload_completed = 0;
+}
+\f
+/* The last argument passed to mips_set_mips16_mode, or negative if the
+   function hasn't been called yet.
 
-  /* Store the original value of insns_len in current_frame_info, so
-     that simple_memory_operand can look at it.  */
-  current_frame_info.insns_len = insns_len;
+   There are two copies of this information.  One is saved and restored
+   by the PCH process while the other is specific to this compiler
+   invocation.  The information calculated by mips_set_mips16_mode
+   is invalid unless the two variables are the same.  */
+static int was_mips16_p = -1;
+static GTY(()) int was_mips16_pch_p = -1;
 
-  pool_size = get_pool_size ();
-  if (insns_len + pool_size + mips_string_length < 0x8000)
+/* 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
+      && mips16_p == was_mips16_pch_p)
     return;
 
-  /* Loop over the insns and figure out what the maximum internal pool
-     size could be.  */
-  max_internal_pool_size = 0;
-  for (insn = first; insn; insn = NEXT_INSN (insn))
+  /* Restore base settings of various flags.  */
+  target_flags = mips_base_target_flags;
+  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)
     {
-      if (GET_CODE (insn) == INSN
-         && GET_CODE (PATTERN (insn)) == SET)
-       {
-         rtx src;
-
-         src = mips_find_symbol (SET_SRC (PATTERN (insn)));
-         if (src == NULL_RTX)
-           continue;
-         if (CONSTANT_POOL_ADDRESS_P (src))
-           max_internal_pool_size += GET_MODE_SIZE (get_pool_mode (src));
-         else if (SYMBOL_REF_FLAG (src))
-           max_internal_pool_size += GET_MODE_SIZE (Pmode);
-       }
-    }
+      /* Switch to MIPS16 mode.  */
+      target_flags |= MASK_MIPS16;
 
-  constants = NULL;
-  addr = 0;
-  first_constant_ref = -1;
+      /* Don't run the scheduler before reload, since it tends to
+         increase register pressure.  */
+      flag_schedule_insns = 0;
 
-  for (insn = first; insn; insn = NEXT_INSN (insn))
-    {
-      if (GET_CODE (insn) == INSN
-         && GET_CODE (PATTERN (insn)) == SET)
-       {
-         rtx val, src;
-         enum machine_mode mode = VOIDmode;
+      /* Don't do hot/cold partitioning.  mips16_lay_out_constants expects
+        the whole function to be in a single section.  */
+      flag_reorder_blocks_and_partition = 0;
 
-         val = NULL_RTX;
-         src = mips_find_symbol (SET_SRC (PATTERN (insn)));
-         if (src != NULL_RTX && CONSTANT_POOL_ADDRESS_P (src))
-           {
-             /* ??? This is very conservative, which means that we
-                 will generate too many copies of the constant table.
-                 The only solution would seem to be some form of
-                 relaxing.  */
-             if (((insns_len - addr)
-                  + max_internal_pool_size
-                  + get_pool_offset (src))
-                 >= 0x8000)
-               {
-                 val = get_pool_constant (src);
-                 mode = get_pool_mode (src);
-               }
-             max_internal_pool_size -= GET_MODE_SIZE (get_pool_mode (src));
-           }
-         else if (src != NULL_RTX && SYMBOL_REF_FLAG (src))
-           {
-             /* Including all of mips_string_length is conservative,
-                 and so is including all of max_internal_pool_size.  */
-             if (((insns_len - addr)
-                  + max_internal_pool_size
-                  + pool_size
-                  + mips_string_length)
-                 >= 0x8000)
-               {
-                 val = src;
-                 mode = Pmode;
-               }
-             max_internal_pool_size -= Pmode;
-           }
+      /* 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 (val != NULL_RTX)
-           {
-             rtx lab, newsrc;
-
-             /* This PC relative load is out of range.  ??? In the
-                case of a string constant, we are only guessing that
-                it is range, since we don't know the offset of a
-                particular string constant.  */
-
-             lab = add_constant (&constants, val, mode);
-             newsrc = gen_rtx (MEM, mode,
-                               gen_rtx (LABEL_REF, VOIDmode, lab));
-             RTX_UNCHANGING_P (newsrc) = 1;
-             PATTERN (insn) = gen_rtx (SET, VOIDmode,
-                                       SET_DEST (PATTERN (insn)),
-                                       newsrc);
-             INSN_CODE (insn) = -1;
-
-             if (first_constant_ref < 0)
-               first_constant_ref = addr;
-           }
-       }
+      target_flags |= MASK_EXPLICIT_RELOCS;
 
-      addr += get_attr_length (insn);
+      /* Experiments suggest we get the best overall section-anchor
+        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;
 
-      /* ??? We put switch tables in .text, but we don't define
-         JUMP_TABLES_IN_TEXT_SECTION, so get_attr_length will not
-         compute their lengths correctly.  */
-      if (GET_CODE (insn) == JUMP_INSN)
-       {
-         rtx body;
+      if (flag_pic && !TARGET_OLDABI)
+       sorry ("MIPS16 PIC for ABIs other than o32 and o64");
 
-         body = PATTERN (insn);
-         if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
-           addr += (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC)
-                         * GET_MODE_SIZE (GET_MODE (body)));
-         addr += GET_MODE_SIZE (GET_MODE (body)) - 1;
-       }
+      if (TARGET_XGOT)
+       sorry ("MIPS16 -mxgot code");
 
-      if (GET_CODE (insn) == BARRIER)
-       {
-         /* Output any constants we have accumulated.  Note that we
-             don't need to change ADDR, since its only use is
-             subtraction from INSNS_LEN, and both would be changed by
-             the same amount.
-            ??? If the instructions up to the next barrier reuse a
-            constant, it would often be better to continue
-            accumulating.  */
-         if (constants != NULL)
-           dump_constants (constants, insn);
-         constants = NULL;
-         first_constant_ref = -1;
-       }
+      if (TARGET_HARD_FLOAT_ABI && !TARGET_OLDABI)
+       sorry ("hard-float MIPS16 code for ABIs other than o32 and o64");
+    }
+  else
+    {
+      /* Switch to normal (non-MIPS16) mode.  */
+      target_flags &= ~MASK_MIPS16;
 
-      if (constants != NULL
-              && (NEXT_INSN (insn) == NULL
-                  || (first_constant_ref >= 0
-                      && (((addr - first_constant_ref)
-                           + 2 /* for alignment */
-                           + 2 /* for a short jump insn */
-                           + pool_size)
-                          >= 0x8000))))
+      /* Provide default values for align_* for 64-bit targets.  */
+      if (TARGET_64BIT)
        {
-         /* If we haven't had a barrier within 0x8000 bytes of a
-             constant reference or we are at the end of the function,
-             emit a barrier now.  */
-
-         rtx label, jump, barrier;
-
-         label = gen_label_rtx ();
-         jump = emit_jump_insn_after (gen_jump (label), insn);
-         JUMP_LABEL (jump) = label;
-         LABEL_NUSES (label) = 1;
-         barrier = emit_barrier_after (jump);
-         emit_label_after (label, barrier);
-         first_constant_ref = -1;
+         if (align_loops == 0)
+           align_loops = 8;
+         if (align_jumps == 0)
+           align_jumps = 8;
+         if (align_functions == 0)
+           align_functions = 8;
        }
-     }
 
-  /* ??? If we output all references to a constant in internal
-     constants table, we don't need to output the constant in the real
-     constant table, but we have no way to prevent that.  */
+      targetm.min_anchor_offset = -32768;
+      targetm.max_anchor_offset = 32767;
+    }
+
+  /* (Re)initialize MIPS target internals for new ISA.  */
+  mips_init_relocs ();
+
+  if (was_mips16_p >= 0 || was_mips16_pch_p >= 0)
+    /* Reinitialize target-dependent state.  */
+    target_reinit ();
+
+  was_mips16_p = mips16_p;
+  was_mips16_pch_p = mips16_p;
 }
 
-/* Return nonzero if X is a SIGN or ZERO extend operator.  */
-int
-extend_operator (x, mode)
-     rtx x;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+/* 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)
 {
-  enum rtx_code code = GET_CODE (x);
-  return code == SIGN_EXTEND || code == ZERO_EXTEND;
+  mips_set_mips16_mode (mips_use_mips16_mode_p (fndecl));
 }
+\f
+/* Allocate a chunk of memory for per-function machine-dependent data.  */
 
-/* Accept any operator that can be used to shift the high half of the
-   input value to the lower half, suitable for truncation.  The
-   remainder (the lower half of the input, and the upper half of the
-   output) will be discarded.  */
-int
-highpart_shift_operator (x, mode)
-     rtx x;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+static struct machine_function *
+mips_init_machine_status (void)
 {
-  enum rtx_code code = GET_CODE (x);
-  return (code == LSHIFTRT
-         || code == ASHIFTRT
-         || code == ROTATERT
-         || code == ROTATE);
+  return ((struct machine_function *)
+         ggc_alloc_cleared (sizeof (struct machine_function)));
 }
 
-/* Return the length of INSN.  LENGTH is the initial length computed by
-   attributes in the machine-description file.  */
+/* Return the processor associated with the given ISA level, or null
+   if the ISA isn't valid.  */
 
-int
-mips_adjust_insn_length (insn, length)
-     rtx insn;
-     int length;
+static const struct mips_cpu_info *
+mips_cpu_info_from_isa (int isa)
 {
-  /* 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 (simplejump_p (insn)
-      || (!TARGET_MIPS16  && (GET_CODE (insn) == JUMP_INSN
-                             || GET_CODE (insn) == CALL_INSN)))
-    length += 4;
+  unsigned int i;
 
-  /* All MIPS16 instructions are a measly two bytes.  */
-  if (TARGET_MIPS16)
-    length /= 2;
+  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 length;
+  return NULL;
 }
 
-/* Output assembly instructions to peform a conditional branch.
+/* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL
+   with a final "000" replaced by "k".  Ignore case.
 
-   INSN is the branch instruction.  OPERANDS[0] is the condition.
-   OPERANDS[1] is the target of the branch.  OPERANDS[2] is the target
-   of the first operand to the condition.  If TWO_OPERANDS_P is
-   non-zero the comparison takes two operands; OPERANDS[3] will be the
-   second operand.
+   Note: this function is shared between GCC and GAS.  */
 
-   If INVERTED_P is non-zero we are to branch if the condition does
-   not hold.  If FLOAT_P is non-zero this is a floating-point comparison.
+static bool
+mips_strict_matching_cpu_name_p (const char *canonical, const char *given)
+{
+  while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
+    given++, canonical++;
 
-   LENGTH is the length (in bytes) of the sequence we are to generate.
-   That tells us whether to generate a simple conditional branch, or a
-   reversed conditional branch around a `jr' instruction.  */
-const char *
-mips_output_conditional_branch (insn,
-                               operands,
-                               two_operands_p,
-                               float_p,
-                               inverted_p,
-                               length)
-     rtx insn;
-     rtx *operands;
-     int two_operands_p;
-     int float_p;
-     int inverted_p;
-     int length;
-{
-  static char buffer[200];
-  /* The kind of comparison we are doing.  */
-  enum rtx_code code = GET_CODE (operands[0]);
-  /* Non-zero if the opcode for the comparison needs a `z' indicating
-     that it is a comparision against zero.  */
-  int need_z_p;
-  /* A string to use in the assembly output to represent the first
-     operand.  */
-  const char *op1 = "%z2";
-  /* A string to use in the assembly output to represent the second
-     operand.  Use the hard-wired zero register if there's no second
-     operand.  */
-  const char *op2 = (two_operands_p ? ",%z3" : ",%.");
-  /* The operand-printing string for the comparison.  */
-  const char *const comp = (float_p ? "%F0" : "%C0");
-  /* The operand-printing string for the inverted comparison.  */
-  const char *const inverted_comp = (float_p ? "%W0" : "%N0");
-
-  /* The MIPS processors (for levels of the ISA at least two), have
-     "likely" variants of each branch instruction.  These instructions
-     annul the instruction in the delay slot if the branch is not
-     taken.  */
-  mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
-
-  if (!two_operands_p)
-    {
-      /* To compute whether than A > B, for example, we normally
-        subtract B from A and then look at the sign bit.  But, if we
-        are doing an unsigned comparison, and B is zero, we don't
-        have to do the subtraction.  Instead, we can just check to
-        see if A is non-zero.  Thus, we change the CODE here to
-        reflect the simpler comparison operation.  */
-      switch (code)
-       {
-       case GTU:
-         code = NE;
-         break;
+  return ((*given == 0 && *canonical == 0)
+         || (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0));
+}
 
-       case LEU:
-         code = EQ;
-         break;
+/* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied
+   CPU name.  We've traditionally allowed a lot of variation here.
 
-       case GEU:
-         /* A condition which will always be true.  */
-         code = EQ;
-         op1 = "%.";
-         break;
+   Note: this function is shared between GCC and GAS.  */
 
-       case LTU:
-         /* A condition which will always be false.  */
-         code = NE;
-         op1 = "%.";
-         break;
+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;
 
-       default:
-         /* Not a special case.  */
-         break;
-       }
-    }
+  /* 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;
 
-  /* Relative comparisons are always done against zero.  But
-     equality comparisons are done between two operands, and therefore
-     do not require a `z' in the assembly language output.  */
-  need_z_p = (!float_p && code != EQ && code != NE);
-  /* For comparisons against zero, the zero is not provided
-     explicitly.  */
-  if (need_z_p)
-    op2 = "";
+  /* 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;
 
-  /* Begin by terminating the buffer.  That way we can always use
-     strcat to add to it.  */
-  buffer[0] = '\0';
+  return mips_strict_matching_cpu_name_p (canonical, given);
+}
 
-  switch (length)
-    {
-    case 4:
-    case 8:
-      /* Just a simple conditional branch.  */
-      if (float_p)
-       sprintf (buffer, "%%*b%s%%?\t%%Z2%%1",
-                inverted_p ? inverted_comp : comp);
-      else
-       sprintf (buffer, "%%*b%s%s%%?\t%s%s,%%1",
-                inverted_p ? inverted_comp : comp,
-                need_z_p ? "z" : "",
-                op1,
-                op2);
-      return buffer;
-
-    case 12:
-    case 16:
-      {
-       /* Generate a reversed conditional branch around ` j'
-          instruction:
-
-               .set noreorder
-               .set nomacro
-               bc    l
-               nop
-               j     target
-               .set macro
-               .set reorder
-            l:
-
-       */
-
-        rtx orig_target;
-       rtx target = gen_label_rtx ();
-
-        output_asm_insn ("%(%<", 0);
-        orig_target = operands[1];
-        operands[1] = target;
-       /* Generate the reversed comparison.  This takes four
-          bytes.  */
-       if (float_p)
-         sprintf (buffer, "%%*b%s\t%%Z2%%1",
-                  inverted_p ? comp : inverted_comp);
-       else
-         sprintf (buffer, "%%*b%s%s\t%s%s,%%1",
-                  inverted_p ? comp : inverted_comp,
-                  need_z_p ? "z" : "",
-                  op1,
-                  op2);
-        output_asm_insn (buffer, operands);
-        operands[1] = orig_target;
-
-       output_asm_insn ("nop\n\tj\t%1", operands);
-
-        if (length == 16)
-         output_asm_insn ("nop", 0);
-        else
-          {
-            /* Output delay slot instruction.  */
-            rtx insn = final_sequence;
-            final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file,
-                             optimize, 0, 1);
-            INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
-          }
-       output_asm_insn ("%>%)", 0);
-        ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
-                                   CODE_LABEL_NUMBER (target));
-        return "";
-      }
+/* Return the mips_cpu_info entry for the processor or ISA given
+   by CPU_STRING.  Return null if the string isn't recognized.
 
-    /* We do not currently use this code.  It handles jumps to
-       arbitrary locations, using `jr', even across a 256MB boundary.
-       We could add a -mhuge switch, and then use this code instead of
-       the `j' alternative above when -mhuge was used.  */
-#if 0
-    case 16:
-    case 20:
+   A similar function exists in GAS.  */
+
+static const struct mips_cpu_info *
+mips_parse_cpu (const char *cpu_string)
+{
+  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))
       {
-       /* Generate a reversed conditional branch around a `jr'
-          instruction:
-
-                .set noreorder
-                .set nomacro
-                .set noat
-                bc    l
-                la    $at, target
-                jr    $at
-                .set at
-                .set macro
-                .set reorder
-             l:
-
-          Not pretty, but allows a conditional branch anywhere in the
-          32-bit address space.  If the original branch is annulled,
-          then the instruction in the delay slot should be executed
-          only if the branch is taken.  The la instruction is really
-          a macro which will usually take eight bytes, but sometimes
-          takes only four, if the instruction to which we're jumping
-          gets its own entry in the global pointer table, which will
-          happen if its a case label.  The assembler will then
-          generate only a four-byte sequence, rather than eight, and
-          there seems to be no way to tell it not to.  Thus, we can't
-          just use a `.+x' addressing form; we don't know what value
-          to give for `x'.
-
-          So, we resort to using the explicit relocation syntax
-          available in the assembler and do:
-
-             lw $at,%got_page(target)($gp)
-             daddiu $at,$at,%got_ofst(target)
-
-          That way, this always takes up eight bytes, and we can use
-          the `.+x' form.  Of course, these explicit machinations
-          with relocation will not work with old assemblers.  Then
-          again, neither do out-of-range branches, so we haven't lost
-          anything.  */
-
-       /* The target of the reversed branch.  */
-       const char *const target
-         = ((mips_branch_likely || length == 20) ? ".+20" : ".+16");
-       const char *at_register = mips_reg_names[ASSEMBLER_SCRATCH_REGNUM];
-       const char *gp_register = mips_reg_names[PIC_OFFSET_TABLE_REGNUM];
-       char *c;
-
-       strcpy (buffer, "%(%<%[");
-       c = strchr (buffer, '\0');
-       /* Generate the reversed comparision.  This takes four
-          bytes.  */
-       if (float_p)
-         sprintf (c, "%%*b%s\t%%Z2%s",
-                  inverted_p ? comp : inverted_comp,
-                  target);
-       else
-         sprintf (c, "%%*b%s%s\t%s%s,%s",
-                  inverted_p ? comp : inverted_comp,
-                  need_z_p ? "z" : "",
-                  op1,
-                  op2,
-                  target);
-       c = strchr (buffer, '\0');
-       /* Generate the load-address, and jump.  This takes twelve
-          bytes, for a total of 16.  */
-       sprintf (c,
-                "\n\tlw\t%s,%%%%got_page(%%1)(%s)\n\tdaddiu\t%s,%s,%%%%got_ofst(%%1)\n\tjr\t%s",
-                at_register,
-                gp_register,
-                at_register,
-                at_register,
-                at_register);
-       if (length == 20)
-         /* The delay slot was unfilled.  Since we're inside
-            .noreorder, the assembler will not fill in the NOP for
-            us, so we must do it ourselves.  */
-         strcat (buffer, "\n\tnop");
-       strcat (buffer, "%]%>%)");
-       return buffer;
+       warning (0, "CPU names must be lower case");
+       break;
       }
-#endif
 
-    default:
-      abort ();
-    }
+  /* '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.  */
+  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));
 
-  /* NOTREACHED */
-  return 0;
+  /* 'default' has traditionally been a no-op.  Probably not very useful.  */
+  if (strcasecmp (cpu_string, "default") == 0)
+    return NULL;
+
+  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 NULL;
 }
 
-/* Called to register all of our global variables with the garbage
-   collector.  */
+/* Set up globals to generate code for the ISA or processor
+   described by INFO.  */
 
 static void
-mips_add_gc_roots ()
+mips_set_architecture (const struct mips_cpu_info *info)
 {
-  ggc_add_rtx_root (&mips_load_reg, 1);
-  ggc_add_rtx_root (&mips_load_reg2, 1);
-  ggc_add_rtx_root (&mips_load_reg3, 1);
-  ggc_add_rtx_root (&mips_load_reg4, 1);
-  ggc_add_rtx_root (branch_cmp, sizeof (branch_cmp) / sizeof (rtx));
-  ggc_add_rtx_root (&embedded_pic_fnaddr_rtx, 1);
-  ggc_add_rtx_root (&mips16_gp_pseudo_rtx, 1);
+  if (info != 0)
+    {
+      mips_arch_info = info;
+      mips_arch = info->cpu;
+      mips_isa = info->isa;
+    }
 }
 
-static enum processor_type
-mips_parse_cpu (cpu_string)
-     const char *cpu_string;
-{
-  const char *p = cpu_string;
-  int seen_v = 0;
-  enum processor_type cpu;
-
-  /* We need to cope with the various "vr" prefixes for the NEC 4300
-     and 4100 processors.  */
-  if (*p == 'v' || *p == 'V')
-    seen_v = 1, p++;
+/* Likewise for tuning.  */
 
-  if (*p == 'r' || *p == 'R')
-    p++;
+static void
+mips_set_tune (const struct mips_cpu_info *info)
+{
+  if (info != 0)
+    {
+      mips_tune_info = info;
+      mips_tune = info->cpu;
+    }
+}
 
-  /* Since there is no difference between a R2000 and R3000 in
-     terms of the scheduler, we collapse them into just an R3000.  */
+/* Implement TARGET_HANDLE_OPTION.  */
 
-  cpu = PROCESSOR_DEFAULT;
-  switch (*p)
+static bool
+mips_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+{
+  switch (code)
     {
-    case '2':
-      if (!strcmp (p, "2000") || !strcmp (p, "2k") || !strcmp (p, "2K"))
-       cpu = PROCESSOR_R3000;
-      else if (!strcmp (p, "20kc") || !strcmp (p, "20Kc") )
-        cpu = PROCESSOR_R20KC;
-      break;
+    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 '3':
-      if (!strcmp (p, "3000") || !strcmp (p, "3k") || !strcmp (p, "3K"))
-       cpu = PROCESSOR_R3000;
-      else if (!strcmp (p, "3900"))
-       cpu = PROCESSOR_R3900;
-      break;
+    case OPT_march_:
+    case OPT_mtune_:
+      return mips_parse_cpu (arg) != 0;
 
-    case '4':
-      if (!strcmp (p, "4000") || !strcmp (p, "4k") || !strcmp (p, "4K"))
-       cpu = PROCESSOR_R4000;
-      /* The vr4100 is a non-FP ISA III processor with some extra
-        instructions.  */
-      else if (!strcmp (p, "4100"))
-         cpu = PROCESSOR_R4100;
-      /* The vr4300 is a standard ISA III processor, but with a different
-        pipeline.  */
-      else if (!strcmp (p, "4300"))
-       cpu = PROCESSOR_R4300;
-      /* The r4400 is exactly the same as the r4000 from the compiler's
-        viewpoint.  */
-      else if (!strcmp (p, "4400"))
-       cpu = PROCESSOR_R4000;
-      else if (!strcmp (p, "4600"))
-       cpu = PROCESSOR_R4600;
-      else if (!strcmp (p, "4650"))
-       cpu = PROCESSOR_R4650;
-      /* The 4kc and 4kp processor cores are the same for
-        scheduling purposes; they both implement the MIPS32
-        ISA and only differ in their memory management
-        methods.  */
-      else if (!strcmp (p, "4kc") || !strcmp (p, "4Kc")
-               || !strcmp (p, "4kp") || !strcmp (p, "4Kp") )
-       cpu = PROCESSOR_R4KC;
-      break;
+    case OPT_mips:
+      mips_isa_option_info = mips_parse_cpu (ACONCAT (("mips", arg, NULL)));
+      return mips_isa_option_info != 0;
 
-    case '5':
-      if (!strcmp (p, "5000") || !strcmp (p, "5k") || !strcmp (p, "5K"))
-       cpu = PROCESSOR_R5000;
-      else if (!strcmp (p, "5kc") || !strcmp (p, "5Kc") )
-          cpu = PROCESSOR_R5KC;
-      break;
+    case OPT_mno_flush_func:
+      mips_cache_flush_func = NULL;
+      return true;
 
-    case '6':
-      if (!strcmp (p, "6000") || !strcmp (p, "6k") || !strcmp (p, "6K"))
-       cpu = PROCESSOR_R6000;
-      break;
+    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;
 
-    case '8':
-      if (!strcmp (p, "8000"))
-       cpu = PROCESSOR_R8000;
-      break;
+    case OPT_mr10k_cache_barrier_:
+      if (strcmp (arg, "load-store") == 0)
+       mips_r10k_cache_barrier = R10K_CACHE_BARRIER_LOAD_STORE;
+      else if (strcmp (arg, "store") == 0)
+       mips_r10k_cache_barrier = R10K_CACHE_BARRIER_STORE;
+      else if (strcmp (arg, "none") == 0)
+       mips_r10k_cache_barrier = R10K_CACHE_BARRIER_NONE;
+      else
+       return false;
+      return true;
 
-    case 'o':
-      if (!strcmp (p, "orion"))
-       cpu = PROCESSOR_R4600;
-      break;
+    default:
+      return true;
     }
-
-  if (seen_v
-      && cpu != PROCESSOR_R4300
-      && cpu != PROCESSOR_R4100
-      && cpu != PROCESSOR_R5000)
-    cpu = PROCESSOR_DEFAULT;
-
-  return cpu;
 }
 
-/* Adjust the cost of INSN based on the relationship between INSN that
-   is dependent on DEP_INSN through the dependence LINK.  The default
-   is to make no adjustment to COST.
+/* Implement OVERRIDE_OPTIONS.  */
 
-   On the MIPS, ignore the cost of anti- and output-dependencies.  */
-static int
-mips_adjust_cost (insn, link, dep, cost)
-     rtx insn ATTRIBUTE_UNUSED;
-     rtx link;
-     rtx dep ATTRIBUTE_UNUSED;
-     int cost;
+void
+mips_override_options (void)
 {
-  if (REG_NOTE_KIND (link) != 0)
-    return 0;  /* Anti or output dependence.  */
-  return cost;
-}
+  int i, start, regno, mode;
 
-/* Cover function for UNIQUE_SECTION.  */
+  /* Process flags as though we were generating non-MIPS16 code.  */
+  mips_base_mips16 = TARGET_MIPS16;
+  target_flags &= ~MASK_MIPS16;
 
-void
-mips_unique_section (decl, reloc)
-     tree decl;
-     int reloc;
-{
-  int len, size, sec;
-  const char *name, *prefix;
-  char *string;
-  static const char *const prefixes[4][2] = {
-    { ".text.", ".gnu.linkonce.t." },
-    { ".rodata.", ".gnu.linkonce.r." },
-    { ".data.", ".gnu.linkonce.d." },
-    { ".sdata.", ".gnu.linkonce.s." }
-  };
+#ifdef SUBTARGET_OVERRIDE_OPTIONS
+  SUBTARGET_OVERRIDE_OPTIONS;
+#endif
 
-  name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
-  size = int_size_in_bytes (TREE_TYPE (decl));
+  /* Set the small data limit.  */
+  mips_small_data_threshold = (g_switch_set
+                              ? g_switch_value
+                              : MIPS_DEFAULT_GVALUE);
 
-  /* Determine the base section we are interested in:
-     0=text, 1=rodata, 2=data, 3=sdata, [4=bss].  */
-  if (TREE_CODE (decl) == FUNCTION_DECL)
-    sec = 0;
-  else if (DECL_INITIAL (decl) == 0
-           || DECL_INITIAL (decl) == error_mark_node)
-    sec = 2;
-  else if ((TARGET_EMBEDDED_PIC || TARGET_MIPS16)
-      && TREE_CODE (decl) == STRING_CST
-      && !flag_writable_strings)
-    {
-      /* For embedded position independent code, put constant
-        strings in the text section, because the data section
-        is limited to 64K in size.  For mips16 code, put
-        strings in the text section so that a PC relative load
-        instruction can be used to get their address.  */
-      sec = 0;
+  /* 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.  */
+
+  if (mips_arch_string != 0)
+    mips_set_architecture (mips_parse_cpu (mips_arch_string));
+
+  if (mips_isa_option_info != 0)
+    {
+      if (mips_arch_info == 0)
+       mips_set_architecture (mips_isa_option_info);
+      else if (mips_arch_info->isa != mips_isa_option_info->isa)
+       error ("%<-%s%> conflicts with the other architecture options, "
+              "which specify a %s processor",
+              mips_isa_option_info->name,
+              mips_cpu_info_from_isa (mips_arch_info->isa)->name);
     }
-  else if (TARGET_EMBEDDED_DATA)
+
+  if (mips_arch_info == 0)
     {
-      /* For embedded applications, always put an object in
-        read-only data if possible, in order to reduce RAM
-        usage.  */
+#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
+    }
 
-      if (DECL_READONLY_SECTION (decl, reloc))
-       sec = 1;
-      else if (size > 0 && size <= mips_section_threshold)
-       sec = 3;
-      else
-       sec = 2;
+  if (ABI_NEEDS_64BIT_REGS && !ISA_HAS_64BIT_REGS)
+    error ("%<-march=%s%> is not compatible with the selected ABI",
+          mips_arch_info->name);
+
+  /* Optimize for mips_arch, unless -mtune selects a different processor.  */
+  if (mips_tune_string != 0)
+    mips_set_tune (mips_parse_cpu (mips_tune_string));
+
+  if (mips_tune_info == 0)
+    mips_set_tune (mips_arch_info);
+
+  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
     {
-      /* For hosted applications, always put an object in
-        small data if possible, as this gives the best
-        performance.  */
+      /* 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 (size > 0 && size <= mips_section_threshold)
-       sec = 3;
-      else if (DECL_READONLY_SECTION (decl, reloc))
-       sec = 1;
+  if ((target_flags_explicit & MASK_FLOAT64) != 0)
+    {
+      if (TARGET_SINGLE_FLOAT && TARGET_FLOAT64)
+       error ("unsupported combination: %s", "-mfp64 -msingle-float");
+      else if (TARGET_64BIT && TARGET_DOUBLE_FLOAT && !TARGET_FLOAT64)
+       error ("unsupported combination: %s", "-mgp64 -mfp32 -mdouble-float");
+      else if (!TARGET_64BIT && TARGET_FLOAT64)
+       {
+         if (!ISA_HAS_MXHC1)
+           error ("%<-mgp32%> and %<-mfp64%> can only be combined if"
+                  " the target supports the mfhc1 and mthc1 instructions");
+         else if (mips_abi != ABI_32)
+           error ("%<-mgp32%> and %<-mfp64%> can only be combined when using"
+                  " the o32 ABI");
+       }
+    }
+  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
-       sec = 2;
+       target_flags &= ~MASK_FLOAT64;
     }
 
-  prefix = prefixes[sec][DECL_ONE_ONLY (decl)];
-  len = strlen (name) + strlen (prefix);
-  string = alloca (len + 1);
-  sprintf (string, "%s%s", prefix, name);
+  /* End of code shared with GAS.  */
 
-  DECL_SECTION_NAME (decl) = build_string (len, string);
-}
+  /* If no -mlong* option was given, infer it from the other options.  */
+  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;
+    }
 
-unsigned int
-mips_hard_regno_nregs (regno, mode)
-    int regno;
-    enum machine_mode mode;
-{
-  if (! FP_REG_P (regno))
-    return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD);
+  if (!TARGET_OLDABI)
+    flag_pcc_struct_return = 0;
+
+  /* Decide which rtx_costs structure to use.  */
+  if (optimize_size)
+    mips_cost = &mips_rtx_cost_optimize_size;
   else
-    return ((GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG);
-}
+    mips_cost = &mips_rtx_cost_data[mips_tune];
+
+  /* If the user hasn't specified a branch cost, use the processor's
+     default.  */
+  if (mips_branch_cost == 0)
+    mips_branch_cost = mips_cost->branch_cost;
+
+  /* If 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 ((target_flags_explicit & MASK_BRANCHLIKELY) == 0)
+    {
+      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);
 
-\f
-#ifdef TARGET_IRIX6
-/* Output assembly to switch to section NAME with attribute FLAGS.  */
+  /* 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;
+    }
 
-static void
-iris6_asm_named_section_1 (name, flags, align)
-     const char *name;
-     unsigned int flags;
-     unsigned int align;
-{
-  unsigned int sh_type, sh_flags, sh_entsize;
-
-  sh_flags = 0;
-  if (!(flags & SECTION_DEBUG))
-    sh_flags |= 2; /* SHF_ALLOC */
-  if (flags & SECTION_WRITE)
-    sh_flags |= 1; /* SHF_WRITE */
-  if (flags & SECTION_CODE)
-    sh_flags |= 4; /* SHF_EXECINSTR */
-  if (flags & SECTION_SMALL)
-    sh_flags |= 0x10000000; /* SHF_MIPS_GPREL */
-  if (strcmp (name, ".debug_frame") == 0)
-    sh_flags |= 0x08000000; /* SHF_MIPS_NOSTRIP */
-  if (flags & SECTION_DEBUG)
-    sh_type = 0x7000001e; /* SHT_MIPS_DWARF */
-  else if (flags & SECTION_BSS)
-    sh_type = 8; /* SHT_NOBITS */
-  else
-    sh_type = 1; /* SHT_PROGBITS */
+  if (TARGET_ABICALLS_PIC2)
+    /* 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.)
 
-  if (flags & SECTION_CODE)
-    sh_entsize = 4;
-  else
-    sh_entsize = 0;
+       There is a body of code that uses __PIC__ to distinguish
+       between -mabicalls and -mno-abicalls code.  The non-__PIC__
+       variant is usually appropriate for TARGET_ABICALLS_PIC0, as
+       long as any indirect jumps use $25.  */
+    flag_pic = 1;
 
-  fprintf (asm_out_file, "\t.section %s,%#x,%#x,%u,%u\n",
-          name, sh_type, sh_flags, sh_entsize, align);
-}
+  /* -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;
 
-static void
-iris6_asm_named_section (name, flags)
-     const char *name;
-     unsigned int flags;
-{
-  if (TARGET_FILE_SWITCHING && (flags & SECTION_CODE))
-    asm_out_file = asm_out_text_file;
-  iris6_asm_named_section_1 (name, flags, 0);
-}
+  /* Prefer a call to memcpy over inline code when optimizing for size,
+     though see MOVE_RATIO in mips.h.  */
+  if (optimize_size && (target_flags_explicit & MASK_MEMCPY) == 0)
+    target_flags |= MASK_MEMCPY;
+
+  /* If we have a nonzero small-data limit, check that the -mgpopt
+     setting is consistent with the other target flags.  */
+  if (mips_small_data_threshold > 0)
+    {
+      if (!TARGET_GPOPT)
+       {
+         if (!TARGET_EXPLICIT_RELOCS)
+           error ("%<-mno-gpopt%> needs %<-mexplicit-relocs%>");
 
-/* In addition to emitting a .align directive, record the maximum
-   alignment requested for the current section.  */
+         TARGET_LOCAL_SDATA = false;
+         TARGET_EXTERN_SDATA = false;
+       }
+      else
+       {
+         if (TARGET_VXWORKS_RTP)
+           warning (0, "cannot use small-data accesses for %qs", "-mrtp");
 
-struct iris_section_align_entry
-{
-  const char *name;
-  unsigned int log;
-  unsigned int flags;
-};
+         if (TARGET_ABICALLS)
+           warning (0, "cannot use small-data accesses for %qs",
+                    "-mabicalls");
+       }
+    }
 
-static htab_t iris_section_align_htab;
-static FILE *iris_orig_asm_out_file;
+#ifdef MIPS_TFMODE_FORMAT
+  REAL_MODE_FORMAT (TFmode) = &MIPS_TFMODE_FORMAT;
+#endif
 
-static int
-iris_section_align_entry_eq (p1, p2)
-     const PTR p1;
-     const PTR p2;
-{
-  const struct iris_section_align_entry *old = p1;
-  const char *new = p2;
+  /* 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%>");
+
+  /* If TARGET_MIPS3D, enable MASK_PAIRED_SINGLE_FLOAT.  */
+  if (TARGET_MIPS3D)
+    target_flags |= MASK_PAIRED_SINGLE_FLOAT;
+
+  /* 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 ("%qs must be used with %qs",
+          TARGET_MIPS3D ? "-mips3d" : "-mpaired-single",
+          TARGET_HARD_FLOAT_ABI ? "-mfp64" : "-mhard-float");
+
+  /* Make sure that the ISA supports TARGET_PAIRED_SINGLE_FLOAT when it is
+     enabled.  */
+  if (TARGET_PAIRED_SINGLE_FLOAT && !ISA_HAS_PAIRED_SINGLE)
+    warning (0, "the %qs architecture does not support paired-single"
+            " instructions", mips_arch_info->name);
+
+  if (mips_r10k_cache_barrier != R10K_CACHE_BARRIER_NONE
+      && !TARGET_CACHE_BUILTIN)
+    {
+      error ("%qs requires a target that provides the %qs instruction",
+            "-mr10k-cache-barrier", "cache");
+      mips_r10k_cache_barrier = R10K_CACHE_BARRIER_NONE;
+    }
+
+  /* If TARGET_DSPR2, enable MASK_DSP.  */
+  if (TARGET_DSPR2)
+    target_flags |= MASK_DSP;
+
+  /* Use the traditional method of generating .eh_frames.
+     We need this for two reasons:
+
+     - .eh_frame addresses should be the same width as a C pointer.
+       Most MIPS ABIs support only one pointer size, so the assembler
+       will usually know exactly how big an .eh_frame address is.
+
+       Unfortunately, this is not true of the 64-bit EABI.  The ABI was
+       originally defined to use 64-bit pointers (i.e. it is LP64), and
+       this is still the default mode.  However, we also support an n32-like
+       ILP32 mode, which is selected by -mlong32.  The problem is that the
+       assembler has traditionally not had an -mlong option, so it has
+       traditionally not known whether we're using the ILP32 or LP64 form.
+
+       As it happens, gas versions up to and including 2.19 use _32-bit_
+       addresses for EABI64 .cfi_* directives.  This is wrong for the
+       default LP64 mode, so we can't use the directives by default.
+       Moreover, since gas's current behavior is at odds with gcc's
+       default behavior, it seems unwise to rely on future versions
+       of gas behaving the same way.  We therefore avoid using .cfi
+       directives for -mlong32 as well.
+
+     - .cfi* directives generate read-only .eh_frame sections.
+       However, MIPS has traditionally not allowed directives like:
+
+           .long   x-.
+
+       in cases where "x" is in a different section, or is not defined
+       in the same assembly file.  We have therefore traditionally
+       used absolute addresses and a writable .eh_frame instead.
+
+       The linker is able to convert most of these absolute addresses
+       into PC-relative form where doing so is necessary to avoid
+       relocations.  However, until 2.21, it wasn't able to do this
+       for indirect encodings or personality routines.
+
+       GNU ld 2.21 and GCC 4.5 have support for read-only .eh_frames,
+       but for the time being, we should stick to the approach used
+       in 4.3 and earlier.  */
+  flag_dwarf2_cfi_asm = 0;
+
+  mips_init_print_operand_punct ();
 
-  return strcmp (old->name, new) == 0;
-}
+  /* Set up array to map GCC register number to debug register number.
+     Ignore the special purpose register numbers.  */
 
-static hashval_t
-iris_section_align_entry_hash (p)
-     const PTR p;
+  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;
+    }
+
+  start = GP_DBX_FIRST - GP_REG_FIRST;
+  for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
+    mips_dbx_regno[i] = i + start;
+
+  start = FP_DBX_FIRST - FP_REG_FIRST;
+  for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
+    mips_dbx_regno[i] = i + start;
+
+  /* Accumulator debug registers use big-endian ordering.  */
+  mips_dbx_regno[HI_REGNUM] = MD_DBX_FIRST + 0;
+  mips_dbx_regno[LO_REGNUM] = MD_DBX_FIRST + 1;
+  mips_dwarf_regno[HI_REGNUM] = MD_REG_FIRST + 0;
+  mips_dwarf_regno[LO_REGNUM] = MD_REG_FIRST + 1;
+  for (i = DSP_ACC_REG_FIRST; i <= DSP_ACC_REG_LAST; i += 2)
+    {
+      mips_dwarf_regno[i + TARGET_LITTLE_ENDIAN] = i;
+      mips_dwarf_regno[i + TARGET_BIG_ENDIAN] = i + 1;
+    }
+
+  /* Set up mips_hard_regno_mode_ok.  */
+  for (mode = 0; mode < MAX_MACHINE_MODE; mode++)
+    for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+      mips_hard_regno_mode_ok[(int)mode][regno]
+       = mips_hard_regno_mode_ok_p (regno, mode);
+
+  /* Function to allocate machine-dependent function status.  */
+  init_machine_status = &mips_init_machine_status;
+
+  /* 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;
+
+  /* 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;
+
+  /* Default to working around R10000 errata only if the processor
+     was selected explicitly.  */
+  if ((target_flags_explicit & MASK_FIX_R10000) == 0
+      && mips_matching_cpu_name_p (mips_arch_info->name, "r10000"))
+    target_flags |= MASK_FIX_R10000;
+
+  /* Make sure that branch-likely instructions available when using
+     -mfix-r10000.  The instructions are not available if either:
+
+       1. -mno-branch-likely was passed.
+       2. The selected ISA does not support branch-likely and
+          the command line does not include -mbranch-likely.  */
+  if (TARGET_FIX_R10000
+      && ((target_flags_explicit & MASK_BRANCHLIKELY) == 0
+          ? !ISA_HAS_BRANCHLIKELY
+          : !TARGET_BRANCHLIKELY))
+    sorry ("%qs requires branch-likely instructions", "-mfix-r10000");
+
+  /* Save base state of options.  */
+  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;
+
+  /* Now select the ISA mode.
+
+     Do all CPP-sensitive stuff in non-MIPS16 mode; we'll switch to
+     MIPS16 mode afterwards if need be.  */
+  mips_set_mips16_mode (false);
+}
+
+/* Swap the register information for registers I and I + 1, which
+   currently have the wrong endianness.  Note that the registers'
+   fixedness and call-clobberedness might have been set on the
+   command line.  */
+
+static void
+mips_swap_registers (unsigned int i)
 {
-  const struct iris_section_align_entry *old = p;
-  return htab_hash_string (old->name);
+  int tmpi;
+  const char *tmps;
+
+#define SWAP_INT(X, Y) (tmpi = (X), (X) = (Y), (Y) = tmpi)
+#define SWAP_STRING(X, Y) (tmps = (X), (X) = (Y), (Y) = tmps)
+
+  SWAP_INT (fixed_regs[i], fixed_regs[i + 1]);
+  SWAP_INT (call_used_regs[i], call_used_regs[i + 1]);
+  SWAP_INT (call_really_used_regs[i], call_really_used_regs[i + 1]);
+  SWAP_STRING (reg_names[i], reg_names[i + 1]);
+
+#undef SWAP_STRING
+#undef SWAP_INT
 }
 
+/* Implement CONDITIONAL_REGISTER_USAGE.  */
+
 void
-iris6_asm_output_align (file, log)
-     FILE *file;
-     unsigned int log;
+mips_conditional_register_usage (void)
 {
-  const char *section = current_section_name ();
-  struct iris_section_align_entry **slot, *entry;
 
-  if (! section)
-    abort ();
+  if (ISA_HAS_DSP)
+    {
+      /* These DSP control register fields are global.  */
+      global_regs[CCDSP_PO_REGNUM] = 1;
+      global_regs[CCDSP_SC_REGNUM] = 1;
+    }
+  else 
+    {
+      int regno;
+
+      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;
 
-  slot = (struct iris_section_align_entry **)
-    htab_find_slot_with_hash (iris_section_align_htab, section,
-                             htab_hash_string (section), INSERT);
-  entry = *slot;
-  if (! entry)
+      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)
     {
-      entry = (struct iris_section_align_entry *)
-       xmalloc (sizeof (struct iris_section_align_entry));
-      *slot = entry;
-      entry->name = section;
-      entry->log = log;
-      entry->flags = current_section_flags ();
+      int regno;
+
+      /* We only have a single condition-code register.  We implement
+        this by fixing 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 call-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;
     }
-  else if (entry->log < log)
-    entry->log = log;
+  /* $f20-$f23 are call-clobbered for n64.  */
+  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 in the range $f21-$f31 (inclusive) are call-clobbered
+     for n32.  */
+  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)
+    {
+      unsigned int regno;
 
-  fprintf (file, "\t.align\t%u\n", log);
+      mips_swap_registers (MD_REG_FIRST);
+      for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno += 2)
+       mips_swap_registers (regno);
+    }
 }
 
-/* The Iris assembler does not record alignment from .align directives,
-   but takes it from the first .section directive seen.  Play yet more
-   file switching games so that we can emit a .section directive at the
-   beginning of the file with the proper alignment attached.  */
+/* Initialize vector TARGET to VALS.  */
 
 void
-iris6_asm_file_start (stream)
-     FILE *stream;
+mips_expand_vector_init (rtx target, rtx vals)
 {
-  mips_asm_file_start (stream);
+  enum machine_mode mode;
+  enum machine_mode inner;
+  unsigned int i, n_elts;
+  rtx mem;
 
-  iris_orig_asm_out_file = asm_out_file;
-  stream = tmpfile ();
-  asm_out_file = stream;
-  asm_out_data_file = stream;
-  if (! TARGET_FILE_SWITCHING)
-    asm_out_text_file = stream;
+  mode = GET_MODE (target);
+  inner = GET_MODE_INNER (mode);
+  n_elts = GET_MODE_NUNITS (mode);
 
-  iris_section_align_htab = htab_create (31, iris_section_align_entry_hash,
-                                        iris_section_align_entry_eq, NULL);
-}
+  gcc_assert (VECTOR_MODE_P (mode));
 
-static int
-iris6_section_align_1 (slot, data)
-     void **slot;
-     void *data ATTRIBUTE_UNUSED;
-{
-  const struct iris_section_align_entry *entry
-    = *(const struct iris_section_align_entry **) slot;
+  mem = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+  for (i = 0; i < n_elts; i++)
+    emit_move_insn (adjust_address_nv (mem, inner, i * GET_MODE_SIZE (inner)),
+                    XVECEXP (vals, 0, i));
 
-  iris6_asm_named_section_1 (entry->name, entry->flags, 1 << entry->log);
-  return 1;
+  emit_move_insn (target, mem);
 }
 
+/* When generating MIPS16 code, we want to allocate $24 (T_REG) before
+   other registers for instructions for which it is possible.  This
+   encourages the compiler to use CMP in cases where an XOR would
+   require some register shuffling.  */
+
 void
-iris6_asm_file_end (stream)
-     FILE *stream;
+mips_order_regs_for_local_alloc (void)
 {
-  /* Emit section directives with the proper alignment at the top of the
-     real output file.  */
-  asm_out_file = iris_orig_asm_out_file;
-  htab_traverse (iris_section_align_htab, iris6_section_align_1, NULL);
+  int i;
 
-  /* Copy the data emitted to the temp file to the real output file.  */
-  copy_file_data (asm_out_file, stream);
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    reg_alloc_order[i] = i;
 
-  mips_asm_file_end (stream);
+  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;
+    }
 }
-#endif /* TARGET_IRIX6 */
+\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_INIT_DFA_POST_CYCLE_INSN
+#define TARGET_SCHED_INIT_DFA_POST_CYCLE_INSN mips_init_dfa_post_cycle_insn
+#undef TARGET_SCHED_DFA_POST_ADVANCE_CYCLE
+#define TARGET_SCHED_DFA_POST_ADVANCE_CYCLE mips_dfa_post_advance_cycle
+#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_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START mips_va_start
+#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
+
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO mips_encode_section_info
+
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE mips_attribute_table
+/* All our function attributes are related to how out-of-line copies should
+   be compiled or called.  They don't in themselves prevent inlining.  */
+#undef TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P
+#define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_true
+
+#undef TARGET_EXTRA_LIVE_ON_ENTRY
+#define TARGET_EXTRA_LIVE_ON_ENTRY mips_extra_live_on_entry
+
+#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
+
+#undef  TARGET_COMP_TYPE_ATTRIBUTES
+#define TARGET_COMP_TYPE_ATTRIBUTES mips_comp_type_attributes
+
+#ifdef HAVE_AS_DTPRELWORD
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL mips_output_dwarf_dtprel
+#endif
+#undef TARGET_DWARF_REGISTER_SPAN
+#define TARGET_DWARF_REGISTER_SPAN mips_dwarf_register_span
+
+#undef TARGET_IRA_COVER_CLASSES
+#define TARGET_IRA_COVER_CLASSES mips_ira_cover_classes
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+\f
+#include "gt-mips.h"