OSDN Git Service

* c-decl.c, config/m32r/m32r.c, expr.c, optabs.c: Don't check
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.c
index c9f3573..8382b02 100644 (file)
@@ -1,6 +1,6 @@
 /* Subroutines used for MIPS code generation.
    Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004 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
@@ -52,6 +52,9 @@ Boston, MA 02111-1307, USA.  */
 #include "target.h"
 #include "target-def.h"
 #include "integrate.h"
+#include "langhooks.h"
+#include "cfglayout.h"
+#include "sched-int.h"
 
 /* Enumeration for all of the relational tests, so that we can build
    arrays indexed by the test type, and not worry about the order
@@ -71,79 +74,70 @@ enum internal_test {
   ITEST_MAX
 };
 
-/* Return true if it is likely that the given mode will be accessed
-   using only a single instruction.  */
-#define SINGLE_WORD_MODE_P(MODE) \
-  ((MODE) != BLKmode && GET_MODE_SIZE (MODE) <= UNITS_PER_WORD)
-
-/* True if the given SYMBOL_REF is for an internally-generated symbol.  */
-#define INTERNAL_SYMBOL_P(SYM) \
-  (XSTR (SYM, 0)[0] == '*' && XSTR (SYM, 0)[1] == LOCAL_LABEL_PREFIX[0])
-
-/* Classifies a non-literal integer constant.
-
-   CONSTANT_NONE
-       Not one of the constants below.
-
-   CONSTANT_GP
-       The global pointer, treated as a constant when TARGET_MIPS16.
-       The rtx has the form:
-
-          (const (reg $gp)).
-
-   CONSTANT_RELOC
-       A signed 16-bit relocation against either a symbol
-       or a symbol plus an offset.  The relocation has the form:
-
-          (unspec [(SYMBOL) ...] RELOC)
-
-       Any offset is added outside the unspec, such as:
-
-          (plus (unspec [(SYMBOL) ...] RELOC) (const_int OFFSET))
-
-       In either case, the whole expression is wrapped in a (const ...).
-
-   CONSTANT_SYMBOLIC
-       A reference to a symbol, possibly with an offset.  */
-enum mips_constant_type {
-  CONSTANT_NONE,
-  CONSTANT_GP,
-  CONSTANT_RELOC,
-  CONSTANT_SYMBOLIC
-};
-
-
-/* Classifies a SYMBOL_REF or LABEL_REF.
-
-   SYMBOL_GENERAL
-       Used when none of the below apply.
-
-   SYMBOL_SMALL_DATA
-       The symbol refers to something in a small data section.
-
-   SYMBOL_CONSTANT_POOL
-       The symbol refers to something in the mips16 constant pool.
-
-   SYMBOL_GOT_LOCAL
-       The symbol refers to local data that will be found using
-       the global offset table.
-
-   SYMBOL_GOT_GLOBAL
-       Likewise non-local data.  */
-enum mips_symbol_type {
-  SYMBOL_GENERAL,
-  SYMBOL_SMALL_DATA,
-  SYMBOL_CONSTANT_POOL,
-  SYMBOL_GOT_LOCAL,
-  SYMBOL_GOT_GLOBAL
-};
-
+/* 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))
+
+/* True if X is (const (unspec [(const_int 0)] UNSPEC_GP)).  This is used
+   to initialize the mips16 gp pseudo register.  */
+#define CONST_GP_P(X) \
+  (GET_CODE (X) == CONST                       \
+   && GET_CODE (XEXP (X, 0)) == UNSPEC         \
+   && XINT (XEXP (X, 0), 1) == UNSPEC_GP)
+
+/* The maximum distance between the top of the stack frame and the
+   value $sp has when we save & restore registers.
+
+   Use a maximum gap of 0x100 in the mips16 case.  We can then use
+   unextended instructions to save and restore registers, and to
+   allocate and deallocate the top part of the frame.
+
+   The value in the !mips16 case must be a SMALL_OPERAND and must
+   preserve the maximum stack alignment.  It could really be 0x7ff0,
+   but SGI's assemblers implement daddiu $sp,$sp,-0x7ff0 as a
+   multi-instruction addu sequence.  Use 0x7fe0 to work around this.  */
+#define MIPS_MAX_FIRST_STACK_STEP (TARGET_MIPS16 ? 0x100 : 0x7fe0)
+
+/* 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))
 
 /* Classifies an address.
 
-   ADDRESS_INVALID
-       The address should be rejected as invalid.
-
    ADDRESS_REG
        A natural register + offset address.  The register satisfies
        mips_valid_base_register_p and the offset is a const_arith_operand.
@@ -158,42 +152,36 @@ enum mips_symbol_type {
    ADDRESS_SYMBOLIC:
        A constant symbolic address (equivalent to CONSTANT_SYMBOLIC).  */
 enum mips_address_type {
-  ADDRESS_INVALID,
   ADDRESS_REG,
   ADDRESS_LO_SUM,
   ADDRESS_CONST_INT,
   ADDRESS_SYMBOLIC
 };
 
+/* A function to save or store a register.  The first argument is the
+   register and the second is the stack slot.  */
+typedef void (*mips_save_restore_fn) (rtx, rtx);
 
-struct constant;
+struct mips16_constant;
 struct mips_arg_info;
-struct mips_constant_info;
 struct mips_address_info;
 struct mips_integer_op;
+struct mips_sim;
 
-static bool mips_reloc_offset_ok_p (int, HOST_WIDE_INT);
-static enum mips_constant_type
-  mips_classify_constant (struct mips_constant_info *, rtx);
 static enum mips_symbol_type mips_classify_symbol (rtx);
+static void mips_split_const (rtx, rtx *, HOST_WIDE_INT *);
+static bool mips_offset_within_object_p (rtx, HOST_WIDE_INT);
+static bool mips_symbolic_constant_p (rtx, enum mips_symbol_type *);
 static bool mips_valid_base_register_p (rtx, enum machine_mode, int);
-static bool mips_symbolic_address_p (rtx, HOST_WIDE_INT,
-                                    enum machine_mode, int);
-static enum mips_address_type
-  mips_classify_address (struct mips_address_info *, rtx,
-                        enum machine_mode, int, int);
-static bool mips_splittable_symbol_p (enum mips_symbol_type, HOST_WIDE_INT);
+static bool mips_symbolic_address_p (enum mips_symbol_type, enum machine_mode);
+static bool mips_classify_address (struct mips_address_info *, rtx,
+                                  enum machine_mode, int);
 static int mips_symbol_insns (enum mips_symbol_type);
 static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx);
-static rtx mips_reloc (rtx, int);
-static rtx mips_lui_reloc (rtx, int);
 static rtx mips_force_temporary (rtx, rtx);
+static rtx mips_split_symbol (rtx, rtx);
+static rtx mips_unspec_offset_high (rtx, rtx, rtx, enum mips_symbol_type);
 static rtx mips_add_offset (rtx, HOST_WIDE_INT);
-static rtx mips_load_got (rtx, rtx, int);
-static rtx mips_load_got16 (rtx, int);
-static rtx mips_load_got32 (rtx, rtx, int, int);
-static rtx mips_emit_high (rtx, rtx);
-static bool mips_legitimize_symbol (rtx, rtx *, int);
 static unsigned int mips_build_shift (struct mips_integer_op *, HOST_WIDE_INT);
 static unsigned int mips_build_lower (struct mips_integer_op *,
                                      unsigned HOST_WIDE_INT);
@@ -207,6 +195,7 @@ static int mips_address_cost (rtx);
 static enum internal_test map_test_to_internal_test (enum rtx_code);
 static void get_float_compare_codes (enum rtx_code, enum rtx_code *,
                                     enum rtx_code *);
+static void mips_load_call_address (rtx, rtx, int);
 static bool mips_function_ok_for_sibcall (tree, tree);
 static void mips_block_move_straight (rtx, rtx, HOST_WIDE_INT);
 static void mips_adjust_block_mem (rtx, HOST_WIDE_INT, rtx *, rtx *);
@@ -217,35 +206,63 @@ static bool mips_get_unaligned_mem (rtx *, unsigned int, int, rtx *, rtx *);
 static void mips_set_architecture (const struct mips_cpu_info *);
 static void mips_set_tune (const struct mips_cpu_info *);
 static struct machine_function *mips_init_machine_status (void);
-static const char *mips_reloc_string (int);
+static void print_operand_reloc (FILE *, rtx, const char **);
 static bool mips_assemble_integer (rtx, unsigned int, int);
 static void mips_file_start (void);
 static void mips_file_end (void);
+static bool mips_rewrite_small_data_p (rtx);
+static int small_data_pattern_1 (rtx *, void *);
+static int mips_rewrite_small_data_1 (rtx *, void *);
+static bool mips_function_has_gp_insn (void);
 static unsigned int mips_global_pointer        (void);
 static bool mips_save_reg_p (unsigned int);
-static rtx mips_add_large_offset_to_sp (HOST_WIDE_INT);
-static void mips_set_frame_expr (rtx);
-static rtx mips_frame_set (rtx, int);
-static void mips_emit_frame_related_store (rtx, rtx, HOST_WIDE_INT);
-static void save_restore_insns (int, rtx, long);
+static void mips_save_restore_reg (enum machine_mode, int, HOST_WIDE_INT,
+                                  mips_save_restore_fn);
+static void mips_for_each_saved_reg (HOST_WIDE_INT, mips_save_restore_fn);
+static void mips_output_cplocal (void);
+static void mips_emit_loadgp (void);
 static void mips_output_function_prologue (FILE *, HOST_WIDE_INT);
-static void mips_gp_insn (rtx, rtx);
+static void mips_set_frame_expr (rtx);
+static rtx mips_frame_set (rtx, rtx);
+static void mips_save_reg (rtx, rtx);
 static void mips_output_function_epilogue (FILE *, HOST_WIDE_INT);
+static void mips_restore_reg (rtx, rtx);
+static void mips_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
+                                 HOST_WIDE_INT, tree);
 static int symbolic_expression_p (rtx);
 static void mips_select_rtx_section (enum machine_mode, rtx,
                                     unsigned HOST_WIDE_INT);
 static void mips_select_section (tree, int, unsigned HOST_WIDE_INT)
                                  ATTRIBUTE_UNUSED;
 static bool mips_in_small_data_p (tree);
-static void mips_encode_section_info (tree, rtx, int);
-static rtx mips_sdata_pointer (void);
+static int mips_fpr_return_fields (tree, tree *);
+static bool mips_return_in_msb (tree);
+static rtx mips_return_fpr_pair (enum machine_mode mode,
+                                enum machine_mode mode1, HOST_WIDE_INT,
+                                enum machine_mode mode2, HOST_WIDE_INT);
+static rtx mips16_gp_pseudo_reg (void);
 static void mips16_fp_args (FILE *, int, int);
 static void build_mips16_function_stub (FILE *);
-static void mips16_optimize_gp (void);
-static rtx add_constant        (struct constant **, rtx, enum machine_mode);
-static void dump_constants (struct constant *, rtx);
-static rtx mips_find_symbol (rtx);
+static rtx dump_constants_1 (enum machine_mode, rtx, rtx);
+static void dump_constants (struct mips16_constant *, rtx);
+static int mips16_insn_length (rtx);
+static int mips16_rewrite_pool_refs (rtx *, void *);
 static void mips16_lay_out_constants (void);
+static void mips_sim_reset (struct mips_sim *);
+static void mips_sim_init (struct mips_sim *, state_t);
+static void mips_sim_next_cycle (struct mips_sim *);
+static void mips_sim_wait_reg (struct mips_sim *, rtx, rtx);
+static int mips_sim_wait_regs_2 (rtx *, void *);
+static void mips_sim_wait_regs_1 (rtx *, void *);
+static void mips_sim_wait_regs (struct mips_sim *, rtx);
+static void mips_sim_wait_units (struct mips_sim *, rtx);
+static void mips_sim_wait_insn (struct mips_sim *, rtx);
+static void mips_sim_record_set (rtx, rtx, void *);
+static void mips_sim_issue_insn (struct mips_sim *, rtx);
+static void mips_sim_issue_nop (struct mips_sim *);
+static void mips_sim_finish_insn (struct mips_sim *, rtx);
+static void vr4130_avoid_branch_rt_conflict (rtx);
+static void vr4130_align_insns (void);
 static void mips_avoid_hazard (rtx, rtx, int *, rtx *, rtx);
 static void mips_avoid_hazards (void);
 static void mips_reorg (void);
@@ -253,22 +270,37 @@ static bool mips_strict_matching_cpu_name_p (const char *, const char *);
 static bool mips_matching_cpu_name_p (const char *, const char *);
 static const struct mips_cpu_info *mips_parse_cpu (const char *, const char *);
 static const struct mips_cpu_info *mips_cpu_info_from_isa (int);
+static bool mips_return_in_memory (tree, tree);
+static bool mips_strict_argument_naming (CUMULATIVE_ARGS *);
+static void mips_macc_chains_record (rtx);
+static void mips_macc_chains_reorder (rtx *, int);
+static void vr4130_true_reg_dependence_p_1 (rtx, rtx, void *);
+static bool vr4130_true_reg_dependence_p (rtx);
+static bool vr4130_swap_insns_p (rtx, rtx);
+static void vr4130_reorder (rtx *, int);
+static void mips_promote_ready (rtx *, int, int);
+static int mips_sched_reorder (FILE *, int, rtx *, int *, int);
+static int mips_variable_issue (FILE *, int, rtx, int);
 static int mips_adjust_cost (rtx, rtx, rtx, int);
 static int mips_issue_rate (void);
 static int mips_use_dfa_pipeline_interface (void);
+static int mips_multipass_dfa_lookahead (void);
 static void mips_init_libfuncs (void);
-
-#ifdef TARGET_IRIX6
-static void iris6_asm_named_section_1 (const char *, unsigned int,
-                                      unsigned int);
-static void iris6_asm_named_section (const char *, unsigned int);
-static int iris_section_align_entry_eq (const void *, const void *);
-static hashval_t iris_section_align_entry_hash (const void *);
-static void iris6_file_start (void);
-static int iris6_section_align_1 (void **, void *);
+static void mips_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
+                                        tree, int *, int);
+static tree mips_build_builtin_va_list (void);
+
+#if TARGET_IRIX
+static void irix_asm_named_section_1 (const char *, unsigned int,
+                                     unsigned int);
+static void irix_asm_named_section (const char *, unsigned int);
+static int irix_section_align_entry_eq (const void *, const void *);
+static hashval_t irix_section_align_entry_hash (const void *);
+static void irix_file_start (void);
+static int irix_section_align_1 (void **, void *);
 static void copy_file_data (FILE *, FILE *);
-static void iris6_file_end (void);
-static unsigned int iris6_section_type_flags (tree, const char *, int);
+static void irix_file_end (void);
+static unsigned int irix_section_type_flags (tree, const char *, int);
 #endif
 
 /* Structure to be filled in by compute_frame_size with register
@@ -276,27 +308,24 @@ static unsigned int iris6_section_type_flags (tree, const char *, int);
 
 struct mips_frame_info GTY(())
 {
-  long total_size;             /* # bytes that the entire frame takes up */
-  long var_size;               /* # bytes that variables take up */
-  long args_size;              /* # bytes that outgoing arguments take up */
-  int  gp_reg_size;            /* # bytes needed to store gp regs */
-  int  fp_reg_size;            /* # bytes needed to store fp regs */
-  long mask;                   /* mask of saved gp registers */
-  long fmask;                  /* mask of saved fp registers */
-  long gp_save_offset;         /* offset from vfp to store gp registers */
-  long fp_save_offset;         /* offset from vfp to store fp registers */
-  long gp_sp_offset;           /* offset from new sp to store gp registers */
-  long fp_sp_offset;           /* offset from new sp to store fp registers */
-  int  initialized;            /* != 0 if frame size already calculated */
-  int  num_gp;                 /* number of gp registers saved */
-  int  num_fp;                 /* number of fp registers saved */
+  HOST_WIDE_INT total_size;    /* # bytes that the entire frame takes up */
+  HOST_WIDE_INT var_size;      /* # bytes that variables take up */
+  HOST_WIDE_INT args_size;     /* # bytes that outgoing arguments take up */
+  HOST_WIDE_INT cprestore_size;        /* # bytes that the .cprestore slot takes up */
+  HOST_WIDE_INT gp_reg_size;   /* # bytes needed to store gp regs */
+  HOST_WIDE_INT fp_reg_size;   /* # bytes needed to store fp regs */
+  unsigned int mask;           /* mask of saved gp registers */
+  unsigned int fmask;          /* mask of saved fp registers */
+  HOST_WIDE_INT gp_save_offset;        /* offset from vfp to store gp registers */
+  HOST_WIDE_INT fp_save_offset;        /* offset from vfp to store fp registers */
+  HOST_WIDE_INT gp_sp_offset;  /* offset from new sp to store gp registers */
+  HOST_WIDE_INT fp_sp_offset;  /* offset from new sp to store fp registers */
+  bool initialized;            /* true if frame size already calculated */
+  int num_gp;                  /* number of gp registers saved */
+  int num_fp;                  /* number of fp registers saved */
 };
 
 struct machine_function GTY(()) {
-  /* Pseudo-reg holding the address of the current function when
-     generating embedded PIC code.  */
-  rtx embedded_pic_fnaddr_rtx;
-
   /* Pseudo-reg holding the value of $28 in a mips16 function which
      refers to GP relative global variables.  */
   rtx mips16_gp_pseudo_rtx;
@@ -304,9 +333,6 @@ struct machine_function GTY(()) {
   /* Current frame information, calculated by compute_frame_size.  */
   struct mips_frame_info frame;
 
-  /* Length of instructions in function; mips16 only.  */
-  long insns_len;
-
   /* The register to use as the global pointer within this function.  */
   unsigned int global_pointer;
 
@@ -317,6 +343,9 @@ struct machine_function GTY(()) {
   /* True if the whole function is suitable for .set noreorder and
      .set nomacro.  */
   bool all_noreorder_p;
+
+  /* True if the function is known to have an instruction that needs $gp.  */
+  bool has_gp_insn_p;
 };
 
 /* Information about a single argument.  */
@@ -346,30 +375,8 @@ struct mips_arg_info
 };
 
 
-/* Struct for recording constants.  The meaning of the fields depends
-   on a mips_constant_type:
-
-   CONSTANT_NONE
-   CONSTANT_GP
-       No fields are valid.
-
-   CONSTANT_SYMBOLIC
-       SYMBOL is the referenced symbol and OFFSET is the constant offset.
-
-   CONSTANT_RELOC
-       SYMBOL and OFFSET are the same as for CONSTANT_SYMBOLIC.  RELOC is
-       the relocation number.  */
-struct mips_constant_info
-{
-  int reloc;
-  rtx symbol;
-  HOST_WIDE_INT offset;
-};
-
-
 /* Information about an address described by mips_address_type.
 
-   ADDRESS_INVALID
    ADDRESS_CONST_INT
        No fields are used.
 
@@ -378,16 +385,18 @@ struct mips_constant_info
 
    ADDRESS_LO_SUM
        REG is the register that contains the high part of the address,
-       OFFSET is the symbolic address being referenced, and C contains
-       the individual components of the symbolic address.
+       OFFSET is the symbolic address being referenced and SYMBOL_TYPE
+       is the type of OFFSET's symbol.
 
    ADDRESS_SYMBOLIC
-       C contains the symbol and offset.  */
+       SYMBOL_TYPE is the type of symbol being referenced.  */
+
 struct mips_address_info
 {
+  enum mips_address_type type;
   rtx reg;
   rtx offset;
-  struct mips_constant_info c;
+  enum mips_symbol_type symbol_type;
 };
 
 
@@ -500,42 +509,29 @@ static enum machine_mode gpr_mode;
    can support a given mode.  */
 char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
 
-/* 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;
-
-/* When generating mips16 code, a list of all strings that are to be
-   output after the current function.  */
-
-static GTY(()) rtx mips16_strings;
-
-/* In mips16 mode, we build a list of all the string constants we see
-   in a particular function.  */
-
-struct string_constant
-{
-  struct string_constant *next;
-  const char *label;
-};
-
-static struct string_constant *string_constants;
-
 /* List of all MIPS punctuation characters used by print_operand.  */
 char mips_print_operand_punct[256];
 
 /* Map GCC register number to debugger register number.  */
 int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
 
-/* An alias set for the GOT.  */
-static int mips_got_alias_set;
-
 /* A copy of the original flag_delayed_branch: see override_options.  */
 static int mips_flag_delayed_branch;
 
 static GTY (()) int mips_output_filename_first_time = 1;
 
+/* mips_split_p[X] is true if symbols of type X can be split by
+   mips_split_symbol().  */
+static bool mips_split_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];
+
 /* Hardware names for the registers.  If -mrnames is used, this
    will be overwritten with mips_sw_reg_names.  */
 
@@ -550,7 +546,7 @@ char mips_reg_names[][8] =
  "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
  "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
  "hi",   "lo",   "",     "$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
- "$fcc5","$fcc6","$fcc7","", "",     "",     "",     "",
+ "$fcc5","$fcc6","$fcc7","", "", "$arg", "$frame", "$fakec",
  "$c0r0", "$c0r1", "$c0r2", "$c0r3", "$c0r4", "$c0r5", "$c0r6", "$c0r7",
  "$c0r8", "$c0r9", "$c0r10","$c0r11","$c0r12","$c0r13","$c0r14","$c0r15",
  "$c0r16","$c0r17","$c0r18","$c0r19","$c0r20","$c0r21","$c0r22","$c0r23",
@@ -579,7 +575,7 @@ char mips_sw_reg_names[][8] =
   "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
   "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
   "hi",   "lo",   "",     "$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
-  "$fcc5","$fcc6","$fcc7","$rap", "",     "",     "",     "",
+  "$fcc5","$fcc6","$fcc7","$rap", "", "$arg", "$frame", "$fakec",
   "$c0r0", "$c0r1", "$c0r2", "$c0r3", "$c0r4", "$c0r5", "$c0r6", "$c0r7",
   "$c0r8", "$c0r9", "$c0r10","$c0r11","$c0r12","$c0r13","$c0r14","$c0r15",
   "$c0r16","$c0r17","$c0r18","$c0r19","$c0r20","$c0r21","$c0r22","$c0r23",
@@ -616,7 +612,7 @@ const enum reg_class mips_regno_to_class[] =
   HI_REG,      LO_REG,         NO_REGS,        ST_REGS,
   ST_REGS,     ST_REGS,        ST_REGS,        ST_REGS,
   ST_REGS,     ST_REGS,        ST_REGS,        NO_REGS,
-  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     ALL_REGS,       ALL_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,
@@ -675,6 +671,7 @@ const struct mips_cpu_info mips_cpu_info_table[] = {
   { "vr4100", PROCESSOR_R4100, 3 },
   { "vr4111", PROCESSOR_R4111, 3 },
   { "vr4120", PROCESSOR_R4120, 3 },
+  { "vr4130", PROCESSOR_R4130, 3 },
   { "vr4300", PROCESSOR_R4300, 3 },
   { "r4400", PROCESSOR_R4000, 3 }, /* = r4000 */
   { "r4600", PROCESSOR_R4600, 3 },
@@ -719,17 +716,6 @@ const struct mips_cpu_info mips_cpu_info_table[] = {
 #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"
-/* The IRIX 6 O32 assembler gives an error for `align 0; .dword', contrary
-   to the documentation, so disable it.  */
-#undef TARGET_ASM_UNALIGNED_DI_OP
-#define TARGET_ASM_UNALIGNED_DI_OP NULL
-#endif
-
 #undef TARGET_ASM_FUNCTION_PROLOGUE
 #define TARGET_ASM_FUNCTION_PROLOGUE mips_output_function_prologue
 #undef TARGET_ASM_FUNCTION_EPILOGUE
@@ -737,12 +723,19 @@ const struct mips_cpu_info mips_cpu_info_table[] = {
 #undef TARGET_ASM_SELECT_RTX_SECTION
 #define TARGET_ASM_SELECT_RTX_SECTION mips_select_rtx_section
 
+#undef TARGET_SCHED_REORDER
+#define TARGET_SCHED_REORDER 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_USE_DFA_PIPELINE_INTERFACE
 #define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE mips_use_dfa_pipeline_interface
+#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
+#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \
+  mips_multipass_dfa_lookahead
 
 #undef TARGET_FUNCTION_OK_FOR_SIBCALL
 #define TARGET_FUNCTION_OK_FOR_SIBCALL mips_function_ok_for_sibcall
@@ -753,11 +746,7 @@ const struct mips_cpu_info mips_cpu_info_table[] = {
 #define TARGET_RTX_COSTS mips_rtx_costs
 #undef TARGET_ADDRESS_COST
 #define TARGET_ADDRESS_COST mips_address_cost
-#undef TARGET_DELEGITIMIZE_ADDRESS
-#define TARGET_DELEGITIMIZE_ADDRESS mips_delegitimize_address
 
-#undef TARGET_ENCODE_SECTION_INFO
-#define TARGET_ENCODE_SECTION_INFO mips_encode_section_info
 #undef TARGET_IN_SMALL_DATA_P
 #define TARGET_IN_SMALL_DATA_P mips_in_small_data_p
 
@@ -766,9 +755,9 @@ const struct mips_cpu_info mips_cpu_info_table[] = {
 
 #undef TARGET_ASM_FILE_START
 #undef TARGET_ASM_FILE_END
-#ifdef TARGET_IRIX6
-#define TARGET_ASM_FILE_START iris6_file_start
-#define TARGET_ASM_FILE_END iris6_file_end
+#if TARGET_IRIX
+#define TARGET_ASM_FILE_START irix_file_start
+#define TARGET_ASM_FILE_END irix_file_end
 #else
 #define TARGET_ASM_FILE_START mips_file_start
 #define TARGET_ASM_FILE_END mips_file_end
@@ -776,148 +765,259 @@ const struct mips_cpu_info mips_cpu_info_table[] = {
 #undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
 #define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
 
-#ifdef TARGET_IRIX6
+#if TARGET_IRIX
 #undef TARGET_SECTION_TYPE_FLAGS
-#define TARGET_SECTION_TYPE_FLAGS iris6_section_type_flags
+#define TARGET_SECTION_TYPE_FLAGS irix_section_type_flags
 #endif
 
 #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_PROMOTE_FUNCTION_ARGS
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_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_tree_hwi_hwi_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
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
-/* Return true if RELOC is a valid relocation number and OFFSET can be
-   added to the relocation symbol.
-
-   Note that OFFSET might not refer to part of the object.   For example,
-   in an expression like x[i - 0x12345], we might try to take the address
-   of "x - 0x12345".  */
+/* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF.  */
 
-static bool
-mips_reloc_offset_ok_p (int reloc, HOST_WIDE_INT offset)
+static enum mips_symbol_type
+mips_classify_symbol (rtx x)
 {
-  switch (reloc)
+  if (GET_CODE (x) == LABEL_REF)
     {
-    case RELOC_GOT_PAGE:
-      /* The linker should provide enough page entries to cope with
-        16-bit offsets from a valid segment address.  */
-      return SMALL_OPERAND (offset);
+      if (TARGET_MIPS16)
+       return SYMBOL_CONSTANT_POOL;
+      if (TARGET_ABICALLS)
+       return SYMBOL_GOT_LOCAL;
+      return SYMBOL_GENERAL;
+    }
 
-    case RELOC_GOT_HI:
-    case RELOC_GOT_LO:
-    case RELOC_GOT_DISP:
-    case RELOC_CALL16:
-    case RELOC_CALL_HI:
-    case RELOC_CALL_LO:
-    case RELOC_LOADGP_HI:
-    case RELOC_LOADGP_LO:
-      /* These relocations should be applied to bare symbols only.  */
-      return offset == 0;
+  if (GET_CODE (x) != SYMBOL_REF)
+    abort ();
 
-    default:
-      return false;
-    }
-}
+  if (CONSTANT_POOL_ADDRESS_P (x))
+    {
+      if (TARGET_MIPS16)
+       return SYMBOL_CONSTANT_POOL;
 
+      if (TARGET_ABICALLS)
+       return SYMBOL_GOT_LOCAL;
 
-/* If X is one of the constants described by mips_constant_type,
-   store its components in INFO and return its type.  */
+      if (GET_MODE_SIZE (get_pool_mode (x)) <= mips_section_threshold)
+       return SYMBOL_SMALL_DATA;
 
-static enum mips_constant_type
-mips_classify_constant (struct mips_constant_info *info, rtx x)
-{
-  enum mips_constant_type type;
+      return SYMBOL_GENERAL;
+    }
 
-  type = CONSTANT_SYMBOLIC;
-  info->offset = 0;
+  if (SYMBOL_REF_SMALL_P (x))
+    return SYMBOL_SMALL_DATA;
 
-  if (GET_CODE (x) == CONST)
+  if (TARGET_ABICALLS)
     {
-      x = XEXP (x, 0);
+      if (SYMBOL_REF_DECL (x) == 0)
+       return SYMBOL_REF_LOCAL_P (x) ? SYMBOL_GOT_LOCAL : SYMBOL_GOT_GLOBAL;
 
-      if (x == pic_offset_table_rtx)
-       return CONSTANT_GP;
+      /* There are three cases to consider:
 
-      while (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
-       {
-         info->offset += INTVAL (XEXP (x, 1));
-         x = XEXP (x, 0);
-       }
+            - o32 PIC (either with or without explicit relocs)
+            - n32/n64 PIC without explicit relocs
+            - n32/n64 PIC with explicit relocs
 
-      if (TARGET_EXPLICIT_RELOCS
-         && GET_CODE (x) == UNSPEC
-         && mips_reloc_offset_ok_p (XINT (x, 1), info->offset))
-       {
-         info->reloc = XINT (x, 1);
-         x = XVECEXP (x, 0, 0);
-         type = CONSTANT_RELOC;
-       }
+         In the first case, both local and global accesses will use an
+         R_MIPS_GOT16 relocation.  We must correctly predict which of
+         the two semantics (local or global) the assembler and linker
+         will apply.  The choice doesn't depend on the symbol's
+         visibility, so we deliberately ignore decl_visibility and
+         binds_local_p here.
+
+         In the second case, the assembler will not use R_MIPS_GOT16
+         relocations, but it chooses between local and global accesses
+         in the same way as for o32 PIC.
+
+         In the third case we have more freedom since both forms of
+         access will work for any kind of symbol.  However, there seems
+         little point in doing things differently.  */
+      if (DECL_P (SYMBOL_REF_DECL (x)) && TREE_PUBLIC (SYMBOL_REF_DECL (x)))
+       return SYMBOL_GOT_GLOBAL;
+
+      return SYMBOL_GOT_LOCAL;
     }
 
-  if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+  return SYMBOL_GENERAL;
+}
+
+
+/* Split X into a base and a constant offset, storing them in *BASE
+   and *OFFSET respectively.  */
+
+static void
+mips_split_const (rtx x, rtx *base, HOST_WIDE_INT *offset)
+{
+  *offset = 0;
+
+  if (GET_CODE (x) == CONST)
+    x = XEXP (x, 0);
+
+  if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
     {
-      info->symbol = x;
-      return type;
+      *offset += INTVAL (XEXP (x, 1));
+      x = XEXP (x, 0);
     }
-  return CONSTANT_NONE;
+  *base = x;
 }
 
 
-/* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF.  */
+/* Return true if SYMBOL is a SYMBOL_REF and OFFSET + SYMBOL points
+   to the same object as SYMBOL.  */
 
-static enum mips_symbol_type
-mips_classify_symbol (rtx x)
+static bool
+mips_offset_within_object_p (rtx symbol, HOST_WIDE_INT offset)
 {
-  if (GET_CODE (x) == LABEL_REF)
-    return (TARGET_ABICALLS ? SYMBOL_GOT_LOCAL : SYMBOL_GENERAL);
+  if (GET_CODE (symbol) != SYMBOL_REF)
+    return false;
 
-  if (GET_CODE (x) != SYMBOL_REF)
-    abort ();
+  if (CONSTANT_POOL_ADDRESS_P (symbol)
+      && offset >= 0
+      && offset < (int) GET_MODE_SIZE (get_pool_mode (symbol)))
+    return true;
 
-  if (CONSTANT_POOL_ADDRESS_P (x))
-    {
-      if (TARGET_MIPS16)
-       return SYMBOL_CONSTANT_POOL;
+  if (SYMBOL_REF_DECL (symbol) != 0
+      && offset >= 0
+      && offset < int_size_in_bytes (TREE_TYPE (SYMBOL_REF_DECL (symbol))))
+    return true;
 
-      if (TARGET_ABICALLS)
-       return SYMBOL_GOT_LOCAL;
+  return false;
+}
 
-      if (GET_MODE_SIZE (get_pool_mode (x)) <= mips_section_threshold)
-       return SYMBOL_SMALL_DATA;
 
-      return SYMBOL_GENERAL;
-    }
+/* Return true if X is a symbolic constant that can be calculated in
+   the same way as a bare symbol.  If it is, store the type of the
+   symbol in *SYMBOL_TYPE.  */
+
+static bool
+mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type)
+{
+  HOST_WIDE_INT offset;
+
+  mips_split_const (x, &x, &offset);
+  if (UNSPEC_ADDRESS_P (x))
+    *symbol_type = UNSPEC_ADDRESS_TYPE (x);
+  else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+    *symbol_type = mips_classify_symbol (x);
+  else
+    return false;
 
-  if (INTERNAL_SYMBOL_P (x))
+  if (offset == 0)
+    return true;
+
+  /* Check whether a nonzero offset is valid for the underlying
+     relocations.  */
+  switch (*symbol_type)
     {
-      /* The symbol is a local label.  For TARGET_MIPS16, SYMBOL_REF_FLAG
-        will be set if the symbol refers to a string in the current
-        function's constant pool.  */
-      if (TARGET_MIPS16 && SYMBOL_REF_FLAG (x))
-       return SYMBOL_CONSTANT_POOL;
+    case SYMBOL_GENERAL:
+    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 mips_offset_within_object_p (x, offset);
+
+      /* In other cases the relocations can handle any offset.  */
+      return true;
 
-      if (TARGET_ABICALLS)
-       return SYMBOL_GOT_LOCAL;
-    }
+    case SYMBOL_CONSTANT_POOL:
+      /* 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;
 
-  if (SYMBOL_REF_SMALL_P (x))
-    return SYMBOL_SMALL_DATA;
+      /* Fall through.  */
 
-  if (TARGET_ABICALLS)
-    return (SYMBOL_REF_FLAG (x) ? SYMBOL_GOT_LOCAL : SYMBOL_GOT_GLOBAL);
+    case SYMBOL_SMALL_DATA:
+      /* Make sure that the offset refers to something within the
+        underlying object.  This should guarantee that the final
+        PC- or GP-relative offset is within the 16-bit limit.  */
+      return mips_offset_within_object_p (x, offset);
 
-  return SYMBOL_GENERAL;
+    case SYMBOL_GOT_LOCAL:
+    case SYMBOL_GOTOFF_PAGE:
+      /* The linker should provide enough local GOT entries for a
+        16-bit offset.  Larger offsets may lead to GOT overflow.  */
+      return SMALL_OPERAND (offset);
+
+    case SYMBOL_GOT_GLOBAL:
+    case SYMBOL_GOTOFF_GLOBAL:
+    case SYMBOL_GOTOFF_CALL:
+    case SYMBOL_GOTOFF_LOADGP:
+      return false;
+    }
+  abort ();
 }
 
 
 /* This function is used to implement REG_MODE_OK_FOR_BASE_P.  */
 
 int
-mips_reg_mode_ok_for_base_p (rtx reg, enum machine_mode mode, int strict)
+mips_regno_mode_ok_for_base_p (int regno, 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));
+  if (regno >= FIRST_PSEUDO_REGISTER)
+    {
+      if (!strict)
+       return true;
+      regno = reg_renumber[regno];
+    }
+
+  /* These fake registers will be eliminated to either the stack or
+     hard frame pointer, both of which are usually valid base registers.
+     Reload deals with the cases where the eliminated form isn't valid.  */
+  if (regno == ARG_POINTER_REGNUM || regno == FRAME_POINTER_REGNUM)
+    return true;
+
+  /* In mips16 mode, the stack pointer can only address word and doubleword
+     values, nothing smaller.  There are two problems here:
+
+       (a) Instantiating virtual registers can introduce new uses of the
+          stack pointer.  If these virtual registers are valid addresses,
+          the stack pointer should be too.
+
+       (b) Most uses of the stack pointer are not made explicit until
+          FRAME_POINTER_REGNUM and ARG_POINTER_REGNUM have been eliminated.
+          We don't know until that stage whether we'll be eliminating to the
+          stack pointer (which needs the restriction) or the hard frame
+          pointer (which doesn't).
+
+     All in all, it seems more consistent to only enforce this restriction
+     during and after reload.  */
+  if (TARGET_MIPS16 && regno == STACK_POINTER_REGNUM)
+    return !strict || GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
+
+  return TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
 }
 
 
@@ -931,159 +1031,101 @@ mips_valid_base_register_p (rtx x, enum machine_mode mode, int strict)
     x = SUBREG_REG (x);
 
   return (GET_CODE (x) == REG
-         && mips_reg_mode_ok_for_base_p (x, mode, strict));
+         && mips_regno_mode_ok_for_base_p (REGNO (x), mode, strict));
 }
 
 
-/* Return true if SYMBOL + OFFSET should be considered a legitimate
-   address.  LEA_P is true and MODE is word_mode if the address
-   will be used in an LA or DLA macro.  Otherwise MODE is the
-   mode of the value being accessed.
-
-   Some guiding principles:
-
-   - Allow a nonzero offset when it takes no additional instructions.
-     Ask for other offsets to be added separately.
-
-   - Only allow multi-instruction load or store macros when MODE is
-     word-sized or smaller.  For other modes (including BLKmode)
-     it is better to move the address into a register first.  */
+/* Return true if symbols of type SYMBOL_TYPE can directly address a value
+   with mode MODE.  This is used for both symbolic and LO_SUM addresses.  */
 
 static bool
-mips_symbolic_address_p (rtx symbol, HOST_WIDE_INT offset,
-                        enum machine_mode mode, int lea_p)
+mips_symbolic_address_p (enum mips_symbol_type symbol_type,
+                        enum machine_mode mode)
 {
-  if (TARGET_EXPLICIT_RELOCS)
-    return false;
-
-  switch (mips_classify_symbol (symbol))
+  switch (symbol_type)
     {
     case SYMBOL_GENERAL:
-      /* General symbols aren't valid addresses in mips16 code:
-        they have to go into the constant pool.  */
-      return (!TARGET_MIPS16
-             && !mips_split_addresses
-             && SINGLE_WORD_MODE_P (mode));
+      return !TARGET_MIPS16;
 
     case SYMBOL_SMALL_DATA:
-      /* Small data references are normally OK for any address.
-        But for mips16 code, we need to use a pseudo register
-        instead of $gp as the base register.  */
-      return !TARGET_MIPS16;
+      return true;
 
     case SYMBOL_CONSTANT_POOL:
-      /* PC-relative addressing is only available for lw, sw, ld and sd.
-        There's also a PC-relative add instruction.  */
-      return lea_p || GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
+      /* PC-relative addressing is only available for lw and ld.  */
+      return GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
+
+    case SYMBOL_GOT_LOCAL:
+      return true;
 
     case SYMBOL_GOT_GLOBAL:
-      /* The address of the symbol is stored in the GOT.  We can load
-        it using an LA or DLA instruction, but any offset is added
-        afterwards.  */
-      return lea_p && offset == 0;
+      /* The address will have to be loaded from the GOT first.  */
+      return false;
 
-    case SYMBOL_GOT_LOCAL:
-      /* The symbol is part of a block of local memory.  We fetch the
-        address of the local memory from the GOT and then add the
-        offset for this symbol.  This addition can take the form of an
-        offset(base) address, so the symbol is a legitimate address.  */
-      return SINGLE_WORD_MODE_P (mode);
+    case SYMBOL_GOTOFF_PAGE:
+    case SYMBOL_GOTOFF_GLOBAL:
+    case SYMBOL_GOTOFF_CALL:
+    case SYMBOL_GOTOFF_LOADGP:
+    case SYMBOL_64_HIGH:
+    case SYMBOL_64_MID:
+    case SYMBOL_64_LOW:
+      return true;
     }
   abort ();
 }
 
 
-/* If X is a valid address, describe it in INFO and return its type.
-   STRICT says to only allow hard registers.  MODE and LEA_P are
-   the same as for mips_symbolic_address_p.  */
+/* Return true if X is a valid address for machine mode MODE.  If it is,
+   fill in INFO appropriately.  STRICT is true if we should only accept
+   hard base registers.  */
 
-static enum mips_address_type
+static bool
 mips_classify_address (struct mips_address_info *info, rtx x,
-                      enum machine_mode mode, int strict, int lea_p)
+                      enum machine_mode mode, int strict)
 {
   switch (GET_CODE (x))
     {
     case REG:
     case SUBREG:
-      if (mips_valid_base_register_p (x, mode, strict))
-       {
-         info->reg = x;
-         info->offset = const0_rtx;
-         return ADDRESS_REG;
-       }
-      return ADDRESS_INVALID;
+      info->type = ADDRESS_REG;
+      info->reg = x;
+      info->offset = const0_rtx;
+      return mips_valid_base_register_p (info->reg, mode, strict);
 
     case PLUS:
-      if (mips_valid_base_register_p (XEXP (x, 0), mode, strict)
-         && const_arith_operand (XEXP (x, 1), VOIDmode))
-       {
-         info->reg = XEXP (x, 0);
-         info->offset = XEXP (x, 1);
-         return ADDRESS_REG;
-       }
-      return ADDRESS_INVALID;
+      info->type = ADDRESS_REG;
+      info->reg = XEXP (x, 0);
+      info->offset = XEXP (x, 1);
+      return (mips_valid_base_register_p (info->reg, mode, strict)
+             && const_arith_operand (info->offset, VOIDmode));
 
     case LO_SUM:
-      if (SINGLE_WORD_MODE_P (mode)
-         && mips_valid_base_register_p (XEXP (x, 0), mode, strict)
-         && (mips_classify_constant (&info->c, XEXP (x, 1))
-             == CONSTANT_SYMBOLIC)
-         && mips_splittable_symbol_p (mips_classify_symbol (info->c.symbol),
-                                      info->c.offset))
-       {
-         info->reg = XEXP (x, 0);
-         info->offset = XEXP (x, 1);
-         return ADDRESS_LO_SUM;
-       }
-      return ADDRESS_INVALID;
+      info->type = ADDRESS_LO_SUM;
+      info->reg = XEXP (x, 0);
+      info->offset = XEXP (x, 1);
+      return (mips_valid_base_register_p (info->reg, mode, strict)
+             && mips_symbolic_constant_p (info->offset, &info->symbol_type)
+             && mips_symbolic_address_p (info->symbol_type, mode)
+             && mips_lo_relocs[info->symbol_type] != 0);
 
     case CONST_INT:
       /* Small-integer addresses don't occur very often, but they
         are legitimate if $0 is a valid base register.  */
-      if (!TARGET_MIPS16 && SMALL_INT (x))
-       return ADDRESS_CONST_INT;
-      return ADDRESS_INVALID;
+      info->type = ADDRESS_CONST_INT;
+      return !TARGET_MIPS16 && SMALL_INT (x);
 
     case CONST:
     case LABEL_REF:
     case SYMBOL_REF:
-      if (mips_classify_constant (&info->c, x) == CONSTANT_SYMBOLIC
-         && mips_symbolic_address_p (info->c.symbol, info->c.offset,
-                                     mode, lea_p))
-       return ADDRESS_SYMBOLIC;
-      return ADDRESS_INVALID;
-
-    default:
-      return ADDRESS_INVALID;
-    }
-}
-\f
-/* Return true if symbols of the given type can be split into a
-   high part and a LO_SUM.  In the case of small data symbols,
-   the high part will be $gp.  */
-
-static bool
-mips_splittable_symbol_p (enum mips_symbol_type type, HOST_WIDE_INT offset)
-{
-  switch (type)
-    {
-    case SYMBOL_GENERAL:
-      return TARGET_EXPLICIT_RELOCS || mips_split_addresses;
-
-    case SYMBOL_GOT_LOCAL:
-      return TARGET_EXPLICIT_RELOCS && SMALL_OPERAND (offset);
-
-    case SYMBOL_SMALL_DATA:
-      return ((TARGET_EXPLICIT_RELOCS || TARGET_MIPS16)
-             && (offset == 0
-                 || (offset > 0 && offset <= mips_section_threshold)));
+      info->type = ADDRESS_SYMBOLIC;
+      return (mips_symbolic_constant_p (x, &info->symbol_type)
+             && mips_symbolic_address_p (info->symbol_type, mode)
+             && !mips_split_p[info->symbol_type]);
 
     default:
       return false;
     }
 }
-
-
+\f
 /* Return the number of instructions needed to load a symbol of the
    given type into a register.  If valid in an address, the same number
    of instructions are needed for loads and stores.  Treat extended
@@ -1095,6 +1137,11 @@ mips_symbol_insns (enum mips_symbol_type type)
   switch (type)
     {
     case SYMBOL_GENERAL:
+      /* In mips16 code, general symbols must be fetched from the
+        constant pool.  */
+      if (TARGET_MIPS16)
+       return 0;
+
       /* When using 64-bit symbols, we need 5 preparatory instructions,
         such as:
 
@@ -1116,28 +1163,39 @@ mips_symbol_insns (enum mips_symbol_type type)
         extended instruction.  */
       return 2;
 
+    case SYMBOL_GOT_LOCAL:
     case SYMBOL_GOT_GLOBAL:
-      /* When using a small GOT, we just fetch the address using
-        a gp-relative load.   For a big GOT, we need a sequence
-        such as:
+      /* Unless -funit-at-a-time is in effect, we can't be sure whether
+        the local/global classification is accurate.  See override_options
+        for details.
 
-             lui     $at,%got_hi(symbol)
-             daddu   $at,$at,$gp
+        The worst cases are:
 
-        and the final address is $at + %got_lo(symbol).  */
-      return (TARGET_XGOT ? 3 : 1);
+        (1) For local symbols when generating o32 or o64 code.  The assembler
+            will use:
 
-    case SYMBOL_GOT_LOCAL:
-      /* For o32 and o64, the sequence is:
+                lw           $at,%got(symbol)
+                nop
+
+            ...and the final address will be $at + %lo(symbol).
 
-            lw       $at,%got(symbol)
-            nop
+        (2) For global symbols when -mxgot.  The assembler will use:
 
-        and the final address is $at + %lo(symbol).  A load/add
-        sequence is also needed for n32 and n64.  Some versions
-        of GAS insert a nop in the n32/n64 sequences too so, for
-        simplicity, use the worst case of 3 instructions.  */
+                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_GLOBAL:
+    case SYMBOL_GOTOFF_CALL:
+    case SYMBOL_GOTOFF_LOADGP:
+    case SYMBOL_64_HIGH:
+    case SYMBOL_64_MID:
+    case SYMBOL_64_LOW:
+      /* Check whether the offset is a 16- or 32-bit value.  */
+      return mips_split_p[type] ? 2 : 1;
     }
   abort ();
 }
@@ -1179,29 +1237,32 @@ mips_address_insns (rtx x, enum machine_mode mode)
   struct mips_address_info addr;
   int factor;
 
-  /* Each word of a multi-word value will be accessed individually.  */
-  factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
-  switch (mips_classify_address (&addr, x, mode, 0, 0))
-    {
-    case ADDRESS_INVALID:
-      return 0;
+  if (mode == BLKmode)
+    /* BLKmode is used for single unaligned loads and stores.  */
+    factor = 1;
+  else
+    /* Each word of a multi-word value will be accessed individually.  */
+    factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 
-    case ADDRESS_REG:
-      if (TARGET_MIPS16
-         && !mips16_unextended_reference_p (mode, addr.reg, addr.offset))
-       return factor * 2;
-      return factor;
+  if (mips_classify_address (&addr, x, mode, false))
+    switch (addr.type)
+      {
+      case ADDRESS_REG:
+       if (TARGET_MIPS16
+           && !mips16_unextended_reference_p (mode, addr.reg, addr.offset))
+         return factor * 2;
+       return factor;
 
-    case ADDRESS_LO_SUM:
-      return (TARGET_MIPS16 ? factor * 2 : factor);
+      case ADDRESS_LO_SUM:
+       return (TARGET_MIPS16 ? factor * 2 : factor);
 
-    case ADDRESS_CONST_INT:
-      return factor;
+      case ADDRESS_CONST_INT:
+       return factor;
 
-    case ADDRESS_SYMBOLIC:
-      return factor * mips_symbol_insns (mips_classify_symbol (addr.c.symbol));
-    }
-  abort ();
+      case ADDRESS_SYMBOLIC:
+       return factor * mips_symbol_insns (addr.symbol_type);
+      }
+  return 0;
 }
 
 
@@ -1210,15 +1271,20 @@ mips_address_insns (rtx x, enum machine_mode mode)
 int
 mips_const_insns (rtx x)
 {
-  struct mips_constant_info c;
   struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS];
+  enum mips_symbol_type symbol_type;
+  HOST_WIDE_INT offset;
 
   switch (GET_CODE (x))
     {
-    case CONSTANT_P_RTX:
     case HIGH:
-      return 1;
-
+      if (TARGET_MIPS16
+         || !mips_symbolic_constant_p (XEXP (x, 0), &symbol_type)
+         || !mips_split_p[symbol_type])
+       return 0;
+
+      return 1;
+
     case CONST_INT:
       if (TARGET_MIPS16)
        /* Unsigned 8-bit constants can be loaded using an unextended
@@ -1236,20 +1302,37 @@ mips_const_insns (rtx x)
     case CONST_DOUBLE:
       return (!TARGET_MIPS16 && x == CONST0_RTX (GET_MODE (x)) ? 1 : 0);
 
-    default:
-      switch (mips_classify_constant (&c, x))
+    case CONST:
+      if (CONST_GP_P (x))
+       return 1;
+
+      /* See if we can refer to X directly.  */
+      if (mips_symbolic_constant_p (x, &symbol_type))
+       return mips_symbol_insns (symbol_type);
+
+      /* Otherwise try splitting the constant into a base and offset.
+        16-bit offsets can be added using an extra addiu.  Larger offsets
+        must be calculated separately and then added to the base.  */
+      mips_split_const (x, &x, &offset);
+      if (offset != 0)
        {
-       case CONSTANT_NONE:
-         return 0;
+         int n = mips_const_insns (x);
+         if (n != 0)
+           {
+             if (SMALL_OPERAND (offset))
+               return n + 1;
+             else
+               return n + 1 + mips_build_integer (codes, offset);
+           }
+       }
+      return 0;
 
-       case CONSTANT_GP:
-       case CONSTANT_RELOC:
-         return 1;
+    case SYMBOL_REF:
+    case LABEL_REF:
+      return mips_symbol_insns (mips_classify_symbol (x));
 
-       case CONSTANT_SYMBOLIC:
-         return mips_symbol_insns (mips_classify_symbol (c.symbol));
-       }
-      abort ();
+    default:
+      return 0;
     }
 }
 
@@ -1267,16 +1350,19 @@ mips_fetch_insns (rtx x)
 }
 
 
-/* Return true if OP is a symbolic constant that refers to a
-   global PIC symbol.  */
+/* Return the number of instructions needed for an integer division.  */
 
-bool
-mips_global_pic_constant_p (rtx op)
+int
+mips_idiv_insns (void)
 {
-  struct mips_constant_info c;
+  int count;
 
-  return (mips_classify_constant (&c, op) == CONSTANT_SYMBOLIC
-         && mips_classify_symbol (c.symbol) == SYMBOL_GOT_GLOBAL);
+  count = 1;
+  if (TARGET_CHECK_ZERO_DIV)
+    count += 2;
+  if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
+    count++;
+  return count;
 }
 
 
@@ -1298,10 +1384,7 @@ uns_arith_operand (rtx op, enum machine_mode mode)
 int
 const_arith_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
-  struct mips_constant_info c;
-
-  return ((GET_CODE (op) == CONST_INT && SMALL_INT (op))
-         || mips_classify_constant (&c, op) == CONSTANT_RELOC);
+  return GET_CODE (op) == CONST_INT && SMALL_INT (op);
 }
 
 
@@ -1345,26 +1428,13 @@ reg_or_0_operand (rtx op, enum machine_mode mode)
     }
 }
 
-/* Accept a register or the floating point constant 1 in the appropriate mode.  */
+/* Accept a register or the floating point constant 1 in the
+   appropriate mode.  */
 
 int
 reg_or_const_float_1_operand (rtx op, enum machine_mode mode)
 {
-  REAL_VALUE_TYPE d;
-
-  switch (GET_CODE (op))
-    {
-    case CONST_DOUBLE:
-      if (mode != GET_MODE (op)
-         || (mode != DFmode && mode != SFmode))
-       return 0;
-
-      REAL_VALUE_FROM_CONST_DOUBLE (d, op);
-      return REAL_VALUES_EQUAL (d, dconst1);
-
-    default:
-      return register_operand (op, mode);
-    }
+  return const_float_1_operand (op, mode) || register_operand (op, mode);
 }
 
 /* Accept the floating point constant 1 in the appropriate mode.  */
@@ -1402,6 +1472,22 @@ extend_operator (rtx op, enum machine_mode mode)
          && (GET_CODE (op) == ZERO_EXTEND || GET_CODE (op) == SIGN_EXTEND));
 }
 
+/* Return true if X is the right hand side of a "macc" or "msac" instruction.
+   This predicate is intended for use in peephole optimizations.  */
+
+int
+macc_msac_operand (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  if (ISA_HAS_MACC && GET_CODE (x) == PLUS && REG_P (XEXP (x, 1)))
+    x = XEXP (x, 0);
+  else if (ISA_HAS_MSAC && GET_CODE (x) == MINUS && REG_P (XEXP (x, 0)))
+    x = XEXP (x, 1);
+  else
+    return false;
+
+  return GET_CODE (x) == MULT && REG_P (XEXP (x, 0)) && REG_P (XEXP (x, 1));
+}
+
 /* Return nonzero if the code of this rtx pattern is EQ or NE.  */
 
 int
@@ -1421,7 +1507,7 @@ cmp_op (rtx op, enum machine_mode mode)
   if (mode != GET_MODE (op))
     return 0;
 
-  return GET_RTX_CLASS (GET_CODE (op)) == '<';
+  return COMPARISON_P (op);
 }
 
 /* Return nonzero if the code is a relational operation suitable for a
@@ -1469,10 +1555,10 @@ pc_or_label_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 int
 call_insn_operand (rtx op, enum machine_mode mode)
 {
-  struct mips_constant_info c;
+  enum mips_symbol_type symbol_type;
 
-  if (mips_classify_constant (&c, op) == CONSTANT_SYMBOLIC)
-    switch (mips_classify_symbol (c.symbol))
+  if (mips_symbolic_constant_p (op, &symbol_type))
+    switch (symbol_type)
       {
       case SYMBOL_GENERAL:
        /* If -mlong-calls, force all calls to use register addressing.  */
@@ -1484,7 +1570,7 @@ call_insn_operand (rtx op, enum machine_mode mode)
           Using "la $25,foo; jal $25" would prevent the lazy binding
           of "foo", so keep the address of global symbols with the
           jal macro.  */
-       return c.offset == 0 && !TARGET_EXPLICIT_RELOCS;
+       return !TARGET_EXPLICIT_RELOCS;
 
       default:
        return false;
@@ -1499,15 +1585,40 @@ call_insn_operand (rtx op, enum machine_mode mode)
 int
 move_operand (rtx op, enum machine_mode mode)
 {
-  struct mips_constant_info c;
+  enum mips_symbol_type symbol_type;
 
-  if (GET_CODE (op) == HIGH && TARGET_ABICALLS)
+  if (!general_operand (op, mode))
     return false;
-  if (GET_CODE (op) == CONST_INT && !TARGET_MIPS16)
-    return (SMALL_INT (op) || SMALL_INT_UNSIGNED (op) || LUI_INT (op));
-  if (mips_classify_constant (&c, op) == CONSTANT_SYMBOLIC)
-    return mips_symbolic_address_p (c.symbol, c.offset, word_mode, 1);
-  return general_operand (op, mode);
+
+  switch (GET_CODE (op))
+    {
+    case CONST_INT:
+      /* When generating mips16 code, LEGITIMATE_CONSTANT_P rejects
+        CONST_INTs that can't be loaded using simple insns.  */
+      if (TARGET_MIPS16)
+       return true;
+
+      /* When generating 32-bit code, allow DImode move_operands to
+        match arbitrary constants.  We split them after reload.  */
+      if (!TARGET_64BIT && mode == DImode)
+       return true;
+
+      /* Otherwise check whether the constant can be loaded in a single
+        instruction.  */
+      return LUI_INT (op) || SMALL_INT (op) || SMALL_INT_UNSIGNED (op);
+
+    case CONST:
+    case SYMBOL_REF:
+    case LABEL_REF:
+      if (CONST_GP_P (op))
+       return true;
+
+      return (mips_symbolic_constant_p (op, &symbol_type)
+             && !mips_split_p[symbol_type]);
+
+    default:
+      return true;
+    }
 }
 
 
@@ -1528,234 +1639,178 @@ consttable_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 int
 symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
-  struct mips_constant_info c;
+  enum mips_symbol_type symbol_type;
 
-  return mips_classify_constant (&c, op) == CONSTANT_SYMBOLIC;
+  return mips_symbolic_constant_p (op, &symbol_type);
 }
 
 
-/* Return true if OP is a memory reference that uses the stack pointer
-   as a base register.  */
+/* Return true if OP is a symbolic constant of type SYMBOL_GENERAL.  */
 
 int
-stack_operand (rtx op, enum machine_mode mode)
+general_symbolic_operand (rtx op, enum machine_mode mode)
 {
-  struct mips_address_info addr;
+  enum mips_symbol_type symbol_type;
 
   return ((mode == VOIDmode || mode == GET_MODE (op))
-         && GET_CODE (op) == MEM
-         && mips_classify_address (&addr, XEXP (op, 0),
-                                   GET_MODE (op), false, true) == ADDRESS_REG
-         && addr.reg == stack_pointer_rtx);
+         && mips_symbolic_constant_p (op, &symbol_type)
+         && symbol_type == SYMBOL_GENERAL);
 }
 
 
-/* This function is used to implement GO_IF_LEGITIMATE_ADDRESS.  It
-   returns a nonzero value if X is a legitimate address for a memory
-   operand of the indicated MODE.  STRICT is nonzero if this function
-   is called during reload.  */
+/* Return true if we're generating PIC and OP is a global symbol.  */
 
-bool
-mips_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
+int
+global_got_operand (rtx op, enum machine_mode mode)
 {
-  struct mips_address_info addr;
+  enum mips_symbol_type symbol_type;
 
-  return mips_classify_address (&addr, x, mode, strict, 0) != ADDRESS_INVALID;
+  return ((mode == VOIDmode || mode == GET_MODE (op))
+         && mips_symbolic_constant_p (op, &symbol_type)
+         && symbol_type == SYMBOL_GOT_GLOBAL);
 }
 
 
-/* Return an rtx that represents the effect of applying relocation
-   RELOC to symbolic address ADDR.  */
+/* Likewise for local symbols.  */
 
-static rtx
-mips_reloc (rtx addr, int reloc)
+int
+local_got_operand (rtx op, enum machine_mode mode)
 {
-  struct mips_constant_info c;
-  rtx x;
-
-  if (mips_classify_constant (&c, addr) != CONSTANT_SYMBOLIC)
-    abort ();
+  enum mips_symbol_type symbol_type;
 
-  x = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, c.symbol), reloc);
-  return plus_constant (gen_rtx_CONST (VOIDmode, x), c.offset);
+  return ((mode == VOIDmode || mode == GET_MODE (op))
+         && mips_symbolic_constant_p (op, &symbol_type)
+         && symbol_type == SYMBOL_GOT_LOCAL);
 }
 
 
-/* Likewise, but shift the result left 16 bits.  The expression can be
-   used as the right hand side of an LUISI or LUIDI pattern.  */
+/* Return true if OP is a memory reference that uses the stack pointer
+   as a base register.  */
 
-static rtx
-mips_lui_reloc (rtx addr, int reloc)
+int
+stack_operand (rtx op, enum machine_mode mode)
 {
-  return gen_rtx_UNSPEC (Pmode,
-                        gen_rtvec (1, mips_reloc (addr, reloc)),
-                        UNSPEC_HIGH);
-}
+  struct mips_address_info addr;
 
-/* Copy VALUE to a register and return that register.  Use DEST as the
-   register if non-null, otherwise create a new one.
+  return ((mode == VOIDmode || mode == GET_MODE (op))
+         && GET_CODE (op) == MEM
+         && mips_classify_address (&addr, XEXP (op, 0), GET_MODE (op), false)
+         && addr.type == ADDRESS_REG
+         && addr.reg == stack_pointer_rtx);
+}
 
-   VALUE must be valid on the right hand side of a simple SET pattern.
-   The operation happens in Pmode.  */
+/* Helper function for DFA schedulers.  Return true if OP is a floating
+   point register.  */
 
-static rtx
-mips_force_temporary (rtx dest, rtx value)
+int
+fp_register_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
-  if (dest == 0)
-    return force_reg (Pmode, value);
-  else
-    {
-      if (!rtx_equal_p (dest, value))
-       emit_insn (gen_rtx_SET (VOIDmode, copy_rtx (dest), value));
-      return dest;
-    }
+  return REG_P (op) && FP_REG_P (REGNO (op));
 }
 
+/* Helper function for DFA schedulers.  Return true if OP is a LO reg.  */
 
-/* Return a legitimate address for REG + OFFSET.  This function will
-   create a temporary register if OFFSET is not a SMALL_OPERAND.  */
-
-static rtx
-mips_add_offset (rtx reg, HOST_WIDE_INT offset)
+int
+lo_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
-  if (!SMALL_OPERAND (offset))
-    reg = expand_simple_binop (GET_MODE (reg), PLUS,
-                              GEN_INT (CONST_HIGH_PART (offset)),
-                              reg, NULL, 0, OPTAB_WIDEN);
-
-  return plus_constant (reg, CONST_LOW_PART (offset));
+  return REG_P (op) && REGNO (op) == LO_REGNUM;
 }
+\f
 
+/* This function is used to implement GO_IF_LEGITIMATE_ADDRESS.  It
+   returns a nonzero value if X is a legitimate address for a memory
+   operand of the indicated MODE.  STRICT is nonzero if this function
+   is called during reload.  */
 
-/* Return the GOT entry whose address is given by %RELOC(ADDR)(BASE).
-   BASE is a base register (such as $gp), ADDR is addresses being
-   sought and RELOC is the relocation that should be used.  */
-
-static rtx
-mips_load_got (rtx base, rtx addr, int reloc)
+bool
+mips_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
 {
-  rtx mem;
-
-  mem = gen_rtx_MEM (ptr_mode,
-                    gen_rtx_PLUS (Pmode, base, mips_reloc (addr, reloc)));
-  set_mem_alias_set (mem, mips_got_alias_set);
-
-  /* GOT references can't trap.  */
-  MEM_NOTRAP_P (mem) = 1;
-
-  /* If we allow a function's address to be lazily bound, its entry
-     may change after the first call.  Other entries are constant.  */
-  if (reloc != RELOC_CALL16 && reloc != RELOC_CALL_LO)
-    RTX_UNCHANGING_P (mem) = 1;
-
-  if (Pmode != ptr_mode)
-    mem = gen_rtx_SIGN_EXTEND (Pmode, mem);
+  struct mips_address_info addr;
 
-  return mem;
+  return mips_classify_address (&addr, x, mode, strict);
 }
 
 
-/* Obtain the address of ADDR from the GOT using relocation RELOC.
-   The returned address may be used on the right hand side of a SET.  */
+/* Copy VALUE to a register and return that register.  If new psuedos
+   are allowed, copy it into a new register, otherwise use DEST.  */
 
 static rtx
-mips_load_got16 (rtx addr, int reloc)
+mips_force_temporary (rtx dest, rtx value)
 {
-  return mips_load_got (pic_offset_table_rtx, addr, reloc);
+  if (!no_new_pseudos)
+    return force_reg (Pmode, value);
+  else
+    {
+      emit_move_insn (copy_rtx (dest), value);
+      return dest;
+    }
 }
 
 
-/* Like mips_load_got16, but for 32-bit offsets.  HIGH_RELOC is the
-   relocation that gives the high 16 bits of the offset and LOW_RELOC is
-   the relocation that gives the low 16 bits.  TEMP is a Pmode register
-   to use a temporary, or null if new registers can be created at will.  */
+/* Return a LO_SUM expression for ADDR.  TEMP is as for mips_force_temporary
+   and is used to load the high part into a register.  */
 
 static rtx
-mips_load_got32 (rtx temp, rtx addr, int high_reloc, int low_reloc)
+mips_split_symbol (rtx temp, rtx addr)
 {
-  rtx x;
+  rtx high;
 
-  x = mips_force_temporary (temp, mips_lui_reloc (addr, high_reloc));
-  x = mips_force_temporary (temp,
-                           gen_rtx_PLUS (Pmode, pic_offset_table_rtx, x));
-  return mips_load_got (x, addr, low_reloc);
+  if (TARGET_MIPS16)
+    high = mips16_gp_pseudo_reg ();
+  else
+    high = mips_force_temporary (temp, gen_rtx_HIGH (Pmode, copy_rtx (addr)));
+  return gen_rtx_LO_SUM (Pmode, high, addr);
 }
 
 
-/* Copy the high part of ADDR into a register and return the register.
-   Use DEST as the register if non-null.  */
+/* Return an UNSPEC address with underlying address ADDRESS and symbol
+   type SYMBOL_TYPE.  */
 
-static rtx
-mips_emit_high (rtx dest, rtx addr)
+rtx
+mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
 {
-  rtx high, x;
-
-  high = gen_rtx_HIGH (Pmode, addr);
-  if (TARGET_ABICALLS)
-    {
-      x = mips_load_got16 (copy_rtx (addr), RELOC_GOT_PAGE);
-      x = mips_force_temporary (dest, x);
-      set_unique_reg_note (get_last_insn (), REG_EQUAL, high);
-    }
-  else
-    x = mips_force_temporary (dest, high);
+  rtx base;
+  HOST_WIDE_INT offset;
 
-  return x;
+  mips_split_const (address, &base, &offset);
+  base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
+                        UNSPEC_ADDRESS_FIRST + symbol_type);
+  return plus_constant (gen_rtx_CONST (Pmode, base), offset);
 }
 
 
-/* See if *XLOC is a symbolic constant that can be reduced in some way.
-   If it is, set *XLOC to the reduced expression and return true.
-   The new expression will be both a legitimate address and a legitimate
-   source operand for a mips.md SET pattern.  If OFFSETABLE_P, the
-   address will be offsetable.
+/* 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 available as a temporary register if needed.
 
-   DEST is a register to use a temporary, or null if new registers
-   can be created at will.  */
+   The returned expression can be used as the first operand to a LO_SUM.  */
 
-static bool
-mips_legitimize_symbol (rtx dest, rtx *xloc, int offsetable_p)
+static rtx
+mips_unspec_offset_high (rtx temp, rtx base, rtx addr,
+                        enum mips_symbol_type symbol_type)
 {
-  struct mips_constant_info c;
-  enum mips_symbol_type symbol_type;
-  rtx x;
-
-  if (mips_classify_constant (&c, *xloc) != CONSTANT_SYMBOLIC)
-    return false;
-
-  symbol_type = mips_classify_symbol (c.symbol);
-
-  /* If a non-offsetable address is OK, try splitting it into a
-     high part and a LO_SUM.  */
-  if (!offsetable_p && mips_splittable_symbol_p (symbol_type, c.offset))
+  if (mips_split_p[symbol_type])
     {
-      if (symbol_type == SYMBOL_SMALL_DATA)
-       x = mips_sdata_pointer ();
-      else
-       x = mips_emit_high (dest, *xloc);
-      if (x != 0)
-       {
-         *xloc = gen_rtx_LO_SUM (Pmode, x, copy_rtx (*xloc));
-         return true;
-       }
+      addr = gen_rtx_HIGH (Pmode, mips_unspec_address (addr, symbol_type));
+      addr = mips_force_temporary (temp, addr);
+      return mips_force_temporary (temp, gen_rtx_PLUS (Pmode, addr, base));
     }
+  return base;
+}
 
-  /* If the offset is nonzero, move the symbol into a register (always valid)
-     and add the constant in afterwards.  This requires an extra temporary if
-     the offset isn't a signed 16-bit number.
 
-     For mips16, it's better to force the constant into memory instead.  */
-  if (!TARGET_MIPS16
-      && c.offset != 0
-      && (SMALL_OPERAND (c.offset) || dest == 0))
-    {
-      x = (dest == 0 ? gen_reg_rtx (Pmode) : dest);
-      emit_move_insn (copy_rtx (x), c.symbol);
-      *xloc = mips_add_offset (x, c.offset);
-      return true;
-    }
+/* Return a legitimate address for REG + OFFSET.  This function will
+   create a temporary register if OFFSET is not a SMALL_OPERAND.  */
 
-  return false;
+static rtx
+mips_add_offset (rtx reg, HOST_WIDE_INT offset)
+{
+  if (!SMALL_OPERAND (offset))
+    reg = expand_simple_binop (GET_MODE (reg), PLUS,
+                              GEN_INT (CONST_HIGH_PART (offset)),
+                              reg, NULL, 0, OPTAB_WIDEN);
+
+  return plus_constant (reg, CONST_LOW_PART (offset));
 }
 
 
@@ -1767,8 +1822,16 @@ mips_legitimize_symbol (rtx dest, rtx *xloc, int offsetable_p)
 bool
 mips_legitimize_address (rtx *xloc, enum machine_mode mode)
 {
-  if (mips_legitimize_symbol (0, xloc, !SINGLE_WORD_MODE_P (mode)))
-    return true;
+  enum mips_symbol_type symbol_type;
+
+  /* See if the address can split into a high part and a LO_SUM.  */
+  if (mips_symbolic_constant_p (*xloc, &symbol_type)
+      && mips_symbolic_address_p (symbol_type, mode)
+      && mips_split_p[symbol_type])
+    {
+      *xloc = mips_split_symbol (0, *xloc);
+      return true;
+    }
 
   if (GET_CODE (*xloc) == PLUS && GET_CODE (XEXP (*xloc, 1)) == CONST_INT)
     {
@@ -1924,54 +1987,48 @@ mips_move_integer (rtx dest, unsigned HOST_WIDE_INT value)
 static void
 mips_legitimize_const_move (enum machine_mode mode, rtx dest, rtx src)
 {
-  rtx temp;
-
-  temp = no_new_pseudos ? dest : 0;
-
-  /* If generating PIC, the high part of an address is loaded from the GOT.  */
-  if (GET_CODE (src) == HIGH)
-    {
-      mips_emit_high (dest, XEXP (src, 0));
-      return;
-    }
+  rtx base;
+  HOST_WIDE_INT offset;
+  enum mips_symbol_type symbol_type;
 
+  /* Split moves of big integers into smaller pieces.  In mips16 code,
+     it's better to force the constant into memory instead.  */
   if (GET_CODE (src) == CONST_INT && !TARGET_MIPS16)
     {
       mips_move_integer (dest, INTVAL (src));
       return;
     }
 
-  /* Fetch global symbols from the GOT.  */
-  if (TARGET_EXPLICIT_RELOCS
-      && GET_CODE (src) == SYMBOL_REF
-      && mips_classify_symbol (src) == SYMBOL_GOT_GLOBAL)
+  /* See if the symbol can be split.  For mips16, this is often worse than
+     forcing it in the constant pool since it needs the single-register form
+     of addiu or daddiu.  */
+  if (!TARGET_MIPS16
+      && mips_symbolic_constant_p (src, &symbol_type)
+      && mips_split_p[symbol_type])
     {
-      if (TARGET_XGOT)
-       src = mips_load_got32 (temp, src, RELOC_GOT_HI, RELOC_GOT_LO);
-      else
-       src = mips_load_got16 (src, RELOC_GOT_DISP);
-      emit_insn (gen_rtx_SET (VOIDmode, dest, src));
+      emit_move_insn (dest, mips_split_symbol (dest, src));
       return;
     }
 
-  /* Try handling the source operand as a symbolic address.  */
-  if (mips_legitimize_symbol (temp, &src, false))
+  /* If we have (const (plus symbol offset)), load the symbol first
+     and then add in the offset.  This is usually better than forcing
+     the constant into memory, at least in non-mips16 code.  */
+  mips_split_const (src, &base, &offset);
+  if (!TARGET_MIPS16
+      && offset != 0
+      && (!no_new_pseudos || SMALL_OPERAND (offset)))
     {
-      emit_insn (gen_rtx_SET (VOIDmode, dest, src));
+      base = mips_force_temporary (dest, base);
+      emit_move_insn (dest, mips_add_offset (base, offset));
       return;
     }
 
   src = force_const_mem (mode, src);
 
   /* When using explicit relocs, constant pool references are sometimes
-     not legitimate addresses.  mips_legitimize_symbol must be able to
-     deal with all such cases.  */
-  if (GET_CODE (src) == MEM && !memory_operand (src, VOIDmode))
-    {
-      src = copy_rtx (src);
-      if (!mips_legitimize_symbol (temp, &XEXP (src, 0), false))
-       abort ();
-    }
+     not legitimate addresses.  */
+  if (!memory_operand (src, VOIDmode))
+    src = replace_equiv_address (src, mips_split_symbol (dest, XEXP (src, 0)));
   emit_move_insn (dest, src);
 }
 
@@ -1988,13 +2045,26 @@ mips_legitimize_move (enum machine_mode mode, rtx dest, rtx src)
       return true;
     }
 
-  /* The source of an SImode move must be a move_operand.  Likewise
-     DImode moves on 64-bit targets.  We need to deal with constants
-     that would be legitimate immediate_operands but not legitimate
-     move_operands.  */
+  /* Check for individual, fully-reloaded mflo and mfhi instructions.  */
   if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
-      && CONSTANT_P (src)
-      && !move_operand (src, mode))
+      && REG_P (src) && MD_REG_P (REGNO (src))
+      && REG_P (dest) && GP_REG_P (REGNO (dest)))
+    {
+      int other_regno = REGNO (src) == HI_REGNUM ? LO_REGNUM : HI_REGNUM;
+      if (GET_MODE_SIZE (mode) <= 4)
+       emit_insn (gen_mfhilo_si (gen_rtx_REG (SImode, REGNO (dest)),
+                                 gen_rtx_REG (SImode, REGNO (src)),
+                                 gen_rtx_REG (SImode, other_regno)));
+      else
+       emit_insn (gen_mfhilo_di (gen_rtx_REG (DImode, REGNO (dest)),
+                                 gen_rtx_REG (DImode, REGNO (src)),
+                                 gen_rtx_REG (DImode, other_regno)));
+      return true;
+    }
+
+  /* We need to deal with constants that would be legitimate
+     immediate_operands but not 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));
@@ -2002,30 +2072,6 @@ mips_legitimize_move (enum machine_mode mode, rtx dest, rtx src)
     }
   return false;
 }
-
-
-/* Convert GOT and GP-relative accesses back into their original form.
-   Used by both TARGET_DELEGITIMIZE_ADDRESS and FIND_BASE_TERM.  */
-
-rtx
-mips_delegitimize_address (rtx x)
-{
-  struct mips_constant_info c;
-
-  if (GET_CODE (x) == MEM
-      && GET_CODE (XEXP (x, 0)) == PLUS
-      && mips_classify_constant (&c, XEXP (XEXP (x, 0), 1)) == CONSTANT_RELOC
-      && mips_classify_symbol (c.symbol) == SYMBOL_GOT_GLOBAL)
-    return c.symbol;
-
-  if (GET_CODE (x) == LO_SUM
-      && XEXP (x, 0) == (TARGET_MIPS16
-                        ? cfun->machine->mips16_gp_pseudo_rtx
-                        : pic_offset_table_rtx))
-    return XEXP (x, 1);
-
-  return x;
-}
 \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
@@ -2136,57 +2182,6 @@ m16_nsimm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
   return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7);
 }
-
-/* References to the string table on the mips16 only use a small
-   offset if the function is small.  We can't check for LABEL_REF here,
-   because the offset is always large if the label is before the
-   referencing instruction.  */
-
-int
-m16_usym8_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-  if (GET_CODE (op) == SYMBOL_REF
-      && SYMBOL_REF_FLAG (op)
-      && cfun->machine->insns_len > 0
-      && INTERNAL_SYMBOL_P (op)
-      && (cfun->machine->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;
-    }
-
-  return 0;
-}
-
-int
-m16_usym5_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-  if (GET_CODE (op) == SYMBOL_REF
-      && SYMBOL_REF_FLAG (op)
-      && cfun->machine->insns_len > 0
-      && INTERNAL_SYMBOL_P (op)
-      && (cfun->machine->insns_len + get_pool_size () + mips_string_length
-         < 4 * 0x20))
-    {
-      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;
-    }
-
-  return 0;
-}
 \f
 static bool
 mips_rtx_costs (rtx x, int code, int outer_code, int *total)
@@ -2252,17 +2247,17 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total)
     case SYMBOL_REF:
     case LABEL_REF:
     case CONST_DOUBLE:
-      if (((outer_code) == PLUS || (outer_code) == MINUS)
-          && const_arith_operand (x, VOIDmode))
-        {
-          *total = 0;
-          return true;
-        }
+      if (LEGITIMATE_CONSTANT_P (x))
+       {
+         *total = COSTS_N_INSNS (1);
+         return true;
+       }
       else
-        {
-          int n = mips_const_insns (x);
-          return (n == 0 ? CONSTANT_POOL_COST : COSTS_N_INSNS (n));
-        }
+       {
+         /* The value will need to be fetched from the constant pool.  */
+         *total = CONSTANT_POOL_COST;
+         return true;
+       }
 
     case MEM:
       {
@@ -2325,6 +2320,8 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total)
             *total = COSTS_N_INSNS (2);
           else if (TUNE_MIPS6000)
             *total = COSTS_N_INSNS (3);
+         else if (TUNE_SB1)
+           *total = COSTS_N_INSNS (4);
           else
             *total = COSTS_N_INSNS (6);
           return true;
@@ -2349,7 +2346,8 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total)
         {
           if (TUNE_MIPS3000
               || TUNE_MIPS3900
-              || TUNE_MIPS5000)
+              || TUNE_MIPS5000
+             || TUNE_SB1)
             *total = COSTS_N_INSNS (4);
           else if (TUNE_MIPS6000
                    || TUNE_MIPS5400
@@ -2362,7 +2360,9 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total)
 
       if (mode == DFmode)
         {
-          if (TUNE_MIPS3000
+          if (TUNE_SB1)
+           *total = COSTS_N_INSNS (4);
+          else if (TUNE_MIPS3000
               || TUNE_MIPS3900
               || TUNE_MIPS5000)
             *total = COSTS_N_INSNS (5);
@@ -2379,9 +2379,11 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total)
         *total = COSTS_N_INSNS (12);
       else if (TUNE_MIPS3900)
         *total = COSTS_N_INSNS (2);
-      else if (TUNE_MIPS5400 || TUNE_MIPS5500)
-        *total = COSTS_N_INSNS ((mode == DImode) ? 4 : 3);
-      else if (TUNE_MIPS7000)
+      else if (TUNE_MIPS4130)
+       *total = COSTS_N_INSNS (mode == DImode ? 6 : 4);
+      else if (TUNE_MIPS5400 || TUNE_SB1)
+        *total = COSTS_N_INSNS (mode == DImode ? 4 : 3);
+      else if (TUNE_MIPS5500 || TUNE_MIPS7000)
         *total = COSTS_N_INSNS (mode == DImode ? 9 : 5);
       else if (TUNE_MIPS9000)
         *total = COSTS_N_INSNS (mode == DImode ? 8 : 3);
@@ -2402,6 +2404,8 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total)
             *total = COSTS_N_INSNS (12);
           else if (TUNE_MIPS6000)
             *total = COSTS_N_INSNS (15);
+         else if (TUNE_SB1)
+           *total = COSTS_N_INSNS (24);
           else if (TUNE_MIPS5400 || TUNE_MIPS5500)
             *total = COSTS_N_INSNS (30);
           else
@@ -2418,11 +2422,13 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total)
             *total = COSTS_N_INSNS (59);
           else if (TUNE_MIPS6000)
             *total = COSTS_N_INSNS (16);
+         else if (TUNE_SB1)
+           *total = COSTS_N_INSNS (32);
           else
             *total = COSTS_N_INSNS (36);
           return true;
         }
-      /* FALLTHRU */
+      /* Fall through.  */
 
     case UDIV:
     case UMOD:
@@ -2433,6 +2439,8 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total)
         *total = COSTS_N_INSNS (38);
       else if (TUNE_MIPS5000)
         *total = COSTS_N_INSNS (36);
+      else if (TUNE_SB1)
+       *total = COSTS_N_INSNS ((mode == SImode) ? 36 : 68);
       else if (TUNE_MIPS5400 || TUNE_MIPS5500)
         *total = COSTS_N_INSNS ((mode == SImode) ? 42 : 74);
       else
@@ -2472,64 +2480,15 @@ mips_address_cost (rtx addr)
   return mips_address_insns (addr, SImode);
 }
 \f
-/* Return a pseudo that points to the address of the current function.
-   The first time it is called for a function, an initializer for the
-   pseudo is emitted in the beginning of the function.  */
+/* 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
-embedded_pic_fnaddr_reg (void)
+mips_subword (rtx op, int high_p)
 {
-  if (cfun->machine->embedded_pic_fnaddr_rtx == NULL)
-    {
-      rtx seq;
-
-      cfun->machine->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 (cfun->machine->embedded_pic_fnaddr_rtx,
-                                XEXP (DECL_RTL (current_function_decl), 0)));
-      seq = get_insns ();
-      end_sequence ();
-      push_topmost_sequence ();
-      emit_insn_after (seq, get_insns ());
-      pop_topmost_sequence ();
-    }
-
-  return cfun->machine->embedded_pic_fnaddr_rtx;
-}
-
-/* Return RTL for the offset from the current function to the argument.
-   X is the symbol whose offset from the current function we want.  */
-
-rtx
-embedded_pic_offset (rtx x)
-{
-  /* Make sure it is emitted.  */
-  embedded_pic_fnaddr_reg ();
-
-  return
-    gen_rtx_CONST (Pmode,
-                  gen_rtx_MINUS (Pmode, x,
-                                 XEXP (DECL_RTL (current_function_decl), 0)));
-}
-\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, int high_p)
-{
-  unsigned int byte;
-  enum machine_mode mode;
+  unsigned int byte;
+  enum machine_mode mode;
 
   mode = GET_MODE (op);
   if (mode == VOIDmode)
@@ -2549,7 +2508,7 @@ mips_subword (rtx op, int high_p)
     }
 
   if (GET_CODE (op) == MEM)
-    return adjust_address (op, word_mode, byte);
+    return mips_rewrite_small_data (adjust_address (op, word_mode, byte));
 
   return simplify_gen_subreg (word_mode, op, mode, byte);
 }
@@ -2639,7 +2598,6 @@ const char *
 mips_output_move (rtx dest, rtx src)
 {
   enum rtx_code dest_code, src_code;
-  struct mips_constant_info c;
   bool dbl_p;
 
   dest_code = GET_CODE (dest);
@@ -2678,9 +2636,6 @@ mips_output_move (rtx dest, rtx src)
     {
       if (src_code == REG)
        {
-         if (MD_REG_P (REGNO (src)))
-           return "mf%1\t%0";
-
          if (ST_REG_P (REGNO (src)) && ISA_HAS_8CC)
            return "lui\t%0,0x3f80\n\tmovf\t%0,%.,%1";
 
@@ -2716,20 +2671,11 @@ mips_output_move (rtx dest, rtx src)
       if (src_code == HIGH)
        return "lui\t%0,%h1";
 
-      switch (mips_classify_constant (&c, src))
-       {
-       case CONSTANT_NONE:
-         break;
-
-       case CONSTANT_GP:
-         return "move\t%0,%1";
+      if (CONST_GP_P (src))
+       return "move\t%0,%1";
 
-       case CONSTANT_RELOC:
-         return "li\t%0,%1";
-
-       case CONSTANT_SYMBOLIC:
-         return (dbl_p ? "dla\t%0,%a1" : "la\t%0,%a1");
-       }
+      if (symbolic_operand (src, VOIDmode))
+       return (dbl_p ? "dla\t%0,%1" : "la\t%0,%1");
     }
   if (src_code == REG && FP_REG_P (REGNO (src)))
     {
@@ -2886,13 +2832,13 @@ gen_int_relational (enum rtx_code test_code, rtx result, rtx cmp0,
   if (mode == VOIDmode)
     mode = GET_MODE (cmp1);
 
-  /* Eliminate simple branches */
+  /* Eliminate simple branches */
   branch_p = (result == 0);
   if (branch_p)
     {
       if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
        {
-         /* Comparisons against zero are simple branches */
+         /* Comparisons against zero are simple branches */
          if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0
              && (! TARGET_MIPS16 || eqne_p))
            return 0;
@@ -2902,7 +2848,7 @@ gen_int_relational (enum rtx_code test_code, rtx result, rtx cmp0,
            return 0;
        }
 
-      /* allocate a pseudo to calculate the value in.  */
+      /* Allocate a pseudo to calculate the value in.  */
       result = gen_reg_rtx (mode);
     }
 
@@ -2978,14 +2924,15 @@ gen_int_relational (enum rtx_code test_code, rtx result, rtx cmp0,
   else
     {
       reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result;
-      convert_move (reg, gen_rtx (p_info->test_code, mode, cmp0, cmp1), 0);
+      convert_move (reg, gen_rtx_fmt_ee (p_info->test_code,
+                                        mode, cmp0, cmp1), 0);
     }
 
   if (test == ITEST_NE)
     {
       if (! TARGET_MIPS16)
        {
-         convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0);
+         convert_move (result, gen_rtx_GTU (mode, reg, const0_rtx), 0);
          if (p_invert != NULL)
            *p_invert = 0;
          invert = 0;
@@ -2993,7 +2940,7 @@ gen_int_relational (enum rtx_code test_code, rtx result, rtx cmp0,
       else
        {
          reg2 = invert ? gen_reg_rtx (mode) : result;
-         convert_move (reg2, gen_rtx (LTU, mode, reg, const1_rtx), 0);
+         convert_move (reg2, gen_rtx_LTU (mode, reg, const1_rtx), 0);
          reg = reg2;
        }
     }
@@ -3021,7 +2968,7 @@ gen_int_relational (enum rtx_code test_code, rtx result, rtx cmp0,
          reg = reg2;
          one = force_reg (mode, const1_rtx);
        }
-      convert_move (result, gen_rtx (XOR, mode, reg, one), 0);
+      convert_move (result, gen_rtx_XOR (mode, reg, one), 0);
     }
 
   return result;
@@ -3101,7 +3048,7 @@ gen_conditional_branch (rtx *operands, enum rtx_code test_code)
 
       get_float_compare_codes (test_code, &cmp_code, &test_code);
       emit_insn (gen_rtx_SET (VOIDmode, reg,
-                             gen_rtx (cmp_code, CCmode, cmp0, cmp1)));
+                             gen_rtx_fmt_ee (cmp_code, CCmode, cmp0, cmp1)));
 
       mode = CCmode;
       cmp0 = reg;
@@ -3110,7 +3057,8 @@ gen_conditional_branch (rtx *operands, enum rtx_code test_code)
       break;
 
     default:
-      fatal_insn ("bad test", gen_rtx (test_code, VOIDmode, cmp0, cmp1));
+      fatal_insn ("bad test",
+                 gen_rtx_fmt_ee (test_code, VOIDmode, cmp0, cmp1));
     }
 
   /* Generate the branch.  */
@@ -3124,11 +3072,12 @@ gen_conditional_branch (rtx *operands, enum rtx_code test_code)
       label1 = pc_rtx;
     }
 
-  emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
-                              gen_rtx_IF_THEN_ELSE (VOIDmode,
-                                                    gen_rtx (test_code, mode,
-                                                             cmp0, cmp1),
-                                                    label1, label2)));
+  emit_jump_insn
+    (gen_rtx_SET (VOIDmode, pc_rtx,
+                 gen_rtx_IF_THEN_ELSE (VOIDmode,
+                                       gen_rtx_fmt_ee (test_code, mode,
+                                                       cmp0, cmp1),
+                                       label1, label2)));
 }
 
 /* Emit the common code for conditional moves.  OPERANDS is the array
@@ -3207,13 +3156,14 @@ gen_conditional_move (rtx *operands)
 
   cmp_reg = gen_reg_rtx (cmp_mode);
   emit_insn (gen_rtx_SET (cmp_mode, cmp_reg,
-                         gen_rtx (cmp_code, cmp_mode, op0, op1)));
+                         gen_rtx_fmt_ee (cmp_code, cmp_mode, op0, op1)));
 
   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)),
+                                               gen_rtx_fmt_ee (move_code,
+                                                               VOIDmode,
+                                                               cmp_reg,
+                                                               const0_rtx),
                                                operands[2], operands[3])));
 }
 
@@ -3252,10 +3202,40 @@ mips_gen_conditional_trap (rtx *operands)
     op1 = force_reg (mode, op1);
 
   emit_insn (gen_rtx_TRAP_IF (VOIDmode,
-                             gen_rtx (cmp_code, GET_MODE (operands[0]), op0, op1),
+                             gen_rtx_fmt_ee (cmp_code, GET_MODE (operands[0]),
+                                             op0, op1),
                              operands[1]));
 }
 \f
+/* Load function address ADDR into register DEST.  SIBCALL_P is true
+   if the address is needed for a sibling call.  */
+
+static void
+mips_load_call_address (rtx dest, rtx addr, int sibcall_p)
+{
+  /* If we're generating PIC, and this call is to a global function,
+     try to allow its address to be resolved lazily.  This isn't
+     possible for NewABI sibcalls since the value of $gp on entry
+     to the stub would be our caller's gp, not ours.  */
+  if (TARGET_EXPLICIT_RELOCS
+      && !(sibcall_p && TARGET_NEWABI)
+      && global_got_operand (addr, VOIDmode))
+    {
+      rtx high, lo_sum_symbol;
+
+      high = mips_unspec_offset_high (dest, pic_offset_table_rtx,
+                                     addr, SYMBOL_GOTOFF_CALL);
+      lo_sum_symbol = mips_unspec_address (addr, SYMBOL_GOTOFF_CALL);
+      if (Pmode == SImode)
+       emit_insn (gen_load_callsi (dest, high, lo_sum_symbol));
+      else
+       emit_insn (gen_load_calldi (dest, high, lo_sum_symbol));
+    }
+  else
+    emit_move_insn (dest, addr);
+}
+
+
 /* Expand a call or call_value instruction.  RESULT is where the
    result will go (null for calls), ADDR is the address of the
    function, ARGS_SIZE is the size of the arguments and AUX is
@@ -3266,46 +3246,46 @@ mips_gen_conditional_trap (rtx *operands)
 void
 mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
 {
+  rtx orig_addr, pattern, insn;
+
+  orig_addr = addr;
   if (!call_insn_operand (addr, VOIDmode))
     {
-      /* When generating PIC, try to allow global functions to be
-        lazily bound.  */
-      if (TARGET_EXPLICIT_RELOCS
-         && GET_CODE (addr) == SYMBOL_REF
-         && mips_classify_symbol (addr) == SYMBOL_GOT_GLOBAL)
-       {
-         if (TARGET_XGOT)
-           addr = mips_load_got32 (0, addr, RELOC_CALL_HI, RELOC_CALL_LO);
-         else
-           addr = mips_load_got16 (addr, RELOC_CALL16);
-       }
-      addr = force_reg (Pmode, addr);
+      addr = gen_reg_rtx (Pmode);
+      mips_load_call_address (addr, orig_addr, sibcall_p);
     }
 
   if (TARGET_MIPS16
       && mips16_hard_float
       && build_mips16_call_stub (result, addr, args_size,
                                 aux == 0 ? 0 : (int) GET_MODE (aux)))
-    /* Nothing more to do */;
-  else if (result == 0)
-    emit_call_insn (sibcall_p
-                   ? gen_sibcall_internal (addr, args_size)
-                   : gen_call_internal (addr, args_size));
+    return;
+
+  if (result == 0)
+    pattern = (sibcall_p
+              ? gen_sibcall_internal (addr, args_size)
+              : gen_call_internal (addr, args_size));
   else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2)
     {
       rtx reg1, reg2;
 
       reg1 = XEXP (XVECEXP (result, 0, 0), 0);
       reg2 = XEXP (XVECEXP (result, 0, 1), 0);
-      emit_call_insn
+      pattern =
        (sibcall_p
         ? gen_sibcall_value_multiple_internal (reg1, addr, args_size, reg2)
         : gen_call_value_multiple_internal (reg1, addr, args_size, reg2));
     }
   else
-    emit_call_insn (sibcall_p
-                   ? gen_sibcall_value_internal (result, addr, args_size)
-                   : gen_call_value_internal (result, addr, args_size));
+    pattern = (sibcall_p
+              ? gen_sibcall_value_internal (result, addr, args_size)
+              : gen_call_value_internal (result, addr, args_size));
+
+  insn = emit_call_insn (pattern);
+
+  /* Lazy-binding stubs require $gp to be valid on entry.  */
+  if (global_got_operand (orig_addr, VOIDmode))
+    use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
 }
 
 
@@ -3422,33 +3402,33 @@ mips_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length)
      the source has enough alignment, otherwise use left/right pairs.  */
   for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
     {
-      rtx part;
-
       regs[i] = gen_reg_rtx (mode);
-      part = adjust_address (src, mode, offset);
-      if (MEM_ALIGN (part) >= bits)
-       emit_move_insn (regs[i], part);
-      else if (!mips_expand_unaligned_load (regs[i], part, bits, 0))
-       abort ();
+      if (MEM_ALIGN (src) >= bits)
+       emit_move_insn (regs[i], adjust_address (src, mode, offset));
+      else
+       {
+         rtx part = adjust_address (src, BLKmode, offset);
+         if (!mips_expand_unaligned_load (regs[i], part, bits, 0))
+           abort ();
+       }
     }
 
   /* Copy the chunks to the destination.  */
   for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
-    {
-      rtx part;
-
-      part = adjust_address (dest, mode, offset);
-      if (MEM_ALIGN (part) >= bits)
-       emit_move_insn (part, regs[i]);
-      else if (!mips_expand_unaligned_store (part, regs[i], bits, 0))
-       abort ();
-    }
+    if (MEM_ALIGN (dest) >= bits)
+      emit_move_insn (adjust_address (dest, mode, offset), regs[i]);
+    else
+      {
+       rtx part = adjust_address (dest, BLKmode, offset);
+       if (!mips_expand_unaligned_store (part, regs[i], bits, 0))
+         abort ();
+      }
 
   /* Mop up any left-over bytes.  */
   if (offset < length)
     {
-      src = adjust_address (src, mode, offset);
-      dest = adjust_address (dest, mode, offset);
+      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);
     }
@@ -3556,23 +3536,6 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
   static CUMULATIVE_ARGS zero_cum;
   tree param, next_param;
 
-  if (TARGET_DEBUG_E_MODE)
-    {
-      fprintf (stderr,
-              "\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype);
-
-      if (!fntype)
-       fputc ('\n', stderr);
-
-      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)]);
-       }
-    }
-
   *cum = zero_cum;
   cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
 
@@ -3743,7 +3706,7 @@ function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
   if (type != 0
       && TREE_CODE (type) == RECORD_TYPE
-      && (mips_abi == ABI_N32 || mips_abi == ABI_64)
+      && TARGET_NEWABI
       && TYPE_SIZE_UNIT (type)
       && host_integerp (TYPE_SIZE_UNIT (type), 1)
       && named)
@@ -3878,9 +3841,9 @@ mips_pad_reg_upward (enum machine_mode mode, tree type)
   return mips_pad_arg_upward (mode, type);
 }
 \f
-int
-mips_setup_incoming_varargs (const CUMULATIVE_ARGS *cum,
-                            enum machine_mode mode, tree type, int no_rtl)
+static void
+mips_setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                            tree type, int *pretend_size, int no_rtl)
 {
   CUMULATIVE_ARGS local_cum;
   int gp_saved, fp_saved;
@@ -3925,12 +3888,12 @@ mips_setup_incoming_varargs (const CUMULATIVE_ARGS *cum,
       if (fp_saved > 0)
        {
          /* We can't use move_block_from_reg, because it will use
-            the wrong mode. */
+            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 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;
          off &= ~(UNITS_PER_FPVALUE - 1);
@@ -3950,11 +3913,14 @@ mips_setup_incoming_varargs (const CUMULATIVE_ARGS *cum,
            }
        }
     }
-  if (mips_abi == ABI_32 || mips_abi == ABI_O64)
-    /* No need for pretend arguments: the register parameter area was
-       allocated by the caller.  */
-    return 0;
-  return (gp_saved * UNITS_PER_WORD) + (fp_saved * UNITS_PER_FPREG);
+  if (TARGET_OLDABI)
+    {
+      /* No need for pretend arguments: the register parameter area was
+        allocated by the caller.  */
+      *pretend_size = 0;
+      return;
+    }
+  *pretend_size = (gp_saved * UNITS_PER_WORD) + (fp_saved * UNITS_PER_FPREG);
 }
 
 /* Create the va_list data type.
@@ -3977,16 +3943,15 @@ mips_setup_incoming_varargs (const CUMULATIVE_ARGS *cum,
      and two offsets, although we could have designed this with two pointers
      and three offsets.  */
 
-
-tree
-mips_build_va_list (void)
+static tree
+mips_build_builtin_va_list (void)
 {
   if (EABI_FLOAT_VARARGS_P)
     {
       tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff, f_res, record;
       tree array, index;
 
-      record = make_node (RECORD_TYPE);
+      record = (*lang_hooks.types.make_type) (RECORD_TYPE);
 
       f_ovfl = build_decl (FIELD_DECL, get_identifier ("__overflow_argptr"),
                          ptr_type_node);
@@ -4022,7 +3987,11 @@ mips_build_va_list (void)
       layout_type (record);
       return record;
     }
+  else if (TARGET_IRIX && !TARGET_IRIX5)
+    /* On IRIX 6, this type is 'char *'.  */
+    return build_pointer_type (char_type_node);
   else
+    /* Otherwise, we use 'void *'.  */
     return ptr_type_node;
 }
 
@@ -4063,11 +4032,16 @@ mips_va_start (tree valist, rtx nextarg)
          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);
+         ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
+                       NULL_TREE);
+         gtop = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop,
+                       NULL_TREE);
+         ftop = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop,
+                       NULL_TREE);
+         goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff,
+                       NULL_TREE);
+         foff = build (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
@@ -4148,8 +4122,6 @@ mips_va_arg (tree valist, tree type)
          rsize = UNITS_PER_WORD;
        }
 
-      addr_rtx = gen_reg_rtx (Pmode);
-
       if (!EABI_FLOAT_VARARGS_P)
        {
          /* Case of all args in a merged stack.  No need to check bounds,
@@ -4172,12 +4144,12 @@ mips_va_arg (tree valist, tree type)
 
          /* Emit code to set addr_rtx to the valist, and postincrement
             the valist by the size of the argument, rounded up to the
-            next word.  */
+            next word.  Account for padding on big-endian targets.  */
          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);
+         addr_rtx = expand_expr (t, 0, Pmode, EXPAND_NORMAL);
+         if (BYTES_BIG_ENDIAN)
+           addr_rtx = plus_constant (addr_rtx, rsize - size);
 
          /* Flush the POSTINCREMENT.  */
          emit_queue();
@@ -4191,6 +4163,8 @@ mips_va_arg (tree valist, tree type)
          rtx lab_over = NULL_RTX, lab_false;
          HOST_WIDE_INT osize;
 
+         addr_rtx = gen_reg_rtx (Pmode);
+
          f_ovfl = TYPE_FIELDS (va_list_type_node);
          f_gtop = TREE_CHAIN (f_ovfl);
          f_ftop = TREE_CHAIN (f_gtop);
@@ -4230,22 +4204,46 @@ mips_va_arg (tree valist, tree type)
          lab_false = gen_label_rtx ();
          lab_over = gen_label_rtx ();
 
-         ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
+         ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
+                       NULL_TREE);
          if (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_FLOAT
              && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FPVALUE)
            {
-             top = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
-             off = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
+             top = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop,
+                          NULL_TREE);
+             off = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff,
+                          NULL_TREE);
 
              /* When floating-point registers are saved to the stack,
                 each one will take up UNITS_PER_HWFPVALUE bytes, regardless
                 of the float'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 = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
-             off = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
+             top = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop,
+                          NULL_TREE);
+             off = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff,
+                          NULL_TREE);
              if (rsize > UNITS_PER_WORD)
                {
                  /* [1] Emit code for: off &= -rsize.  */
@@ -4254,14 +4252,8 @@ mips_va_arg (tree valist, tree type)
                  t = build (MODIFY_EXPR, TREE_TYPE (off), off, t);
                  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
                }
+             osize = rsize;
            }
-         /* Every overflow argument must take up at least UNITS_PER_WORD
-            bytes (= PARM_BOUNDARY bits).  RSIZE can sometimes be smaller
-            than that, such as in the combination -mgp64 -msingle-float
-            -fshort-double.  Doubles passed in registers will then take
-            up UNITS_PER_HWFPVALUE bytes, but those passed on the stack
-            take up UNITS_PER_WORD bytes.  */
-         osize = MAX (rsize, UNITS_PER_WORD);
 
          /* [2] Emit code to branch if off == 0.  */
          r = expand_expr (off, NULL_RTX, TYPE_MODE (TREE_TYPE (off)),
@@ -4269,8 +4261,12 @@ mips_va_arg (tree valist, tree type)
          emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
                                   1, lab_false);
 
-         /* [4] Emit code for: addr_rtx = top - off.  */
+         /* [4] Emit code for: addr_rtx = top - off.  On big endian machines,
+            the argument has RSIZE - SIZE bytes of leading padding.  */
          t = build (MINUS_EXPR, TREE_TYPE (top), top, off);
+         if (BYTES_BIG_ENDIAN && rsize > size)
+           t = build (PLUS_EXPR, TREE_TYPE (t), t,
+                      build_int_2 (rsize - size, 0));
          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
          if (r != addr_rtx)
            emit_move_insn (addr_rtx, r);
@@ -4300,12 +4296,12 @@ mips_va_arg (tree valist, tree type)
 
          /* [10, 11].  Emit code to store ovfl in addr_rtx, then
             post-increment ovfl by osize.  On big-endian machines,
-            the argument has OSIZE - RSIZE bytes of leading padding.  */
+            the argument has OSIZE - SIZE bytes of leading padding.  */
          t = build (POSTINCREMENT_EXPR, TREE_TYPE (ovfl), ovfl,
                     size_int (osize));
-         if (BYTES_BIG_ENDIAN && osize > rsize)
+         if (BYTES_BIG_ENDIAN && osize > size)
            t = build (PLUS_EXPR, TREE_TYPE (t), t,
-                      build_int_2 (osize - rsize, 0));
+                      build_int_2 (osize - size, 0));
          r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
          if (r != addr_rtx)
            emit_move_insn (addr_rtx, r);
@@ -4313,8 +4309,6 @@ mips_va_arg (tree valist, tree type)
          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);
@@ -4334,8 +4328,7 @@ mips_va_arg (tree valist, tree type)
         that alignments <= UNITS_PER_WORD are preserved by the va_arg
         increment mechanism.  */
 
-      if ((mips_abi == ABI_N32 || mips_abi == ABI_64)
-         && TYPE_ALIGN (type) > 64)
+      if (TARGET_NEWABI && TYPE_ALIGN (type) > 64)
        align = 16;
       else if (TARGET_64BIT)
        align = 8;
@@ -4423,7 +4416,7 @@ mips_get_unaligned_mem (rtx *op, unsigned int width, int bitpos,
   first = adjust_address (*op, QImode, 0);
   last = adjust_address (*op, QImode, width / BITS_PER_UNIT - 1);
 
-  /* Allocate to LEFT and RIGHT according to endiannes.  LEFT should
+  /* Allocate to LEFT and RIGHT according to endianness.  LEFT should
      be the upper word and RIGHT the lower word.  */
   if (TARGET_BIG_ENDIAN)
     *left = first, *right = last;
@@ -4441,7 +4434,7 @@ mips_get_unaligned_mem (rtx *op, unsigned int width, int bitpos,
 bool
 mips_expand_unaligned_load (rtx dest, rtx src, unsigned int width, int bitpos)
 {
-  rtx left, right;
+  rtx left, right, temp;
 
   /* If TARGET_64BIT, the destination of a 32-bit load will be a
      paradoxical word_mode subreg.  This is the only case in which
@@ -4460,17 +4453,16 @@ mips_expand_unaligned_load (rtx dest, rtx src, unsigned int width, int bitpos)
   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 (dest, src, left));
-      emit_insn (gen_mov_ldr (copy_rtx (dest), copy_rtx (src),
-                             right, copy_rtx (dest)));
+      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 (dest, src, left));
-      emit_insn (gen_mov_lwr (copy_rtx (dest), copy_rtx (src),
-                             right, copy_rtx (dest)));
+      emit_insn (gen_mov_lwl (temp, src, left));
+      emit_insn (gen_mov_lwr (dest, copy_rtx (src), right, temp));
     }
   return true;
 }
@@ -4673,7 +4665,9 @@ override_options (void)
       switch ((int) mips_arch)
        {
        case PROCESSOR_R4100:
+       case PROCESSOR_R4111:
        case PROCESSOR_R4120:
+       case PROCESSOR_R4130:
          target_flags |= MASK_SOFT_FLOAT;
          break;
 
@@ -4683,9 +4677,61 @@ override_options (void)
        }
     }
 
-  if (mips_abi != ABI_32 && mips_abi != ABI_O64)
+  if (!TARGET_OLDABI)
     flag_pcc_struct_return = 0;
 
+#if defined(USE_COLLECT2)
+  /* For IRIX 5 or IRIX 6 with integrated O32 ABI support, USE_COLLECT2 is
+     always defined when GNU as is not in use, but collect2 is only used
+     for the O32 ABI, so override the toplev.c and target-def.h defaults
+     for flag_gnu_linker, TARGET_ASM_{CONSTRUCTOR, DESTRUCTOR} and
+     TARGET_HAVE_CTORS_DTORS.
+
+     Since the IRIX 5 and IRIX 6 O32 assemblers cannot handle named
+     sections, constructor/destructor handling depends on the ABI in use.
+
+     Since USE_COLLECT2 is defined, we only need to restore the non-collect2
+     defaults for the N32/N64 ABIs.  */
+  if (TARGET_IRIX && !TARGET_SGI_O32_AS)
+    {
+      targetm.have_ctors_dtors = true;
+      targetm.asm_out.constructor = default_named_section_asm_out_constructor;
+      targetm.asm_out.destructor = default_named_section_asm_out_destructor;
+    }
+#endif
+
+  /* Handle some quirks of the IRIX 5 and IRIX 6 O32 assemblers.  */
+
+  if (TARGET_SGI_O32_AS)
+    {
+      /* They don't recognize `.[248]byte'.  */
+      targetm.asm_out.unaligned_op.hi = "\t.align 0\n\t.half\t";
+      targetm.asm_out.unaligned_op.si = "\t.align 0\n\t.word\t";
+      /* The IRIX 6 O32 assembler gives an error for `align 0; .dword',
+        contrary to the documentation, so disable it.  */
+      targetm.asm_out.unaligned_op.di = NULL;
+
+      /* They cannot handle named sections.  */
+      targetm.have_named_sections = false;
+      /* Therefore, EH_FRAME_SECTION_NAME isn't defined and we must use
+        collect2.  */
+      targetm.terminate_dw2_eh_frame_info = true;
+      targetm.asm_out.eh_frame_section = collect2_eh_frame_section;
+
+      /* They cannot handle debug information.  */
+      if (write_symbols != NO_DEBUG)
+       {
+         /* Adapt wording to IRIX version: IRIX 5 only had a single ABI,
+            so -mabi=32 isn't usually specified.  */
+         if (TARGET_IRIX5)
+           inform ("-g is only supported using GNU as,");
+         else
+           inform ("-g is only supported using GNU as with -mabi=32,");
+         inform ("-g option disabled");
+         write_symbols = NO_DEBUG;
+       }
+    }
+
   if ((target_flags_explicit & MASK_BRANCHLIKELY) == 0)
     {
       /* If neither -mbranch-likely nor -mno-branch-likely was given
@@ -4693,9 +4739,10 @@ override_options (void)
         architecture.
 
         By default, we enable use of Branch Likely instructions on
-        all architectures which support them except for MIPS32 and MIPS64
-        (i.e., the generic MIPS32 and MIPS64 ISAs, and processors which
-        implement them).
+        all architectures which support them with the following
+        exceptions: when creating MIPS32 or MIPS64 code, and when
+        tuning for architectures where their use tends to hurt
+        performance.
 
         The MIPS32 and MIPS64 architecture specifications say "Software
         is strongly encouraged to avoid use of Branch Likely
@@ -4703,7 +4750,9 @@ override_options (void)
         of the [MIPS32 and MIPS64] architecture."  Therefore, we do not
         issue those instructions unless instructed to do so by
         -mbranch-likely.  */
-      if (ISA_HAS_BRANCHLIKELY && !(ISA_MIPS32 || ISA_MIPS32R2 || ISA_MIPS64))
+      if (ISA_HAS_BRANCHLIKELY
+         && !(ISA_MIPS32 || ISA_MIPS32R2 || ISA_MIPS64)
+         && !(TUNE_MIPS5500 || TUNE_SB1))
        target_flags |= MASK_BRANCHLIKELY;
       else
        target_flags &= ~MASK_BRANCHLIKELY;
@@ -4743,30 +4792,6 @@ override_options (void)
   if (!targetm.have_named_sections)
     mips_section_threshold = 0;
 
-  /* -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;
-    }
-
   /* mips_split_addresses is a half-way house between explicit
      relocations and the traditional assembler macros.  It can
      split absolute 32-bit symbolic constants into a high/lo_sum
@@ -4784,15 +4809,6 @@ override_options (void)
   else
     mips_split_addresses = 0;
 
-  /* -mexplicit-relocs doesn't yet support non-PIC n64.  We don't know
-     how to generate %highest/%higher/%hi/%lo sequences.  */
-  if (mips_abi == ABI_64 && !TARGET_ABICALLS)
-    {
-      if ((target_flags_explicit & target_flags & MASK_EXPLICIT_RELOCS) != 0)
-       sorry ("non-PIC n64 with explicit relocations");
-      target_flags &= ~MASK_EXPLICIT_RELOCS;
-    }
-
   /* Explicit relocations for "old" ABIs are a GNU extension.  Unless
      the user has said otherwise, assume that they are not available
      with assemblers other than gas.  */
@@ -4800,6 +4816,28 @@ override_options (void)
       && (target_flags_explicit & MASK_EXPLICIT_RELOCS) == 0)
     target_flags &= ~MASK_EXPLICIT_RELOCS;
 
+  /* Make -mabicalls -fno-unit-at-a-time imply -mno-explicit-relocs
+     unless the user says otherwise.
+
+     There are two problems here:
+
+       (1) The value of an R_MIPS_GOT16 relocation depends on whether
+          the symbol is local or global.  We therefore need to know
+          a symbol's binding before referring to it using %got().
+
+       (2) R_MIPS_CALL16 can only be applied to global symbols.
+
+     When not using -funit-at-a-time, a symbol's binding may change
+     after it has been used.  For example, the C++ front-end will
+     initially assume that the typeinfo for an incomplete type will be
+     comdat, on the basis that the type could be completed later in the
+     file.  But if the type never is completed, the typeinfo will become
+     local instead.  */
+  if (!flag_unit_at_a_time
+      && TARGET_ABICALLS
+      && (target_flags_explicit & MASK_EXPLICIT_RELOCS) == 0)
+    target_flags &= ~MASK_EXPLICIT_RELOCS;
+
   /* -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
@@ -4808,6 +4846,12 @@ override_options (void)
   if (TARGET_NAME_REGS)
     memcpy (mips_reg_names, mips_sw_reg_names, sizeof (mips_reg_names));
 
+  /* -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;
+
   /* 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)
@@ -4837,12 +4881,8 @@ override_options (void)
       flag_delayed_branch = 0;
     }
 
-  REAL_MODE_FORMAT (SFmode) = &mips_single_format;
-  REAL_MODE_FORMAT (DFmode) = &mips_double_format;
 #ifdef MIPS_TFMODE_FORMAT
   REAL_MODE_FORMAT (TFmode) = &MIPS_TFMODE_FORMAT;
-#else
-  REAL_MODE_FORMAT (TFmode) = &mips_quad_format;
 #endif
 
   mips_print_operand_punct['?'] = 1;
@@ -4867,7 +4907,6 @@ override_options (void)
   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;
@@ -4939,7 +4978,7 @@ override_options (void)
                        || (ISA_HAS_8CC && mode == TFmode));
 
          else if (MD_REG_P (regno))
-           temp = (class == MODE_INT
+           temp = (INTEGRAL_MODE_P (mode)
                    && (size <= UNITS_PER_WORD
                        || (regno == MD_REG_FIRST
                            && size == 2 * UNITS_PER_WORD)));
@@ -4971,8 +5010,102 @@ override_options (void)
   /* Function to allocate machine-dependent function status.  */
   init_machine_status = &mips_init_machine_status;
 
-  /* Create a unique alias set for GOT references.  */
-  mips_got_alias_set = new_alias_set ();
+  if (ABI_HAS_64BIT_SYMBOLS)
+    {
+      if (TARGET_EXPLICIT_RELOCS)
+       {
+         mips_split_p[SYMBOL_64_HIGH] = true;
+         mips_hi_relocs[SYMBOL_64_HIGH] = "%highest(";
+         mips_lo_relocs[SYMBOL_64_HIGH] = "%higher(";
+
+         mips_split_p[SYMBOL_64_MID] = true;
+         mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
+         mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
+
+         mips_split_p[SYMBOL_64_LOW] = true;
+         mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
+         mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
+
+         mips_split_p[SYMBOL_GENERAL] = true;
+         mips_lo_relocs[SYMBOL_GENERAL] = "%lo(";
+       }
+    }
+  else
+    {
+      if (TARGET_EXPLICIT_RELOCS || mips_split_addresses)
+       {
+         mips_split_p[SYMBOL_GENERAL] = true;
+         mips_hi_relocs[SYMBOL_GENERAL] = "%hi(";
+         mips_lo_relocs[SYMBOL_GENERAL] = "%lo(";
+       }
+    }
+
+  if (TARGET_MIPS16)
+    {
+      /* The high part is provided by a pseudo copy of $gp.  */
+      mips_split_p[SYMBOL_SMALL_DATA] = true;
+      mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gprel(";
+    }
+
+  if (TARGET_EXPLICIT_RELOCS)
+    {
+      /* Small data constants are kept whole until after reload,
+        then lowered by mips_rewrite_small_data.  */
+      mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gp_rel(";
+
+      mips_split_p[SYMBOL_GOT_LOCAL] = true;
+      if (TARGET_NEWABI)
+       {
+         mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page(";
+         mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%got_ofst(";
+       }
+      else
+       {
+         mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
+         mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%lo(";
+       }
+
+      if (TARGET_XGOT)
+       {
+         /* The HIGH and LO_SUM are matched by special .md patterns.  */
+         mips_split_p[SYMBOL_GOT_GLOBAL] = true;
+
+         mips_split_p[SYMBOL_GOTOFF_GLOBAL] = true;
+         mips_hi_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_hi(";
+         mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_lo(";
+
+         mips_split_p[SYMBOL_GOTOFF_CALL] = true;
+         mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi(";
+         mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo(";
+       }
+      else
+       {
+         if (TARGET_NEWABI)
+           mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_disp(";
+         else
+           mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got(";
+         mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
+       }
+    }
+
+  if (TARGET_NEWABI)
+    {
+      mips_split_p[SYMBOL_GOTOFF_LOADGP] = true;
+      mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel(";
+      mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel(";
+    }
+
+  /* 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;
 }
 
 /* Implement CONDITIONAL_REGISTER_USAGE.  */
@@ -5022,7 +5155,7 @@ mips_conditional_register_usage (void)
       for (regno = FP_REG_FIRST + 20; regno < FP_REG_FIRST + 24; regno++)
        call_really_used_regs[regno] = call_used_regs[regno] = 1;
     }
-  /* odd registers from fp21 to fp31 are now caller saved.  */
+  /* Odd registers from fp21 to fp31 are now caller saved.  */
   if (mips_abi == ABI_N32)
     {
       int regno;
@@ -5089,7 +5222,7 @@ mips_debugger_offset (rtx addr, HOST_WIDE_INT offset)
 
       /* MIPS16 frame is smaller */
       if (frame_pointer_needed && TARGET_MIPS16)
-       frame_size -= current_function_outgoing_args_size;
+       frame_size -= cfun->machine->frame.args_size;
 
       offset = offset - frame_size;
     }
@@ -5118,7 +5251,6 @@ mips_debugger_offset (rtx addr, HOST_WIDE_INT offset)
    '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'  OP 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
@@ -5153,8 +5285,6 @@ void
 print_operand (FILE *file, rtx op, int letter)
 {
   register enum rtx_code code;
-  struct mips_constant_info c;
-  const char *reloc;
 
   if (PRINT_OPERAND_PUNCT_VALID_P (letter))
     {
@@ -5295,16 +5425,7 @@ print_operand (FILE *file, rtx op, int letter)
 
   code = GET_CODE (op);
 
-  if (letter == 'h')
-    {
-      if (GET_CODE (op) != HIGH)
-       abort ();
-      fputs ("%hi(", file);
-      output_addr_const (file, XEXP (op, 0));
-      fputc (')', file);
-    }
-
-  else if (letter == 'C')
+  if (letter == 'C')
     switch (code)
       {
       case EQ: fputs ("eq",  file); break;
@@ -5356,14 +5477,17 @@ print_operand (FILE *file, rtx op, int letter)
        fatal_insn ("PRINT_OPERAND, invalid insn for %%W", op);
       }
 
-  else if (letter == 'S')
+  else if (letter == 'h')
     {
-      char buffer[100];
+      if (GET_CODE (op) == HIGH)
+       op = XEXP (op, 0);
 
-      ASM_GENERATE_INTERNAL_LABEL (buffer, "LS", CODE_LABEL_NUMBER (op));
-      assemble_name (file, buffer);
+      print_operand_reloc (file, op, mips_hi_relocs);
     }
 
+  else if (letter == 'R')
+    print_operand_reloc (file, op, mips_lo_relocs);
+
   else if (letter == 'Z')
     {
       register int regnum;
@@ -5428,89 +5552,65 @@ print_operand (FILE *file, rtx op, int letter)
   else if (letter == 't')
     fputs (code == EQ ? "t" : "f", file);
 
-  else
-    switch (mips_classify_constant (&c, op))
-      {
-      case CONSTANT_SYMBOLIC:
-       if (letter == 'R')
-         {
-           if (mips_classify_symbol (c.symbol) == SYMBOL_SMALL_DATA)
-             fputs (TARGET_MIPS16 ? "%gprel(" : "%gp_rel(", file);
-           else if (TARGET_ABICALLS && TARGET_NEWABI)
-             fputs ("%got_ofst(", file);
-           else
-             fputs ("%lo(", file);
-           output_addr_const (file, op);
-           fputc (')', file);
-           break;
-         }
-       /* ... fall through ... */
+  else if (CONST_GP_P (op))
+    fputs (reg_names[GLOBAL_POINTER_REGNUM], file);
 
-      case CONSTANT_NONE:
-       output_addr_const (file, op);
-       break;
+  else
+    output_addr_const (file, op);
+}
 
-      case CONSTANT_GP:
-       fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file);
-       break;
 
-      case CONSTANT_RELOC:
-       reloc = mips_reloc_string (c.reloc);
-       fputs (reloc, file);
-       output_addr_const (file, plus_constant (c.symbol, c.offset));
-       while (*reloc != 0)
-         if (*reloc++ == '(')
-           fputc (')', file);
-      }
-}
-\f
-/* Return the assembly operator used for the given type of relocation.  */
+/* Print symbolic operand OP, which is part of a HIGH or LO_SUM.
+   RELOCS is the array of relocations to use.  */
 
-static const char *
-mips_reloc_string (int reloc)
+static void
+print_operand_reloc (FILE *file, rtx op, const char **relocs)
 {
-  switch (reloc)
-    {
-    case RELOC_GOT_HI:   return "%got_hi(";
-    case RELOC_GOT_LO:   return "%got_lo(";
-    case RELOC_GOT_PAGE:  return (TARGET_NEWABI ? "%got_page(" : "%got(");
-    case RELOC_GOT_DISP:  return (TARGET_NEWABI ? "%got_disp(" : "%got(");
-    case RELOC_CALL16:   return "%call16(";
-    case RELOC_CALL_HI:          return "%call_hi(";
-    case RELOC_CALL_LO:          return "%call_lo(";
-    case RELOC_LOADGP_HI: return "%hi(%neg(%gp_rel(";
-    case RELOC_LOADGP_LO: return "%lo(%neg(%gp_rel(";
-    }
-  abort ();
-}
+  enum mips_symbol_type symbol_type;
+  const char *p;
+  rtx base;
+  HOST_WIDE_INT offset;
+
+  if (!mips_symbolic_constant_p (op, &symbol_type) || relocs[symbol_type] == 0)
+    fatal_insn ("PRINT_OPERAND, invalid operand for relocation", op);
 
-/* Output address operand X to FILE.   */
+  /* If OP uses an UNSPEC address, we want to print the inner symbol.  */
+  mips_split_const (op, &base, &offset);
+  if (UNSPEC_ADDRESS_P (base))
+    op = plus_constant (UNSPEC_ADDRESS (base), offset);
+
+  fputs (relocs[symbol_type], file);
+  output_addr_const (file, op);
+  for (p = relocs[symbol_type]; *p != 0; p++)
+    if (*p == '(')
+      fputc (')', file);
+}
+\f
+/* Output address operand X to FILE.  */
 
 void
 print_operand_address (FILE *file, rtx x)
 {
   struct mips_address_info addr;
 
-  switch (mips_classify_address (&addr, x, word_mode, 1, 1))
-    {
-    case ADDRESS_INVALID:
-      abort ();
-
-    case ADDRESS_REG:
-      print_operand (file, addr.offset, 0);
-      fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
-      return;
+  if (mips_classify_address (&addr, x, word_mode, true))
+    switch (addr.type)
+      {
+      case ADDRESS_REG:
+       print_operand (file, addr.offset, 0);
+       fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
+       return;
 
-    case ADDRESS_LO_SUM:
-      print_operand (file, addr.offset, 'R');
-      fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
-      return;
+      case ADDRESS_LO_SUM:
+       print_operand (file, addr.offset, 'R');
+       fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
+       return;
 
-    case ADDRESS_CONST_INT:
-    case ADDRESS_SYMBOLIC:
-      output_addr_const (file, x);
-      return;
-    }
+      case ADDRESS_CONST_INT:
+      case ADDRESS_SYMBOLIC:
+       output_addr_const (file, x);
+       return;
+      }
   abort ();
 }
 \f
@@ -5556,14 +5656,7 @@ mips_output_external (FILE *file ATTRIBUTE_UNUSED, tree decl, const char *name)
       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"))
+  if (TARGET_IRIX && mips_abi == ABI_32 && TREE_CODE (decl) == FUNCTION_DECL)
     {
       p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list));
       p->next = extern_head;
@@ -5571,14 +5664,13 @@ mips_output_external (FILE *file ATTRIBUTE_UNUSED, tree decl, const char *name)
       p->size = -1;
       extern_head = p;
     }
-#endif
 
   return 0;
 }
 
-#if TARGET_IRIX5 || TARGET_IRIX6
+#if TARGET_IRIX
 void
-mips_output_external_libcall (rtx fun)
+irix_output_external_libcall (rtx fun)
 {
   register struct extern_list *p;
 
@@ -5655,17 +5747,20 @@ mips_output_lineno (FILE *stream, int line)
     }
 }
 \f
-/* Output an ASCII string, in a space-saving way.  */
+/* Output an ASCII string, in a space-saving way.  PREFIX is the string
+   that should be written before the opening quote, such as "\t.ascii\t"
+   for real string data or "\t# " for a comment.  */
 
 void
-mips_output_ascii (FILE *stream, const char *string_param, size_t len)
+mips_output_ascii (FILE *stream, const char *string_param, size_t len,
+                  const char *prefix)
 {
   size_t i;
   int cur_pos = 17;
   register const unsigned char *string =
     (const unsigned char *)string_param;
 
-  fprintf (stream, "\t.ascii\t\"");
+  fprintf (stream, "%s\"", prefix);
   for (i = 0; i < len; i++)
     {
       register int c = string[i];
@@ -5725,7 +5820,7 @@ mips_output_ascii (FILE *stream, const char *string_param, size_t len)
       if (cur_pos > 72 && i+1 < len)
        {
          cur_pos = 17;
-         fprintf (stream, "\"\n\t.ascii\t\"");
+         fprintf (stream, "\"\n%s\"", prefix);
        }
     }
   fprintf (stream, "\"\n");
@@ -5748,7 +5843,7 @@ mips_file_start (void)
 
   if (TARGET_GAS)
     {
-#if defined(OBJECT_FORMAT_ELF) && !(TARGET_IRIX5 || TARGET_IRIX6)
+#if defined(OBJECT_FORMAT_ELF) && !TARGET_IRIX
       /* Generate a special section to describe the ABI switches used to
         produce the resultant binary.  This used to be done by the assembler
         setting bits in the ELF header's flags field, but we have run out of
@@ -5774,6 +5869,13 @@ mips_file_start (void)
         executable.  */
       fprintf (asm_out_file, "\t.section .mdebug.%s\n", abi_string);
 
+      /* There is no ELF header flag to distinguish long32 forms of the
+        EABI from long64 forms.  Emit a special section to help tools
+        such as GDB.  */
+      if (mips_abi == ABI_EABI)
+       fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n",
+                TARGET_LONG64 ? 64 : 32);
+
       /* Restore the default section.  */
       fprintf (asm_out_file, "\t.previous\n");
 #endif
@@ -5835,14 +5937,22 @@ mips_file_end (void)
          name_tree = get_identifier (p->name);
 
          /* Positively ensure only one .extern for any given symbol.  */
-         if (! TREE_ASM_WRITTEN (name_tree))
+         if (!TREE_ASM_WRITTEN (name_tree)
+             && TREE_SYMBOL_REFERENCED (name_tree))
            {
              TREE_ASM_WRITTEN (name_tree) = 1;
-#ifdef ASM_OUTPUT_UNDEF_FUNCTION
-             if (p->size == -1)
-               ASM_OUTPUT_UNDEF_FUNCTION (asm_out_file, p->name);
+             /* 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.  */
+             if (TARGET_IRIX && mips_abi == ABI_32 && p->size == -1)
+               {
+                 fputs ("\t.globl ", asm_out_file);
+                 assemble_name (asm_out_file, p->name);
+                 fputs (" .text\n", asm_out_file);
+               }
              else
-#endif
                {
                  fputs ("\t.extern\t", asm_out_file);
                  assemble_name (asm_out_file, p->name);
@@ -5853,17 +5963,63 @@ mips_file_end (void)
     }
 }
 
+/* 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
+   and the limitations of the SGI o32 assembler.  */
+
+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);
+
+      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 if (TARGET_SGI_O32_AS)
+    {
+      /* The SGI o32 assembler doesn't accept an alignment, so round up
+        the size instead.  */
+      size += (align / BITS_PER_UNIT) - 1;
+      size -= size % (align / BITS_PER_UNIT);
+      mips_declare_object (stream, name, "\n\t.comm\t",
+                          "," HOST_WIDE_INT_PRINT_UNSIGNED "\n", size);
+    }
+  else
+    mips_declare_object (stream, name, "\n\t.comm\t",
+                        "," HOST_WIDE_INT_PRINT_UNSIGNED ",%u\n",
+                        size, align / BITS_PER_UNIT);
+}
+
 /* Emit either a label, .comm, or .lcomm directive.  When using assembler
    macros, mark the symbol as written so that mips_file_end won't emit an
-   .extern for it.  */
+   .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, int size)
+                    const char *final_string, ...)
 {
-  fputs (init_string, stream);         /* "", "\t.comm\t", or "\t.lcomm\t" */
+  va_list ap;
+
+  fputs (init_string, stream);
   assemble_name (stream, name);
-  fprintf (stream, final_string, size);        /* ":\n", ",%u\n", ",%u\n" */
+  va_start (ap, final_string);
+  vfprintf (stream, final_string, ap);
+  va_end (ap);
 
   if (!TARGET_EXPLICIT_RELOCS)
     {
@@ -5882,18 +6038,21 @@ void
 mips_declare_object_name (FILE *stream, const char *name,
                          tree decl ATTRIBUTE_UNUSED)
 {
+  if (!TARGET_SGI_O32_AS)
+    {
 #ifdef ASM_OUTPUT_TYPE_DIRECTIVE
-  ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "object");
+      ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "object");
 #endif
 
-  size_directive_output = 0;
-  if (!flag_inhibit_size_directive && DECL_SIZE (decl))
-    {
-      HOST_WIDE_INT size;
+      size_directive_output = 0;
+      if (!flag_inhibit_size_directive && DECL_SIZE (decl))
+       {
+         HOST_WIDE_INT size;
 
-      size_directive_output = 1;
-      size = int_size_in_bytes (TREE_TYPE (decl));
-      ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+         size_directive_output = 1;
+         size = int_size_in_bytes (TREE_TYPE (decl));
+         ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+       }
     }
 
   mips_declare_object (stream, name, "", ":\n", 0);
@@ -5907,7 +6066,8 @@ 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
+  if (!TARGET_SGI_O32_AS
+      && !flag_inhibit_size_directive
       && DECL_SIZE (decl) != 0
       && !at_end && top_level
       && DECL_INITIAL (decl) == error_mark_node
@@ -5922,34 +6082,126 @@ mips_finish_declare_object (FILE *stream, tree decl, int top_level, int at_end)
 }
 #endif
 \f
-/* Return the register that should be used as the global pointer
-   within this function.  Return 0 if the function doesn't need
-   a global pointer.  */
+/* Return true if X is a small data address that can be rewritten
+   as a LO_SUM.  */
 
-static unsigned int
-mips_global_pointer (void)
+static bool
+mips_rewrite_small_data_p (rtx x)
 {
-  unsigned int regno;
+  enum mips_symbol_type symbol_type;
 
-  /* $gp is always available in non-abicalls code.  */
-  if (!TARGET_ABICALLS)
-    return GLOBAL_POINTER_REGNUM;
+  return (TARGET_EXPLICIT_RELOCS
+         && mips_symbolic_constant_p (x, &symbol_type)
+         && symbol_type == SYMBOL_SMALL_DATA);
+}
 
-  /* We must always provide $gp when it is used implicitly.  */
-  if (!TARGET_EXPLICIT_RELOCS)
-    return GLOBAL_POINTER_REGNUM;
 
-  /* FUNCTION_PROFILER includes a jal macro, so we need to give it
-     a valid gp.  */
-  if (current_function_profile)
-    return GLOBAL_POINTER_REGNUM;
+/* A for_each_rtx callback for small_data_pattern.  */
 
-  /* If the gp is never referenced, there's no need to initialize it.
-     Note that reload can sometimes introduce constant pool references
-     into a function that otherwise didn't need them.  For example,
-     suppose we have an instruction like:
+static int
+small_data_pattern_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+{
+  if (GET_CODE (*loc) == LO_SUM)
+    return -1;
 
-         (set (reg:DF R1) (float:DF (reg:SI R2)))
+  return mips_rewrite_small_data_p (*loc);
+}
+
+/* Return true if OP refers to small data symbols directly, not through
+   a LO_SUM.  */
+
+int
+small_data_pattern (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return (GET_CODE (op) != SEQUENCE
+         && for_each_rtx (&op, small_data_pattern_1, 0));
+}
+\f
+/* A for_each_rtx callback, used by mips_rewrite_small_data.  */
+
+static int
+mips_rewrite_small_data_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+{
+  if (mips_rewrite_small_data_p (*loc))
+    *loc = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, *loc);
+
+  if (GET_CODE (*loc) == LO_SUM)
+    return -1;
+
+  return 0;
+}
+
+/* If possible, rewrite OP so that it refers to small data using
+   explicit relocations.  */
+
+rtx
+mips_rewrite_small_data (rtx op)
+{
+  op = copy_insn (op);
+  for_each_rtx (&op, mips_rewrite_small_data_1, 0);
+  return op;
+}
+\f
+/* Return true if the current function has an insn that implicitly
+   refers to $gp.  */
+
+static bool
+mips_function_has_gp_insn (void)
+{
+  /* Don't bother rechecking if we found one last time.  */
+  if (!cfun->machine->has_gp_insn_p)
+    {
+      rtx insn;
+
+      push_topmost_sequence ();
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+       if (INSN_P (insn)
+           && GET_CODE (PATTERN (insn)) != USE
+           && GET_CODE (PATTERN (insn)) != CLOBBER
+           && (get_attr_got (insn) != GOT_UNSET
+               || small_data_pattern (PATTERN (insn), VOIDmode)))
+         break;
+      pop_topmost_sequence ();
+
+      cfun->machine->has_gp_insn_p = (insn != 0);
+    }
+  return cfun->machine->has_gp_insn_p;
+}
+
+
+/* Return the register that should be used as the global pointer
+   within this function.  Return 0 if the function doesn't need
+   a global pointer.  */
+
+static unsigned int
+mips_global_pointer (void)
+{
+  unsigned int regno;
+
+  /* $gp is always available in non-abicalls code.  */
+  if (!TARGET_ABICALLS)
+    return GLOBAL_POINTER_REGNUM;
+
+  /* We must always provide $gp when it is used implicitly.  */
+  if (!TARGET_EXPLICIT_RELOCS)
+    return GLOBAL_POINTER_REGNUM;
+
+  /* FUNCTION_PROFILER includes a jal macro, so we need to give it
+     a valid gp.  */
+  if (current_function_profile)
+    return GLOBAL_POINTER_REGNUM;
+
+  /* If the function has a nonlocal goto, $gp must hold the correct
+     global pointer for the target function.  */
+  if (current_function_has_nonlocal_goto)
+    return GLOBAL_POINTER_REGNUM;
+
+  /* If the gp is never referenced, there's no need to initialize it.
+     Note that reload can sometimes introduce constant pool references
+     into a function that otherwise didn't need them.  For example,
+     suppose we have an instruction like:
+
+         (set (reg:DF R1) (float:DF (reg:SI R2)))
 
      If R2 turns out to be constant such as 1, the instruction may have a
      REG_EQUAL note saying that R1 == 1.0.  Reload then has the option of
@@ -5958,7 +6210,8 @@ mips_global_pointer (void)
      In cases like these, reload will have added the constant to the pool
      but no instruction will yet refer to it.  */
   if (!regs_ever_live[GLOBAL_POINTER_REGNUM]
-      && !current_function_uses_const_pool)
+      && !current_function_uses_const_pool
+      && !mips_function_has_gp_insn ())
     return 0;
 
   /* We need a global pointer, but perhaps we can use a call-clobbered
@@ -5967,7 +6220,8 @@ mips_global_pointer (void)
     for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
       if (!regs_ever_live[regno]
          && call_used_regs[regno]
-         && !fixed_regs[regno])
+         && !fixed_regs[regno]
+         && regno != PIC_FUNCTION_ADDR_REGNUM)
        return regno;
 
   return GLOBAL_POINTER_REGNUM;
@@ -6084,11 +6338,12 @@ compute_frame_size (HOST_WIDE_INT size)
   HOST_WIDE_INT total_size;    /* # bytes that the entire frame takes up */
   HOST_WIDE_INT var_size;      /* # bytes that variables take up */
   HOST_WIDE_INT args_size;     /* # bytes that outgoing arguments take up */
+  HOST_WIDE_INT cprestore_size; /* # bytes that the cprestore slot takes up */
   HOST_WIDE_INT gp_reg_rounded;        /* # bytes needed to store gp after rounding */
   HOST_WIDE_INT gp_reg_size;   /* # bytes needed to store gp regs */
   HOST_WIDE_INT fp_reg_size;   /* # bytes needed to store fp regs */
-  long mask;                   /* mask of saved gp registers */
-  long fmask;                  /* mask of saved fp registers */
+  unsigned int mask;           /* mask of saved gp registers */
+  unsigned int fmask;          /* mask of saved fp registers */
 
   cfun->machine->global_pointer = mips_global_pointer ();
 
@@ -6097,13 +6352,14 @@ compute_frame_size (HOST_WIDE_INT size)
   mask = 0;
   fmask        = 0;
   var_size = MIPS_STACK_ALIGN (size);
-  args_size = MIPS_STACK_ALIGN (STARTING_FRAME_OFFSET);
+  args_size = current_function_outgoing_args_size;
+  cprestore_size = MIPS_STACK_ALIGN (STARTING_FRAME_OFFSET) - args_size;
 
   /* The space set aside by STARTING_FRAME_OFFSET isn't needed in leaf
      functions.  If the function has local variables, we're committed
      to allocating it anyway.  Otherwise reclaim it here.  */
   if (var_size == 0 && current_function_is_leaf)
-    args_size = 0;
+    cprestore_size = args_size = 0;
 
   /* The MIPS 3.0 linker does not like functions that dynamically
      allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
@@ -6113,14 +6369,14 @@ compute_frame_size (HOST_WIDE_INT size)
   if (args_size == 0 && current_function_calls_alloca)
     args_size = 4 * UNITS_PER_WORD;
 
-  total_size = var_size + args_size;
+  total_size = var_size + args_size + cprestore_size;
 
   /* Calculate space needed for gp registers.  */
   for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
     if (mips_save_reg_p (regno))
       {
        gp_reg_size += GET_MODE_SIZE (gpr_mode);
-       mask |= 1L << (regno - GP_REG_FIRST);
+       mask |= 1 << (regno - GP_REG_FIRST);
       }
 
   /* We need to restore these for the handler.  */
@@ -6133,7 +6389,7 @@ compute_frame_size (HOST_WIDE_INT size)
          if (regno == INVALID_REGNUM)
            break;
          gp_reg_size += GET_MODE_SIZE (gpr_mode);
-         mask |= 1L << (regno - GP_REG_FIRST);
+         mask |= 1 << (regno - GP_REG_FIRST);
        }
     }
 
@@ -6155,13 +6411,14 @@ compute_frame_size (HOST_WIDE_INT size)
 
   /* 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)
+  if (!TARGET_OLDABI)
     total_size += MIPS_STACK_ALIGN (current_function_pretend_args_size);
 
   /* Save other computed information.  */
   cfun->machine->frame.total_size = total_size;
   cfun->machine->frame.var_size = var_size;
   cfun->machine->frame.args_size = args_size;
+  cfun->machine->frame.cprestore_size = cprestore_size;
   cfun->machine->frame.gp_reg_size = gp_reg_size;
   cfun->machine->frame.fp_reg_size = fp_reg_size;
   cfun->machine->frame.mask = mask;
@@ -6172,9 +6429,10 @@ compute_frame_size (HOST_WIDE_INT size)
 
   if (mask)
     {
-      unsigned long offset;
+      HOST_WIDE_INT offset;
 
-      offset = args_size + var_size + gp_reg_size - GET_MODE_SIZE (gpr_mode);
+      offset = (args_size + cprestore_size + var_size
+               + gp_reg_size - GET_MODE_SIZE (gpr_mode));
       cfun->machine->frame.gp_sp_offset = offset;
       cfun->machine->frame.gp_save_offset = offset - total_size;
     }
@@ -6186,9 +6444,11 @@ compute_frame_size (HOST_WIDE_INT size)
 
   if (fmask)
     {
-      unsigned long offset = (args_size + var_size
-                             + gp_reg_rounded + fp_reg_size
-                             - FP_INC * UNITS_PER_FPREG);
+      HOST_WIDE_INT offset;
+
+      offset = (args_size + cprestore_size + var_size
+               + gp_reg_rounded + fp_reg_size
+               - FP_INC * UNITS_PER_FPREG);
       cfun->machine->frame.fp_sp_offset = offset;
       cfun->machine->frame.fp_save_offset = offset - total_size;
     }
@@ -6206,10 +6466,12 @@ compute_frame_size (HOST_WIDE_INT size)
    pointer or argument pointer.  TO is either the stack pointer or
    hard frame pointer.  */
 
-int
+HOST_WIDE_INT
 mips_initial_elimination_offset (int from, int to)
 {
-  int offset;
+  HOST_WIDE_INT offset;
+
+  compute_frame_size (get_frame_size ());
 
   /* Set OFFSET to the offset from the stack pointer.  */
   switch (from)
@@ -6219,9 +6481,8 @@ mips_initial_elimination_offset (int from, int to)
       break;
 
     case ARG_POINTER_REGNUM:
-      compute_frame_size (get_frame_size ());
       offset = cfun->machine->frame.total_size;
-      if (mips_abi == ABI_N32 || mips_abi == ABI_64)
+      if (TARGET_NEWABI)
        offset -= current_function_pretend_args_size;
       break;
 
@@ -6230,19 +6491,11 @@ mips_initial_elimination_offset (int from, int to)
     }
 
   if (TARGET_MIPS16 && to == HARD_FRAME_POINTER_REGNUM)
-    offset -= current_function_outgoing_args_size;
+    offset -= cfun->machine->frame.args_size;
 
   return offset;
 }
 \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.  */
-
-#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
-
 /* Implement RETURN_ADDR_RTX.  Note, we do not support moving
    back to a previous frame.  */
 rtx
@@ -6253,296 +6506,103 @@ mips_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
 
   return get_hard_reg_initial_val (Pmode, GP_REG_FIRST + 31);
 }
-
-
-/* Emit instructions to load the value (SP + OFFSET) into MIPS_TEMP2_REGNUM
-   and return an rtl expression for the register.
-
-   This function is a subroutine of save_restore_insns.  It is used when
-   OFFSET is too large to add in a single instruction.  */
-
-static rtx
-mips_add_large_offset_to_sp (HOST_WIDE_INT offset)
-{
-  rtx reg = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
-  rtx offset_rtx = GEN_INT (offset);
-
-  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));
-  return reg;
-}
-
-/* Make the last instruction frame related and note that it performs
-   the operation described by FRAME_PATTERN.  */
+\f
+/* Use FN to save or restore register REGNO.  MODE is the register's
+   mode and OFFSET is the offset of its save slot from the current
+   stack pointer.  */
 
 static void
-mips_set_frame_expr (rtx frame_pattern)
+mips_save_restore_reg (enum machine_mode mode, int regno,
+                      HOST_WIDE_INT offset, mips_save_restore_fn fn)
 {
-  rtx insn;
-
-  insn = get_last_insn ();
-  RTX_FRAME_RELATED_P (insn) = 1;
-  REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                                     frame_pattern,
-                                     REG_NOTES (insn));
-}
+  rtx mem;
 
-/* Return a frame-related rtx that stores REG at (SP + OFFSET).
-   REG must be a single register.  */
+  mem = gen_rtx_MEM (mode, plus_constant (stack_pointer_rtx, offset));
+  if (!current_function_calls_eh_return)
+    RTX_UNCHANGING_P (mem) = 1;
 
-static rtx
-mips_frame_set (rtx reg, int offset)
-{
-  rtx address = plus_constant (stack_pointer_rtx, offset);
-  rtx set = gen_rtx_SET (VOIDmode, gen_rtx_MEM (GET_MODE (reg), address), reg);
-  RTX_FRAME_RELATED_P (set) = 1;
-  return set;
+  fn (gen_rtx_REG (mode, regno), mem);
 }
 
 
-/* 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.  */
+/* Call FN for each register that is saved by the current function.
+   SP_OFFSET is the offset of the current stack pointer from the start
+   of the frame.  */
 
 static void
-mips_emit_frame_related_store (rtx mem, rtx reg, HOST_WIDE_INT offset)
+mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
 {
-  if (GET_MODE (reg) == DFmode && mips_split_64bit_move_p (mem, reg))
-    mips_split_64bit_move (mem, reg);
-  else
-    emit_move_insn (mem, reg);
-
-  if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64)
-    {
-      rtx x1, x2;
-
-      /* Two registers are being stored, so the frame-related expression
-        must be a PARALLEL rtx with one SET for each register.  */
-      x1 = mips_frame_set (mips_subword (reg, TARGET_BIG_ENDIAN), offset);
-      x2 = mips_frame_set (mips_subword (reg, !TARGET_BIG_ENDIAN),
-                          offset + UNITS_PER_FPREG);
-      mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
-    }
-  else
-    mips_set_frame_expr (mips_frame_set (reg, offset));
-}
-
-
-/* Emit instructions to save or restore the registers in
-   cfun->machine->frame.mask and cfun->machine->frame.fmask.
-   STORE_P is true to save registers (meaning we are expanding
-   the prologue).  If nonnull, LARGE_REG stores the value LARGE_OFFSET,
-   which the caller thinks might be useful to us.  */
+#define BITSET_P(VALUE, BIT) (((VALUE) & (1L << (BIT))) != 0)
 
-static void
-save_restore_insns (int store_p, rtx large_reg, long large_offset)
-{
-  long mask = cfun->machine->frame.mask;
-  long fmask = cfun->machine->frame.fmask;
+  enum machine_mode fpr_mode;
+  HOST_WIDE_INT offset;
   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;
-
-  if (frame_pointer_needed
-      && ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
-    abort ();
-
-  if (mask == 0 && fmask == 0)
-    return;
 
   /* 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.  */
+  offset = cfun->machine->frame.gp_sp_offset - sp_offset;
+  for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
+    if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST))
+      {
+       mips_save_restore_reg (gpr_mode, regno, offset, fn);
+       offset -= GET_MODE_SIZE (gpr_mode);
+      }
 
-  /* Save 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.  */
-
-      gp_offset = cfun->machine->frame.gp_sp_offset;
-      end_offset
-       = gp_offset - (cfun->machine->frame.gp_reg_size
-                      - GET_MODE_SIZE (gpr_mode));
-
-      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);
-
-      /* 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;
-
-      else if (gp_offset < 32768)
-       base_reg_rtx = stack_pointer_rtx, base_offset  = 0;
-
-      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 (Pmode == DImode)
-           emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
-         else
-           emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
-       }
-      else
-       {
-         base_offset = gp_offset;
-         base_reg_rtx = mips_add_large_offset_to_sp (base_offset);
-       }
-
-      /* 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;
+  /* This loop must iterate over the same space as its companion in
+     compute_frame_size.  */
+  offset = cfun->machine->frame.fp_sp_offset - sp_offset;
+  fpr_mode = (TARGET_SINGLE_FLOAT ? SFmode : DFmode);
+  for (regno = (FP_REG_LAST - FP_INC + 1);
+       regno >= FP_REG_FIRST;
+       regno -= FP_INC)
+    if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST))
+      {
+       mips_save_restore_reg (fpr_mode, regno, offset, fn);
+       offset -= GET_MODE_SIZE (fpr_mode);
+      }
+#undef BITSET_P
+}
+\f
+/* If we're generating n32 or n64 abicalls, and the current function
+   does not use $28 as its global pointer, emit a cplocal directive.
+   Use pic_offset_table_rtx as the argument to the directive.  */
 
-      for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
-       {
-         if (BITSET_P (mask, regno - GP_REG_FIRST))
-           {
-             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);
+static void
+mips_output_cplocal (void)
+{
+  if (!TARGET_EXPLICIT_RELOCS
+      && cfun->machine->global_pointer > 0
+      && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM)
+    output_asm_insn (".cplocal %+", 0);
+}
 
-             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);
-               }
-             gp_offset -= GET_MODE_SIZE (gpr_mode);
-           }
-       }
-    }
-  else
-    base_reg_rtx = 0, base_offset  = 0;
+/* If we're generating n32 or n64 abicalls, emit instructions
+   to set up the global pointer.  */
 
-  /* Save floating point registers if needed.  */
-  if (fmask)
+static void
+mips_emit_loadgp (void)
+{
+  if (TARGET_ABICALLS && TARGET_NEWABI && cfun->machine->global_pointer > 0)
     {
-      /* Pick which pointer to use as a base register.  */
-      fp_offset = cfun->machine->frame.fp_sp_offset;
-      end_offset = fp_offset - (cfun->machine->frame.fp_reg_size
-                               - UNITS_PER_HWFPVALUE);
-
-      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);
+      rtx addr, offset, incoming_address;
 
-      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)
-       {
-         base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
-         base_offset = large_offset;
-         if (Pmode == DImode)
-           emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
-         else
-           emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
-       }
-      else
-       {
-         base_offset = fp_offset;
-         base_reg_rtx = mips_add_large_offset_to_sp (fp_offset);
-       }
-
-      /* 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))
-         {
-           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);
-
-           fp_offset -= UNITS_PER_HWFPVALUE;
-         }
+      addr = XEXP (DECL_RTL (current_function_decl), 0);
+      offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP);
+      incoming_address = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+      emit_insn (gen_loadgp (offset, incoming_address));
+      if (!TARGET_EXPLICIT_RELOCS)
+       emit_insn (gen_loadgp_blockage ());
     }
 }
-\f
+
 /* Set up the stack and frame (if desired) for the function.  */
 
 static void
 mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 {
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
   const char *fnname;
-#endif
   HOST_WIDE_INT tsize = cfun->machine->frame.total_size;
 
   /* ??? When is this really needed?  At least the GNU assembler does not
@@ -6564,45 +6624,49 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
       && current_function_args_info.fp_code != 0)
     build_mips16_function_stub (file);
 
-#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);
-
-  if (!flag_inhibit_size_directive)
+  if (!FUNCTION_NAME_ALREADY_DECLARED)
     {
-      fputs ("\t.ent\t", file);
+      /* Get the function name the same way that toplev.c does before calling
+        assemble_start_function.  This is needed so that the name used here
+        exactly matches the name used in ASM_DECLARE_FUNCTION_NAME.  */
+      fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+
+      if (!flag_inhibit_size_directive)
+       {
+         fputs ("\t.ent\t", file);
+         assemble_name (file, fnname);
+         fputs ("\n", file);
+       }
+
       assemble_name (file, fnname);
-      fputs ("\n", file);
+      fputs (":\n", file);
     }
 
-  assemble_name (file, fnname);
-  fputs (":\n", file);
-#endif
-
   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, gp= %ld\n",
+              "\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s\t\t"
+              "# vars= " HOST_WIDE_INT_PRINT_DEC ", regs= %d/%d"
+              ", args= " HOST_WIDE_INT_PRINT_DEC
+              ", gp= " HOST_WIDE_INT_PRINT_DEC "\n",
               (reg_names[(frame_pointer_needed)
                          ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
               ((frame_pointer_needed && TARGET_MIPS16)
-               ? ((long) tsize - current_function_outgoing_args_size)
-               : (long) tsize),
+               ? tsize - cfun->machine->frame.args_size
+               : tsize),
               reg_names[GP_REG_FIRST + 31],
               cfun->machine->frame.var_size,
               cfun->machine->frame.num_gp,
               cfun->machine->frame.num_fp,
-              current_function_outgoing_args_size,
-              cfun->machine->frame.args_size
-              - current_function_outgoing_args_size);
+              cfun->machine->frame.args_size,
+              cfun->machine->frame.cprestore_size);
 
       /* .mask MASK, GPOFFSET; .fmask FPOFFSET */
-      fprintf (file, "\t.mask\t0x%08lx,%ld\n\t.fmask\t0x%08lx,%ld\n",
+      fprintf (file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
               cfun->machine->frame.mask,
-              cfun->machine->frame.gp_save_offset,
+              cfun->machine->frame.gp_save_offset);
+      fprintf (file, "\t.fmask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
               cfun->machine->frame.fmask,
               cfun->machine->frame.fp_save_offset);
 
@@ -6621,195 +6685,167 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
     }
   else if (cfun->machine->all_noreorder_p)
     output_asm_insn ("%(%<", 0);
+
+  /* Tell the assembler which register we're using as the global
+     pointer.  This is needed for thunks, since they can use either
+     explicit relocs or assembler macros.  */
+  mips_output_cplocal ();
 }
 \f
-/* Emit an instruction to move SRC into DEST.  When generating
-   explicit reloc code, mark the instruction as potentially dead.  */
+/* Make the last instruction frame related and note that it performs
+   the operation described by FRAME_PATTERN.  */
 
 static void
-mips_gp_insn (rtx dest, rtx src)
+mips_set_frame_expr (rtx frame_pattern)
 {
   rtx insn;
 
-  insn = emit_insn (gen_rtx_SET (VOIDmode, dest, src));
-  if (TARGET_EXPLICIT_RELOCS)
+  insn = get_last_insn ();
+  RTX_FRAME_RELATED_P (insn) = 1;
+  REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                                     frame_pattern,
+                                     REG_NOTES (insn));
+}
+
+
+/* Return a frame-related rtx that stores REG at MEM.
+   REG must be a single register.  */
+
+static rtx
+mips_frame_set (rtx mem, rtx reg)
+{
+  rtx set = gen_rtx_SET (VOIDmode, mem, reg);
+  RTX_FRAME_RELATED_P (set) = 1;
+  return set;
+}
+
+
+/* Save register REG to MEM.  Make the instruction frame-related.  */
+
+static void
+mips_save_reg (rtx reg, rtx mem)
+{
+  if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64)
+    {
+      rtx x1, x2;
+
+      if (mips_split_64bit_move_p (mem, reg))
+       mips_split_64bit_move (mem, reg);
+      else
+       emit_move_insn (mem, reg);
+
+      x1 = mips_frame_set (mips_subword (mem, 0), mips_subword (reg, 0));
+      x2 = mips_frame_set (mips_subword (mem, 1), mips_subword (reg, 1));
+      mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
+    }
+  else
     {
-      /* compute_frame_size assumes that any function which uses the
-        constant pool will need a gp.  However, all constant
-        pool references could be eliminated, in which case
-        it is OK for flow to delete the gp load as well.  */
-      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, const0_rtx,
-                                           REG_NOTES (insn));
+      if (TARGET_MIPS16
+         && REGNO (reg) != GP_REG_FIRST + 31
+         && !M16_REG_P (REGNO (reg)))
+       {
+         /* Save a non-mips16 register by moving it through a temporary.
+            We don't need to do this for $31 since there's a special
+            instruction for it.  */
+         emit_move_insn (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
+         emit_move_insn (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
+       }
+      else
+       emit_move_insn (mem, reg);
+
+      mips_set_frame_expr (mips_frame_set (mem, reg));
     }
 }
 
+
 /* Expand the prologue into a bunch of separate insns.  */
 
 void
 mips_expand_prologue (void)
 {
-  HOST_WIDE_INT tsize;
-  rtx tmp_rtx = 0;
-  tree fndecl = current_function_decl;
-  tree fntype = TREE_TYPE (fndecl);
-  tree fnargs = DECL_ARGUMENTS (fndecl);
-  tree cur_arg;
-  CUMULATIVE_ARGS args_so_far;
+  HOST_WIDE_INT size;
 
   if (cfun->machine->global_pointer > 0)
     REGNO (pic_offset_table_rtx) = cfun->machine->global_pointer;
 
-  /* If struct value address is treated as the first argument, make it so.  */
-  if (aggregate_value_p (DECL_RESULT (fndecl), fndecl)
-      && ! current_function_returns_pcc_struct
-      && targetm.calls.struct_value_rtx (fndecl, 0) == 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;
-    }
+  size = compute_frame_size (get_frame_size ());
 
-  /* Go through the function arguments, leaving args_so_far reflecting
-     the final state.  */
-  INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, current_function_decl);
-  for (cur_arg = fnargs; cur_arg != 0; cur_arg = TREE_CHAIN (cur_arg))
+  /* Save the registers.  Allocate up to MIPS_MAX_FIRST_STACK_STEP
+     bytes beforehand; this is enough to cover the register save area
+     without going out of range.  */
+  if ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 0)
     {
-      tree passed_type;
-      enum machine_mode passed_mode;
+      HOST_WIDE_INT step1;
 
-      passed_type = DECL_ARG_TYPE (cur_arg);
-      if (TREE_ADDRESSABLE (passed_type))
-       {
-         passed_type = build_pointer_type (passed_type);
-         passed_mode = Pmode;
-       }
-      else
-       passed_mode = TYPE_MODE (passed_type);
-      FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1);
+      step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP);
+      RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
+                                                    stack_pointer_rtx,
+                                                    GEN_INT (-step1)))) = 1;
+      size -= step1;
+      mips_for_each_saved_reg (size, mips_save_reg);
     }
 
-  tsize = compute_frame_size (get_frame_size ());
-
-  if (tsize > 0)
+  /* Allocate the rest of the frame.  */
+  if (size > 0)
     {
-      rtx tsize_rtx = GEN_INT (tsize);
-
-      /* In mips16 mode with a large frame, we save the registers before
-         adjusting the stack.  */
-      if (!TARGET_MIPS16 || tsize <= 32768)
+      if (SMALL_OPERAND (-size))
+       RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
+                                                      stack_pointer_rtx,
+                                                      GEN_INT (-size)))) = 1;
+      else
        {
-         if (tsize > 32768)
+         emit_move_insn (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size));
+         if (TARGET_MIPS16)
            {
-             rtx adjustment_rtx;
+             /* 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.  */
+             if (!frame_pointer_needed)
+               abort ();
 
-             adjustment_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
-             emit_move_insn (adjustment_rtx, tsize_rtx);
-             emit_insn (gen_sub3_insn (stack_pointer_rtx,
-                                       stack_pointer_rtx,
-                                       adjustment_rtx));
+             emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
+             emit_insn (gen_sub3_insn (hard_frame_pointer_rtx,
+                                       hard_frame_pointer_rtx,
+                                       MIPS_PROLOGUE_TEMP (Pmode)));
+             emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
            }
          else
-           emit_insn (gen_add3_insn (stack_pointer_rtx,
+           emit_insn (gen_sub3_insn (stack_pointer_rtx,
                                      stack_pointer_rtx,
-                                     GEN_INT (-tsize)));
+                                     MIPS_PROLOGUE_TEMP (Pmode)));
 
+         /* Describe the combined effect of the previous instructions.  */
          mips_set_frame_expr
            (gen_rtx_SET (VOIDmode, stack_pointer_rtx,
-                         plus_constant (stack_pointer_rtx, -tsize)));
-       }
-
-      save_restore_insns (1, tmp_rtx, tsize);
-
-      if (TARGET_ABICALLS && !TARGET_NEWABI && !current_function_is_leaf)
-       emit_insn (gen_cprestore
-                  (GEN_INT (current_function_outgoing_args_size)));
-
-      if (TARGET_MIPS16 && tsize > 32768)
-       {
-         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);
-         emit_insn (gen_sub3_insn (hard_frame_pointer_rtx,
-                                   hard_frame_pointer_rtx,
-                                   reg_rtx));
-         emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
+                         plus_constant (stack_pointer_rtx, -size)));
        }
+    }
 
-      if (frame_pointer_needed)
+  /* Set up the frame pointer, if we're using one.  In mips16 code,
+     we point the frame pointer ahead of the outgoing argument area.
+     This should allow more variables & incoming arguments to be
+     accessed with unextended instructions.  */
+  if (frame_pointer_needed)
+    {
+      if (TARGET_MIPS16 && cfun->machine->frame.args_size != 0)
        {
-          rtx 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_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));
-
-         if (insn)
-           RTX_FRAME_RELATED_P (insn) = 1;
+         rtx offset = GEN_INT (cfun->machine->frame.args_size);
+         RTX_FRAME_RELATED_P
+           (emit_insn (gen_add3_insn (hard_frame_pointer_rtx,
+                                      stack_pointer_rtx,
+                                      offset))) = 1;
        }
+      else
+       RTX_FRAME_RELATED_P (emit_move_insn (hard_frame_pointer_rtx,
+                                            stack_pointer_rtx)) = 1;
     }
 
-  if (TARGET_ABICALLS && TARGET_NEWABI && cfun->machine->global_pointer > 0)
-    {
-      rtx temp, fnsymbol, fnaddr;
-
-      temp = gen_rtx_REG (Pmode, MIPS_TEMP1_REGNUM);
-      fnsymbol = XEXP (DECL_RTL (current_function_decl), 0);
-      fnaddr = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+  /* If generating o32/o64 abicalls, save $gp on the stack.  */
+  if (TARGET_ABICALLS && !TARGET_NEWABI && !current_function_is_leaf)
+    emit_insn (gen_cprestore (GEN_INT (current_function_outgoing_args_size)));
 
-      mips_gp_insn (temp, mips_lui_reloc (fnsymbol, RELOC_LOADGP_HI));
-      mips_gp_insn (temp, gen_rtx_PLUS (Pmode, temp, fnaddr));
-      mips_gp_insn (pic_offset_table_rtx,
-                   gen_rtx_PLUS (Pmode, temp,
-                                 mips_reloc (fnsymbol, RELOC_LOADGP_LO)));
-
-      if (!TARGET_EXPLICIT_RELOCS)
-       emit_insn (gen_loadgp_blockage ());
-    }
+  mips_emit_loadgp ();
 
   /* If we are profiling, make sure no instructions are scheduled before
      the call to mcount.  */
@@ -6822,13 +6858,14 @@ mips_expand_prologue (void)
    and regs.  */
 
 #define RA_MASK BITMASK_HIGH   /* 1 << 31 */
-#define PIC_OFFSET_TABLE_MASK (1 << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
 
 static void
 mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
                               HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 {
-  rtx string;
+  /* Reinstate the normal $gp.  */
+  REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM;
+  mips_output_cplocal ();
 
   if (cfun->machine->all_noreorder_p)
     {
@@ -6838,8 +6875,7 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
       set_noreorder = set_nomacro = 0;
     }
 
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
-  if (!flag_inhibit_size_directive)
+  if (!FUNCTION_NAME_ALREADY_DECLARED && !flag_inhibit_size_directive)
     {
       const char *fnname;
 
@@ -6851,32 +6887,29 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
       assemble_name (file, fnname);
       fputs ("\n", file);
     }
-#endif
+}
+\f
+/* Emit instructions to restore register REG from slot MEM.  */
 
-  while (string_constants != NULL)
-    {
-      struct string_constant *next;
+static void
+mips_restore_reg (rtx reg, rtx mem)
+{
+  /* There's no mips16 instruction to load $31 directly.  Load into
+     $7 instead and adjust the return insn appropriately.  */
+  if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31)
+    reg = gen_rtx_REG (GET_MODE (reg), 7);
 
-      next = string_constants->next;
-      free (string_constants);
-      string_constants = next;
+  if (TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
+    {
+      /* Can't restore directly; move through a temporary.  */
+      emit_move_insn (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
+      emit_move_insn (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
     }
+  else
+    emit_move_insn (reg, mem);
+}
 
-  /* If any following function uses the same strings as this one, force
-     them to refer those strings indirectly.  Nearby functions could
-     refer them using pc-relative addressing, but it isn't safe in
-     general.  For instance, some functions may be placed in sections
-     other than .text, and we don't know whether they be close enough
-     to this one.  In large files, even other .text functions can be
-     too far away.  */
-  for (string = mips16_strings; string != 0; string = XEXP (string, 1))
-    SYMBOL_REF_FLAG (XEXP (string, 0)) = 0;
-  free_EXPR_LIST_list (&mips16_strings);
 
-  /* Reinstate the normal $gp.  */
-  REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM;
-}
-\f
 /* Expand the epilogue into a bunch of separate insns.  SIBCALL_P is true
    if this epilogue precedes a sibling call, false if it is for a normal
    "epilogue" pattern.  */
@@ -6884,9 +6917,8 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
 void
 mips_expand_epilogue (int sibcall_p)
 {
-  HOST_WIDE_INT tsize = cfun->machine->frame.total_size;
-  rtx tsize_rtx = GEN_INT (tsize);
-  rtx tmp_rtx = (rtx)0;
+  HOST_WIDE_INT step1, step2;
+  rtx base, target;
 
   if (!sibcall_p && mips_can_use_return_insn ())
     {
@@ -6894,131 +6926,101 @@ mips_expand_epilogue (int sibcall_p)
       return;
     }
 
-  if (tsize > 32767 && ! TARGET_MIPS16)
+  /* 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.
+
+     Start off by assuming that no registers need to be restored.  */
+  step1 = cfun->machine->frame.total_size;
+  step2 = 0;
+
+  /* Work out which register holds the frame address.  Account for the
+     frame pointer offset used by mips16 code.  */
+  if (!frame_pointer_needed)
+    base = stack_pointer_rtx;
+  else
     {
-      tmp_rtx = gen_rtx_REG (Pmode, MIPS_TEMP1_REGNUM);
-      emit_move_insn (tmp_rtx, tsize_rtx);
-      tsize_rtx = tmp_rtx;
+      base = hard_frame_pointer_rtx;
+      if (TARGET_MIPS16)
+       step1 -= cfun->machine->frame.args_size;
     }
 
-  if (tsize > 0)
+  /* If we need to restore registers, deallocate as much stack as
+     possible in the second step without going out of range.  */
+  if ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 0)
     {
-      long orig_tsize = tsize;
-
-      if (frame_pointer_needed)
-       {
-         emit_insn (gen_blockage ());
-
-         /* 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;
-
-             /* 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;
-               }
+      step2 = MIN (step1, MIPS_MAX_FIRST_STACK_STEP);
+      step1 -= step2;
+    }
 
-             if (tsize && tsize != orig_tsize)
-               tsize_rtx = GEN_INT (tsize);
-           }
+  /* Set TARGET to BASE + STEP1.  */
+  target = base;
+  if (step1 > 0)
+    {
+      rtx adjust;
 
-         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));
+      /* Get an rtx for STEP1 that we can add to BASE.  */
+      adjust = GEN_INT (step1);
+      if (!SMALL_OPERAND (step1))
+       {
+         emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), adjust);
+         adjust = MIPS_EPILOGUE_TEMP (Pmode);
        }
 
-      /* 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
-              && (cfun->machine->frame.mask
-                  & (1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))))
-       emit_insn (gen_blockage ());
-
-      save_restore_insns (0, tmp_rtx, orig_tsize);
-
-      /* 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 ();
+      /* Normal mode code can copy the result straight into $sp.  */
+      if (!TARGET_MIPS16)
+       target = stack_pointer_rtx;
 
-      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;
-       }
+      emit_insn (gen_add3_insn (target, base, adjust));
+    }
 
-      emit_insn (gen_blockage ());
+  /* Copy TARGET into the stack pointer.  */
+  if (target != stack_pointer_rtx)
+    emit_move_insn (stack_pointer_rtx, target);
 
-      if (tsize != 0 || current_function_calls_eh_return)
-       {
-         if (!TARGET_MIPS16 || !current_function_calls_eh_return)
-           {
-             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.  */
+  /* If we're using addressing macros for n32/n64 abicalls, $gp is
+     implicitly used by all SYMBOL_REFs.  We must emit a blockage
+     insn before restoring it.  */
+  if (TARGET_ABICALLS && TARGET_NEWABI && !TARGET_EXPLICIT_RELOCS)
+    emit_insn (gen_blockage ());
 
-             rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6);
+  /* Restore the registers.  */
+  mips_for_each_saved_reg (cfun->machine->frame.total_size - step2,
+                          mips_restore_reg);
 
-             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));
-               }
-           }
+  /* Deallocate the final bit of the frame.  */
+  if (step2 > 0)
+    emit_insn (gen_add3_insn (stack_pointer_rtx,
+                             stack_pointer_rtx,
+                             GEN_INT (step2)));
 
+  /* Add in the __builtin_eh_return stack adjustment.  We need to
+     use a temporary in mips16 code.  */
+  if (current_function_calls_eh_return)
+    {
+      if (TARGET_MIPS16)
+       {
+         emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), stack_pointer_rtx);
+         emit_insn (gen_add3_insn (MIPS_EPILOGUE_TEMP (Pmode),
+                                   MIPS_EPILOGUE_TEMP (Pmode),
+                                   EH_RETURN_STACKADJ_RTX));
+         emit_move_insn (stack_pointer_rtx, MIPS_EPILOGUE_TEMP (Pmode));
        }
+      else
+       emit_insn (gen_add3_insn (stack_pointer_rtx,
+                                 stack_pointer_rtx,
+                                 EH_RETURN_STACKADJ_RTX));
     }
+
   if (!sibcall_p)
     {
       /* The mips16 loads the return address into $7, not $31.  */
       if (TARGET_MIPS16 && (cfun->machine->frame.mask & RA_MASK) != 0)
-       emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode,
-                                                     GP_REG_FIRST + 7)));
+       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)));
+       emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode,
+                                                         GP_REG_FIRST + 31)));
     }
 }
 \f
@@ -7039,20 +7041,149 @@ mips_can_use_return_insn (void)
 
   return_type = DECL_RESULT (current_function_decl);
 
-  /* 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 (return_type, current_function_decl)
-      && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
-      && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE)
-    return 0;
+  /* 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 (return_type, current_function_decl)
+      && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
+      && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE)
+    return 0;
+
+  if (cfun->machine->frame.initialized)
+    return cfun->machine->frame.total_size == 0;
+
+  return compute_frame_size (get_frame_size ()) == 0;
+}
+\f
+/* Implement TARGET_ASM_OUTPUT_MI_THUNK.  Generate rtl rather than asm text
+   in order to avoid duplicating too much logic from elsewhere.  */
+
+static void
+mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
+                     HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+                     tree function)
+{
+  rtx this, temp1, temp2, insn, fnaddr;
+
+  /* Pretend to be a post-reload pass while generating rtl.  */
+  no_new_pseudos = 1;
+  reload_completed = 1;
+  reset_block_changes ();
+
+  /* Pick a global pointer for -mabicalls.  Use $15 rather than $28
+     for TARGET_NEWABI since the latter is a call-saved register.  */
+  if (TARGET_ABICALLS)
+    cfun->machine->global_pointer
+      = REGNO (pic_offset_table_rtx)
+      = TARGET_NEWABI ? 15 : GLOBAL_POINTER_REGNUM;
+
+  /* Set up the global pointer for n32 or n64 abicalls.  */
+  mips_emit_loadgp ();
+
+  /* We need two temporary registers in some cases.  */
+  temp1 = gen_rtx_REG (Pmode, 2);
+  temp2 = gen_rtx_REG (Pmode, 3);
+
+  /* Find out which register contains the "this" pointer.  */
+  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
+    this = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1);
+  else
+    this = gen_rtx_REG (Pmode, GP_ARG_FIRST);
+
+  /* Add DELTA to THIS.  */
+  if (delta != 0)
+    {
+      rtx offset = GEN_INT (delta);
+      if (!SMALL_OPERAND (delta))
+       {
+         emit_move_insn (temp1, offset);
+         offset = temp1;
+       }
+      emit_insn (gen_add3_insn (this, this, offset));
+    }
+
+  /* If needed, add *(*THIS + VCALL_OFFSET) to THIS.  */
+  if (vcall_offset != 0)
+    {
+      rtx addr;
+
+      /* Set TEMP1 to *THIS.  */
+      emit_move_insn (temp1, gen_rtx_MEM (Pmode, this));
+
+      /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET.  */
+      if (SMALL_OPERAND (vcall_offset))
+       addr = gen_rtx_PLUS (Pmode, temp1, GEN_INT (vcall_offset));
+      else if (TARGET_MIPS16)
+       {
+         /* Load the full offset into a register so that we can use
+            an unextended instruction for the load itself.  */
+         emit_move_insn (temp2, GEN_INT (vcall_offset));
+         emit_insn (gen_add3_insn (temp1, temp1, temp2));
+         addr = temp1;
+       }
+      else
+       {
+         /* Load the high part of the offset into a register and
+            leave the low part for the address.  */
+         emit_move_insn (temp2, GEN_INT (CONST_HIGH_PART (vcall_offset)));
+         emit_insn (gen_add3_insn (temp1, temp1, temp2));
+         addr = gen_rtx_PLUS (Pmode, temp1,
+                              GEN_INT (CONST_LOW_PART (vcall_offset)));
+       }
+
+      /* Load the offset and add it to THIS.  */
+      emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr));
+      emit_insn (gen_add3_insn (this, this, temp1));
+    }
+
+  /* Jump to the target function.  Use a sibcall if direct jumps are
+     allowed, otherwise load the address into a register first.  */
+  fnaddr = XEXP (DECL_RTL (function), 0);
+  if (TARGET_MIPS16 || TARGET_ABICALLS || TARGET_LONG_CALLS)
+    {
+      /* This is messy.  gas treats "la $25,foo" as part of a call
+        sequence and may allow a global "foo" to be lazily bound.
+        The general move patterns therefore reject this combination.
+
+        In this context, lazy binding would actually be OK for o32 and o64,
+        but it's still wrong for n32 and n64; see mips_load_call_address.
+        We must therefore load the address via a temporary register if
+        mips_dangerous_for_la25_p.
+
+        If we jump to the temporary register rather than $25, the assembler
+        can use the move insn to fill the jump's delay slot.  */
+      if (TARGET_ABICALLS && !mips_dangerous_for_la25_p (fnaddr))
+       temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+      mips_load_call_address (temp1, fnaddr, true);
+
+      if (TARGET_ABICALLS && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
+       emit_move_insn (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
+      emit_jump_insn (gen_indirect_jump (temp1));
+    }
+  else
+    {
+      insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
+      SIBLING_CALL_P (insn) = 1;
+    }
 
-  if (cfun->machine->frame.initialized)
-    return cfun->machine->frame.total_size == 0;
+  /* Run just enough of rest_of_compilation.  This sequence was
+     "borrowed" from alpha.c.  */
+  insn = get_insns ();
+  insn_locators_initialize ();
+  split_all_insns_noflow ();
+  if (TARGET_MIPS16)
+    mips16_lay_out_constants ();
+  shorten_branches (insn);
+  final_start_function (insn, file, 1);
+  final (insn, file, 1, 0);
+  final_end_function ();
 
-  return compute_frame_size (get_frame_size ()) == 0;
+  /* Clean up the vars set above.  Note that final_end_function resets
+     the global pointer for us.  */
+  reload_completed = 0;
+  no_new_pseudos = 0;
 }
 \f
 /* Returns nonzero if X contains a SYMBOL_REF.  */
@@ -7066,11 +7197,10 @@ symbolic_expression_p (rtx x)
   if (GET_CODE (x) == CONST)
     return symbolic_expression_p (XEXP (x, 0));
 
-  if (GET_RTX_CLASS (GET_CODE (x)) == '1')
+  if (UNARY_P (x))
     return symbolic_expression_p (XEXP (x, 0));
 
-  if (GET_RTX_CLASS (GET_CODE (x)) == 'c'
-      || GET_RTX_CLASS (GET_CODE (x)) == '2')
+  if (ARITHMETIC_P (x))
     return (symbolic_expression_p (XEXP (x, 0))
            || symbolic_expression_p (XEXP (x, 1)));
 
@@ -7125,15 +7255,7 @@ static void
 mips_select_section (tree decl, int reloc,
                     unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
 {
-  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 (targetm.have_named_sections)
+  if (targetm.have_named_sections)
     default_elf_select_section (decl, reloc, align);
   else
     /* The native irix o32 assembler doesn't support named sections.  */
@@ -7152,6 +7274,11 @@ mips_in_small_data_p (tree decl)
   if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL)
     return false;
 
+  /* We don't yet generate small-data references for -mabicalls.  See related
+     -G handling in override_options.  */
+  if (TARGET_ABICALLS)
+    return false;
+
   if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
     {
       const char *name;
@@ -7182,102 +7309,97 @@ mips_in_small_data_p (tree decl)
   size = int_size_in_bytes (TREE_TYPE (decl));
   return (size > 0 && size <= mips_section_threshold);
 }
+\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.  */
 
-/* When generating embedded PIC code, SYMBOL_REF_FLAG is set for
-   symbols which are not in the .text section.
-
-   When generating mips16 code, SYMBOL_REF_FLAG is set for string
-   constants which are put in the .text section.  We also record the
-   total length of all such strings; this total is used to decide
-   whether we need to split the constant table, and need not be
-   precisely correct.
+static int
+mips_fpr_return_fields (tree valtype, tree *fields)
+{
+  tree field;
+  int i;
 
-   When generating -mabicalls code, SYMBOL_REF_FLAG is set if we
-   should treat the symbol as SYMBOL_GOT_LOCAL.  */
+  if (!TARGET_NEWABI)
+    return 0;
 
-static void
-mips_encode_section_info (tree decl, rtx rtl, int first)
-{
-  rtx symbol;
+  if (TREE_CODE (valtype) != RECORD_TYPE)
+    return 0;
 
-  if (GET_CODE (rtl) != MEM)
-    return;
+  i = 0;
+  for (field = TYPE_FIELDS (valtype); field != 0; field = TREE_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+       continue;
 
-  symbol = XEXP (rtl, 0);
+      if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE)
+       return 0;
 
-  if (GET_CODE (symbol) != SYMBOL_REF)
-    return;
+      if (i == 2)
+       return 0;
 
-  if (TARGET_MIPS16)
-    {
-      if (first && TREE_CODE (decl) == STRING_CST
-          && ! flag_writable_strings
-          /* If this string is from a function, and the function will
-             go in a gnu linkonce section, then we can't directly
-             access the string.  This gets an assembler error
-             "unsupported PC relative reference to different section".
-             If we modify SELECT_SECTION to put it in function_section
-             instead of text_section, it still fails because
-             DECL_SECTION_NAME isn't set until assemble_start_function.
-             If we fix that, it still fails because strings are shared
-             among multiple functions, and we have cross section
-             references again.  We force it to work by putting string
-             addresses in the constant pool and indirecting.  */
-          && (! current_function_decl
-              || ! DECL_ONE_ONLY (current_function_decl)))
-        {
-          mips16_strings = alloc_EXPR_LIST (0, symbol, mips16_strings);
-          SYMBOL_REF_FLAG (symbol) = 1;
-          mips_string_length += TREE_STRING_LENGTH (decl);
-        }
+      fields[i++] = field;
     }
+  return i;
+}
 
-  if (TARGET_EMBEDDED_PIC)
-    {
-      if (TREE_CODE (decl) == VAR_DECL)
-        SYMBOL_REF_FLAG (symbol) = 1;
-      else if (TREE_CODE (decl) == FUNCTION_DECL)
-        SYMBOL_REF_FLAG (symbol) = 0;
-      else if (TREE_CODE (decl) == STRING_CST
-               && ! flag_writable_strings)
-        SYMBOL_REF_FLAG (symbol) = 0;
-      else
-        SYMBOL_REF_FLAG (symbol) = 1;
-    }
 
-  else if (TARGET_ABICALLS)
-    {
-      /* Mark the symbol if we should treat it as SYMBOL_GOT_LOCAL.
-         There are three cases to consider:
+/* Implement TARGET_RETURN_IN_MSB.  For n32 & n64, we should return
+   a value in the most significant part of $2/$3 if:
 
-            - o32 PIC (either with or without explicit relocs)
-            - n32/n64 PIC without explicit relocs
-            - n32/n64 PIC with explicit relocs
+      - the target is big-endian;
 
-         In the first case, both local and global accesses will use an
-         R_MIPS_GOT16 relocation.  We must correctly predict which of
-         the two semantics (local or global) the assembler and linker
-         will apply.  The choice doesn't depend on the symbol's
-         visibility, so we deliberately ignore decl_visibility and
-         binds_local_p here.
+      - the value has a structure or union type (we generalize this to
+       cover aggregates from other languages too); and
 
-         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.
+      - the structure is not returned in floating-point registers.  */
 
-         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 (DECL_P (decl) && TREE_PUBLIC (decl))
-        SYMBOL_REF_FLAG (symbol) = 0;
-      else
-        SYMBOL_REF_FLAG (symbol) = 1;
-    }
+static bool
+mips_return_in_msb (tree valtype)
+{
+  tree fields[2];
 
-  default_encode_section_info (decl, rtl, first);
+  return (TARGET_NEWABI
+         && TARGET_BIG_ENDIAN
+         && AGGREGATE_TYPE_P (valtype)
+         && mips_fpr_return_fields (valtype, fields) == 0);
 }
-\f
+
+
+/* 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 : FP_INC);
+  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.  */
@@ -7286,121 +7408,63 @@ rtx
 mips_function_value (tree valtype, tree func ATTRIBUTE_UNUSED,
                     enum machine_mode mode)
 {
-  int reg = GP_RETURN;
-  enum mode_class mclass;
-  int unsignedp = 1;
-
   if (valtype)
     {
+      tree fields[2];
+      int unsignedp;
+
       mode = TYPE_MODE (valtype);
-      unsignedp = TREE_UNSIGNED (valtype);
+      unsignedp = TYPE_UNSIGNED (valtype);
 
-      /* Since we define PROMOTE_FUNCTION_RETURN, we must promote
-        the mode just as PROMOTE_MODE does.  */
+      /* Since we define TARGET_PROMOTE_FUNCTION_RETURN that returns
+        true, we must promote the mode just as PROMOTE_MODE does.  */
       mode = promote_mode (valtype, mode, &unsignedp, 1);
-    }
-  mclass = GET_MODE_CLASS (mode);
-
-  if (mclass == MODE_FLOAT && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE)
-    reg = FP_RETURN;
-
-  else if (mclass == MODE_FLOAT && mode == TFmode)
-    /* long doubles are really split between f0 and f2, not f1.  Eek.
-       Use DImode for each component, since GCC wants integer modes
-       for subregs.  */
-    return gen_rtx_PARALLEL
-      (VOIDmode,
-       gen_rtvec (2,
-                 gen_rtx_EXPR_LIST (VOIDmode,
-                                    gen_rtx_REG (DImode, FP_RETURN),
-                                    GEN_INT (0)),
-                 gen_rtx_EXPR_LIST (VOIDmode,
-                                    gen_rtx_REG (DImode, FP_RETURN + 2),
-                                    GEN_INT (GET_MODE_SIZE (mode) / 2))));
-
-
-  else if (mclass == MODE_COMPLEX_FLOAT
-          && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE * 2)
-    {
-      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 + FP_INC),
-                                      GEN_INT (GET_MODE_SIZE (cmode)))));
-    }
-
-  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;
-
-      for (i = 0, field = TYPE_FIELDS (valtype); field;
-          field = TREE_CHAIN (field))
-       {
-         if (TREE_CODE (field) != FIELD_DECL)
-           continue;
 
-         if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE || i >= 2)
-           break;
+      /* Handle structures whose fields are returned in $f0/$f2.  */
+      switch (mips_fpr_return_fields (valtype, fields))
+       {
+       case 1:
+         return gen_rtx_REG (mode, FP_RETURN);
 
-         fields[i++] = field;
+       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]));
        }
 
-      /* Must check i, so that we reject structures with no elements.  */
-      if (! field)
+      /* 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))
        {
-         if (i == 1)
+         HOST_WIDE_INT size = int_size_in_bytes (valtype);
+         if (size % UNITS_PER_WORD != 0)
            {
-             /* 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)));
-           }
-
-         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))));
+             size += UNITS_PER_WORD - size % UNITS_PER_WORD;
+             mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
            }
        }
     }
 
-  return gen_rtx_REG (mode, reg);
+  if (GET_MODE_CLASS (mode) == MODE_FLOAT
+      && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE)
+    return gen_rtx_REG (mode, FP_RETURN);
+
+  /* Handle long doubles for n32 & n64.  */
+  if (mode == TFmode)
+    return mips_return_fpr_pair (mode,
+                                DImode, 0,
+                                DImode, GET_MODE_SIZE (mode) / 2);
+
+  if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+      && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE * 2)
+    return mips_return_fpr_pair (mode,
+                                GET_MODE_INNER (mode), 0,
+                                GET_MODE_INNER (mode),
+                                GET_MODE_SIZE (mode) / 2);
+
+  return gen_rtx_REG (mode, GP_RETURN);
 }
 
 /* The implementation of FUNCTION_ARG_PASS_BY_REFERENCE.  Return
@@ -7457,6 +7521,44 @@ mips_cannot_change_mode_class (enum machine_mode from,
   return false;
 }
 
+/* Return true if X should not be moved directly into register $25.
+   We need this because many versions of GAS will treat "la $25,foo" as
+   part of a call sequence and so allow a global "foo" to be lazily bound.  */
+
+bool
+mips_dangerous_for_la25_p (rtx x)
+{
+  HOST_WIDE_INT offset;
+
+  if (TARGET_EXPLICIT_RELOCS)
+    return false;
+
+  mips_split_const (x, &x, &offset);
+  return global_got_operand (x, VOIDmode);
+}
+
+/* Implement PREFERRED_RELOAD_CLASS.  */
+
+enum reg_class
+mips_preferred_reload_class (rtx x, enum reg_class class)
+{
+  if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, class))
+    return LEA_REGS;
+
+  if (TARGET_HARD_FLOAT
+      && FLOAT_MODE_P (GET_MODE (x))
+      && reg_class_subset_p (FP_REGS, class))
+    return FP_REGS;
+
+  if (reg_class_subset_p (GR_REGS, class))
+    class = GR_REGS;
+
+  if (TARGET_MIPS16 && reg_class_subset_p (M16_REGS, class))
+    class = M16_REGS;
+
+  return class;
+}
+
 /* This function returns the register class required for a secondary
    register when copying between one of the registers in CLASS, and X,
    using MODE.  If IN_P is nonzero, the copy is going from X to the
@@ -7476,9 +7578,12 @@ mips_secondary_reload_class (enum reg_class class,
 
   gp_reg_p = TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
 
-  if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], 25)
-      && DANGEROUS_FOR_LA25_P (x))
-    return LEA_REGS;
+  if (mips_dangerous_for_la25_p (x))
+    {
+      gr_regs = LEA_REGS;
+      if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], 25))
+       return gr_regs;
+    }
 
   /* Copying from HI or LO to anywhere other than a general register
      requires a general register.  */
@@ -7509,20 +7614,20 @@ mips_secondary_reload_class (enum reg_class class,
     {
       if (in_p)
        return FP_REGS;
-      return GP_REG_P (regno) ? NO_REGS : GR_REGS;
+      return gp_reg_p ? NO_REGS : gr_regs;
     }
   if (ST_REG_P (regno))
     {
       if (! in_p)
        return FP_REGS;
-      return class == GR_REGS ? NO_REGS : GR_REGS;
+      return class == gr_regs ? NO_REGS : gr_regs;
     }
 
   if (class == FP_REGS)
     {
       if (GET_CODE (x) == MEM)
        {
-         /* In this case we can use lwc1, swc1, ldc1 or sdc1. */
+         /* In this case we can use lwc1, swc1, ldc1 or sdc1.  */
          return NO_REGS;
        }
       else if (CONSTANT_P (x) && GET_MODE_CLASS (mode) == MODE_FLOAT)
@@ -7532,7 +7637,7 @@ mips_secondary_reload_class (enum reg_class class,
             code by returning GR_REGS here.  */
          return NO_REGS;
        }
-      else if (GP_REG_P (regno) || x == CONST0_RTX (mode))
+      else if (gp_reg_p || x == CONST0_RTX (mode))
        {
          /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1.  */
          return NO_REGS;
@@ -7545,7 +7650,7 @@ mips_secondary_reload_class (enum reg_class class,
       else
        {
          /* Otherwise, we need to reload through an integer register.  */
-         return GR_REGS;
+         return gr_regs;
        }
     }
 
@@ -7599,17 +7704,11 @@ mips_valid_pointer_mode (enum machine_mode mode)
    hold the $gp value.  */
 
 static rtx
-mips_sdata_pointer (void)
+mips16_gp_pseudo_reg (void)
 {
-  if (TARGET_EXPLICIT_RELOCS)
-    return pic_offset_table_rtx;
-
-  if (!TARGET_MIPS16 || no_new_pseudos)
-    return 0;
-
   if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX)
     {
-      rtx const_gp;
+      rtx unspec;
       rtx insn, scan;
 
       cfun->machine->mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
@@ -7617,10 +7716,10 @@ mips_sdata_pointer (void)
 
       /* We want to initialize this to a value which gcc will believe
          is constant.  */
-      const_gp = gen_rtx_CONST (Pmode, pic_offset_table_rtx);
       start_sequence ();
+      unspec = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, const0_rtx), UNSPEC_GP);
       emit_move_insn (cfun->machine->mips16_gp_pseudo_rtx,
-                     const_gp);
+                     gen_rtx_CONST (Pmode, unspec));
       insn = get_insns ();
       end_sequence ();
 
@@ -7654,7 +7753,7 @@ mips16_fp_args (FILE *file, int fp_code, int from_fp_p)
   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)
+  if (!TARGET_OLDABI)
     abort ();
 
   if (from_fp_p)
@@ -7725,7 +7824,7 @@ build_mips16_function_stub (FILE *file)
                         build_function_type (void_type_node, NULL_TREE));
   DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
 
-  fprintf (file, "\t# Stub function for %s (", current_function_name);
+  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)
     {
@@ -7742,11 +7841,12 @@ build_mips16_function_stub (FILE *file)
 
   /* ??? 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
+  if (!FUNCTION_NAME_ALREADY_DECLARED)
+    {
+      fputs ("\t.ent\t", file);
+      assemble_name (file, stubname);
+      fputs ("\n", file);
+    }
 
   assemble_name (file, stubname);
   fputs (":\n", file);
@@ -7772,11 +7872,12 @@ build_mips16_function_stub (FILE *file)
 
   fprintf (file, "\t.set\treorder\n");
 
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
-  fputs ("\t.end\t", file);
-  assemble_name (file, stubname);
-  fputs ("\n", file);
-#endif
+  if (!FUNCTION_NAME_ALREADY_DECLARED)
+    {
+      fputs ("\t.end\t", file);
+      assemble_name (file, stubname);
+      fputs ("\n", file);
+    }
 
   fprintf (file, "\t.set\tmips16\n");
 
@@ -7849,7 +7950,7 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
 
   /* 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)
+  if (!TARGET_OLDABI)
     abort ();
 
   /* We can only handle SFmode and DFmode floating point return
@@ -7877,9 +7978,9 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
                : ""),
               fp_code);
       id = get_identifier (buf);
-      stub_fn = gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (id));
+      stub_fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
 
-      emit_move_insn (gen_rtx (REG, Pmode, 2), fn);
+      emit_move_insn (gen_rtx_REG (Pmode, 2), fn);
 
       if (retval == NULL_RTX)
        insn = gen_call_internal (stub_fn, arg_size);
@@ -7891,9 +7992,9 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
       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));
+       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
@@ -7902,9 +8003,10 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
          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));
+         gen_rtx_EXPR_LIST (VOIDmode,
+                            gen_rtx_USE (VOIDmode,
+                                         gen_rtx_REG (word_mode, 18)),
+                            CALL_INSN_FUNCTION_USAGE (insn));
 
       /* Return 1 to tell the caller that we've generated the call
          insn.  */
@@ -7971,14 +8073,15 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
       fprintf (asm_out_file, "\t.set\tnomips16\n");
       assemble_start_function (stubdecl, stubname);
 
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
-      fputs ("\t.ent\t", asm_out_file);
-      assemble_name (asm_out_file, stubname);
-      fputs ("\n", asm_out_file);
+      if (!FUNCTION_NAME_ALREADY_DECLARED)
+       {
+         fputs ("\t.ent\t", asm_out_file);
+         assemble_name (asm_out_file, stubname);
+         fputs ("\n", asm_out_file);
 
-      assemble_name (asm_out_file, stubname);
-      fputs (":\n", asm_out_file);
-#endif
+         assemble_name (asm_out_file, stubname);
+         fputs (":\n", asm_out_file);
+       }
 
       /* 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
@@ -8045,11 +8148,12 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
       ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
 #endif
 
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
-      fputs ("\t.end\t", asm_out_file);
-      assemble_name (asm_out_file, stubname);
-      fputs ("\n", asm_out_file);
-#endif
+      if (!FUNCTION_NAME_ALREADY_DECLARED)
+       {
+         fputs ("\t.end\t", asm_out_file);
+         assemble_name (asm_out_file, stubname);
+         fputs ("\n", asm_out_file);
+       }
 
       fprintf (asm_out_file, "\t.set\tmips16\n");
 
@@ -8091,9 +8195,9 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
        abort ();
 
       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));
+       gen_rtx_EXPR_LIST (VOIDmode,
+                          gen_rtx_USE (VOIDmode, gen_rtx_REG (word_mode, 18)),
+                          CALL_INSN_FUNCTION_USAGE (insn));
 
       /* Return 1 to tell the caller that we've generated the call
          insn.  */
@@ -8104,549 +8208,604 @@ build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
   return 0;
 }
 
-/* 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.
+/* 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.  */
 
-   This is an optimization, and the code which gcc has actually
-   generated is correct, so we do not need to catch all cases.  */
+struct mips16_constant {
+  struct mips16_constant *next;
+  rtx value;
+  rtx label;
+  enum machine_mode mode;
+};
 
-static void
-mips16_optimize_gp (void)
+/* Information about an incomplete mips16 constant pool.  FIRST is the
+   first constant, HIGHEST_ADDRESS is the highest address that the first
+   byte of the pool can have, and INSN_ADDRESS is the current instruction
+   address.  */
+
+struct mips16_constant_pool {
+  struct mips16_constant *first;
+  int highest_address;
+  int insn_address;
+};
+
+/* Add constant VALUE to POOL and return its label.  MODE is the
+   value's mode (used for CONST_INTs, etc.).  */
+
+static rtx
+add_constant (struct mips16_constant_pool *pool,
+             rtx value, enum machine_mode mode)
 {
-  rtx gpcopy, slot, insn;
+  struct mips16_constant **p, *c;
+  bool first_of_size_p;
 
-  /* 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.  */
+  /* 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.
 
-  gpcopy = NULL_RTX;
-  slot = NULL_RTX;
-  for (insn = get_insns (); insn != NULL_RTX; insn = next_active_insn (insn))
+     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)
     {
-      rtx set;
+      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 pc-relative lw, addiu and daddiu instructions, the base PC value
+       is the address of the instruction with the lowest two bits clear.
+       The base PC value for ld has the lowest three bits clear.  Assume
+       the worst case here.  */
+    pool->highest_address = pool->insn_address - (UNITS_PER_WORD - 2) + 0x8000;
+  pool->highest_address -= GET_MODE_SIZE (mode);
+  if (first_of_size_p)
+    /* Take into account the worst possible padding due to alignment.  */
+    pool->highest_address -= GET_MODE_SIZE (mode) - 1;
+
+  /* Create a new entry.  */
+  c = (struct mips16_constant *) xmalloc (sizeof *c);
+  c->value = value;
+  c->mode = mode;
+  c->label = gen_label_rtx ();
+  c->next = *p;
+  *p = c;
 
-      if (! INSN_P (insn))
-       continue;
+  return c->label;
+}
 
-      set = PATTERN (insn);
+/* Output constant VALUE after instruction INSN and return the last
+   instruction emitted.  MODE is the mode of the constant.  */
 
-      /* 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 rtx
+dump_constants_1 (enum machine_mode mode, rtx value, rtx insn)
+{
+  switch (GET_MODE_CLASS (mode))
+    {
+    case MODE_INT:
+      {
+       rtx size = GEN_INT (GET_MODE_SIZE (mode));
+       return emit_insn_after (gen_consttable_int (value, size), insn);
+      }
+
+    case MODE_FLOAT:
+      return emit_insn_after (gen_consttable_float (value), insn);
+
+    case MODE_VECTOR_FLOAT:
+    case MODE_VECTOR_INT:
+      {
+       int i;
+       for (i = 0; i < CONST_VECTOR_NUNITS (value); i++)
+         insn = dump_constants_1 (GET_MODE_INNER (mode),
+                                  CONST_VECTOR_ELT (value, i), insn);
+       return insn;
+      }
+
+    default:
+      abort ();
+    }
+}
+
+
+/* Dump out the constants in CONSTANTS after INSN.  */
+
+static void
+dump_constants (struct mips16_constant *constants, rtx insn)
+{
+  struct mips16_constant *c, *next;
+  int align;
 
-      if (gpcopy == NULL_RTX
-         && GET_CODE (SET_SRC (set)) == CONST
-         && XEXP (SET_SRC (set), 0) == pic_offset_table_rtx
-         && GET_CODE (SET_DEST (set)) == REG)
-       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))
+  align = 0;
+  for (c = constants; c != NULL; c = next)
+    {
+      /* If necessary, increase the alignment of PC.  */
+      if (align < GET_MODE_SIZE (c->mode))
        {
-         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);
+         int align_log = floor_log2 (GET_MODE_SIZE (c->mode));
+         insn = emit_insn_after (gen_align (GEN_INT (align_log)), insn);
        }
-      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_CODE (SET_SRC (set)) != CONST
-                       || XEXP (SET_SRC (set), 0) != pic_offset_table_rtx)
-                      && ! 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;
+      align = GET_MODE_SIZE (c->mode);
+
+      insn = emit_label_after (c->label, insn);
+      insn = dump_constants_1 (c->mode, c->value, insn);
+
+      next = c->next;
+      free (c);
     }
 
-  /* 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.
+  emit_barrier_after (insn);
+}
 
-     This could be a peephole optimization, but I don't know if the
-     peephole code can call force_const_mem.
+/* Return the length of instruction INSN.
 
-     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.  */
+   ??? MIPS16 switch tables go in .text, but we don't define
+   JUMP_TABLES_IN_TEXT_SECTION, so get_attr_length will not
+   compute their lengths correctly.  */
 
-  if (insn != NULL_RTX || gpcopy == NULL_RTX || slot == NULL_RTX)
+static int
+mips16_insn_length (rtx insn)
+{
+  if (GET_CODE (insn) == JUMP_INSN)
     {
-#if 0
-      /* Used below in #if 0 area.  */
-      rtx next;
-#endif
-      /* This optimization is only reasonable if the constant table
-         entries are only 4 bytes.  */
-      if (Pmode != SImode)
-       return;
+      rtx body = PATTERN (insn);
+      if (GET_CODE (body) == ADDR_VEC)
+       return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 0);
+      if (GET_CODE (body) == ADDR_DIFF_VEC)
+       return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 1);
+    }
+  return get_attr_length (insn);
+}
 
-#if 0
-  /* ??? FIXME.  Rewrite for new UNSPEC_RELOC stuff.  */
-      for (insn = get_insns (); insn != NULL_RTX; insn = next)
-       {
-         rtx set1, set2;
+/* Rewrite *X so that constant pool references refer to the constant's
+   label instead.  DATA points to the constant pool structure.  */
 
-         next = insn;
-         do
-           {
-             next = NEXT_INSN (next);
-           }
-         while (next != NULL_RTX
-                && (GET_CODE (next) == NOTE
-                    || (GET_CODE (next) == INSN
-                        && (GET_CODE (PATTERN (next)) == USE
-                            || GET_CODE (PATTERN (next)) == CLOBBER))));
+static int
+mips16_rewrite_pool_refs (rtx *x, void *data)
+{
+  struct mips16_constant_pool *pool = data;
+  if (GET_CODE (*x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (*x))
+    *x = gen_rtx_LABEL_REF (Pmode, add_constant (pool,
+                                                get_pool_constant (*x),
+                                                get_pool_mode (*x)));
+  return 0;
+}
 
-         if (next == NULL_RTX)
-           break;
+/* Build MIPS16 constant pools.  */
+
+static void
+mips16_lay_out_constants (void)
+{
+  struct mips16_constant_pool pool;
+  rtx insn, barrier;
+
+  barrier = 0;
+  memset (&pool, 0, sizeof (pool));
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      /* Rewrite constant pool references in INSN.  */
+      if (INSN_P (insn))
+       for_each_rtx (&PATTERN (insn), mips16_rewrite_pool_refs, &pool);
 
-         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
-             && XEXP (SET_SRC (set1), 0) == pic_offset_table_rtx
-             && 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)
+      pool.insn_address += mips16_insn_length (insn);
+
+      if (pool.first != NULL)
+       {
+         /* If there are no natural barriers between the first user of
+            the pool and the highest acceptable address, we'll need to
+            create a new instruction to jump around the constant pool.
+            In the worst case, this instruction will be 4 bytes long.
+
+            If it's too late to do this transformation after INSN,
+            do it immediately before INSN.  */
+         if (barrier == 0 && pool.insn_address + 4 > pool.highest_address)
            {
-             rtx sym;
+             rtx label, jump;
 
-             /* We've found a case we can change to load from the
-                 constant table.  */
+             label = gen_label_rtx ();
 
-             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);
+             jump = emit_jump_insn_before (gen_jump (label), insn);
+             JUMP_LABEL (jump) = label;
+             LABEL_NUSES (label) = 1;
+             barrier = emit_barrier_after (jump);
 
-             PUT_CODE (insn, NOTE);
-             NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
-             NOTE_SOURCE_FILE (insn) = 0;
+             emit_label_after (label, barrier);
+             pool.insn_address += 4;
+           }
 
-             PUT_CODE (next, NOTE);
-             NOTE_LINE_NUMBER (next) = NOTE_INSN_DELETED;
-             NOTE_SOURCE_FILE (next) = 0;
+         /* See whether the constant pool is now out of range of the first
+            user.  If so, output the constants after the previous barrier.
+            Note that any instructions between BARRIER and INSN (inclusive)
+            will use negative offsets to refer to the pool.  */
+         if (pool.insn_address > pool.highest_address)
+           {
+             dump_constants (pool.first, barrier);
+             pool.first = NULL;
+             barrier = 0;
            }
+         else if (BARRIER_P (insn))
+           barrier = insn;
        }
-#endif
-
-      return;
     }
-  /* We can safely remove all assignments to SLOT from GPCOPY, and
-     replace all assignments from SLOT to GPCOPY with assignments from
-     $28.  */
+  dump_constants (pool.first, get_last_insn ());
+}
+\f
+/* A temporary variable used by for_each_rtx callbacks, etc.  */
+static rtx mips_sim_insn;
+
+/* 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;
+
+  /* The current simulation time.  */
+  unsigned int time;
+
+  /* How many more instructions can be issued in the current cycle.  */
+  unsigned int insns_left;
+
+  /* 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];
+
+  /* The pipeline's current DFA state.  */
+  state_t dfa_state;
+};
 
-  for (insn = get_insns (); insn != NULL_RTX; insn = next_active_insn (insn))
-    {
-      rtx set;
+/* Reset STATE to the initial simulation state.  */
 
-      if (! INSN_P (insn))
-       continue;
+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);
+}
 
-      set = PATTERN (insn);
-      if (GET_CODE (set) != SET)
-       continue;
+/* Initialize STATE before its first use.  DFA_STATE points to an
+   allocated but uninitialized DFA state.  */
 
-      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))
-       {
-         PUT_CODE (insn, NOTE);
-         NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
-         NOTE_SOURCE_FILE (insn) = 0;
-       }
-      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))
-       {
-         enum machine_mode mode;
-         rtx src;
-
-         mode = GET_MODE (SET_DEST (set));
-         src = gen_rtx_CONST (mode, pic_offset_table_rtx);
-         emit_insn_after (gen_rtx_SET (VOIDmode, SET_DEST (set), src), insn);
-         PUT_CODE (insn, NOTE);
-         NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
-         NOTE_SOURCE_FILE (insn) = 0;
-       }
+static void
+mips_sim_init (struct mips_sim *state, state_t dfa_state)
+{
+  state->issue_rate = mips_issue_rate ();
+  state->dfa_state = dfa_state;
+  mips_sim_reset (state);
+}
+
+/* Advance STATE by one clock cycle.  */
+
+static void
+mips_sim_next_cycle (struct mips_sim *state)
+{
+  state->time++;
+  state->insns_left = state->issue_rate;
+  state_transition (state->dfa_state, 0);
+}
+
+/* Advance simulation state STATE until instruction INSN can read
+   register REG.  */
+
+static void
+mips_sim_wait_reg (struct mips_sim *state, rtx insn, rtx reg)
+{
+  unsigned int i;
+
+  for (i = 0; i < HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)); i++)
+    if (state->last_set[REGNO (reg) + i].insn != 0)
+      {
+       unsigned int t;
+
+       t = state->last_set[REGNO (reg) + i].time;
+       t += insn_latency (state->last_set[REGNO (reg) + i].insn, insn);
+       while (state->time < t)
+         mips_sim_next_cycle (state);
     }
 }
 
-/* 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.  If *X is a register, advance simulation state
+   DATA until mips_sim_insn can read the register's value.  */
 
-struct constant
+static int
+mips_sim_wait_regs_2 (rtx *x, void *data)
 {
-  struct constant *next;
-  rtx value;
-  rtx label;
-  enum machine_mode mode;
-};
+  if (REG_P (*x))
+    mips_sim_wait_reg (data, mips_sim_insn, *x);
+  return 0;
+}
 
-/* Add a constant to the list in *PCONSTANTS.  */
+/* Call mips_sim_wait_regs_2 (R, DATA) for each register R mentioned in *X.  */
 
-static rtx
-add_constant (struct constant **pconstants, rtx val, enum machine_mode mode)
+static void
+mips_sim_wait_regs_1 (rtx *x, void *data)
 {
-  struct constant *c;
+  for_each_rtx (x, mips_sim_wait_regs_2, data);
+}
 
-  for (c = *pconstants; c != NULL; c = c->next)
-    if (mode == c->mode && rtx_equal_p (val, c->value))
-      return c->label;
+/* Advance simulation state STATE until all of INSN's register
+   dependencies are satisfied.  */
 
-  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;
+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);
 }
 
-/* Dump out the constants in CONSTANTS after INSN.  */
+/* Advance simulation state STATE until the units required by
+   instruction INSN are available.  */
 
 static void
-dump_constants (struct constant *constants, rtx insn)
+mips_sim_wait_units (struct mips_sim *state, rtx insn)
 {
-  struct constant *c;
-  int align;
-
-  c = constants;
-  align = 0;
-  while (c != NULL)
-    {
-      rtx r;
-      struct constant *next;
+  state_t tmp_state;
 
-      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;
-       }
+  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);
+}
 
-      insn = emit_label_after (c->label, insn);
+/* Advance simulation state STATE until INSN is ready to issue.  */
 
-      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 ();
-       }
+static void
+mips_sim_wait_insn (struct mips_sim *state, rtx insn)
+{
+  mips_sim_wait_regs (state, insn);
+  mips_sim_wait_units (state, insn);
+}
 
-      insn = emit_insn_after (r, insn);
+/* mips_sim_insn has just set X.  Update the LAST_SET array
+   in simulation state DATA.  */
 
-      next = c->next;
-      free (c);
-      c = next;
-    }
+static void
+mips_sim_record_set (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+  struct mips_sim *state;
+  unsigned int i;
 
-  emit_barrier_after (insn);
+  state = data;
+  if (REG_P (x))
+    for (i = 0; i < HARD_REGNO_NREGS (REGNO (x), GET_MODE (x)); i++)
+      {
+       state->last_set[REGNO (x) + i].insn = mips_sim_insn;
+       state->last_set[REGNO (x) + i].time = state->time;
+      }
 }
 
-/* Find the symbol in an address expression.  */
+/* Issue instruction INSN in scheduler state STATE.  Assume that INSN
+   can issue immediately (i.e., that mips_sim_wait_insn has already
+   been called).  */
 
-static rtx
-mips_find_symbol (rtx addr)
+static void
+mips_sim_issue_insn (struct mips_sim *state, rtx insn)
 {
-  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;
+  state_transition (state->dfa_state, insn);
+  state->insns_left--;
 
-      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;
-    }
-  return NULL_RTX;
+  mips_sim_insn = insn;
+  note_stores (PATTERN (insn), mips_sim_record_set, state);
 }
 
-/* In mips16 mode, we need to look through the function to check for
-   PC relative loads that are out of range.  */
+/* Simulate issuing a NOP in state STATE.  */
 
 static void
-mips16_lay_out_constants (void)
+mips_sim_issue_nop (struct mips_sim *state)
 {
-  int insns_len, max_internal_pool_size, pool_size, addr, first_constant_ref;
-  rtx first, insn;
-  struct constant *constants;
+  if (state->insns_left == 0)
+    mips_sim_next_cycle (state);
+  state->insns_left--;
+}
 
-  first = get_insns ();
+/* 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.  */
 
-  /* 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.  */
+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);
 
-  insns_len = 0;
-  for (insn = first; insn; insn = NEXT_INSN (insn))
+  switch (GET_CODE (SEQ_BEGIN (insn)))
     {
-      insns_len += get_attr_length (insn);
+    case CODE_LABEL:
+    case CALL_INSN:
+      /* We can't predict the processor state after a call or label.  */
+      mips_sim_reset (state);
+      break;
 
-      /* ??? 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;
+    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;
 
-         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;
-       }
+    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.  */
 
-  /* Store the original value of insns_len in cfun->machine, so
-     that simple_memory_operand can look at it.  */
-  cfun->machine->insns_len = insns_len;
-
-  pool_size = get_pool_size ();
-  if (insns_len + pool_size + mips_string_length < 0x8000)
-    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))
-    {
-      if (GET_CODE (insn) == INSN
-         && GET_CODE (PATTERN (insn)) == SET)
+static void
+vr4130_avoid_branch_rt_conflict (rtx insn)
+{
+  rtx first, second;
+
+  first = SEQ_BEGIN (insn);
+  second = SEQ_END (insn);
+  if (GET_CODE (first) == JUMP_INSN
+      && GET_CODE (second) == INSN
+      && 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 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);
+         /* 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;
        }
     }
+}
 
-  constants = NULL;
-  addr = 0;
-  first_constant_ref = -1;
+/* Implement -mvr4130-align.  Go through each basic block and simulate the
+   processor pipeline.  If we find that a pair of instructions could execute
+   in parallel, and the first of those instruction is not 8-byte aligned,
+   insert a nop to make it aligned.  */
 
-  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;
+static void
+vr4130_align_insns (void)
+{
+  struct mips_sim state;
+  rtx insn, subinsn, last, last2, next;
+  bool aligned_p;
 
-         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;
-           }
+  dfa_start ();
 
-         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;
-           }
-       }
+  /* LAST is the last instruction before INSN to have a nonzero length.
+     LAST2 is the last such instruction before LAST.  */
+  last = 0;
+  last2 = 0;
 
-      addr += get_attr_length (insn);
+  /* ALIGNED_P is true if INSN is known to be at an aligned address.  */
+  aligned_p = true;
 
-      /* ??? 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;
+  mips_sim_init (&state, alloca (state_size ()));
+  for (insn = get_insns (); insn != 0; insn = next)
+    {
+      unsigned int length;
 
-         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;
-       }
+      next = NEXT_INSN (insn);
 
-      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;
-       }
+      /* 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);
+
+      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
+               && GET_CODE (subinsn) != CALL_INSN)
+             {
+               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);
 
-      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))))
+      /* Update LAST, LAST2 and ALIGNED_P for the next instruction.  */
+      length = get_attr_length (insn);
+      if (length > 0)
        {
-         /* 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 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 (GET_CODE (SEQ_BEGIN (insn)) == INSN
+             && (recog_memoized (insn) < 0 || length >= 8))
+           {
+             next = emit_insn_after (gen_align (GEN_INT (3)), insn);
+             next = NEXT_INSN (next);
+             mips_sim_next_cycle (&state);
+             aligned_p = true;
+           }
+         else if (length & 4)
+           aligned_p = !aligned_p;
+         last2 = last;
+         last = insn;
        }
-     }
 
-  /* ??? 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.  */
+      /* See whether INSN is an aligned label.  */
+      if (LABEL_P (insn) && label_to_alignment (insn) >= 3)
+       aligned_p = true;
+    }
+  dfa_finish ();
 }
-
-
+\f
 /* Subroutine of mips_reorg.  If there is a hazard between INSN
    and a previous instruction, avoid it by inserting nops after
    instruction AFTER.
@@ -8735,12 +8894,17 @@ mips_avoid_hazards (void)
   rtx insn, last_insn, lo_reg, delayed_reg;
   int hilo_delay, i;
 
+  /* 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 ());
 
-  /* The profiler code uses assembler macros.  */
-  cfun->machine->all_noreorder_p = !current_function_profile;
+  /* The profiler code uses assembler macros.  -mfix-vr4120 relies on
+     assembler nop insertion.  */
+  cfun->machine->all_noreorder_p = (!current_function_profile
+                                   && !TARGET_FIX_VR4120);
 
   last_insn = 0;
   hilo_delay = 2;
@@ -8769,27 +8933,34 @@ static void
 mips_reorg (void)
 {
   if (TARGET_MIPS16)
-    {
-      if (optimize)
-       mips16_optimize_gp ();
-      mips16_lay_out_constants ();
-    }
+    mips16_lay_out_constants ();
   else if (TARGET_EXPLICIT_RELOCS)
     {
       if (mips_flag_delayed_branch)
-       dbr_schedule (get_insns (), rtl_dump_file);
+       dbr_schedule (get_insns (), dump_file);
       mips_avoid_hazards ();
+      if (TUNE_MIPS4130 && TARGET_VR4130_ALIGN)
+       vr4130_align_insns ();
     }
 }
 
-/* We need to use a special set of functions to handle hard floating
-   point code in mips16 mode.  Also, allow for --enable-gofast.  */
+/* This function does three things:
+
+   - Register the special divsi3 and modsi3 functions if -mfix-vr4120.
+   - Register the mips16 hardware floating point stubs.
+   - Register the gofast functions if selected using --enable-gofast.  */
 
 #include "config/gofast.h"
 
 static void
 mips_init_libfuncs (void)
 {
+  if (TARGET_FIX_VR4120)
+    {
+      set_optab_libfunc (sdiv_optab, SImode, "__vr4120_divsi3");
+      set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
+    }
+
   if (TARGET_MIPS16 && mips16_hard_float)
     {
       set_optab_libfunc (add_optab, SFmode, "__mips16_addsf3");
@@ -8804,7 +8975,7 @@ mips_init_libfuncs (void)
       set_optab_libfunc (lt_optab, SFmode, "__mips16_ltsf2");
       set_optab_libfunc (le_optab, SFmode, "__mips16_lesf2");
 
-      set_conv_libfunc (sfix_optab, SImode, SFmode, "__mips16_fixsfsi");
+      set_conv_libfunc (sfix_optab, SImode, SFmode, "__mips16_fix_truncsfsi");
       set_conv_libfunc (sfloat_optab, SFmode, SImode, "__mips16_floatsisf");
 
       if (TARGET_DOUBLE_FLOAT)
@@ -8824,7 +8995,7 @@ mips_init_libfuncs (void)
          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_fixdfsi");
+         set_conv_libfunc (sfix_optab, SImode, DFmode, "__mips16_fix_truncdfsi");
          set_conv_libfunc (sfloat_optab, DFmode, SImode, "__mips16_floatsidf");
        }
     }
@@ -8916,7 +9087,7 @@ mips_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
          else
            return 6;
        }
-    }  /* from == HI_REG, etc. */
+    }  /* from == HI_REG, etc.  */
   else if (from == ST_REGS && GR_REG_CLASS_P (to))
     return 4;
   else if (COP_REG_CLASS_P (from))
@@ -8924,7 +9095,7 @@ mips_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
       return 5;
     }  /* COP_REG_CLASS_P (from) */
 
-  /* fallthru */
+  /* Fall through.  */
 
   return 12;
 }
@@ -9135,7 +9306,7 @@ mips_output_conditional_branch (rtx insn, rtx *operands, int two_operands_p,
                .set macro
                .set reorder
 
-          When generating non-embedded PIC, instead of:
+          When generating PIC, instead of:
 
                j     target
 
@@ -9170,7 +9341,7 @@ mips_output_conditional_branch (rtx insn, rtx *operands, int two_operands_p,
             /* Output delay slot instruction.  */
             rtx insn = final_sequence;
             final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file,
-                             optimize, 0, 1);
+                             optimize, 0, 1, NULL);
             INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
           }
        else
@@ -9189,7 +9360,7 @@ mips_output_conditional_branch (rtx insn, rtx *operands, int two_operands_p,
             /* Output delay slot instruction.  */
             rtx insn = final_sequence;
             final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file,
-                             optimize, 0, 1);
+                             optimize, 0, 1, NULL);
             INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
           }
        else
@@ -9209,24 +9380,121 @@ mips_output_conditional_branch (rtx insn, rtx *operands, int two_operands_p,
   return 0;
 }
 \f
-/* Used to output div or ddiv instruction DIVISION, which has the
-   operands given by OPERANDS.  If we need a divide-by-zero check,
-   output the instruction and return an asm string that traps if
-   operand 2 is zero.  Otherwise just return DIVISION itself.  */
+/* Used to output 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.  */
 
 const char *
 mips_output_division (const char *division, rtx *operands)
 {
+  const char *s;
+
+  s = division;
+  if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
+    {
+      output_asm_insn (s, operands);
+      s = "nop";
+    }
   if (TARGET_CHECK_ZERO_DIV)
     {
-      output_asm_insn (division, operands);
-
       if (TARGET_MIPS16)
-       return "bnez\t%2,1f\n\tbreak\t7\n1:";
+       {
+         output_asm_insn (s, operands);
+         s = "bnez\t%2,1f\n\tbreak\t7\n1:";
+       }
       else
-       return "bne\t%2,%.,1f%#\n\tbreak\t7\n1:";
+       {
+         output_asm_insn ("%(bne\t%2,%.,1f", operands);
+         output_asm_insn (s, operands);
+         s = "break\t7%)\n1:";
+       }
     }
-  return division;
+  return s;
 }
 \f
 /* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL
@@ -9338,20 +9606,6 @@ mips_cpu_info_from_isa (int isa)
   return 0;
 }
 \f
-/* 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.
-
-   On the MIPS, ignore the cost of anti- and output-dependencies.  */
-static int
-mips_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link,
-                 rtx dep ATTRIBUTE_UNUSED, int cost)
-{
-  if (REG_NOTE_KIND (link) != 0)
-    return 0;  /* Anti or output dependence.  */
-  return cost;
-}
-
 /* Implement HARD_REGNO_NREGS.  The size of FP registers are controlled
    by UNITS_PER_FPREG.  All other registers are word sized.  */
 
@@ -9364,33 +9618,289 @@ mips_hard_regno_nregs (int regno, enum machine_mode mode)
     return ((GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG);
 }
 
-/* Implement RETURN_IN_MEMORY.  Under the old (i.e., 32 and O64 ABIs)
+/* Implement TARGET_RETURN_IN_MEMORY.  Under the old (i.e., 32 and O64 ABIs)
    all BLKmode objects are returned in memory.  Under the new (N32 and
    64-bit MIPS ABIs) small structures are returned in a register.
    Objects with varying size must still be returned in memory, of
    course.  */
 
-int
-mips_return_in_memory (tree type)
+static bool
+mips_return_in_memory (tree type, tree fndecl ATTRIBUTE_UNUSED)
 {
-  if (mips_abi == ABI_32 || mips_abi == ABI_O64)
+  if (TARGET_OLDABI)
     return (TYPE_MODE (type) == BLKmode);
   else
     return ((int_size_in_bytes (type) > (2 * UNITS_PER_WORD))
            || (int_size_in_bytes (type) == -1));
 }
 
+static bool
+mips_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
+{
+  return !TARGET_OLDABI;
+}
+\f
+/* Return true if INSN is a multiply-add or multiply-subtract
+   instruction and PREV assigns to the accumulator operand.  */
+
+bool
+mips_linked_madd_p (rtx prev, rtx insn)
+{
+  rtx x;
+
+  x = single_set (insn);
+  if (x == 0)
+    return false;
+
+  x = SET_SRC (x);
+
+  if (GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 0)) == MULT
+      && reg_set_p (XEXP (x, 1), prev))
+    return true;
+
+  if (GET_CODE (x) == MINUS
+      && GET_CODE (XEXP (x, 1)) == MULT
+      && reg_set_p (XEXP (x, 0), prev))
+    return true;
+
+  return false;
+}
+\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
+mips_macc_chains_record (rtx insn)
+{
+  if (get_attr_may_clobber_hilo (insn))
+    mips_macc_chains_last_hilo = insn;
+}
+
+/* A TUNE_MACC_CHAINS helper function.  Search ready queue READY, which
+   has NREADY elements, looking for a multiply-add or multiply-subtract
+   instruction that is cumulative with mips_macc_chains_last_hilo.
+   If there is one, promote it ahead of anything else that might
+   clobber hi or lo.  */
+
+static void
+mips_macc_chains_reorder (rtx *ready, int nready)
+{
+  int i, j;
+
+  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;
+
+/* 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.  */
+
+static void
+vr4130_true_reg_dependence_p_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+  rtx *insn_ptr = data;
+  if (REG_P (x)
+      && *insn_ptr != 0
+      && reg_referenced_p (x, PATTERN (*insn_ptr)))
+    *insn_ptr = 0;
+}
+
+/* Return true if there is true register dependence between vr4130_last_insn
+   and INSN.  */
+
+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;
+}
+
+/* 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.  */
+
+static bool
+vr4130_swap_insns_p (rtx insn1, rtx insn2)
+{
+  rtx dep;
+
+  /* Check for the following case:
+
+     1) there is some other instruction X with an anti dependence on INSN1;
+     2) X has a higher priority than INSN2; and
+     3) X is an arithmetic instruction (and thus has no unit restrictions).
+
+     If INSN1 is the last instruction blocking X, it would better to
+     choose (INSN1, X) over (INSN2, INSN1).  */
+  for (dep = INSN_DEPEND (insn1); dep != 0; dep = XEXP (dep, 1))
+    if (REG_NOTE_KIND (dep) == REG_DEP_ANTI
+       && INSN_PRIORITY (XEXP (dep, 0)) > INSN_PRIORITY (insn2)
+       && recog_memoized (XEXP (dep, 0)) >= 0
+       && get_attr_vr4130_class (XEXP (dep, 0)) == VR4130_CLASS_ALU)
+      return false;
+
+  if (vr4130_last_insn != 0
+      && recog_memoized (insn1) >= 0
+      && recog_memoized (insn2) >= 0)
+    {
+      /* See whether INSN1 and INSN2 use different execution units,
+        or if they are both ALU-type instructions.  If so, they can
+        probably execute in parallel.  */
+      enum attr_vr4130_class class1 = get_attr_vr4130_class (insn1);
+      enum attr_vr4130_class class2 = get_attr_vr4130_class (insn2);
+      if (class1 != class2 || class1 == VR4130_CLASS_ALU)
+       {
+         /* If only one of the instructions has a dependence on
+            vr4130_last_insn, prefer to schedule the other one first.  */
+         bool dep1 = vr4130_true_reg_dependence_p (insn1);
+         bool dep2 = vr4130_true_reg_dependence_p (insn2);
+         if (dep1 != dep2)
+           return dep1;
+
+         /* Prefer to schedule INSN2 ahead of INSN1 if vr4130_last_insn
+            is not an ALU-type instruction and if INSN1 uses the same
+            execution unit.  (Note that if this condition holds, we already
+            know that INSN2 uses a different execution unit.)  */
+         if (class1 != VR4130_CLASS_ALU
+             && recog_memoized (vr4130_last_insn) >= 0
+             && class1 == get_attr_vr4130_class (vr4130_last_insn))
+           return true;
+       }
+    }
+  return false;
+}
+
+/* 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
+vr4130_reorder (rtx *ready, int nready)
+{
+  if (vr4130_swap_insns_p (ready[nready - 1], ready[nready - 2]))
+    mips_promote_ready (ready, nready - 2, nready - 1);
+}
+\f
+/* 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.  */
+
+static void
+mips_promote_ready (rtx *ready, int lower, int higher)
+{
+  rtx new_head;
+  int i;
+
+  new_head = ready[lower];
+  for (i = lower; i < higher; i++)
+    ready[i] = ready[i + 1];
+  ready[i] = new_head;
+}
+
+/* Implement TARGET_SCHED_REORDER.  */
+
+static int
+mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                   rtx *ready, int *nreadyp, int cycle)
+{
+  if (!reload_completed && TUNE_MACC_CHAINS)
+    {
+      if (cycle == 0)
+       mips_macc_chains_last_hilo = 0;
+      if (*nreadyp > 0)
+       mips_macc_chains_reorder (ready, *nreadyp);
+    }
+  if (reload_completed && TUNE_MIPS4130 && !TARGET_VR4130_ALIGN)
+    {
+      if (cycle == 0)
+       vr4130_last_insn = 0;
+      if (*nreadyp > 1)
+       vr4130_reorder (ready, *nreadyp);
+    }
+  return mips_issue_rate ();
+}
+
+/* Implement TARGET_SCHED_VARIABLE_ISSUE.  */
+
+static int
+mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+                    rtx insn, int more)
+{
+  switch (GET_CODE (PATTERN (insn)))
+    {
+    case USE:
+    case CLOBBER:
+      /* Don't count USEs and CLOBBERs against the issue rate.  */
+      break;
+
+    default:
+      more--;
+      if (!reload_completed && TUNE_MACC_CHAINS)
+       mips_macc_chains_record (insn);
+      vr4130_last_insn = insn;
+      break;
+    }
+  return more;
+}
+\f
+/* Implement TARGET_SCHED_ADJUST_COST.  We assume that anti and output
+   dependencies have no cost.  */
+
+static int
+mips_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link,
+                 rtx dep ATTRIBUTE_UNUSED, int cost)
+{
+  if (REG_NOTE_KIND (link) != 0)
+    return 0;
+  return cost;
+}
+
+/* Return the number of instructions that can be issued per cycle.  */
+
 static int
 mips_issue_rate (void)
 {
   switch (mips_tune)
     {
+    case PROCESSOR_R4130:
     case PROCESSOR_R5400:
     case PROCESSOR_R5500:
     case PROCESSOR_R7000:
     case PROCESSOR_R9000:
       return 2;
 
+    case PROCESSOR_SB1:
+      /* This is actually 4, but we get better performance if we claim 3.
+        This is partly because of unwanted speculative code motion with the
+        larger number, and partly because in most common cases we can't
+        reach the theoretical max of 4.  */
+      return 3;
+
     default:
       return 1;
     }
@@ -9407,10 +9917,13 @@ mips_use_dfa_pipeline_interface (void)
 {
   switch (mips_tune)
     {
+    case PROCESSOR_R3000:
+    case PROCESSOR_R4130:
     case PROCESSOR_R5400:
     case PROCESSOR_R5500:
     case PROCESSOR_R7000:
     case PROCESSOR_R9000:
+    case PROCESSOR_SB1:
     case PROCESSOR_SR71000:
       return true;
 
@@ -9419,6 +9932,19 @@ mips_use_dfa_pipeline_interface (void)
     }
 }
 
+/* Implements TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD.  This should
+   be as wide as the scheduling freedom in the DFA.  */
+
+static int
+mips_multipass_dfa_lookahead (void)
+{
+  /* Can schedule up to 4 of the 6 function units in any one cycle.  */
+  if (mips_tune == PROCESSOR_SB1)
+    return 4;
+
+  return 0;
+}
+\f
 
 const char *
 mips_emit_prefetch (rtx *operands)
@@ -9428,7 +9954,7 @@ mips_emit_prefetch (rtx *operands)
   int indexed = GET_CODE (operands[3]) == REG;
   int code;
   char buffer[30];
-  
+
   if (locality <= 0)
     code = (write ? 5 : 4);    /* store_streamed / load_streamed.  */
   else if (locality <= 2)
@@ -9443,11 +9969,11 @@ mips_emit_prefetch (rtx *operands)
 
 
 \f
-#ifdef TARGET_IRIX6
+#if TARGET_IRIX
 /* Output assembly to switch to section NAME with attribute FLAGS.  */
 
 static void
-iris6_asm_named_section_1 (const char *name, unsigned int flags,
+irix_asm_named_section_1 (const char *name, unsigned int flags,
                           unsigned int align)
 {
   unsigned int sh_type, sh_flags, sh_entsize;
@@ -9480,92 +10006,103 @@ iris6_asm_named_section_1 (const char *name, unsigned int flags,
 }
 
 static void
-iris6_asm_named_section (const char *name, unsigned int flags)
+irix_asm_named_section (const char *name, unsigned int flags)
 {
-  iris6_asm_named_section_1 (name, flags, 0);
+  if (TARGET_SGI_O32_AS)
+    default_no_named_section (name, flags);
+  else if (mips_abi == ABI_32 && TARGET_GAS)
+    default_elf_asm_named_section (name, flags);
+  else
+    irix_asm_named_section_1 (name, flags, 0);
 }
 
 /* In addition to emitting a .align directive, record the maximum
    alignment requested for the current section.  */
 
-struct GTY (()) iris_section_align_entry
+struct irix_section_align_entry GTY (())
 {
   const char *name;
   unsigned int log;
   unsigned int flags;
 };
 
-static htab_t iris_section_align_htab;
-static FILE *iris_orig_asm_out_file;
+static htab_t irix_section_align_htab;
+static FILE *irix_orig_asm_out_file;
 
 static int
-iris_section_align_entry_eq (const void *p1, const void *p2)
+irix_section_align_entry_eq (const void *p1, const void *p2)
 {
-  const struct iris_section_align_entry *old = p1;
+  const struct irix_section_align_entry *old = p1;
   const char *new = p2;
 
   return strcmp (old->name, new) == 0;
 }
 
 static hashval_t
-iris_section_align_entry_hash (const void *p)
+irix_section_align_entry_hash (const void *p)
 {
-  const struct iris_section_align_entry *old = p;
+  const struct irix_section_align_entry *old = p;
   return htab_hash_string (old->name);
 }
 
 void
-iris6_asm_output_align (FILE *file, unsigned int log)
+irix_asm_output_align (FILE *file, unsigned int log)
 {
   const char *section = current_section_name ();
-  struct iris_section_align_entry **slot, *entry;
+  struct irix_section_align_entry **slot, *entry;
 
-  if (! section)
-    abort ();
-
-  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)
+  if (mips_abi != ABI_32)
     {
-      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 ();
+      if (! section)
+       abort ();
+
+      slot = (struct irix_section_align_entry **)
+       htab_find_slot_with_hash (irix_section_align_htab, section,
+                                 htab_hash_string (section), INSERT);
+      entry = *slot;
+      if (! entry)
+       {
+         entry = (struct irix_section_align_entry *)
+           xmalloc (sizeof (struct irix_section_align_entry));
+         *slot = entry;
+         entry->name = section;
+         entry->log = log;
+         entry->flags = current_section_flags ();
+       }
+      else if (entry->log < log)
+       entry->log = log;
     }
-  else if (entry->log < log)
-    entry->log = log;
 
   fprintf (file, "\t.align\t%u\n", log);
 }
 
-/* The Iris assembler does not record alignment from .align directives,
+/* The IRIX assembler does not record alignment from .align directives,
    but takes it from the first .section directive seen.  Play file
    switching games so that we can emit a .section directive at the
    beginning of the file with the proper alignment attached.  */
 
 static void
-iris6_file_start (void)
+irix_file_start (void)
 {
   mips_file_start ();
 
-  iris_orig_asm_out_file = asm_out_file;
+  if (mips_abi == ABI_32)
+    return;
+
+  irix_orig_asm_out_file = asm_out_file;
   asm_out_file = tmpfile ();
 
-  iris_section_align_htab = htab_create (31, iris_section_align_entry_hash,
-                                        iris_section_align_entry_eq, NULL);
+  irix_section_align_htab = htab_create (31, irix_section_align_entry_hash,
+                                        irix_section_align_entry_eq, NULL);
 }
 
 static int
-iris6_section_align_1 (void **slot, void *data ATTRIBUTE_UNUSED)
+irix_section_align_1 (void **slot, void *data ATTRIBUTE_UNUSED)
 {
-  const struct iris_section_align_entry *entry
-    = *(const struct iris_section_align_entry **) slot;
+  const struct irix_section_align_entry *entry
+    = *(const struct irix_section_align_entry **) slot;
 
-  iris6_asm_named_section_1 (entry->name, entry->flags, 1 << entry->log);
+  irix_asm_named_section_1 (entry->name, entry->flags, 1 << entry->log);
   return 1;
 }
 
@@ -9590,16 +10127,19 @@ copy_file_data (FILE *to, FILE *from)
 }
 
 static void
-iris6_file_end (void)
+irix_file_end (void)
 {
-  /* Emit section directives with the proper alignment at the top of the
-     real output file.  */
-  FILE *temp = asm_out_file;
-  asm_out_file = iris_orig_asm_out_file;
-  htab_traverse (iris_section_align_htab, iris6_section_align_1, NULL);
+  if (mips_abi != ABI_32)
+    {
+      /* Emit section directives with the proper alignment at the top of the
+        real output file.  */
+      FILE *temp = asm_out_file;
+      asm_out_file = irix_orig_asm_out_file;
+      htab_traverse (irix_section_align_htab, irix_section_align_1, NULL);
 
-  /* Copy the data emitted to the temp file to the real output file.  */
-  copy_file_data (asm_out_file, temp);
+      /* Copy the data emitted to the temp file to the real output file.  */
+      copy_file_data (asm_out_file, temp);
+    }
 
   mips_file_end ();
 }
@@ -9610,7 +10150,7 @@ iris6_file_end (void)
    default code.  */
 
 static unsigned int
-iris6_section_type_flags (tree decl, const char *section, int relocs_p)
+irix_section_type_flags (tree decl, const char *section, int relocs_p)
 {
   unsigned int flags;
 
@@ -9625,7 +10165,6 @@ iris6_section_type_flags (tree decl, const char *section, int relocs_p)
   return flags;
 }
 
-
-#endif /* TARGET_IRIX6 */
+#endif /* TARGET_IRIX */
 
 #include "gt-mips.h"