OSDN Git Service

x
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.c
index 3d34ddf..cacbf1f 100644 (file)
@@ -1,9 +1,9 @@
 /* Subroutines for insn-output.c for MIPS
+   Copyright (C) 1989, 90, 91, 93-95, 1996 Free Software Foundation, Inc.
    Contributed by A. Lichnewsky, lich@inria.inria.fr.
-   Changes by     Michael Meissner, meissner@osf.org.
+   Changes by Michael Meissner, meissner@osf.org.
    64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
    Brendan Eich, brendan@microunity.com.
-   Copyright (C) 1989, 1990, 1991, 1993 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -19,7 +19,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 /* ??? The TARGET_FP_CALL_32 macros are intended to simulate a 32 bit
    calling convention in 64 bit mode.  It doesn't work though, and should
@@ -81,7 +82,6 @@ extern tree   lookup_name ();
 extern void   pfatal_with_name ();
 extern void   warning ();
 
-extern tree   current_function_decl;
 extern FILE  *asm_out_file;
 
 /* Enumeration for all of the relational tests, so that we can build
@@ -117,7 +117,7 @@ int num_source_filenames = 0;
    start and end boundaries).  */
 int sdb_label_count = 0;
 
-/* Next label # for each statment for Silicon Graphics IRIS systems. */
+/* Next label # for each statement for Silicon Graphics IRIS systems. */
 int sym_lineno = 0;
 
 /* Non-zero if inside of a function, because the stupid MIPS asm can't
@@ -192,9 +192,19 @@ enum processor_type mips_cpu;
 /* which instruction set architecture to use.  */
 int mips_isa;
 
+#ifdef MIPS_ABI_DEFAULT
+/* which ABI to use.  This is defined to a constant in mips.h if the target
+   doesn't support multiple ABIs.  */
+enum mips_abi_type mips_abi;
+#endif
+
 /* Strings to hold which cpu and instruction set architecture to use.  */
 char *mips_cpu_string;         /* for -mcpu=<xxx> */
-char *mips_isa_string;         /* for -mips{1,2,3} */
+char *mips_isa_string;         /* for -mips{1,2,3,4} */
+char *mips_abi_string;         /* for -mabi={o32,32,n32,n64,64,eabi} */
+
+/* If TRUE, we split addresses into their high and low parts in the RTL.  */
+int mips_split_addresses;
 
 /* Generating calls to position independent functions?  */
 enum mips_abicalls_type mips_abicalls;
@@ -204,12 +214,6 @@ enum mips_abicalls_type mips_abicalls;
    initialized in override_options.  */
 REAL_VALUE_TYPE dfhigh, dflow, sfhigh, sflow;
 
-/* Array to RTX class classification.  At present, we care about
-   whether the operator is an add-type operator, or a divide/modulus,
-   and if divide/modulus, whether it is unsigned.  This is for the
-   peephole code.  */
-char mips_rtx_classify[NUM_RTX_CODE];
-
 /* Array giving truth value on whether or not a given hard register
    can support a given mode.  */
 char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
@@ -224,6 +228,11 @@ struct mips_frame_info zero_frame_info;
    for -mgpopt.  */
 static char *temp_filename;
 
+/* Pseudo-reg holding the address of the current function when
+   generating embedded PIC code.  Created by LEGITIMIZE_ADDRESS, used
+   by mips_finalize_pic if it was created.  */
+rtx embedded_pic_fnaddr_rtx;
+
 /* List of all MIPS punctuation characters used by print_operand.  */
 char mips_print_operand_punct[256];
 
@@ -247,7 +256,8 @@ char mips_reg_names[][8] =
  "$f8",  "$f9",  "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
  "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
  "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
- "hi",   "lo",   "$fcr31"
+ "hi",   "lo",   "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
+ "$fcc5","$fcc6","$fcc7","$rap"
 };
 
 /* Mips software names for the registers, used to overwrite the
@@ -263,7 +273,8 @@ char mips_sw_reg_names[][8] =
   "$f8",  "$f9",  "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
   "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
   "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
-  "hi",   "lo",   "$fcr31"
+  "hi",   "lo",   "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
+  "$fcc5","$fcc6","$fcc7","$rap"
 };
 
 /* Map hard register number to register class */
@@ -285,7 +296,9 @@ enum reg_class mips_regno_to_class[] =
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
-  HI_REG,      LO_REG,         ST_REGS
+  HI_REG,      LO_REG,         HILO_REG,       ST_REGS,
+  ST_REGS,     ST_REGS,        ST_REGS,        ST_REGS,
+  ST_REGS,     ST_REGS,        ST_REGS,        GR_REGS
 };
 
 /* Map register constraint character to register class.  */
@@ -410,7 +423,7 @@ small_int (op, mode)
   return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
 }
 
-/* Return truth value of whether OP is an integer which is too big to
+/* Return truth value of whether OP is a 32 bit integer which is too big to
    be loaded with one instruction.  */
 
 int
@@ -430,9 +443,7 @@ large_int (op, mode)
   if (((unsigned long)(value + 32768)) <= 32767)       /* subu reg,$r0,value */
     return FALSE;
 
-  if ((value & 0x0000ffff) == 0                                /* lui reg,value>>16 */
-      && ((value & ~2147483647) == 0                   /* signed value */
-         || (value & ~2147483647) == ~2147483647))
+  if ((value & 0x0000ffff) == 0)                       /* lui reg,value>>16 */
     return FALSE;
 
   return TRUE;
@@ -467,29 +478,6 @@ reg_or_0_operand (op, mode)
   return FALSE;
 }
 
-/* Return truth value of whether OP is one of the special multiply/divide
-   registers (hi, lo).  */
-
-int
-md_register_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return (GET_MODE_CLASS (mode) == MODE_INT
-         && GET_CODE (op) == REG
-         && MD_REG_P (REGNO (op)));
-}
-
-/* Return truth value of whether OP is the FP status register.  */
-
-int
-fpsw_register_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return (GET_CODE (op) == REG && ST_REG_P (REGNO (op)));
-}
-
 /* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant.  */
 
 int
@@ -497,10 +485,12 @@ mips_const_double_ok (op, mode)
      rtx op;
      enum machine_mode mode;
 {
+  REAL_VALUE_TYPE d;
+
   if (GET_CODE (op) != CONST_DOUBLE)
     return FALSE;
 
-  if (mode == DImode)
+  if (mode == VOIDmode)
     return TRUE;
 
   if (mode != SFmode && mode != DFmode)
@@ -509,35 +499,68 @@ mips_const_double_ok (op, mode)
   if (op == CONST0_RTX (mode))
     return TRUE;
 
-  if (TARGET_MIPS_AS)          /* gas doesn't like li.d/li.s yet */
-    {
-      REAL_VALUE_TYPE d;
+  /* ??? li.s does not work right with SGI's Irix 6 assembler.  */
+  if (mips_abi != ABI_32 && mips_abi != ABI_EABI)
+    return FALSE;
 
-      REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+  REAL_VALUE_FROM_CONST_DOUBLE (d, op);
 
-      if (REAL_VALUE_ISNAN (d))
-       return FALSE;
+  if (REAL_VALUE_ISNAN (d))
+    return FALSE;
 
-      if (REAL_VALUE_NEGATIVE (d))
-       d = REAL_VALUE_NEGATE (d);
+  if (REAL_VALUE_NEGATIVE (d))
+    d = REAL_VALUE_NEGATE (d);
 
-      if (mode == DFmode)
-       {
-         if (REAL_VALUES_LESS (d, dfhigh)
-             && REAL_VALUES_LESS (dflow, d))
-           return TRUE;
-       }
-      else
-       {
-         if (REAL_VALUES_LESS (d, sfhigh)
-             && REAL_VALUES_LESS (sflow, d))
-           return TRUE;
-       }
+  if (mode == DFmode)
+    {
+      if (REAL_VALUES_LESS (d, dfhigh)
+         && REAL_VALUES_LESS (dflow, d))
+       return TRUE;
+    }
+  else
+    {
+      if (REAL_VALUES_LESS (d, sfhigh)
+         && REAL_VALUES_LESS (sflow, d))
+       return TRUE;
     }
 
   return FALSE;
 }
 
+/* Accept the floating point constant 1 in the appropriate mode.  */
+
+int
+const_float_1_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  REAL_VALUE_TYPE d;
+  static REAL_VALUE_TYPE onedf;
+  static REAL_VALUE_TYPE onesf;
+  static int one_initialized;
+
+  if (GET_CODE (op) != CONST_DOUBLE
+      || mode != GET_MODE (op)
+      || (mode != DFmode && mode != SFmode))
+    return FALSE;
+
+  REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+
+  /* We only initialize these values if we need them, since we will
+     never get called unless mips_isa >= 4.  */
+  if (! one_initialized)
+    {
+      onedf = REAL_VALUE_ATOF ("1.0", DFmode);
+      onesf = REAL_VALUE_ATOF ("1.0", SFmode);
+      one_initialized = TRUE;
+    }
+
+  if (mode == DFmode)
+    return REAL_VALUES_EQUAL (d, onedf);
+  else
+    return REAL_VALUES_EQUAL (d, onesf);
+}
+
 /* Return truth value if a memory operand fits in a single instruction
    (ie, register + small offset).  */
 
@@ -553,6 +576,9 @@ simple_memory_operand (op, mode)
     return FALSE;
 
   /* dword operations really put out 2 instructions, so eliminate them.  */
+  /* ??? This isn't strictly correct.  It is OK to accept multiword modes
+     here, since the length attributes are being set correctly, but only
+     if the address is offsettable.  LO_SUM is not offsettable.  */
   if (GET_MODE_SIZE (GET_MODE (op)) > UNITS_PER_WORD)
     return FALSE;
 
@@ -564,6 +590,7 @@ simple_memory_operand (op, mode)
       break;
 
     case REG:
+    case LO_SUM:
       return TRUE;
 
     case CONST_INT:
@@ -630,7 +657,7 @@ equality_op (op, mode)
   if (mode != GET_MODE (op))
     return FALSE;
 
-  return (classify_op (op, mode) & CLASS_EQUALITY_OP) != 0;
+  return (GET_CODE (op) == EQ || GET_CODE (op) == NE);
 }
 
 /* Return true if the code is a relational operations (EQ, LE, etc.) */
@@ -643,93 +670,238 @@ cmp_op (op, mode)
   if (mode != GET_MODE (op))
     return FALSE;
 
-  return (classify_op (op, mode) & CLASS_CMP_OP) != 0;
+  return (GET_RTX_CLASS (GET_CODE (op)) == '<');
 }
 
+/* Return true if the operand is either the PC or a label_ref.  */
 
-/* Genrecog does not take the type of match_operator into consideration,
-   and would complain about two patterns being the same if the same
-   function is used, so make it believe they are different.  */
+int
+pc_or_label_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (op == pc_rtx)
+    return TRUE;
+
+  if (GET_CODE (op) == LABEL_REF)
+    return TRUE;
+
+  return FALSE;
+}
+
+/* Test for a valid operand for a call instruction.
+   Don't allow the arg pointer register or virtual regs
+   since they may change into reg + const, which the patterns
+   can't handle yet.  */
 
 int
-cmp2_op (op, mode)
+call_insn_operand (op, mode)
      rtx op;
      enum machine_mode mode;
 {
-  if (mode != GET_MODE (op))
-    return FALSE;
+  if (CONSTANT_ADDRESS_P (op)
+      || (GET_CODE (op) == REG && op != arg_pointer_rtx
+         && ! (REGNO (op) >= FIRST_PSEUDO_REGISTER
+               && REGNO (op) <= LAST_VIRTUAL_REGISTER)))
+    return 1;
+  return 0;
+}
 
-  return (classify_op (op, mode) & CLASS_CMP_OP) != 0;
+/* Return true if OPERAND is valid as a source operand for a move
+   instruction.  */
+
+int
+move_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return (general_operand (op, mode)
+         && ! (mips_split_addresses && mips_check_split (op, mode)));
 }
 
-/* Return true if the code is an unsigned relational operations (LEU, etc.) */
+/* Return true if OPERAND is valid as a source operand for movdi.
+   This accepts not only general_operand, but also sign extended
+   constants and registers.  We need to accept sign extended constants
+   in case a sign extended register which is used in an expression,
+   and is equivalent to a constant, is spilled.  */
 
 int
-uns_cmp_op (op,mode)
+movdi_operand (op, mode)
      rtx op;
      enum machine_mode mode;
 {
-  if (mode != GET_MODE (op))
-    return FALSE;
+  if (TARGET_64BIT
+      && mode == DImode
+      && GET_CODE (op) == SIGN_EXTEND
+      && GET_MODE (op) == DImode
+      && (GET_MODE (XEXP (op, 0)) == SImode
+         || (GET_CODE (XEXP (op, 0)) == CONST_INT
+             && GET_MODE (XEXP (op, 0)) == VOIDmode))
+      && (register_operand (XEXP (op, 0), SImode)
+         || immediate_operand (XEXP (op, 0), SImode)))
+    return 1;
 
-  return (classify_op (op, mode) & CLASS_UNS_CMP_OP) == CLASS_UNS_CMP_OP;
+  return general_operand (op, mode);
 }
 
-/* Return true if the code is a relational operation FP can use.  */
+/* Like register_operand, but when in 64 bit mode also accept a sign
+   extend of a 32 bit register, since the value is known to be already
+   sign extended.  */
 
 int
-fcmp_op (op, mode)
+se_register_operand (op, mode)
      rtx op;
      enum machine_mode mode;
 {
-  if (mode != GET_MODE (op))
-    return FALSE;
+  if (TARGET_64BIT
+      && mode == DImode
+      && GET_CODE (op) == SIGN_EXTEND
+      && GET_MODE (op) == DImode
+      && GET_MODE (XEXP (op, 0)) == SImode
+      && register_operand (XEXP (op, 0), SImode))
+    return 1;
 
-  return (classify_op (op, mode) & CLASS_FCMP_OP) != 0;
+  return register_operand (op, mode);
 }
 
+/* Like reg_or_0_operand, but when in 64 bit mode also accept a sign
+   extend of a 32 bit register, since the value is known to be already
+   sign extended.  */
 
-/* Return true if the operand is either the PC or a label_ref.  */
+int
+se_reg_or_0_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (TARGET_64BIT
+      && mode == DImode
+      && GET_CODE (op) == SIGN_EXTEND
+      && GET_MODE (op) == DImode
+      && GET_MODE (XEXP (op, 0)) == SImode
+      && register_operand (XEXP (op, 0), SImode))
+    return 1;
+
+  return reg_or_0_operand (op, mode);
+}
+
+/* Like uns_arith_operand, but when in 64 bit mode also accept a sign
+   extend of a 32 bit register, since the value is known to be already
+   sign extended.  */
 
 int
-pc_or_label_operand (op, mode)
+se_uns_arith_operand (op, mode)
      rtx op;
      enum machine_mode mode;
 {
-  if (op == pc_rtx)
-    return TRUE;
+  if (TARGET_64BIT
+      && mode == DImode
+      && GET_CODE (op) == SIGN_EXTEND
+      && GET_MODE (op) == DImode
+      && GET_MODE (XEXP (op, 0)) == SImode
+      && register_operand (XEXP (op, 0), SImode))
+    return 1;
 
-  if (GET_CODE (op) == LABEL_REF)
-    return TRUE;
+  return uns_arith_operand (op, mode);
+}
 
-  return FALSE;
+/* Like arith_operand, but when in 64 bit mode also accept a sign
+   extend of a 32 bit register, since the value is known to be already
+   sign extended.  */
+
+int
+se_arith_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (TARGET_64BIT
+      && mode == DImode
+      && GET_CODE (op) == SIGN_EXTEND
+      && GET_MODE (op) == DImode
+      && GET_MODE (XEXP (op, 0)) == SImode
+      && register_operand (XEXP (op, 0), SImode))
+    return 1;
+
+  return arith_operand (op, mode);
 }
 
-/* Test for a valid operand for a call instruction.
-   Don't allow the arg pointer register or virtual regs
-   since they may change into reg + const, which the patterns
-   can't handle yet.  */
+/* Like nonmemory_operand, but when in 64 bit mode also accept a sign
+   extend of a 32 bit register, since the value is known to be already
+   sign extended.  */
 
 int
-call_insn_operand (op, mode)
+se_nonmemory_operand (op, mode)
      rtx op;
      enum machine_mode mode;
 {
-  if (GET_CODE (op) == MEM
-      && (CONSTANT_ADDRESS_P (XEXP (op, 0))
-         || (GET_CODE (XEXP (op, 0)) == REG
-             && XEXP (op, 0) != arg_pointer_rtx
-             && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER
-                  && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER))))
+  if (TARGET_64BIT
+      && mode == DImode
+      && GET_CODE (op) == SIGN_EXTEND
+      && GET_MODE (op) == DImode
+      && GET_MODE (XEXP (op, 0)) == SImode
+      && register_operand (XEXP (op, 0), SImode))
     return 1;
+
+  return nonmemory_operand (op, mode);
+}
+
+/* Like nonimmediate_operand, but when in 64 bit mode also accept a
+   sign extend of a 32 bit register, since the value is known to be
+   already sign extended.  */
+
+int
+se_nonimmediate_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (TARGET_64BIT
+      && mode == DImode
+      && GET_CODE (op) == SIGN_EXTEND
+      && GET_MODE (op) == DImode
+      && GET_MODE (XEXP (op, 0)) == SImode
+      && register_operand (XEXP (op, 0), SImode))
+    return 1;
+
+  return nonimmediate_operand (op, mode);
+}
+
+/* Return true if we split the address into high and low parts.  */
+
+/* ??? We should also handle reg+array somewhere.  We get four
+   instructions currently, lui %hi/addui %lo/addui reg/lw.  Better is
+   lui %hi/addui reg/lw %lo.  Fixing GO_IF_LEGITIMATE_ADDRESS to accept
+   (plus (reg) (symbol_ref)) doesn't work because the SYMBOL_REF is broken
+   out of the address, then we have 4 instructions to combine.  Perhaps
+   add a 3->2 define_split for combine.  */
+
+/* ??? We could also split a CONST_INT here if it is a large_int().
+   However, it doesn't seem to be very useful to have %hi(constant).
+   We would be better off by doing the masking ourselves and then putting
+   the explicit high part of the constant in the RTL.  This will give better
+   optimization.  Also, %hi(constant) needs assembler changes to work.
+   There is already a define_split that does this.  */
+
+int
+mips_check_split (address, mode)
+     rtx address;
+     enum machine_mode mode;
+{     
+  /* ??? This is the same check used in simple_memory_operand.
+     We use it here because LO_SUM is not offsettable.  */
+  if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+    return 0;
+
+  if ((GET_CODE (address) == SYMBOL_REF && ! SYMBOL_REF_FLAG (address))
+      || (GET_CODE (address) == CONST
+         && GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF
+         && ! SYMBOL_REF_FLAG (XEXP (XEXP (address, 0), 0)))
+      || GET_CODE (address) == LABEL_REF)
+    return 1;
+
   return 0;
 }
 \f
-/* Return an operand string if the given instruction's delay slot or
-   wrap it in a .set noreorder section.  This is for filling delay
-   slots on load type instructions under GAS, which does no reordering
-   on its own.  For the MIPS assembler, all we do is update the filled
-   delay slot statistics.
+/* Returns an operand string for the given instruction's delay slot,
+   after updating filled delay slot statistics.
 
    We assume that operands[0] is the target register that is set.
 
@@ -737,6 +909,9 @@ call_insn_operand (op, mode)
    to FINAL_PRESCAN_INSN, and we just set the global variables that
    it needs.  */
 
+/* ??? This function no longer does anything useful, because final_prescan_insn
+   now will never emit a nop.  */
+
 char *
 mips_fill_delay_slot (ret, type, operands, cur_insn)
      char *ret;                        /* normal string to return */
@@ -792,7 +967,7 @@ mips_fill_delay_slot (ret, type, operands, cur_insn)
   dslots_number_nops = num_nops;
   mips_load_reg = set_reg;
   if (GET_MODE_SIZE (mode)
-      > (FP_REG_P (set_reg) ? UNITS_PER_FPREG : UNITS_PER_WORD))
+      > (FP_REG_P (REGNO (set_reg)) ? UNITS_PER_FPREG : UNITS_PER_WORD))
     mips_load_reg2 = gen_rtx (REG, SImode, REGNO (set_reg) + 1);
   else
     mips_load_reg2 = 0;
@@ -808,9 +983,6 @@ mips_fill_delay_slot (ret, type, operands, cur_insn)
       mips_load_reg4 = 0;
     }
 
-  if (TARGET_GAS && set_noreorder++ == 0)
-    fputs ("\t.set\tnoreorder\n", asm_out_file);
-
   return ret;
 }
 
@@ -850,6 +1022,7 @@ mips_count_memory_refs (op, num)
 
        case REG:
        case CONST_INT:
+       case LO_SUM:
          break;
 
        case PLUS:
@@ -931,6 +1104,42 @@ mips_count_memory_refs (op, num)
 }
 
 \f
+/* Return RTL for the offset from the current function to the
+   argument.  */
+
+rtx
+embedded_pic_offset (x)
+     rtx x;
+{
+  if (embedded_pic_fnaddr_rtx == NULL)
+    {
+      rtx seq;
+
+      embedded_pic_fnaddr_rtx = gen_reg_rtx (Pmode);
+      
+      /* Output code at function start to initialize the pseudo-reg.  */
+      /* ??? We used to do this in FINALIZE_PIC, but that does not work for
+        inline functions, because it is called after RTL for the function
+        has been copied.  The pseudo-reg in embedded_pic_fnaddr_rtx however
+        does not get copied, and ends up not matching the rest of the RTL.
+        This solution works, but means that we get unnecessary code to
+        initialize this value every time a function is inlined into another
+        function.  */
+      start_sequence ();
+      emit_insn (gen_get_fnaddr (embedded_pic_fnaddr_rtx,
+                                XEXP (DECL_RTL (current_function_decl), 0)));
+      seq = gen_sequence ();
+      end_sequence ();
+      push_topmost_sequence ();
+      emit_insn_after (seq, get_insns ());
+      pop_topmost_sequence ();
+    }
+
+  return gen_rtx (CONST, Pmode,
+                 gen_rtx (MINUS, Pmode, x,
+                          XEXP (DECL_RTL (current_function_decl), 0)));
+}
+
 /* Return the appropriate instructions to move one operand to another.  */
 
 char *
@@ -963,6 +1172,10 @@ mips_move_1word (operands, insn, unsignedp)
       code1 = GET_CODE (op1);
     }
 
+  /* For our purposes, a condition code mode is the same as SImode.  */
+  if (mode == CCmode)
+    mode = SImode;
+
   if (code0 == REG)
     {
       int regno0 = REGNO (op0) + subreg_word0;
@@ -984,16 +1197,22 @@ mips_move_1word (operands, insn, unsignedp)
              else if (MD_REG_P (regno1))
                {
                  delay = DELAY_HILO;
-                 ret = "mf%1\t%0";
+                 if (regno1 != HILO_REGNUM)
+                   ret = "mf%1\t%0";
+                 else
+                   ret = "mflo\t%0";
                }
 
+             else if (ST_REG_P (regno1) && mips_isa >= 4)
+               ret = "li\t%0,1\n\tmovf\t%0,%.,%1";
+
              else
                {
                  delay = DELAY_LOAD;
                  if (FP_REG_P (regno1))
                    ret = "mfc1\t%0,%1";
 
-                 else if (regno1 == FPSW_REGNUM)
+                 else if (regno1 == FPSW_REGNUM && mips_isa < 4)
                    ret = "cfc1\t%0,$31";
                }
            }
@@ -1015,11 +1234,12 @@ mips_move_1word (operands, insn, unsignedp)
              if (GP_REG_P (regno1))
                {
                  delay = DELAY_HILO;
-                 ret = "mt%0\t%1";
+                 if (regno0 != HILO_REGNUM)
+                   ret = "mt%0\t%1";
                }
            }
 
-         else if (regno0 == FPSW_REGNUM)
+         else if (regno0 == FPSW_REGNUM && mips_isa < 4)
            {
              if (GP_REG_P (regno1))
                {
@@ -1048,6 +1268,7 @@ mips_move_1word (operands, insn, unsignedp)
                  ret = "lw\t%0,%1";
                  break;
                case SImode:
+               case CCmode:
                  ret = ((unsignedp && TARGET_64BIT)
                         ? "lwu\t%0,%1"
                         : "lw\t%0,%1");
@@ -1075,8 +1296,18 @@ mips_move_1word (operands, insn, unsignedp)
            }
        }
 
-      else if (code1 == CONST_INT)
+      else if (code1 == CONST_INT
+              || (code1 == CONST_DOUBLE
+                  && GET_MODE (op1) == VOIDmode))
        {
+         if (code1 == CONST_DOUBLE)
+           {
+             /* This can happen when storing constants into long long
+                 bitfields.  Just store the least significant word of
+                 the value.  */
+             operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
+           }
+
          if (INTVAL (op1) == 0)
            {
              if (GP_REG_P (regno0))
@@ -1087,10 +1318,18 @@ mips_move_1word (operands, insn, unsignedp)
                  delay = DELAY_LOAD;
                  ret = "mtc1\t%z1,%0";
                }
+
+             else if (MD_REG_P (regno0))
+               {
+                 delay = DELAY_HILO;
+                 ret = "mt%0\t%.";
+               }
            }
 
          else if (GP_REG_P (regno0))
-           ret = (INTVAL (op1) < 0) ? "li\t%0,%1\t\t\t# %X1" : "li\t%0,%X1\t\t# %1";
+           /* Don't use X format, because that will give out of range
+              numbers for 64 bit host and 32 bit target.  */
+           ret = "li\t%0,%1\t\t\t# %X1";
        }
 
       else if (code1 == CONST_DOUBLE && mode == SFmode)
@@ -1184,6 +1423,12 @@ mips_move_1word (operands, insn, unsignedp)
          operands[3] = add_op1;
          ret = "add%:\t%0,%2,%3";
        }
+
+      else if (code1 == HIGH)
+       {
+         operands[1] = XEXP (op1, 0);
+         ret = "lui\t%0,%%hi(%1)";
+       }
     }
 
   else if (code0 == MEM)
@@ -1282,6 +1527,12 @@ mips_move_2words (operands, insn)
       code0 = GET_CODE (op0);
     }
 
+  if (code1 == SIGN_EXTEND)
+    {
+      op1 = XEXP (op1, 0);
+      code1 = GET_CODE (op1);
+    }
+
   while (code1 == SUBREG)
     {
       subreg_word1 += SUBREG_WORD (op1);
@@ -1289,6 +1540,12 @@ mips_move_2words (operands, insn)
       code1 = GET_CODE (op1);
     }
       
+  /* Sanity check.  */
+  if (GET_CODE (operands[1]) == SIGN_EXTEND
+      && code1 != REG
+      && code1 != CONST_INT)
+    abort ();
+
   if (code0 == REG)
     {
       int regno0 = REGNO (op0) + subreg_word0;
@@ -1348,7 +1605,12 @@ mips_move_2words (operands, insn)
            {
              delay = DELAY_HILO;
              if (TARGET_64BIT)
-               ret = "mt%0\t%1";
+               {
+                 if (regno0 != HILO_REGNUM)
+                   ret = "mt%0\t%1";
+                 else if (regno1 == 0)
+                   ret = "mtlo\t%.\n\tmthi\t%.";
+               }
              else
                ret = "mthi\t%M1\n\tmtlo\t%L1";
            }
@@ -1357,7 +1619,10 @@ mips_move_2words (operands, insn)
            {
              delay = DELAY_HILO;
              if (TARGET_64BIT)
-               ret = "mf%1\t%0";
+               {
+                 if (regno1 != HILO_REGNUM)
+                   ret = "mf%1\t%0";
+               }
              else
                ret = "mfhi\t%M0\n\tmflo\t%L0";
            }
@@ -1387,9 +1652,8 @@ mips_move_2words (operands, insn)
                    {
                      if (TARGET_FLOAT64 && !TARGET_64BIT)
                        {
-                         operands[2] = GEN_INT (CONST_DOUBLE_LOW (op1));
-                         operands[3] = GEN_INT (CONST_DOUBLE_HIGH (op1));
-                         ret = "li\t%M0,%3\n\tli\t%L0,%2";
+                         split_double (op1, operands + 2, operands + 3);
+                         ret = "li\t%0,%2\n\tli\t%D0,%3";
                        }
                      else
                        ret = "li.d\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32";
@@ -1399,14 +1663,13 @@ mips_move_2words (operands, insn)
                    ret = "li.d\t%0,%1";
                }
 
-             else if (TARGET_FLOAT64)
-               ret = "li\t%0,%1";
+             else if (TARGET_64BIT)
+               ret = "dli\t%0,%1";
 
              else
                {
-                 operands[2] = GEN_INT (CONST_DOUBLE_LOW (op1));
-                 operands[3] = GEN_INT (CONST_DOUBLE_HIGH (op1));
-                 ret = "li\t%M0,%3\n\tli\t%L0,%2";
+                 split_double (op1, operands + 2, operands + 3);
+                 ret = "li\t%0,%2\n\tli\t%D0,%3";
                }
            }
 
@@ -1447,17 +1710,45 @@ mips_move_2words (operands, insn)
                                   ? "li.d\t%0,%1"
                                   : "mtc1\t%.,%0\n\tmtc1\t%.,%D0");
            }
+         else if (MD_REG_P (regno0))
+           {
+             delay = DELAY_HILO;
+             if (regno0 != HILO_REGNUM)
+               ret = "mt%0\t%.\n";
+             else
+               ret = "mtlo\t%.\n\tmthi\t%.";
+           }
        }
        
       else if (code1 == CONST_INT && GET_MODE (op0) == DImode && GP_REG_P (regno0))
        {
          if (TARGET_64BIT)
-           ret = "li\t%0,%1";
-         else
+           {
+             if (GET_CODE (operands[1]) == SIGN_EXTEND)
+               ret = "li\t%0,%1\t\t# %X1";
+             else if (HOST_BITS_PER_WIDE_INT < 64)
+               /* We can't use 'X' for negative numbers, because then we won't
+                  get the right value for the upper 32 bits.  */
+               ret = ((INTVAL (op1) < 0) ? "dli\t%0,%1\t\t\t# %X1"
+                      : "dli\t%0,%X1\t\t# %1");
+             else
+               /* We must use 'X', because otherwise LONG_MIN will print as
+                  a number that the assembler won't accept.  */
+               ret = "dli\t%0,%X1\t\t# %1";
+           }
+         else if (HOST_BITS_PER_WIDE_INT < 64)
            {
              operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1);
              ret = "li\t%M0,%2\n\tli\t%L0,%1";
            }
+         else
+           {
+             /* We use multiple shifts here, to avoid warnings about out
+                of range shifts on 32 bit hosts.  */
+             operands[2] = GEN_INT (INTVAL (operands[1]) >> 16 >> 16);
+             operands[1] = GEN_INT (INTVAL (operands[1]) << 16 << 16 >> 16 >> 16);
+             ret = "li\t%M0,%2\n\tli\t%L0,%1";
+           }
        }
 
       else if (code1 == MEM)
@@ -1509,9 +1800,10 @@ mips_move_2words (operands, insn)
               || code1 == SYMBOL_REF
               || code1 == CONST)
        {
-         if (! TARGET_64BIT)
-           abort ();
-         return mips_move_1word (operands, insn, 0);
+         if (TARGET_STATS)
+           mips_count_memory_refs (op1, 2);
+
+         ret = "dla\t%0,%a1";
        }
     }
 
@@ -1596,7 +1888,6 @@ mips_address_cost (addr)
       break;
 
     case LO_SUM:
-    case HIGH:
       return 1;
 
     case LABEL_REF:
@@ -1834,7 +2125,7 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
          HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add;
          /* If modification of cmp1 caused overflow,
             we would get the wrong answer if we follow the usual path;
-            thus, x > 0xffffffffu would turn into x > 0u.  */
+            thus, x > 0xffffffffU would turn into x > 0U.  */
          if ((p_info->unsignedp
               ? (unsigned HOST_WIDE_INT) new > INTVAL (cmp1)
               : new > INTVAL (cmp1))
@@ -1894,112 +2185,67 @@ gen_conditional_branch (operands, test_code)
      rtx operands[];
      enum rtx_code test_code;
 {
-  static enum machine_mode mode_map[(int)CMP_MAX][(int)ITEST_MAX] = {
-    {                          /* CMP_SI */
-      SImode,                  /* eq  */
-      SImode,                  /* ne  */
-      SImode,                  /* gt  */
-      SImode,                  /* ge  */
-      SImode,                  /* lt  */
-      SImode,                  /* le  */
-      SImode,                  /* gtu */
-      SImode,                  /* geu */
-      SImode,                  /* ltu */
-      SImode,                  /* leu */
-    },
-    {                          /* CMP_DI */
-      DImode,                  /* eq  */
-      DImode,                  /* ne  */
-      DImode,                  /* gt  */
-      DImode,                  /* ge  */
-      DImode,                  /* lt  */
-      DImode,                  /* le  */
-      DImode,                  /* gtu */
-      DImode,                  /* geu */
-      DImode,                  /* ltu */
-      DImode,                  /* leu */
-    },
-    {                          /* CMP_SF */
-      CC_FPmode,               /* eq  */
-      CC_REV_FPmode,           /* ne  */
-      CC_FPmode,               /* gt  */
-      CC_FPmode,               /* ge  */
-      CC_FPmode,               /* lt  */
-      CC_FPmode,               /* le  */
-      VOIDmode,                        /* gtu */
-      VOIDmode,                        /* geu */
-      VOIDmode,                        /* ltu */
-      VOIDmode,                        /* leu */
-    },
-    {                          /* CMP_DF */
-      CC_FPmode,               /* eq  */
-      CC_REV_FPmode,           /* ne  */
-      CC_FPmode,               /* gt  */
-      CC_FPmode,               /* ge  */
-      CC_FPmode,               /* lt  */
-      CC_FPmode,               /* le  */
-      VOIDmode,                        /* gtu */
-      VOIDmode,                        /* geu */
-      VOIDmode,                        /* ltu */
-      VOIDmode,                        /* leu */
-    },
-  };
-
+  enum cmp_type type = branch_type;
+  rtx cmp0 = branch_cmp[0];
+  rtx cmp1 = branch_cmp[1];
   enum machine_mode mode;
-  enum cmp_type type     = branch_type;
-  rtx cmp0               = branch_cmp[0];
-  rtx cmp1               = branch_cmp[1];
-  rtx label1             = gen_rtx (LABEL_REF, VOIDmode, operands[0]);
-  rtx label2             = pc_rtx;
-  rtx reg                = (rtx)0;
-  int invert             = 0;
-  enum internal_test test = map_test_to_internal_test (test_code);
-
-  if (test == ITEST_MAX)
-    {
-      mode = word_mode;
-      goto fail;
-    }
-
-  /* Get the machine mode to use (CCmode, CC_EQmode, CC_FPmode, or CC_REV_FPmode).  */
-  mode = mode_map[(int)type][(int)test];
-  if (mode == VOIDmode)
-    goto fail;
+  rtx reg;
+  int invert;
+  rtx label1, label2;
 
   switch (type)
     {
     default:
-      goto fail;
+      abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test");
 
     case CMP_SI:
     case CMP_DI:
-      reg = gen_int_relational (test_code, (rtx)0, cmp0, cmp1, &invert);
-      if (reg != (rtx)0)
+      mode = type == CMP_SI ? SImode : DImode;
+      invert = FALSE;
+      reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert);
+      if (reg)
        {
          cmp0 = reg;
          cmp1 = const0_rtx;
          test_code = NE;
        }
-
-      /* Make sure not non-zero constant if ==/!= */
       else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
-       cmp1 = force_reg (mode, cmp1);
-
+       {
+         /* We don't want to build a comparison against a non-zero
+             constant.  */
+         cmp1 = force_reg (mode, cmp1);
+       }
       break;
 
-    case CMP_DF:
     case CMP_SF:
-      {
-       rtx reg = gen_rtx (REG, mode, FPSW_REGNUM);
-       emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (test_code, mode, cmp0, cmp1)));
-       cmp0 = reg;
-       cmp1 = const0_rtx;
-       test_code = NE;
-      }
+    case CMP_DF:
+      if (mips_isa < 4)
+       reg = gen_rtx (REG, CCmode, FPSW_REGNUM);
+      else
+       reg = gen_reg_rtx (CCmode);
+
+      /* For cmp0 != cmp1, build cmp0 == cmp1, and test for result ==
+         0 in the instruction built below.  The MIPS FPU handles
+         inequality testing by testing for equality and looking for a
+         false result.  */
+      emit_insn (gen_rtx (SET, VOIDmode,
+                         reg,
+                         gen_rtx (test_code == NE ? EQ : test_code,
+                                  CCmode, cmp0, cmp1)));
+
+      test_code = test_code == NE ? EQ : NE;
+      mode = CCmode;
+      cmp0 = reg;
+      cmp1 = const0_rtx;
+      invert = FALSE;
       break;
     }
 
-  /* Generate the jump */
+  /* Generate the branch.  */
+
+  label1 = gen_rtx (LABEL_REF, VOIDmode, operands[0]);
+  label2 = pc_rtx;
+
   if (invert)
     {
       label2 = label1;
@@ -2012,174 +2258,102 @@ gen_conditional_branch (operands, test_code)
                                    gen_rtx (test_code, mode, cmp0, cmp1),
                                    label1,
                                    label2)));
-
-  return;
-
-fail:
-  abort_with_insn (gen_rtx (test_code, mode, cmp0, cmp1), "bad test");
 }
 
-\f
-#if 0
-/* Internal code to generate the load and store of one word/short/byte.
-   The load is emitted directly, and the store insn is returned.  */
-
-#define UNITS_PER_MIPS_DWORD   8
-#define UNITS_PER_MIPS_WORD    4
-#define UNITS_PER_MIPS_HWORD   2
-
-static rtx
-block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align, orig_src)
-     rtx src_reg;              /* register holding source memory address */
-     rtx dest_reg;             /* register holding dest. memory address */
-     int *p_bytes;             /* pointer to # bytes remaining */
-     int *p_offset;            /* pointer to current offset */
-     int align;                        /* alignment */
-     rtx orig_src;             /* original source for making a reg note */
-{
-  int bytes;                   /* # bytes remaining */
-  int offset;                  /* offset to use */
-  int size;                    /* size in bytes of load/store */
-  enum machine_mode mode;      /* mode to use for load/store */
-  rtx reg;                     /* temporary register */
-  rtx src_addr;                        /* source address */
-  rtx dest_addr;               /* destination address */
-  rtx insn;                    /* insn of the load */
-  rtx orig_src_addr;           /* original source address */
-  rtx (*load_func)();          /* function to generate load insn */
-  rtx (*store_func)();         /* function to generate destination insn */
-
-  bytes = *p_bytes;
-  if (bytes <= 0 || align <= 0)
-    abort ();
-
-  if (bytes >= UNITS_PER_MIPS_DWORD && align >= UNIS_PER_MIPS_DWORD)
-    {
-      mode = DImode;
-      size = UNITS_PER_MIPS_DWORD;
-      load_func = gen_movdi;
-      store_func = gen_movdi;
-    }
-  else if (bytes >= UNITS_PER_MIPS_WORD && align >= UNITS_PER_MIPS_WORD)
-    {
-      mode = SImode;
-      size = UNITS_PER_MIPS_WORD;
-      load_func = gen_movsi;
-      store_func = gen_movsi;
-    }
+/* Emit the common code for conditional moves.  OPERANDS is the array
+   of operands passed to the conditional move defined_expand.  */
 
-#if 0
-  /* Don't generate unaligned moves here, rather defer those to the
-     general movestrsi_internal pattern.
-     If this gets commented back in, then should add the dword equivalent.  */
-  else if (bytes >= UNITS_PER_MIPS_WORD)
-    {
-      mode = SImode;
-      size = UNITS_PER_MIPS_WORD;
-      load_func = gen_movsi_ulw;
-      store_func = gen_movsi_usw;
-    }
-#endif
-
-  else if (bytes >= UNITS_PER_MIPS_SHORT && align >= UNITS_PER_MIPS_SHORT)
+void
+gen_conditional_move (operands)
+     rtx *operands;
+{
+  rtx op0 = branch_cmp[0];
+  rtx op1 = branch_cmp[1];
+  enum machine_mode mode = GET_MODE (branch_cmp[0]);
+  enum rtx_code cmp_code = GET_CODE (operands[1]);
+  enum rtx_code move_code = NE;
+  enum machine_mode op_mode = GET_MODE (operands[0]);
+  enum machine_mode cmp_mode;
+  rtx cmp_reg;
+
+  if (GET_MODE_CLASS (mode) != MODE_FLOAT)
     {
-      mode = HImode;
-      size = UNITS_PER_MIPS_SHORT;
-      load_func = gen_movhi;
-      store_func = gen_movhi;
+      switch (cmp_code)
+       {
+       case EQ:
+         cmp_code = XOR;
+         move_code = EQ;
+         break;
+       case NE:
+         cmp_code = XOR;
+         break;
+       case LT:
+         break;
+       case GE:
+         cmp_code = LT;
+         move_code = EQ;
+         break;
+       case GT:
+         cmp_code = LT;
+         op0 = force_reg (mode, branch_cmp[1]);
+         op1 = branch_cmp[0];
+         break;
+       case LE:
+         cmp_code = LT;
+         op0 = force_reg (mode, branch_cmp[1]);
+         op1 = branch_cmp[0];
+         move_code = EQ;
+         break;
+       case LTU:
+         break;
+       case GEU:
+         cmp_code = LTU;
+         move_code = EQ;
+         break;
+       case GTU:
+         cmp_code = LTU;
+         op0 = force_reg (mode, branch_cmp[1]);
+         op1 = branch_cmp[0];
+         break;
+       case LEU:
+         cmp_code = LTU;
+         op0 = force_reg (mode, branch_cmp[1]);
+         op1 = branch_cmp[0];
+         move_code = EQ;
+         break;
+       default:
+         abort ();
+       }
     }
-
   else
     {
-      mode = QImode;
-      size = 1;
-      load_func = gen_movqi;
-      store_func = gen_movqi;
-    }
-
-  offset = *p_offset;
-  *p_offset = offset + size;
-  *p_bytes = bytes - size;
-
-  if (offset == 0)
-    {
-      src_addr  = src_reg;
-      dest_addr = dest_reg;
+      if (cmp_code == NE)
+       {
+         cmp_code = EQ;
+         move_code = EQ;
+       }
     }
+         
+  if (mode == SImode || mode == DImode)
+    cmp_mode = mode;
+  else if (mode == SFmode || mode == DFmode)
+    cmp_mode = CCmode;
   else
-    {
-      src_addr  = gen_rtx (PLUS, Pmode, src_reg,  GEN_INT (offset));
-      dest_addr = gen_rtx (PLUS, Pmode, dest_reg, GEN_INT (offset));
-    }
-
-  reg = gen_reg_rtx (mode);
-  insn = emit_insn ((*load_func) (reg, gen_rtx (MEM, mode, src_addr)));
-  orig_src_addr = XEXP (orig_src, 0);
-  if (CONSTANT_P (orig_src_addr))
-    REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUIV,
-                               plus_constant (orig_src_addr, offset),
-                               REG_NOTES (insn));
-
-  return (*store_func) (gen_rtx (MEM, mode, dest_addr), reg);
-}
-#endif
-
-\f
-/* Write a series of loads/stores to move some bytes.  Generate load/stores as follows:
-
-   load  1
-   load  2
-   load  3
-   store 1
-   load  4
-   store 2
-   load  5
-   store 3
-   ...
-
-   This way, no NOP's are needed, except at the end, and only
-   two temp registers are needed.  Two delay slots are used
-   in deference to the R4000.  */
-
-#if 0
-static void
-block_move_sequence (dest_reg, src_reg, bytes, align, orig_src)
-     rtx dest_reg;             /* register holding destination address */
-     rtx src_reg;              /* register holding source address */
-     int bytes;                        /* # bytes to move */
-     int align;                        /* max alignment to assume */
-     rtx orig_src;             /* original source for making a reg note */
-{
-  int offset           = 0;
-  rtx prev2_store      = (rtx)0;
-  rtx prev_store       = (rtx)0;
-  rtx cur_store                = (rtx)0;
-
-  while (bytes > 0)
-    {
-      /* Is there a store to do? */
-      if (prev2_store)
-       emit_insn (prev2_store);
-
-      prev2_store = prev_store;
-      prev_store = cur_store;
-      cur_store = block_move_load_store (dest_reg, src_reg,
-                                        &bytes, &offset,
-                                        align, orig_src);
-    }
-
-  /* Finish up last three stores.  */
-  if (prev2_store)
-    emit_insn (prev2_store);
-
-  if (prev_store)
-    emit_insn (prev_store);
+    abort ();
 
-  if (cur_store)
-    emit_insn (cur_store);
+  cmp_reg = gen_reg_rtx (cmp_mode);
+  emit_insn (gen_rtx (SET, cmp_mode,
+                     cmp_reg,
+                     gen_rtx (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)),
+                              operands[2],
+                              operands[3])));
 }
-#endif
-
 \f
 /* Write a loop to move a constant number of bytes.  Generate load/stores as follows:
 
@@ -2206,18 +2380,17 @@ block_move_sequence (dest_reg, src_reg, bytes, align, orig_src)
 #define MAX_MOVE_REGS 4
 #define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
 
-/* ??? Should add code to use DWORD load/stores.  */
-
 static void
-block_move_loop (dest_reg, src_reg, bytes, align, orig_src)
+block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src)
      rtx dest_reg;             /* register holding destination address */
      rtx src_reg;              /* register holding source address */
      int bytes;                        /* # bytes to move */
      int align;                        /* alignment */
+     rtx orig_dest;            /* original dest for change_address */
      rtx orig_src;             /* original source for making a reg note */
 {
-  rtx dest_mem         = gen_rtx (MEM, BLKmode, dest_reg);
-  rtx src_mem          = gen_rtx (MEM, BLKmode, src_reg);
+  rtx dest_mem         = change_address (orig_dest, BLKmode, dest_reg);
+  rtx src_mem          = change_address (orig_src, BLKmode, src_reg);
   rtx align_rtx                = GEN_INT (align);
   rtx label;
   rtx final_src;
@@ -2278,7 +2451,6 @@ block_move_loop (dest_reg, src_reg, bytes, align, orig_src)
                                      GEN_INT (leftover),
                                      align_rtx));
 }
-
 \f
 /* Use a library function to move some bytes.  */
 
@@ -2299,13 +2471,18 @@ block_move_call (dest_reg, src_reg, bytes_rtx)
                     VOIDmode, 3,
                     dest_reg, Pmode,
                     src_reg, Pmode,
-                    bytes_rtx, Pmode);
+                    convert_to_mode (TYPE_MODE (sizetype), bytes_rtx,
+                                     TREE_UNSIGNED (sizetype)),
+                    TYPE_MODE (sizetype));
 #else
   emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0,
                     VOIDmode, 3,
                     src_reg, Pmode,
                     dest_reg, Pmode,
-                    bytes_rtx, Pmode);
+                    convert_to_mode (TYPE_MODE (integer_type_node),
+                                      bytes_rtx,
+                                      TREE_UNSIGNED (integer_type_node)),
+                    TYPE_MODE (integer_type_node));
 #endif
 }
 
@@ -2327,6 +2504,7 @@ expand_block_move (operands)
   int bytes    = (constp ? INTVAL (bytes_rtx) : 0);
   int align    = INTVAL (align_rtx);
   rtx orig_src = operands[1];
+  rtx orig_dest        = operands[0];
   rtx src_reg;
   rtx dest_reg;
 
@@ -2337,24 +2515,21 @@ expand_block_move (operands)
     align = UNITS_PER_WORD;
 
   /* Move the address into scratch registers.  */
-  dest_reg = copy_addr_to_reg (XEXP (operands[0], 0));
+  dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0));
   src_reg  = copy_addr_to_reg (XEXP (orig_src, 0));
 
   if (TARGET_MEMCPY)
     block_move_call (dest_reg, src_reg, bytes_rtx);
 
-#if 0
-  else if (constp && bytes <= 3*align)
-    block_move_sequence (dest_reg, src_reg, bytes, align, orig_src);
-#endif
-
   else if (constp && bytes <= 2*MAX_MOVE_BYTES)
-    emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg),
-                                     gen_rtx (MEM, BLKmode, src_reg),
+    emit_insn (gen_movstrsi_internal (change_address (orig_dest, BLKmode,
+                                                     dest_reg),
+                                     change_address (orig_src, BLKmode,
+                                                     src_reg),
                                      bytes_rtx, align_rtx));
 
   else if (constp && align >= UNITS_PER_WORD && optimize)
-    block_move_loop (dest_reg, src_reg, bytes, align, orig_src);
+    block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src);
 
   else if (constp && optimize)
     {
@@ -2384,29 +2559,24 @@ expand_block_move (operands)
       emit_jump_insn (gen_beq (aligned_label));
 
       /* Unaligned loop.  */
-      block_move_loop (dest_reg, src_reg, bytes, 1, orig_src);
+      block_move_loop (dest_reg, src_reg, bytes, 1, orig_dest, orig_src);
       emit_jump_insn (gen_jump (join_label));
       emit_barrier ();
 
       /* Aligned loop.  */
       emit_label (aligned_label);
-      block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_src);
+      block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_dest,
+                      orig_src);
       emit_label (join_label);
 
       /* Bytes at the end of the loop.  */
       if (leftover)
-       {
-#if 0
-         if (leftover <= 3*align)
-           block_move_sequence (dest_reg, src_reg, leftover, align, orig_src);
-
-         else
-#endif
-           emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg),
-                                             gen_rtx (MEM, BLKmode, src_reg),
-                                             GEN_INT (leftover),
-                                             GEN_INT (align)));
-       }
+       emit_insn (gen_movstrsi_internal (change_address (orig_dest, BLKmode,
+                                                         dest_reg),
+                                         change_address (orig_src, BLKmode,
+                                                         src_reg),
+                                         GEN_INT (leftover),
+                                         GEN_INT (align)));
     }
 
   else
@@ -2496,7 +2666,10 @@ output_block_move (insn, operands, num_regs, move_type)
            {
              xoperands[1] = operands[1];
              xoperands[0] = src_reg;
-             output_asm_insn ("la\t%0,%1", xoperands);
+             if (Pmode == DImode)
+               output_asm_insn ("dla\t%0,%1", xoperands);
+             else
+               output_asm_insn ("la\t%0,%1", xoperands);
            }
        }
 
@@ -2510,20 +2683,56 @@ output_block_move (insn, operands, num_regs, move_type)
            {
              xoperands[1] = operands[0];
              xoperands[0] = dest_reg;
-             output_asm_insn ("la\t%0,%1", xoperands);
+             if (Pmode == DImode)
+               output_asm_insn ("dla\t%0,%1", xoperands);
+             else
+               output_asm_insn ("la\t%0,%1", xoperands);
            }
        }
     }
 
+  /* ??? We really shouldn't get any LO_SUM addresses here, because they
+     are not offsettable, however, offsettable_address_p says they are
+     offsettable. I think this is a bug in offsettable_address_p.
+     For expediency, we fix this by just loading the address into a register
+     if we happen to get one.  */
+
+  if (GET_CODE (src_reg) == LO_SUM)
+    {
+      src_reg = operands[ 3 + num_regs-- ];
+      if (move_type != BLOCK_MOVE_LAST)
+       {
+         xoperands[2] = XEXP (XEXP (operands[1], 0), 1);
+         xoperands[1] = XEXP (XEXP (operands[1], 0), 0);
+         xoperands[0] = src_reg;
+         if (Pmode == DImode)
+           output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
+         else
+           output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
+       }
+    }
+
+  if (GET_CODE (dest_reg) == LO_SUM)
+    {
+      dest_reg = operands[ 3 + num_regs-- ];
+      if (move_type != BLOCK_MOVE_LAST)
+       {
+         xoperands[2] = XEXP (XEXP (operands[0], 0), 1);
+         xoperands[1] = XEXP (XEXP (operands[0], 0), 0);
+         xoperands[0] = dest_reg;
+         if (Pmode == DImode)
+           output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
+         else
+           output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
+       }
+    }
+
   if (num_regs > (sizeof (load_store) / sizeof (load_store[0])))
     num_regs = (sizeof (load_store) / sizeof (load_store[0]));
 
   else if (num_regs < 1)
     abort_with_insn (insn, "Cannot do block move, not enough scratch registers");
 
-  if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && set_noreorder++ == 0)
-    output_asm_insn (".set\tnoreorder", operands);
-
   while (bytes > 0)
     {
       load_store[num].offset = offset;
@@ -2543,19 +2752,22 @@ output_block_move (insn, operands, num_regs, move_type)
       /* ??? Fails because of a MIPS assembler bug?  */
       else if (TARGET_64BIT && bytes >= 8)
        {
-#if BYTES_BIG_ENDIAN
-         load_store[num].load       = "ldl\t%0,%1\n\tldr\t%0,%2";
-         load_store[num].load_nop   = "ldl\t%0,%1\n\tldr\t%0,%2%#";
-         load_store[num].store      = "sdl\t%0,%1\n\tsdr\t%0,%2";
-         load_store[num].last_store = "sdr\t%0,%2";
-         load_store[num].final      = "sdl\t%0,%1";
-#else
-         load_store[num].load       = "ldl\t%0,%2\n\tldr\t%0,%1";
-         load_store[num].load_nop   = "ldl\t%0,%2\n\tldr\t%0,%1%#";
-         load_store[num].store      = "sdl\t%0,%2\n\tsdr\t%0,%1";
-         load_store[num].last_store = "sdr\t%0,%1";
-         load_store[num].final      = "sdl\t%0,%2";
-#endif
+         if (BYTES_BIG_ENDIAN)
+           {
+             load_store[num].load       = "ldl\t%0,%1\n\tldr\t%0,%2";
+             load_store[num].load_nop   = "ldl\t%0,%1\n\tldr\t%0,%2%#";
+             load_store[num].store      = "sdl\t%0,%1\n\tsdr\t%0,%2";
+             load_store[num].last_store = "sdr\t%0,%2";
+             load_store[num].final      = "sdl\t%0,%1";
+           }
+         else
+           {
+             load_store[num].load           = "ldl\t%0,%2\n\tldr\t%0,%1";
+             load_store[num].load_nop   = "ldl\t%0,%2\n\tldr\t%0,%1%#";
+             load_store[num].store          = "sdl\t%0,%2\n\tsdr\t%0,%1";
+             load_store[num].last_store = "sdr\t%0,%1";
+             load_store[num].final      = "sdl\t%0,%2";
+           }
          load_store[num].mode = DImode;
          offset += 8;
          bytes -= 8;
@@ -2576,19 +2788,22 @@ output_block_move (insn, operands, num_regs, move_type)
 
       else if (bytes >= 4)
        {
-#if BYTES_BIG_ENDIAN
-         load_store[num].load       = "lwl\t%0,%1\n\tlwr\t%0,%2";
-         load_store[num].load_nop   = "lwl\t%0,%1\n\tlwr\t%0,%2%#";
-         load_store[num].store      = "swl\t%0,%1\n\tswr\t%0,%2";
-         load_store[num].last_store = "swr\t%0,%2";
-         load_store[num].final      = "swl\t%0,%1";
-#else
-         load_store[num].load       = "lwl\t%0,%2\n\tlwr\t%0,%1";
-         load_store[num].load_nop   = "lwl\t%0,%2\n\tlwr\t%0,%1%#";
-         load_store[num].store      = "swl\t%0,%2\n\tswr\t%0,%1";
-         load_store[num].last_store = "swr\t%0,%1";
-         load_store[num].final      = "swl\t%0,%2";
-#endif
+         if (BYTES_BIG_ENDIAN)
+           {
+             load_store[num].load       = "lwl\t%0,%1\n\tlwr\t%0,%2";
+             load_store[num].load_nop   = "lwl\t%0,%1\n\tlwr\t%0,%2%#";
+             load_store[num].store      = "swl\t%0,%1\n\tswr\t%0,%2";
+             load_store[num].last_store = "swr\t%0,%2";
+             load_store[num].final      = "swl\t%0,%1";
+           }
+         else
+           {
+             load_store[num].load           = "lwl\t%0,%2\n\tlwr\t%0,%1";
+             load_store[num].load_nop   = "lwl\t%0,%2\n\tlwr\t%0,%1%#";
+             load_store[num].store          = "swl\t%0,%2\n\tswr\t%0,%1";
+             load_store[num].last_store = "swr\t%0,%1";
+             load_store[num].final      = "swl\t%0,%2";
+           }
          load_store[num].mode = SImode;
          offset += 4;
          bytes -= 4;
@@ -2716,9 +2931,6 @@ output_block_move (insn, operands, num_regs, move_type)
        }
     }
 
-  if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && --set_noreorder == 0)
-    output_asm_insn (".set\treorder", operands);
-
   return "";
 }
 
@@ -2806,11 +3018,17 @@ function_arg_advance (cum, mode, type, named)
       break;
 
     case SFmode:
-      cum->arg_words++;
+      if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
+       cum->fp_arg_words++;
+      else
+       cum->arg_words++;
       break;
 
     case DFmode:
-      cum->arg_words += (TARGET_64BIT ? 1 : 2);
+      if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
+       cum->fp_arg_words += (TARGET_64BIT ? 1 : 2);
+      else
+       cum->arg_words += (TARGET_64BIT ? 1 : 2);
       break;
 
     case DImode:
@@ -2840,6 +3058,7 @@ function_arg (cum, mode, type, named)
   rtx ret;
   int regbase = -1;
   int bias = 0;
+  int *arg_words = &cum->arg_words;
   int struct_p = ((type != (tree)0)
                  && (TREE_CODE (type) == RECORD_TYPE
                      || TREE_CODE (type) == UNION_TYPE));
@@ -2850,28 +3069,63 @@ function_arg (cum, mode, type, named)
             cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
             type, named);
 
+  cum->last_arg_fp = 0;
   switch (mode)
     {
     case SFmode:
-      if (cum->gp_reg_found || cum->arg_number >= 2 || TARGET_SOFT_FLOAT)
-       regbase = GP_ARG_FIRST;
-      else
+      if (mips_abi == ABI_32)
+       {
+         if (cum->gp_reg_found || cum->arg_number >= 2 || TARGET_SOFT_FLOAT)
+           regbase = GP_ARG_FIRST;
+         else
+           {
+             regbase = FP_ARG_FIRST;
+             /* If the first arg was a float in a floating point register,
+                then set bias to align this float arg properly.  */
+             if (cum->arg_words == 1)
+               bias = 1;
+           }
+       }
+      else if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
        {
+         if (! TARGET_64BIT)
+           cum->fp_arg_words += cum->fp_arg_words & 1;
+         cum->last_arg_fp = 1;
+         arg_words = &cum->fp_arg_words;
          regbase = FP_ARG_FIRST;
-         /* If the first arg was a float in a floating point register,
-            then set bias to align this float arg properly.  */
-         if (cum->arg_words == 1)
-           bias = 1;
        }
-
+      else
+       regbase = (TARGET_SOFT_FLOAT || ! named ? GP_ARG_FIRST : FP_ARG_FIRST);
       break;
 
     case DFmode:
       if (! TARGET_64BIT)
-       cum->arg_words += (cum->arg_words & 1);
-      regbase = (cum->gp_reg_found || TARGET_SOFT_FLOAT || cum->arg_number >= 2
-                ? GP_ARG_FIRST
-                : FP_ARG_FIRST);
+       {
+         if (mips_abi == ABI_EABI
+             && ! TARGET_SOFT_FLOAT
+             && ! TARGET_SINGLE_FLOAT)
+           cum->fp_arg_words += cum->fp_arg_words & 1;
+         else
+           cum->arg_words += cum->arg_words & 1;
+       }
+      if (mips_abi == ABI_32)
+       regbase = ((cum->gp_reg_found
+                   || TARGET_SOFT_FLOAT
+                   || TARGET_SINGLE_FLOAT
+                   || cum->arg_number >= 2)
+                  ? GP_ARG_FIRST
+                  : FP_ARG_FIRST);
+      else if (mips_abi == ABI_EABI
+              && ! TARGET_SOFT_FLOAT
+              && ! TARGET_SINGLE_FLOAT)
+       {
+         cum->last_arg_fp = 1;
+         arg_words = &cum->fp_arg_words;
+         regbase = FP_ARG_FIRST;
+       }
+      else
+       regbase = (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT || ! named
+                  ? GP_ARG_FIRST : FP_ARG_FIRST);
       break;
 
     default:
@@ -2882,9 +3136,8 @@ function_arg (cum, mode, type, named)
       /* Drops through.  */
     case BLKmode:
       if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD
-         && ! TARGET_64BIT)
+         && ! TARGET_64BIT && mips_abi != ABI_EABI)
        cum->arg_words += (cum->arg_words & 1);
-
       regbase = GP_ARG_FIRST;
       break;
 
@@ -2901,7 +3154,7 @@ function_arg (cum, mode, type, named)
       regbase = GP_ARG_FIRST;
     }
 
-  if (cum->arg_words >= MAX_ARGS_IN_REGISTERS)
+  if (*arg_words >= MAX_ARGS_IN_REGISTERS)
     {
       if (TARGET_DEBUG_E_MODE)
        fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
@@ -2913,10 +3166,85 @@ function_arg (cum, mode, type, named)
       if (regbase == -1)
        abort ();
 
-      ret = gen_rtx (REG, mode, regbase + cum->arg_words + bias);
+      if (! type || TREE_CODE (type) != RECORD_TYPE || mips_abi == ABI_32
+         || mips_abi == ABI_EABI || ! named)
+       ret = gen_rtx (REG, mode, regbase + *arg_words + bias);
+      else
+       {
+         /* The Irix 6 n32/n64 ABIs say that if any 64 bit chunk of the
+            structure contains a double in its entirety, then that 64 bit
+            chunk is passed in a floating point register.  */
+         tree field;
+
+         /* First check to see if there is any such field.  */
+         for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+           if (TREE_CODE (field) == FIELD_DECL
+               && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+               && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
+               && (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field))
+                   % BITS_PER_WORD == 0))
+             break;
+
+         if (! field)
+           ret = gen_rtx (REG, mode, regbase + *arg_words + bias);
+         else
+           {
+             /* Now handle the special case by returning a PARALLEL
+                indicating where each 64 bit chunk goes.  */
+             int chunks;
+             int bitpos;
+             int regno;
+             int i;
+
+             /* ??? If this is a packed structure, then the last hunk won't
+                be 64 bits.  */
+
+             /* ??? If this is a structure with a single double field,
+                it would be more convenient to return (REG:DI %fX) than
+                a parallel.  However, we would have to modify the mips
+                backend to allow DImode values in fp registers.  */
+
+             chunks = TREE_INT_CST_LOW (TYPE_SIZE (type)) / BITS_PER_WORD;
+             if (chunks + *arg_words + bias > MAX_ARGS_IN_REGISTERS)
+               chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias;
+
+             /* assign_parms checks the mode of ENTRY_PARM, so we must
+                use the actual mode here.  */
+             ret = gen_rtx (PARALLEL, mode, rtvec_alloc (chunks));
+
+             bitpos = 0;
+             regno = regbase + *arg_words + bias;
+             field = TYPE_FIELDS (type);
+             for (i = 0; i < chunks; i++)
+               {
+                 rtx reg;
+
+                 for (; field; field = TREE_CHAIN (field))
+                   if (TREE_CODE (field) == FIELD_DECL
+                       && (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field))
+                           >= bitpos))
+                     break;
+
+                 if (field
+                     && TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)) == bitpos
+                     && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+                     && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
+                   reg = gen_rtx (REG, DFmode,
+                                  regno + FP_ARG_FIRST - GP_ARG_FIRST);
+                 else
+                   reg = gen_rtx (REG, word_mode, regno);
+
+                 XVECEXP (ret, 0, i) = gen_rtx (EXPR_LIST, VOIDmode, reg,
+                                                GEN_INT (bitpos / BITS_PER_UNIT));
+
+                 bitpos += 64;
+                 regno++;
+               }
+           }
+       }
 
       if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "%s%s\n", reg_names[regbase + cum->arg_words + bias],
+       fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias],
                 struct_p ? ", [struct]" : "");
 
       /* The following is a hack in order to pass 1 byte structures
@@ -2939,11 +3267,11 @@ function_arg (cum, mode, type, named)
         calling convention for now.  */
 
       if (struct_p && int_size_in_bytes (type) < UNITS_PER_WORD
-         && ! TARGET_64BIT)
+         && ! TARGET_64BIT && mips_abi != ABI_EABI)
        {
          rtx amount = GEN_INT (BITS_PER_WORD
                                - int_size_in_bytes (type) * BITS_PER_UNIT);
-         rtx reg = gen_rtx (REG, word_mode, regbase + cum->arg_words + bias);
+         rtx reg = gen_rtx (REG, word_mode, regbase + *arg_words + bias);
          if (TARGET_64BIT)
            cum->adjust[ cum->num_adjusts++ ] = gen_ashldi3 (reg, reg, amount);
          else
@@ -2968,7 +3296,8 @@ function_arg_partial_nregs (cum, mode, type, named)
   if ((mode == BLKmode
        || GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
        || GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
-      && cum->arg_words < MAX_ARGS_IN_REGISTERS)
+      && cum->arg_words < MAX_ARGS_IN_REGISTERS
+      && mips_abi != ABI_EABI)
     {
       int words;
       if (mode == BLKmode)
@@ -2978,102 +3307,27 @@ function_arg_partial_nregs (cum, mode, type, named)
        words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 
       if (words + cum->arg_words <= MAX_ARGS_IN_REGISTERS)
-       return 0;               /* structure fits in registers */
-
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "function_arg_partial_nregs = %d\n",
-                MAX_ARGS_IN_REGISTERS - cum->arg_words);
-
-      return MAX_ARGS_IN_REGISTERS - cum->arg_words;
-    }
-
-  else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1
-          && ! TARGET_64BIT)
-    {
-      if (TARGET_DEBUG_E_MODE)
-       fprintf (stderr, "function_arg_partial_nregs = 1\n");
-
-      return 1;
-    }
-
-  return 0;
-}
-
-\f
-/* Print the options used in the assembly file.  */
-
-static struct {char *name; int value;} target_switches []
-  = TARGET_SWITCHES;
-
-void
-print_options (out)
-     FILE *out;
-{
-  int line_len;
-  int len;
-  int j;
-  char **p;
-  int mask = TARGET_DEFAULT;
-
-  /* Allow assembly language comparisons with -mdebug eliminating the
-     compiler version number and switch lists.  */
-
-  if (TARGET_DEBUG_MODE)
-    return;
+       return 0;               /* structure fits in registers */
 
-  fprintf (out, "\n # %s %s", language_string, version_string);
-#ifdef TARGET_VERSION_INTERNAL
-  TARGET_VERSION_INTERNAL (out);
-#endif
-#ifdef __GNUC__
-  fprintf (out, " compiled by GNU C\n\n");
-#else
-  fprintf (out, " compiled by CC\n\n");
-#endif
+      if (TARGET_DEBUG_E_MODE)
+       fprintf (stderr, "function_arg_partial_nregs = %d\n",
+                MAX_ARGS_IN_REGISTERS - cum->arg_words);
 
-  fprintf (out, " # Cc1 defaults:");
-  line_len = 32767;
-  for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++)
-    {
-      if (target_switches[j].name[0] != '\0'
-         && target_switches[j].value > 0
-         && (target_switches[j].value & mask) == target_switches[j].value)
-       {
-         mask &= ~ target_switches[j].value;
-         len = strlen (target_switches[j].name) + 1;
-         if (len + line_len > 79)
-           {
-             line_len = 2;
-             fputs ("\n #", out);
-           }
-         fprintf (out, " -m%s", target_switches[j].name);
-         line_len += len;
-       }
+      return MAX_ARGS_IN_REGISTERS - cum->arg_words;
     }
 
-  fprintf (out, "\n\n # Cc1 arguments (-G value = %d, Cpu = %s, ISA = %d):",
-          mips_section_threshold, mips_cpu_string, mips_isa);
-
-  line_len = 32767;
-  for (p = &save_argv[1]; *p != (char *)0; p++)
+  else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1
+          && ! TARGET_64BIT
+          && mips_abi != ABI_EABI)
     {
-      char *arg = *p;
-      if (*arg == '-')
-       {
-         len = strlen (arg) + 1;
-         if (len + line_len > 79)
-           {
-             line_len = 2;
-             fputs ("\n #", out);
-           }
-         fprintf (out, " %s", *p);
-         line_len += len;
-       }
+      if (TARGET_DEBUG_E_MODE)
+       fprintf (stderr, "function_arg_partial_nregs = 1\n");
+
+      return 1;
     }
 
-  fputs ("\n\n", out);
+  return 0;
 }
-
 \f
 /* Abort after printing out a specific insn.  */
 
@@ -3142,7 +3396,7 @@ override_options ()
   else if (isdigit (*mips_isa_string))
     {
       mips_isa = atoi (mips_isa_string);
-      if (mips_isa < 1 || mips_isa > 3)
+      if (mips_isa < 1 || mips_isa > 4)
        {
          error ("-mips%d not supported", mips_isa);
          mips_isa = 1;
@@ -3155,6 +3409,74 @@ override_options ()
       mips_isa = 1;
     }
 
+#ifdef MIPS_ABI_DEFAULT
+  /* Get the ABI to use.  Currently this code is only used for Irix 6.  */
+  if (mips_abi_string == (char *) 0)
+    mips_abi = MIPS_ABI_DEFAULT;
+  else if (! strcmp (mips_abi_string, "32")
+          || ! strcmp (mips_abi_string, "o32"))
+    mips_abi = ABI_32;
+  else if (! strcmp (mips_abi_string, "n32"))
+    mips_abi = ABI_N32;
+  else if (! strcmp (mips_abi_string, "64")
+          || ! strcmp (mips_abi_string, "n64"))
+    mips_abi = ABI_64;
+  else if (! strcmp (mips_abi_string, "eabi"))
+    mips_abi = ABI_EABI;
+  else
+    error ("bad value (%s) for -mabi= switch", mips_abi_string);
+
+  /* A specified ISA defaults the ABI if it was not specified.  */
+  if (mips_abi_string == 0 && mips_isa_string && mips_abi != ABI_EABI)
+    {
+      if (mips_isa <= 2)
+       mips_abi = ABI_32;
+      else
+       mips_abi = ABI_64;
+    }
+  /* A specified ABI defaults the ISA if it was not specified.  */
+  else if (mips_isa_string == 0 && mips_abi_string && mips_abi != ABI_EABI)
+    {
+      if (mips_abi == ABI_32)
+       mips_isa = 1;
+      else if (mips_abi == ABI_N32)
+       mips_isa = 3;
+      else
+       mips_isa = 4;
+    }
+  /* If both ABI and ISA were specified, check for conflicts.  */
+  else if (mips_isa_string && mips_abi_string)
+    {
+      if ((mips_isa <= 2 && (mips_abi == ABI_N32 || mips_abi == ABI_64))
+         || (mips_isa >= 3 && mips_abi == ABI_32))
+       error ("-mabi=%s does not support -mips%d", mips_abi_string, mips_isa);
+    }
+
+  /* Override TARGET_DEFAULT if necessary.  */
+  if (mips_abi == ABI_32)
+    target_flags &= ~ (MASK_FLOAT64|MASK_64BIT);
+
+  /* In the EABI in 64 bit mode, longs and pointers are 64 bits.  */
+  if (mips_abi == ABI_EABI && TARGET_64BIT)
+    target_flags |= MASK_LONG64;
+
+  /* ??? This doesn't work yet, so don't let people try to use it.  */
+  if (mips_abi == ABI_32)
+    error ("The -mabi=32 support does not work yet.");
+#else
+  if (mips_abi_string)
+    error ("This target does not support the -mabi switch.");
+#endif
+
+#ifdef MIPS_CPU_STRING_DEFAULT
+  /* ??? There is a minor inconsistency here.  If the user specifies an ISA
+     greater than that supported by the default processor, then the user gets
+     an error.  Normally, the compiler will just default to the base level cpu
+     for the indicated isa.  */
+  if (mips_cpu_string == (char *)0)
+    mips_cpu_string = MIPS_CPU_STRING_DEFAULT;
+#endif
+
   /* Identify the processor type */
   if (mips_cpu_string == (char *)0
       || !strcmp (mips_cpu_string, "default")
@@ -3174,13 +3496,25 @@ override_options ()
          mips_cpu_string = "4000";
          mips_cpu = PROCESSOR_R4000;
          break;
+       case 4:
+         mips_cpu_string = "8000";
+         mips_cpu = PROCESSOR_R8000;
+         break;
        }
     }
 
   else
     {
       char *p = mips_cpu_string;
+      int seen_v = FALSE;
 
+      /* We need to cope with the various "vr" prefixes for the NEC 4300
+        and 4100 processors.  */
+      if (*p == 'v' || *p == 'V')
+       {
+         seen_v = TRUE;
+         p++;
+        }
       if (*p == 'r' || *p == 'R')
        p++;
 
@@ -3203,12 +3537,29 @@ override_options ()
        case '4':
          if (!strcmp (p, "4000") || !strcmp (p, "4k") || !strcmp (p, "4K"))
            mips_cpu = PROCESSOR_R4000;
+          /* The vr4100 is a non-FP ISA III processor with some extra
+             instructions.  */
+         else if (!strcmp (p, "4100")) {
+            mips_cpu = PROCESSOR_R4100;
+            target_flags |= MASK_SOFT_FLOAT ;
+          }
+         /* The vr4300 is a standard ISA III processor, but with a different
+            pipeline.  */
+         else if (!strcmp (p, "4300"))
+            mips_cpu = PROCESSOR_R4300;
          /* The r4400 is exactly the same as the r4000 from the compiler's
             viewpoint.  */
          else if (!strcmp (p, "4400"))
            mips_cpu = PROCESSOR_R4000;
          else if (!strcmp (p, "4600"))
            mips_cpu = PROCESSOR_R4600;
+         else if (!strcmp (p, "4650"))
+           mips_cpu = PROCESSOR_R4650;
+         break;
+
+       case '5':
+         if (!strcmp (p, "5000") || !strcmp (p, "5k") || !strcmp (p, "5K"))
+           mips_cpu = PROCESSOR_R5000;
          break;
 
        case '6':
@@ -3216,12 +3567,23 @@ override_options ()
            mips_cpu = PROCESSOR_R6000;
          break;
 
+       case '8':
+         if (!strcmp (p, "8000"))
+           mips_cpu = PROCESSOR_R8000;
+         break;
+
        case 'o':
          if (!strcmp (p, "orion"))
            mips_cpu = PROCESSOR_R4600;
          break;
        }
 
+      if (seen_v
+         && mips_cpu != PROCESSOR_R4300
+         && mips_cpu != PROCESSOR_R4100
+         && mips_cpu != PROCESSOR_R5000)
+       mips_cpu = PROCESSOR_DEFAULT;
+
       if (mips_cpu == PROCESSOR_DEFAULT)
        {
          error ("bad value (%s) for -mcpu= switch", mips_cpu_string);
@@ -3230,33 +3592,34 @@ override_options ()
     }
 
   if ((mips_cpu == PROCESSOR_R3000 && mips_isa > 1)
-      || (mips_cpu == PROCESSOR_R6000 && mips_isa > 2))
+      || (mips_cpu == PROCESSOR_R6000 && mips_isa > 2)
+      || ((mips_cpu == PROCESSOR_R4000
+           || mips_cpu == PROCESSOR_R4100
+           || mips_cpu == PROCESSOR_R4300
+          || mips_cpu == PROCESSOR_R4600
+          || mips_cpu == PROCESSOR_R4650)
+         && mips_isa > 3))
     error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa);
 
   /* make sure sizes of ints/longs/etc. are ok */
   if (mips_isa < 3)
     {
       if (TARGET_INT64)
-       fatal ("Only MIPS-III CPUs can support 64 bit ints");
+       fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit ints");
 
       else if (TARGET_LONG64)
-       fatal ("Only MIPS-III CPUs can support 64 bit longs");
-
-      else if (TARGET_LLONG128)
-       fatal ("Only MIPS-III CPUs can support 128 bit long longs");
+       fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit longs");
 
       else if (TARGET_FLOAT64)
-       fatal ("Only MIPS-III CPUs can support 64 bit fp registers");
+       fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit fp registers");
 
       else if (TARGET_64BIT)
-       fatal ("Only MIPS-III CPUs can support 64 bit gp registers");
-    }
-  else
-    {
-      if (TARGET_LLONG128)
-       fatal ("128 bit long longs are not supported");
+       fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit gp registers");
     }
 
+  if (mips_abi != ABI_32)
+    flag_pcc_struct_return = 0;
+
   /* Tell halfpic.c that we have half-pic code if we do.  */
   if (TARGET_HALF_PIC)
     HALF_PIC_INIT ();
@@ -3275,6 +3638,36 @@ override_options ()
   else
     mips_abicalls = MIPS_ABICALLS_NO;
 
+  /* -membedded-pic is a form of PIC code suitable for embedded
+     systems.  All calls are made using PC relative addressing, and
+     all data is addressed using the $gp register.  This requires gas,
+     which does most of the work, and GNU ld, which automatically
+     expands PC relative calls which are out of range into a longer
+     instruction sequence.  All gcc really does differently is
+     generate a different sequence for a switch.  */
+  if (TARGET_EMBEDDED_PIC)
+    {
+      flag_pic = 1;
+      if (TARGET_ABICALLS)
+       warning ("-membedded-pic and -mabicalls are incompatible");
+      if (g_switch_set)
+       warning ("-G and -membedded-pic are incompatible");
+      /* Setting mips_section_threshold is not required, because gas
+        will force everything to be GP addressable anyhow, but
+        setting it will cause gcc to make better estimates of the
+        number of instructions required to access a particular data
+        item.  */
+      mips_section_threshold = 0x7fffffff;
+    }
+
+  /* ??? This does not work when target addresses are DImode.
+     This is because we are missing DImode high/lo_sum patterns.  */
+
+  if (TARGET_GAS && optimize && ! flag_pic && Pmode == SImode)
+    mips_split_addresses = 1;
+  else
+    mips_split_addresses = 0;
+
   /* -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
@@ -3312,24 +3705,6 @@ override_options ()
   sfhigh = REAL_VALUE_ATOF ("1.0e38", SFmode);
   sflow = REAL_VALUE_ATOF ("1.0e-38", SFmode);
 
-  /* Set up the classification arrays now.  */
-  mips_rtx_classify[(int)PLUS]  = CLASS_ADD_OP;
-  mips_rtx_classify[(int)MINUS] = CLASS_ADD_OP;
-  mips_rtx_classify[(int)DIV]   = CLASS_DIVMOD_OP;
-  mips_rtx_classify[(int)MOD]   = CLASS_DIVMOD_OP;
-  mips_rtx_classify[(int)UDIV]  = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP;
-  mips_rtx_classify[(int)UMOD]  = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP;
-  mips_rtx_classify[(int)EQ]    = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP;
-  mips_rtx_classify[(int)NE]    = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP;
-  mips_rtx_classify[(int)GT]    = CLASS_CMP_OP | CLASS_FCMP_OP;
-  mips_rtx_classify[(int)GE]    = CLASS_CMP_OP | CLASS_FCMP_OP;
-  mips_rtx_classify[(int)LT]    = CLASS_CMP_OP | CLASS_FCMP_OP;
-  mips_rtx_classify[(int)LE]    = CLASS_CMP_OP | CLASS_FCMP_OP;
-  mips_rtx_classify[(int)GTU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
-  mips_rtx_classify[(int)GEU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
-  mips_rtx_classify[(int)LTU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
-  mips_rtx_classify[(int)LEU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
-
   mips_print_operand_punct['?'] = TRUE;
   mips_print_operand_punct['#'] = TRUE;
   mips_print_operand_punct['&'] = TRUE;
@@ -3351,7 +3726,9 @@ override_options ()
   mips_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS);
   mips_char_to_class['h'] = HI_REG;
   mips_char_to_class['l'] = LO_REG;
+  mips_char_to_class['a'] = HILO_REG;
   mips_char_to_class['x'] = MD_REGS;
+  mips_char_to_class['b'] = ALL_REGS;
   mips_char_to_class['y'] = GR_REGS;
   mips_char_to_class['z'] = ST_REGS;
 
@@ -3387,8 +3764,15 @@ override_options ()
        {
          register int temp;
 
-         if (mode == CC_FPmode || mode == CC_REV_FPmode)
-           temp = (regno == FPSW_REGNUM);
+         if (mode == CCmode)
+           {
+             if (mips_isa < 4)
+               temp = (regno == FPSW_REGNUM);
+             else
+               temp = (ST_REG_P (regno)
+                       || GP_REG_P (regno)
+                       || FP_REG_P (regno));
+           }
 
          else if (GP_REG_P (regno))
            temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
@@ -3397,17 +3781,13 @@ override_options ()
            temp = ((TARGET_FLOAT64 || ((regno & 1) == 0))
                    && (class == MODE_FLOAT
                        || class == MODE_COMPLEX_FLOAT
-                       || (TARGET_DEBUG_H_MODE && class == MODE_INT)));
+                       || (TARGET_DEBUG_H_MODE && class == MODE_INT))
+                   && (! TARGET_SINGLE_FLOAT || size <= 4));
 
          else if (MD_REG_P (regno))
-           {
-             if (TARGET_64BIT)
-               temp = (mode == DImode
-                       || (regno == MD_REG_FIRST && mode == TImode));
-             else
-               temp = (mode == SImode
-                       || (regno == MD_REG_FIRST && mode == DImode));
-           }
+           temp = (class == MODE_INT
+                   && (size <= UNITS_PER_WORD
+                       || (regno == MD_REG_FIRST && size == 2 * UNITS_PER_WORD)));
 
          else
            temp = FALSE;
@@ -3489,6 +3869,12 @@ mips_debugger_offset (addr, offset)
    'M'  print high-order register of double-word register operand.
    'C'  print part of opcode for a branch condition.
    'N'  print part of opcode for a branch condition, inverted.
+   'S'  X is CODE_LABEL, print with prefix of "LS" (for embedded switch).
+   'B'  print 'z' for EQ, 'n' for NE
+   'b'  print 'n' for EQ, 'z' for NE
+   'T'  print 'f' for EQ, 't' for NE
+   't'  print 't' for EQ, 'f' for NE
+   'Z'  print register and a comma, but print nothing for $fcc0
    '(' Turn on .set noreorder
    ')' Turn on .set reorder
    '[' Turn on .set noat
@@ -3564,7 +3950,7 @@ print_operand (file, op, letter)
          if (set_noreorder != 0)
            fputs ("\n\tnop", file);
 
-         else if (TARGET_GAS || TARGET_STATS)
+         else if (TARGET_STATS)
            fputs ("\n\t#nop", file);
 
          break;
@@ -3635,6 +4021,13 @@ print_operand (file, op, letter)
     }
 
   code = GET_CODE (op);
+
+  if (code == SIGN_EXTEND)
+    {
+      op = XEXP (op, 0);
+      code = GET_CODE (op);
+    }
+
   if (letter == 'C')
     switch (code)
       {
@@ -3650,7 +4043,7 @@ print_operand (file, op, letter)
       case LEU: fputs ("leu", file); break;
 
       default:
-       abort_with_insn (op, "PRINT_OPERAND, illegal insn for %%C");
+       abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%C");
       }
 
   else if (letter == 'N')
@@ -3668,20 +4061,42 @@ print_operand (file, op, letter)
       case LEU: fputs ("gtu", file); break;
 
       default:
-       abort_with_insn (op, "PRINT_OPERAND, illegal insn for %%N");
+       abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%N");
       }
 
-  else if (code == REG)
+  else if (letter == 'S')
+    {
+      char buffer[100];
+
+      ASM_GENERATE_INTERNAL_LABEL (buffer, "LS", CODE_LABEL_NUMBER (op));
+      assemble_name (file, buffer);
+    }
+
+  else if (letter == 'Z')
     {
-      register int regnum = REGNO (op);
+      register int regnum;
+
+      if (code != REG)
+       abort ();
+      regnum = REGNO (op);
+      if (! ST_REG_P (regnum))
+       abort ();
+      if (regnum != ST_REG_FIRST)
+       fprintf (file, "%s,", reg_names[regnum]);
+    }
 
-      if (letter == 'M')
-       regnum += MOST_SIGNIFICANT_WORD;
+  else if (code == REG || code == SUBREG)
+    {
+      register int regnum;
 
-      else if (letter == 'L')
-       regnum += LEAST_SIGNIFICANT_WORD;
+      if (code == REG)
+       regnum = REGNO (op);
+      else
+       regnum = true_regnum (op);
 
-      else if (letter == 'D')
+      if ((letter == 'M' && ! WORDS_BIG_ENDIAN)
+         || (letter == 'L' && WORDS_BIG_ENDIAN)
+         || letter == 'D')
        regnum++;
 
       fprintf (file, "%s", reg_names[regnum]);
@@ -3690,7 +4105,8 @@ print_operand (file, op, letter)
   else if (code == MEM)
     output_address (XEXP (op, 0));
 
-  else if (code == CONST_DOUBLE)
+  else if (code == CONST_DOUBLE
+          && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT)
     {
       REAL_VALUE_TYPE d;
       char s[30];
@@ -3703,9 +4119,14 @@ print_operand (file, op, letter)
   else if ((letter == 'x') && (GET_CODE(op) == CONST_INT))
     fprintf (file, "0x%04x", 0xffff & (INTVAL(op)));
 
-  else if ((letter == 'X') && (GET_CODE(op) == CONST_INT))
+  else if ((letter == 'X') && (GET_CODE(op) == CONST_INT)
+          && HOST_BITS_PER_WIDE_INT == 32)
     fprintf (file, "0x%08x", INTVAL(op));
 
+  else if ((letter == 'X') && (GET_CODE(op) == CONST_INT)
+          && HOST_BITS_PER_WIDE_INT == 64)
+    fprintf (file, "0x%016lx", INTVAL(op));
+
   else if ((letter == 'd') && (GET_CODE(op) == CONST_INT))
     fprintf (file, "%d", (INTVAL(op)));
 
@@ -3717,6 +4138,15 @@ print_operand (file, op, letter)
   else if (letter == 'd' || letter == 'x' || letter == 'X')
     fatal ("PRINT_OPERAND: letter %c was found & insn was not CONST_INT", letter);
 
+  else if (letter == 'B')
+    fputs (code == EQ ? "z" : "n", file);
+  else if (letter == 'b')
+    fputs (code == EQ ? "n" : "z", file);
+  else if (letter == 'T')
+    fputs (code == EQ ? "f" : "t", file);
+  else if (letter == 't')
+    fputs (code == EQ ? "t" : "f", file);
+
   else
     output_addr_const (file, op);
 }
@@ -3743,7 +4173,7 @@ print_operand_address (file, addr)
     switch (GET_CODE (addr))
       {
       default:
-       abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #1");
+       abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1");
        break;
 
       case REG:
@@ -3753,6 +4183,23 @@ print_operand_address (file, addr)
        fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
        break;
 
+      case LO_SUM:
+       {
+         register rtx arg0   = XEXP (addr, 0);
+         register rtx arg1   = XEXP (addr, 1);
+
+         if (! mips_split_addresses)
+           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, Spurious LO_SUM.");
+
+         if (GET_CODE (arg0) != REG)
+           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG.");
+
+         fprintf (file, "%%lo(");
+         print_operand_address (file, arg1);
+         fprintf (file, ")(%s)", reg_names [REGNO (arg0)]);
+       }
+       break;
+
       case PLUS:
        {
          register rtx reg    = (rtx)0;
@@ -3781,10 +4228,10 @@ print_operand_address (file, addr)
            abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs");
 
          if (!CONSTANT_P (offset))
-           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #2");
+           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2");
 
-       if (REGNO (reg) == ARG_POINTER_REGNUM)
-         abort_with_insn (addr, "Arg pointer not eliminated.");
+         if (REGNO (reg) == ARG_POINTER_REGNUM)
+           abort_with_insn (addr, "Arg pointer not eliminated.");
 
          output_addr_const (file, offset);
          fprintf (file, "(%s)", reg_names [REGNO (reg)]);
@@ -3830,8 +4277,44 @@ mips_output_external (file, decl, name)
       p->size = len;
       extern_head = p;
     }
+
+#ifdef ASM_OUTPUT_UNDEF_FUNCTION
+  if (TREE_CODE (decl) == FUNCTION_DECL
+      /* ??? Don't include alloca, since gcc will always expand it
+        inline.  If we don't do this, libg++ fails to build.  */
+      && strcmp (name, "alloca")
+      /* ??? Don't include __builtin_next_arg, because then gcc will not
+        bootstrap under Irix 5.1.  */
+      && strcmp (name, "__builtin_next_arg"))
+    {
+      p = (struct extern_list *)permalloc ((long) sizeof (struct extern_list));
+      p->next = extern_head;
+      p->name = name;
+      p->size = -1;
+      extern_head = p;
+    }
+#endif
+
+  return 0;
+}
+
+#ifdef ASM_OUTPUT_UNDEF_FUNCTION
+int
+mips_output_external_libcall (file, name)
+     FILE *file;
+     char *name;
+{
+  register struct extern_list *p;
+
+  p = (struct extern_list *)permalloc ((long) sizeof (struct extern_list));
+  p->next = extern_head;
+  p->name = name;
+  p->size = -1;
+  extern_head = p;
+
   return 0;
 }
+#endif
 
 \f
 /* Compute a string to use as a temporary file name.  */
@@ -3905,9 +4388,7 @@ mips_output_filename (stream, name)
       first_time = FALSE;
       SET_FILE_NUMBER ();
       current_function_file = name;
-      fprintf (stream, "\t.file\t%d ", num_source_filenames);
-      output_quoted_string (stream, name);
-      fprintf (stream, "\n");
+      ASM_OUTPUT_FILENAME (stream, num_source_filenames, name);
       /* This tells mips-tfile that stabs will follow.  */
       if (!TARGET_GAS && write_symbols == DBX_DEBUG)
        fprintf (stream, "\t#@stabs\n");
@@ -3932,18 +4413,13 @@ mips_output_filename (stream, name)
              ignore_line_number = TRUE;
              warning ("MIPS ECOFF format does not allow changing filenames within functions with #line");
            }
-
-         fprintf (stream, "\t#.file\t%d ", num_source_filenames);
        }
-
       else
        {
          SET_FILE_NUMBER ();
          current_function_file = name;
-         fprintf (stream, "\t.file\t%d ", num_source_filenames);
+         ASM_OUTPUT_FILENAME (stream, num_source_filenames, name);
        }
-      output_quoted_string (stream, name);
-      fprintf (stream, "\n");
     }
 }
 
@@ -3961,8 +4437,9 @@ mips_output_lineno (stream, line)
   if (write_symbols == DBX_DEBUG)
     {
       ++sym_lineno;
-      fprintf (stream, "$LM%d:\n\t%s %d,0,%d,$LM%d\n",
-              sym_lineno, ASM_STABN_OP, N_SLINE, line, sym_lineno);
+      fprintf (stream, "%sLM%d:\n\t%s %d,0,%d,%sLM%d\n",
+              LOCAL_LABEL_PREFIX, sym_lineno, ASM_STABN_OP, N_SLINE, line,
+              LOCAL_LABEL_PREFIX, sym_lineno);
     }
 
   else
@@ -3991,6 +4468,9 @@ mips_output_lineno (stream, line)
    because of load delays, and also to update the delay slot
    statistics.  */
 
+/* ??? There is no real need for this function, because it never actually
+   emits a NOP anymore.  */
+
 void
 final_prescan_insn (insn, opvec, noperands)
      rtx insn;
@@ -4008,21 +4488,18 @@ final_prescan_insn (insn, opvec, noperands)
          || (mips_load_reg2 != (rtx)0 && reg_mentioned_p (mips_load_reg2, pattern))
          || (mips_load_reg3 != (rtx)0 && reg_mentioned_p (mips_load_reg3, pattern))
          || (mips_load_reg4 != (rtx)0 && reg_mentioned_p (mips_load_reg4, pattern)))
-       fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
+       fputs ("\t#nop\n", asm_out_file);
 
       else
        dslots_load_filled++;
 
       while (--dslots_number_nops > 0)
-       fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
+       fputs ("\t#nop\n", asm_out_file);
 
       mips_load_reg  = (rtx)0;
       mips_load_reg2 = (rtx)0;
       mips_load_reg3 = (rtx)0;
       mips_load_reg4 = (rtx)0;
-
-      if (set_noreorder && --set_noreorder == 0)
-       fputs ("\t.set\treorder\n", asm_out_file);
     }
 
   if (TARGET_STATS)
@@ -4064,6 +4541,11 @@ mips_asm_file_start (stream)
     /* ??? but do not want this (or want pic0) if -non-shared? */
     fprintf (stream, "\t%s\n", ABICALLS_ASM_OP);
 
+  /* Start a section, so that the first .popsection directive is guaranteed
+     to have a previously defined section to pop back to.  */
+  if (mips_abi != ABI_32 && mips_abi != ABI_EABI)
+    fprintf (stream, "\t.section\t.text\n");
+
   /* This code exists so that we can put all externs before all symbol
      references.  This is necessary for the assembler's global pointer
      optimizations to work.  */
@@ -4079,7 +4561,10 @@ mips_asm_file_start (stream)
   else
     asm_out_data_file = asm_out_text_file = stream;
 
-  print_options (stream);
+  if (flag_verbose_asm)
+    fprintf (stream, "\n%s -G value = %d, Cpu = %s, ISA = %d\n",
+            ASM_COMMENT_START,
+            mips_section_threshold, mips_cpu_string, mips_isa);
 }
 
 \f
@@ -4101,10 +4586,9 @@ mips_asm_file_end (file)
   if (HALF_PIC_P ())
     HALF_PIC_FINISH (file);
 
-  if (TARGET_GP_OPT)
+  if (extern_head)
     {
-      if (extern_head)
-       fputs ("\n", file);
+      fputs ("\n", file);
 
       for (p = extern_head; p != 0; p = p->next)
        {
@@ -4114,12 +4598,22 @@ mips_asm_file_end (file)
          if (! TREE_ASM_WRITTEN (name_tree))
            {
              TREE_ASM_WRITTEN (name_tree) = 1;
-             fputs ("\t.extern\t", file);
-             assemble_name (file, p->name);
-             fprintf (file, ", %d\n", p->size);
+#ifdef ASM_OUTPUT_UNDEF_FUNCTION
+             if (p->size == -1)
+               ASM_OUTPUT_UNDEF_FUNCTION (file, p->name);
+             else
+#endif
+               {
+                 fputs ("\t.extern\t", file);
+                 assemble_name (file, p->name);
+                 fprintf (file, ", %d\n", p->size);
+               }
            }
        }
-
+    }
+      
+  if (TARGET_GP_OPT)
+    {
       fprintf (file, "\n\t.text\n");
       rewind (asm_out_text_file);
       if (ferror (asm_out_text_file))
@@ -4269,7 +4763,6 @@ epilogue_reg_mentioned_p (insn)
 
   return 0;
 }
-
 \f
 /* Return the bytes needed to compute the frame pointer from the current
    stack pointer.
@@ -4370,7 +4863,7 @@ compute_frame_size (size)
     }
 
   /* Calculate space needed for fp registers.  */
-  if (TARGET_FLOAT64)
+  if (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT)
     {
       fp_inc = 1;
       fp_bits = 1;
@@ -4391,9 +4884,13 @@ compute_frame_size (size)
     }
 
   gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
-  total_size += gp_reg_rounded + fp_reg_size;
+  total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size);
 
-  if (total_size == extra_size)
+  /* The gp reg is caller saved in the 32 bit ABI, so there is no need
+     for leaf routines (total_size == extra_size) to save the gp reg.
+     The gp reg is callee saved in the 64 bit ABI, so all routines must
+     save the gp reg.  */
+  if (total_size == extra_size && (mips_abi == ABI_32 || mips_abi == ABI_EABI))
     total_size = extra_size = 0;
   else if (TARGET_ABICALLS)
     {
@@ -4405,6 +4902,11 @@ compute_frame_size (size)
       total_size += gp_reg_rounded;
     }
 
+  /* Add in space reserved on the stack by the callee for storing arguments
+     passed in registers.  */
+  if (mips_abi != ABI_32)
+    total_size += MIPS_STACK_ALIGN (current_function_pretend_args_size);
+
   /* Save other computed information.  */
   current_frame_info.total_size  = total_size;
   current_frame_info.var_size    = var_size;
@@ -4420,8 +4922,8 @@ compute_frame_size (size)
 
   if (mask)
     {
-      unsigned long offset = args_size + extra_size + var_size
-                            + gp_reg_size - UNITS_PER_WORD;
+      unsigned long offset = (args_size + extra_size + var_size
+                             + gp_reg_size - UNITS_PER_WORD);
       current_frame_info.gp_sp_offset = offset;
       current_frame_info.gp_save_offset = offset - total_size;
     }
@@ -4438,7 +4940,7 @@ compute_frame_size (size)
                              + gp_reg_rounded + fp_reg_size
                              - fp_inc * UNITS_PER_FPREG);
       current_frame_info.fp_sp_offset = offset;
-      current_frame_info.fp_save_offset = offset - total_size + UNITS_PER_WORD;
+      current_frame_info.fp_save_offset = offset - total_size;
     }
   else
     {
@@ -4564,14 +5066,17 @@ save_restore_insns (store_p, large_reg, large_offset, file)
                                                  GEN_INT (gp_offset - base_offset)));
 
                  if (store_p)
-                   emit_move_insn (mem_rtx, reg_rtx);
-                 else if (!TARGET_ABICALLS
+                   {
+                     rtx insn = emit_move_insn (mem_rtx, reg_rtx);
+                     RTX_FRAME_RELATED_P (insn) = 1;
+                   }
+                 else if (!TARGET_ABICALLS || mips_abi != ABI_32
                           || regno != (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
                    emit_move_insn (reg_rtx, mem_rtx);
                }
              else
                {
-                 if (store_p || !TARGET_ABICALLS
+                 if (store_p || !TARGET_ABICALLS || mips_abi != ABI_32
                      || regno != (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
                    fprintf (file, "\t%s\t%s,%ld(%s)\n",
                             (TARGET_64BIT
@@ -4588,14 +5093,14 @@ save_restore_insns (store_p, large_reg, large_offset, file)
     }
   else
     {
-      base_reg_rtx = (rtx)0;           /* Make sure these are initialzed */
+      base_reg_rtx = (rtx)0;           /* Make sure these are initialized */
       base_offset  = 0;
     }
 
   /* Save floating point registers if needed.  */
   if (fmask)
     {
-      int fp_inc = (TARGET_FLOAT64) ? 1 : 2;
+      int fp_inc = (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT) ? 1 : 2;
       int fp_size = fp_inc * UNITS_PER_FPREG;
 
       /* Pick which pointer to use as a base register.  */
@@ -4669,19 +5174,26 @@ save_restore_insns (store_p, large_reg, large_offset, file)
            {
              if (file == (FILE *)0)
                {
-                 rtx reg_rtx = gen_rtx (REG, DFmode, regno);
-                 rtx mem_rtx = gen_rtx (MEM, DFmode,
+                 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 (store_p)
-                   emit_move_insn (mem_rtx, reg_rtx);
+                   {
+                     rtx insn = emit_move_insn (mem_rtx, reg_rtx);
+                     RTX_FRAME_RELATED_P (insn) = 1;
+                   }
                  else
                    emit_move_insn (reg_rtx, mem_rtx);
                }
              else
                fprintf (file, "\t%s\t%s,%ld(%s)\n",
-                        (store_p) ? "s.d" : "l.d",
+                        (TARGET_SINGLE_FLOAT
+                         ? ((store_p) ? "s.s" : "l.s")
+                         : ((store_p) ? "s.d" : "l.d")),
                         reg_names[regno],
                         fp_offset - base_offset,
                         reg_names[REGNO(base_reg_rtx)]);
@@ -4701,20 +5213,31 @@ function_prologue (file, size)
      FILE *file;
      int size;
 {
+  char *fnname;
   long tsize = current_frame_info.total_size;
 
   ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl));
 
-  if (debug_info_level != DINFO_LEVEL_TERSE)
+#ifdef SDB_DEBUGGING_INFO
+  if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG)
     ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
+#endif
 
   inside_function = 1;
+
+#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);
+
   fputs ("\t.ent\t", file);
-  assemble_name (file, current_function_name);
+  assemble_name (file, fnname);
   fputs ("\n", file);
 
-  assemble_name (file, current_function_name);
+  assemble_name (file, fnname);
   fputs (":\n", file);
+#endif
 
   fprintf (file, "\t.frame\t%s,%d,%s\t\t# vars= %d, regs= %d/%d, args= %d, extra= %d\n",
           reg_names[ (frame_pointer_needed) ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM ],
@@ -4732,7 +5255,7 @@ function_prologue (file, size)
           current_frame_info.fmask,
           current_frame_info.fp_save_offset);
 
-  if (TARGET_ABICALLS)
+  if (TARGET_ABICALLS && mips_abi == ABI_32)
     {
       char *sp_str = reg_names[STACK_POINTER_REGNUM];
 
@@ -4760,9 +5283,7 @@ mips_expand_prologue ()
   char *arg_name = (char *)0;
   tree fndecl   = current_function_decl;
   tree fntype   = TREE_TYPE (fndecl);
-  tree fnargs   = (TREE_CODE (fntype) != METHOD_TYPE)
-                       ? DECL_ARGUMENTS (fndecl)
-                       : 0;
+  tree fnargs   = DECL_ARGUMENTS (fndecl);
   rtx next_arg_reg;
   int i;
   tree next_arg;
@@ -4783,17 +5304,22 @@ mips_expand_prologue ()
 
   /* Determine the last argument, and get its name.  */
 
-  INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0);
+  INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0, 0);
   regno = GP_ARG_FIRST;
 
   for (cur_arg = fnargs; cur_arg != (tree)0; cur_arg = next_arg)
     {
-      tree type = DECL_ARG_TYPE (cur_arg);
-      enum machine_mode passed_mode = TYPE_MODE (type);
-      rtx entry_parm = FUNCTION_ARG (args_so_far,
-                                    passed_mode,
-                                    DECL_ARG_TYPE (cur_arg),
-                                    1);
+      tree passed_type = DECL_ARG_TYPE (cur_arg);
+      enum machine_mode passed_mode = TYPE_MODE (passed_type);
+      rtx entry_parm;
+
+      if (TREE_ADDRESSABLE (passed_type))
+       {
+         passed_type = build_pointer_type (passed_type);
+         passed_mode = Pmode;
+       }
+
+      entry_parm = FUNCTION_ARG (args_so_far, passed_mode, passed_type, 1);
 
       if (entry_parm)
        {
@@ -4801,7 +5327,7 @@ mips_expand_prologue ()
 
          /* passed in a register, so will get homed automatically */
          if (GET_MODE (entry_parm) == BLKmode)
-           words = (int_size_in_bytes (type) + 3) / 4;
+           words = (int_size_in_bytes (passed_type) + 3) / 4;
          else
            words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
 
@@ -4813,10 +5339,7 @@ mips_expand_prologue ()
          break;
        }
 
-      FUNCTION_ARG_ADVANCE (args_so_far,
-                           passed_mode,
-                           DECL_ARG_TYPE (cur_arg),
-                           1);
+      FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1);
 
       next_arg = TREE_CHAIN (cur_arg);
       if (next_arg == (tree)0)
@@ -4857,11 +5380,12 @@ mips_expand_prologue ()
 
   /* If this function is a varargs function, store any registers that
      would normally hold arguments ($4 - $7) on the stack.  */
-  if ((TYPE_ARG_TYPES (fntype) != 0
-       && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
-      || (arg_name != (char *)0
-         && ((arg_name[0] == '_' && strcmp (arg_name, "__builtin_va_alist") == 0)
-             || (arg_name[0] == 'v' && strcmp (arg_name, "va_alist") == 0))))
+  if (mips_abi == ABI_32
+      && ((TYPE_ARG_TYPES (fntype) != 0
+          && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
+         || (arg_name != (char *)0
+             && ((arg_name[0] == '_' && strcmp (arg_name, "__builtin_va_alist") == 0)
+                 || (arg_name[0] == 'v' && strcmp (arg_name, "va_alist") == 0)))))
     {
       int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD;
       rtx ptr = stack_pointer_rtx;
@@ -4885,8 +5409,10 @@ mips_expand_prologue ()
       rtx tsize_rtx = GEN_INT (tsize);
 
       /* If we are doing svr4-abi, sp move is done by function_prologue.  */
-      if (!TARGET_ABICALLS)
+      if (!TARGET_ABICALLS || mips_abi != ABI_32)
        {
+         rtx insn;
+
          if (tsize > 32767)
            {
              tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
@@ -4895,22 +5421,31 @@ mips_expand_prologue ()
            }
 
          if (TARGET_LONG64)
-           emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                  tsize_rtx));
+           insn = emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx,
+                                         tsize_rtx));
          else
-           emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                  tsize_rtx));
+           insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
+                                         tsize_rtx));
+
+         RTX_FRAME_RELATED_P (insn) = 1;
        }
 
       save_restore_insns (TRUE, tmp_rtx, tsize, (FILE *)0);
 
       if (frame_pointer_needed)
        {
+         rtx insn;
+
          if (TARGET_64BIT)
-           emit_insn (gen_movdi (frame_pointer_rtx, stack_pointer_rtx));
+           insn= emit_insn (gen_movdi (frame_pointer_rtx, stack_pointer_rtx));
          else
-           emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
+           insn= emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
+
+         RTX_FRAME_RELATED_P (insn) = 1;
        }
+
+      if (TARGET_ABICALLS && mips_abi != ABI_32)
+       emit_insn (gen_loadgp (XEXP (DECL_RTL (current_function_decl), 0)));
     }
 
   /* If we are profiling, make sure no instructions are scheduled before
@@ -4931,11 +5466,12 @@ function_epilogue (file, size)
      FILE *file;
      int size;
 {
+  char *fnname;
   long tsize;
   char *sp_str = reg_names[STACK_POINTER_REGNUM];
   char *t1_str = reg_names[MIPS_TEMP1_REGNUM];
   rtx epilogue_delay = current_function_epilogue_delay_list;
-  int noreorder = !TARGET_MIPS_AS || (epilogue_delay != 0);
+  int noreorder = (epilogue_delay != 0);
   int noepilogue = FALSE;
   int load_nop = FALSE;
   int load_only_r31;
@@ -4958,11 +5494,8 @@ function_epilogue (file, size)
       else
        {
          while (--dslots_number_nops > 0)
-           fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
+           fputs ("\t#nop\n", asm_out_file);
        }
-
-      if (set_noreorder > 0 && --set_noreorder == 0)
-       fputs ("\t.set\treorder\n", file);
     }
 
   if (set_noat != 0)
@@ -5034,7 +5567,8 @@ function_epilogue (file, size)
       save_restore_insns (FALSE, tmp_rtx, tsize, file);
 
       load_only_r31 = (((current_frame_info.mask
-                        & ~ (TARGET_ABICALLS ? PIC_OFFSET_TABLE_MASK : 0))
+                        & ~ (TARGET_ABICALLS && mips_abi == ABI_32
+                             ? PIC_OFFSET_TABLE_MASK : 0))
                        == RA_MASK)
                       && current_frame_info.fmask == 0);
 
@@ -5098,16 +5632,23 @@ function_epilogue (file, size)
        }
     }
 
+#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);
+
   fputs ("\t.end\t", file);
-  assemble_name (file, current_function_name);
+  assemble_name (file, fnname);
   fputs ("\n", file);
+#endif
 
   if (TARGET_STATS)
     {
       int num_gp_regs = current_frame_info.gp_reg_size / 4;
       int num_fp_regs = current_frame_info.fp_reg_size / 8;
       int num_regs    = num_gp_regs + num_fp_regs;
-      char *name      = current_function_name;
+      char *name      = fnname;
 
       if (name[0] == '*')
        name++;
@@ -5259,3 +5800,302 @@ simple_epilogue_p ()
 
   return (compute_frame_size (get_frame_size ())) == 0;
 }
+\f
+/* Choose the section to use for the constant rtx expression X that has
+   mode MODE.  */
+
+mips_select_rtx_section (mode, x)
+     enum machine_mode mode;
+     rtx x;
+{
+  if (TARGET_EMBEDDED_DATA)
+    {
+      /* For embedded applications, always put constants in read-only data,
+        in order to reduce RAM usage.  */
+      READONLY_DATA_SECTION ();
+    }
+  else
+    {
+      /* For hosted applications, always put constants in small data if
+        possible, as this gives the best performance.  */
+     
+      if (GET_MODE_SIZE (mode) <= mips_section_threshold
+         && mips_section_threshold > 0)
+       SMALL_DATA_SECTION ();
+      else
+       READONLY_DATA_SECTION ();
+    }
+}
+
+/* Choose the section to use for DECL.  RELOC is true if its value contains
+   any relocatable expression.  */
+
+mips_select_section (decl, reloc)
+     tree decl;
+     int reloc;
+{
+  int size = int_size_in_bytes (TREE_TYPE (decl));
+
+  if (TARGET_EMBEDDED_PIC
+      && 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.  */
+
+      text_section ();
+    }
+  else if (TARGET_EMBEDDED_DATA)
+    {
+      /* For embedded applications, always put an object in read-only data
+        if possible, in order to reduce RAM usage.  */
+
+      if (((TREE_CODE (decl) == VAR_DECL
+           && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
+           && DECL_INITIAL (decl)
+           && (DECL_INITIAL (decl) == error_mark_node
+               || TREE_CONSTANT (DECL_INITIAL (decl))))
+          /* Deal with calls from output_constant_def_contents.  */
+          || (TREE_CODE (decl) != VAR_DECL
+              && (TREE_CODE (decl) != STRING_CST
+                  || !flag_writable_strings)))
+         && ! (flag_pic && reloc))
+       READONLY_DATA_SECTION ();
+      else if (size > 0 && size <= mips_section_threshold)
+       SMALL_DATA_SECTION ();
+      else
+       data_section ();
+    }
+  else
+    {
+      /* For hosted applications, always put an object in small data if
+        possible, as this gives the best performance.  */
+
+      if (size > 0 && size <= mips_section_threshold)
+       SMALL_DATA_SECTION ();
+      else if (((TREE_CODE (decl) == VAR_DECL
+                && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
+                && DECL_INITIAL (decl)
+                && (DECL_INITIAL (decl) == error_mark_node
+                    || TREE_CONSTANT (DECL_INITIAL (decl))))
+               /* Deal with calls from output_constant_def_contents.  */
+               || (TREE_CODE (decl) != VAR_DECL
+                   && (TREE_CODE (decl) != STRING_CST
+                       || !flag_writable_strings)))
+              && ! (flag_pic && reloc))
+       READONLY_DATA_SECTION ();
+      else
+       data_section ();
+    }
+}
+\f
+#ifdef MIPS_ABI_DEFAULT
+/* Support functions for the 64 bit ABI.  */
+
+/* Return register to use for a function return value with VALTYPE for function
+   FUNC.  */
+
+rtx
+mips_function_value (valtype, func)
+     tree valtype;
+     tree func;
+{
+  int reg = GP_RETURN;
+  enum machine_mode mode = TYPE_MODE (valtype);
+  enum mode_class mclass = GET_MODE_CLASS (mode);
+
+  /* ??? How should we return complex float?  */
+  if (mclass == MODE_FLOAT || mclass == MODE_COMPLEX_FLOAT)
+    {
+      if (TARGET_SINGLE_FLOAT
+         && (mclass == MODE_FLOAT
+             ? GET_MODE_SIZE (mode) > 4
+             : GET_MODE_SIZE (mode) / 2 > 4))
+       reg = GP_RETURN;
+      else
+       reg = FP_RETURN;
+    }
+  else if (TREE_CODE (valtype) == RECORD_TYPE
+          && mips_abi != ABI_32 && 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;
+
+         fields[i++] = field;
+       }
+         
+      /* Must check i, so that we reject structures with no elements.  */
+      if (! field)
+       {
+         if (i == 1)
+           {
+             /* The structure has DImode, but we don't allow DImode values
+                in FP registers, so we use a PARALLEL even though it isn't
+                strictly necessary.  */
+             enum machine_mode field_mode = TYPE_MODE (TREE_TYPE (fields[0]));
+
+             return gen_rtx (PARALLEL, mode,
+                             gen_rtvec (1,
+                                        gen_rtx (EXPR_LIST, VOIDmode,
+                                                 gen_rtx (REG, field_mode, FP_RETURN),
+                                                 const0_rtx)));
+           }
+         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]));
+             int first_offset
+               = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (fields[0]));
+             int second_offset
+               = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (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 / BITS_PER_UNIT)),
+                                        gen_rtx (EXPR_LIST, VOIDmode,
+                                                 gen_rtx (REG, second_mode, FP_RETURN + 2),
+                                                 GEN_INT (second_offset / BITS_PER_UNIT))));
+           }
+       }
+    }
+
+  return gen_rtx (REG, mode, reg);
+}
+
+/* The implementation of FUNCTION_ARG_PASS_BY_REFERENCE.  Return
+   nonzero when an argument must be passed by reference.  */
+
+int
+function_arg_pass_by_reference (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;
+     enum machine_mode mode;
+     tree type;
+     int named;
+{
+  int size;
+
+  if (mips_abi != ABI_EABI)
+    return 0;
+
+  /* ??? How should SCmode be handled?  */
+  if (type == NULL_TREE || mode == DImode || mode == DFmode)
+    return 0;
+
+  size = int_size_in_bytes (type);
+  return size == -1 || size > UNITS_PER_WORD;
+}
+
+#endif
+
+/* This function returns the register class required for a secondary
+   register when copying between one of the registers in CLASS, and X,
+   using MODE.  If IN_P is nonzero, the copy is going from X to the
+   register, otherwise the register is the source.  A return value of
+   NO_REGS means that no secondary register is required.  */
+
+enum reg_class
+mips_secondary_reload_class (class, mode, x, in_p)
+     enum reg_class class;
+     enum machine_mode mode;
+     rtx x;
+     int in_p;
+{
+  int regno = -1;
+
+  if (GET_CODE (x) == SIGN_EXTEND)
+    {
+      int off = 0;
+
+      x = XEXP (x, 0);
+
+      /* We may be called with reg_renumber NULL from regclass.
+        ??? This is probably a bug.  */
+      if (reg_renumber)
+       regno = true_regnum (x);
+      else
+       {
+         while (GET_CODE (x) == SUBREG)
+           {
+             off += SUBREG_WORD (x);
+             x = SUBREG_REG (x);
+           }
+         if (GET_CODE (x) == REG)
+           regno = REGNO (x) + off;
+       }
+    }
+  else if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
+    regno = true_regnum (x);
+
+  /* We always require a general register when copying anything to
+     HILO_REGNUM, except when copying an SImode value from HILO_REGNUM
+     to a general register, or when copying from register 0.  */
+  if (class == HILO_REG && regno != GP_REG_FIRST + 0)
+    {
+      if (! in_p
+         && GP_REG_P (regno)
+         && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode))
+       return NO_REGS;
+      return GR_REGS;
+    }
+  if (regno == HILO_REGNUM)
+    {
+      if (in_p
+         && class == GR_REGS
+         && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode))
+       return NO_REGS;
+      return GR_REGS;
+    }
+
+  /* Copying from HI or LO to anywhere other than a general register
+     requires a general register.  */
+  if (class == HI_REG || class == LO_REG || class == MD_REGS)
+    {
+      if (GP_REG_P (regno))
+       return NO_REGS;
+      return GR_REGS;
+    }
+  if (MD_REG_P (regno))
+    {
+      if (class == GR_REGS)
+       return NO_REGS;
+      return GR_REGS;
+    }
+
+  /* We can only copy a value to a condition code register from a
+     floating point register, and even then we require a scratch
+     floating point register.  We can only copy a value out of a
+     condition code register into a general register.  */
+  if (class == ST_REGS)
+    {
+      if (in_p)
+       return FP_REGS;
+      if (GP_REG_P (regno))
+       return NO_REGS;
+      return GR_REGS;
+    }
+  if (ST_REG_P (regno))
+    {
+      if (! in_p)
+       return FP_REGS;
+      if (class == GR_REGS)
+       return NO_REGS;
+      return GR_REGS;
+    }
+
+  return NO_REGS;
+}