OSDN Git Service

x
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.c
index b7a53c2..cacbf1f 100644 (file)
@@ -1,7 +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.
-   Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
+   Changes by Michael Meissner, meissner@osf.org.
+   64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
+   Brendan Eich, brendan@microunity.com.
 
 This file is part of GNU CC.
 
@@ -17,7 +19,12 @@ 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
+   be replaced with something better designed.  */
 
 #include "config.h"
 #include "rtl.h"
@@ -75,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
@@ -111,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
@@ -186,18 +192,27 @@ 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;
 
-/* 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];
+/* High and low marks for floating point values which we will accept
+   as legitimate constants for LEGITIMATE_CONSTANT_P.  These are
+   initialized in override_options.  */
+REAL_VALUE_TYPE dfhigh, dflow, sfhigh, sflow;
 
 /* Array giving truth value on whether or not a given hard register
    can support a given mode.  */
@@ -213,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];
 
@@ -236,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
@@ -244,15 +265,16 @@ char mips_reg_names[][8] =
 
 char mips_sw_reg_names[][8] =
 {
-  "$0",   "at",   "v0",   "v1",   "a0",   "a1",   "a2",   "a3",
-  "t0",   "t1",   "t2",   "t3",   "t4",   "t5",   "t6",   "t7",
-  "s0",   "s1",   "s2",   "s3",   "s4",   "s5",   "s6",   "s7",
-  "t8",   "t9",   "k0",   "k1",   "gp",   "sp",   "$fp",   "ra",
+  "$zero","$at",  "$v0",  "$v1",  "$a0",  "$a1",  "$a2",  "$a3",
+  "$t0",  "$t1",  "$t2",  "$t3",  "$t4",  "$t5",  "$t6",  "$t7",
+  "$s0",  "$s1",  "$s2",  "$s3",  "$s4",  "$s5",  "$s6",  "$s7",
+  "$t8",  "$t9",  "$k0",  "$k1",  "$gp",  "$sp",  "$fp",  "$ra",
   "$f0",  "$f1",  "$f2",  "$f3",  "$f4",  "$f5",  "$f6",  "$f7",
   "$f8",  "$f9",  "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
   "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
   "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
-  "hi",   "lo",   "$fcr31"
+  "hi",   "lo",   "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
+  "$fcc5","$fcc6","$fcc7","$rap"
 };
 
 /* Map hard register number to register class */
@@ -274,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.  */
@@ -399,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
@@ -419,7 +443,7 @@ large_int (op, mode)
   if (((unsigned long)(value + 32768)) <= 32767)       /* subu reg,$r0,value */
     return FALSE;
 
-  if ((value & 0xffff0000) == value)                   /* lui reg,value>>16 */
+  if ((value & 0x0000ffff) == 0)                       /* lui reg,value>>16 */
     return FALSE;
 
   return TRUE;
@@ -441,7 +465,7 @@ reg_or_0_operand (op, mode)
       return (INTVAL (op) == 0);
 
     case CONST_DOUBLE:
-      if (CONST_DOUBLE_HIGH (op) != 0 || CONST_DOUBLE_LOW (op) != 0)
+      if (op != CONST0_RTX (mode))
        return FALSE;
 
       return TRUE;
@@ -454,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
@@ -484,48 +485,82 @@ 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)
     return FALSE;
 
-  if (CONST_DOUBLE_HIGH (op) == 0 && CONST_DOUBLE_LOW (op) == 0)
+  if (op == CONST0_RTX (mode))
     return TRUE;
 
-#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
-  if (TARGET_MIPS_AS)          /* gas doesn't like li.d/li.s yet */
-    {
-      union { double d; int i[2]; } u;
-      double d;
-
-      u.i[0] = CONST_DOUBLE_LOW (op);
-      u.i[1] = CONST_DOUBLE_HIGH (op);
-      d = u.d;
+  /* ??? li.s does not work right with SGI's Irix 6 assembler.  */
+  if (mips_abi != ABI_32 && mips_abi != ABI_EABI)
+    return FALSE;
 
-      if (d != d)
-       return FALSE;           /* NAN */
+  REAL_VALUE_FROM_CONST_DOUBLE (d, op);
 
-      if (d < 0.0)
-       d = - d;
+  if (REAL_VALUE_ISNAN (d))
+    return FALSE;
 
-      /* Rather than trying to get the accuracy down to the last bit,
-        just use approximate ranges.  */
+  if (REAL_VALUE_NEGATIVE (d))
+    d = REAL_VALUE_NEGATE (d);
 
-      if (mode == DFmode && d > 1.0e-300 && d < 1.0e300)
+  if (mode == DFmode)
+    {
+      if (REAL_VALUES_LESS (d, dfhigh)
+         && REAL_VALUES_LESS (dflow, d))
        return TRUE;
-
-      if (mode == SFmode && d > 1.0e-38 && d < 1.0e+38)
+    }
+  else
+    {
+      if (REAL_VALUES_LESS (d, sfhigh)
+         && REAL_VALUES_LESS (sflow, d))
        return TRUE;
     }
-#endif
 
   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).  */
 
@@ -541,7 +576,10 @@ simple_memory_operand (op, mode)
     return FALSE;
 
   /* dword operations really put out 2 instructions, so eliminate them.  */
-  if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4))
+  /* ??? 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;
 
   /* Decode the address now.  */
@@ -552,6 +590,7 @@ simple_memory_operand (op, mode)
       break;
 
     case REG:
+    case LO_SUM:
       return TRUE;
 
     case CONST_INT:
@@ -585,17 +624,17 @@ simple_memory_operand (op, mode)
     case CONST:
       /* If -G 0, we can never have a GP relative memory operation.
         Also, save some time if not optimizing.  */
-      if (mips_section_threshold == 0 || !optimize || !TARGET_GP_OPT)
+      if (!TARGET_GP_OPT)
        return FALSE;
 
       {
        rtx offset = const0_rtx;
-       addr = eliminate_constant_term (addr, &offset);
+       addr = eliminate_constant_term (XEXP (addr, 0), &offset);
        if (GET_CODE (op) != SYMBOL_REF)
          return FALSE;
 
        /* let's be paranoid.... */
-       if (INTVAL (offset) < 0 || INTVAL (offset) > 0xffff)
+       if (! SMALL_INT (offset))
          return FALSE;
       }
       /* fall through */
@@ -618,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.) */
@@ -631,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.  */
+
+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;
 
-/* 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.  */
+  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 true if OPERAND is valid as a source operand for a move
+   instruction.  */
 
-  return (classify_op (op, mode) & CLASS_CMP_OP) != 0;
+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.
 
@@ -725,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 */
@@ -778,10 +965,12 @@ mips_fill_delay_slot (ret, type, operands, cur_insn)
 
   mode = GET_MODE (set_reg);
   dslots_number_nops = num_nops;
-  mips_load_reg  = set_reg;
-  mips_load_reg2 = (mode == DImode || mode == DFmode)
-                       ? gen_rtx (REG, SImode, REGNO (set_reg) + 1)
-                       : (rtx)0;
+  mips_load_reg = set_reg;
+  if (GET_MODE_SIZE (mode)
+      > (FP_REG_P (REGNO (set_reg)) ? UNITS_PER_FPREG : UNITS_PER_WORD))
+    mips_load_reg2 = gen_rtx (REG, SImode, REGNO (set_reg) + 1);
+  else
+    mips_load_reg2 = 0;
 
   if (type == DELAY_HILO)
     {
@@ -794,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;
 }
 
@@ -836,6 +1022,7 @@ mips_count_memory_refs (op, num)
 
        case REG:
        case CONST_INT:
+       case LO_SUM:
          break;
 
        case PLUS:
@@ -917,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 *
@@ -949,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;
@@ -970,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";
                }
            }
@@ -1001,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))
                {
@@ -1028,11 +1262,23 @@ mips_move_1word (operands, insn, unsignedp)
                 target, so zero/sign extend can use this code as well.  */
              switch (GET_MODE (op1))
                {
-               default:                                                        break;
-               case SFmode: ret = "lw\t%0,%1";                                 break;
-               case SImode: ret = "lw\t%0,%1";                                 break;
-               case HImode: ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1";    break;
-               case QImode: ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1";    break;
+               default:
+                 break;
+               case SFmode:
+                 ret = "lw\t%0,%1";
+                 break;
+               case SImode:
+               case CCmode:
+                 ret = ((unsignedp && TARGET_64BIT)
+                        ? "lwu\t%0,%1"
+                        : "lw\t%0,%1");
+                 break;
+               case HImode:
+                 ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1";
+                 break;
+               case QImode:
+                 ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1";
+                 break;
                }
            }
 
@@ -1050,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))
@@ -1062,15 +1318,23 @@ 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)
        {
-         if (CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0)
+         if (op1 == CONST0_RTX (SFmode))
            {
              if (GP_REG_P (regno0))
                ret = "move\t%0,%.";
@@ -1116,15 +1380,22 @@ mips_move_1word (operands, insn, unsignedp)
                  if (INTVAL (offset) == 0)
                    {
                      delay = DELAY_LOAD;
-                     ret = "lw\t%0,%2";
+                     ret = (unsignedp && TARGET_64BIT
+                            ? "lwu\t%0,%2"
+                            : "lw\t%0,%2");
                    }
                  else
                    {
                      dslots_load_total++;
                      operands[3] = offset;
-                     ret = (SMALL_INT (offset))
-                               ? "lw\t%0,%2%#\n\tadd\t%0,%0,%3"
-                               : "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]";
+                     if (unsignedp && TARGET_64BIT)
+                       ret = (SMALL_INT (offset))
+                                 ? "lwu\t%0,%2%#\n\tadd\t%0,%0,%3"
+                                 : "lwu\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]";
+                     else
+                       ret = (SMALL_INT (offset))
+                                 ? "lw\t%0,%2%#\n\tadd\t%0,%0,%3"
+                                 : "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]";
                    }
                }
            }
@@ -1152,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)
@@ -1191,7 +1468,7 @@ mips_move_1word (operands, insn, unsignedp)
            }
        }
 
-      else if (code1 == CONST_DOUBLE && CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0)
+      else if (code1 == CONST_DOUBLE && op1 == CONST0_RTX (mode))
        {
          switch (mode)
            {
@@ -1250,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);
@@ -1257,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;
@@ -1278,32 +1567,69 @@ mips_move_2words (operands, insn)
              else
                {
                  delay = DELAY_LOAD;
-                 ret = (TARGET_FLOAT64)
-                               ? "dmtc1\t%1,%0"
-                               : "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0";
+                 if (TARGET_FLOAT64)
+                   {
+                     if (!TARGET_64BIT)
+                       abort_with_insn (insn, "Bad move");
+#ifdef TARGET_FP_CALL_32
+                     if (FP_CALL_GP_REG_P (regno1))
+                       ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tdmtc1\t%1,%0";
+                     else
+#endif
+                       ret = "dmtc1\t%1,%0";
+                   }
+                 else
+                   ret = "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0";
                }
            }
 
          else if (FP_REG_P (regno1))
            {
              delay = DELAY_LOAD;
-             ret = (TARGET_FLOAT64)
-                       ? "dmfc1\t%0,%1"
-                       : "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1";
+             if (TARGET_FLOAT64)
+               {
+                 if (!TARGET_64BIT)
+                   abort_with_insn (insn, "Bad move");
+#ifdef TARGET_FP_CALL_32
+                 if (FP_CALL_GP_REG_P (regno0))
+                   ret = "dmfc1\t%0,%1\n\tmfc1\t%D0,%1\n\tdsrl\t%0,32";
+                 else
+#endif
+                   ret = "dmfc1\t%0,%1";
+               }
+             else
+               ret = "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1";
            }
 
          else if (MD_REG_P (regno0) && GP_REG_P (regno1))
            {
              delay = DELAY_HILO;
-             ret = "mthi\t%M1\n\tmtlo\t%L1";
+             if (TARGET_64BIT)
+               {
+                 if (regno0 != HILO_REGNUM)
+                   ret = "mt%0\t%1";
+                 else if (regno1 == 0)
+                   ret = "mtlo\t%.\n\tmthi\t%.";
+               }
+             else
+               ret = "mthi\t%M1\n\tmtlo\t%L1";
            }
 
          else if (GP_REG_P (regno0) && MD_REG_P (regno1))
            {
              delay = DELAY_HILO;
-             ret = "mfhi\t%M0\n\tmflo\t%L0";
+             if (TARGET_64BIT)
+               {
+                 if (regno1 != HILO_REGNUM)
+                   ret = "mf%1\t%0";
+               }
+             else
+               ret = "mfhi\t%M0\n\tmflo\t%L0";
            }
 
+         else if (TARGET_64BIT)
+           ret = "move\t%0,%1";
+
          else if (regno0 != (regno1+1))
            ret = "move\t%0,%1\n\tmove\t%D0,%D1";
 
@@ -1313,31 +1639,55 @@ mips_move_2words (operands, insn)
 
       else if (code1 == CONST_DOUBLE)
        {
-         if (CONST_DOUBLE_HIGH (op1) != 0 || CONST_DOUBLE_LOW (op1) != 0)
+         /* Move zero from $0 unless !TARGET_64BIT and recipient
+            is 64-bit fp reg, in which case generate a constant.  */
+         if (op1 != CONST0_RTX (GET_MODE (op1))
+             || (TARGET_FLOAT64 && !TARGET_64BIT && FP_REG_P (regno0)))
            {
              if (GET_MODE (op1) == DFmode)
                {
                  delay = DELAY_LOAD;
-                 ret = "li.d\t%0,%1";
+#ifdef TARGET_FP_CALL_32
+                 if (FP_CALL_GP_REG_P (regno0))
+                   {
+                     if (TARGET_FLOAT64 && !TARGET_64BIT)
+                       {
+                         split_double (op1, operands + 2, operands + 3);
+                         ret = "li\t%0,%2\n\tli\t%D0,%3";
+                       }
+                     else
+                       ret = "li.d\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32";
+                   }
+                 else
+#endif
+                   ret = "li.d\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";
                }
            }
 
          else
            {
              if (GP_REG_P (regno0))
-               ret = "move\t%0,%.\n\tmove\t%D0,%.";
+               ret = (TARGET_64BIT
+#ifdef TARGET_FP_CALL_32
+                      && ! FP_CALL_GP_REG_P (regno0)
+#endif
+                      )
+                 ? "move\t%0,%."
+                   : "move\t%0,%.\n\tmove\t%D0,%.";
 
              else if (FP_REG_P (regno0))
                {
                  delay = DELAY_LOAD;
-                 ret = (TARGET_FLOAT64)
+                 ret = (TARGET_64BIT)
                                ? "dmtc1\t%.,%0"
                                : "mtc1\t%.,%0\n\tmtc1\t%.,%D0";
                }
@@ -1347,21 +1697,58 @@ mips_move_2words (operands, insn)
       else if (code1 == CONST_INT && INTVAL (op1) == 0)
        {
          if (GP_REG_P (regno0))
-           ret = "move\t%0,%.\n\tmove\t%D0,%.";
+           ret = (TARGET_64BIT)
+                               ? "move\t%0,%."
+                               : "move\t%0,%.\n\tmove\t%D0,%.";
          
          else if (FP_REG_P (regno0))
            {
              delay = DELAY_LOAD;
-             ret = (TARGET_FLOAT64)
+             ret = (TARGET_64BIT)
                                ? "dmtc1\t%.,%0"
-                               : "mtc1\t%.,%0\n\tmtc1\t%.,%D0";
+                               : (TARGET_FLOAT64
+                                  ? "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))
        {
-         operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1);
-         ret = "li\t%M0,%2\n\tli\t%L0,%1";
+         if (TARGET_64BIT)
+           {
+             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)
@@ -1374,6 +1761,21 @@ mips_move_2words (operands, insn)
          if (FP_REG_P (regno0))
            ret = "l.d\t%0,%1";
 
+         else if (TARGET_64BIT)
+           {
+#ifdef TARGET_FP_CALL_32
+             if (FP_CALL_GP_REG_P (regno0))
+               {
+                  if (offsettable_address_p (FALSE, SImode, op1))
+                    ret = "lwu\t%0,%1\n\tlwu\t%D0,4+%1";
+                  else
+                    ret = "ld\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32";
+               }
+             else
+#endif
+               ret = "ld\t%0,%1";
+           }
+
          else if (offsettable_address_p (1, DFmode, XEXP (op1, 0)))
            {
              operands[2] = adj_offsettable_operand (op1, 4);
@@ -1393,6 +1795,16 @@ mips_move_2words (operands, insn)
              ret = volatile_buffer;
            }
        }
+
+      else if (code1 == LABEL_REF
+              || code1 == SYMBOL_REF
+              || code1 == CONST)
+       {
+         if (TARGET_STATS)
+           mips_count_memory_refs (op1, 2);
+
+         ret = "dla\t%0,%a1";
+       }
     }
 
   else if (code0 == MEM)
@@ -1404,6 +1816,16 @@ mips_move_2words (operands, insn)
          if (FP_REG_P (regno1))
            ret = "s.d\t%1,%0";
 
+         else if (TARGET_64BIT)
+           {
+#ifdef TARGET_FP_CALL_32
+             if (FP_CALL_GP_REG_P (regno1))
+               ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tsd\t%1,%0";
+             else
+#endif
+               ret = "sd\t%1,%0";
+           }
+
          else if (offsettable_address_p (1, DFmode, XEXP (op0, 0)))
            {
              operands[2] = adj_offsettable_operand (op0, 4);
@@ -1411,12 +1833,13 @@ mips_move_2words (operands, insn)
            }
        }
 
-      else if (code1 == CONST_DOUBLE
-              && CONST_DOUBLE_HIGH (op1) == 0
-              && CONST_DOUBLE_LOW (op1) == 0
-              && offsettable_address_p (1, DFmode, XEXP (op0, 0)))
+      else if (((code1 == CONST_INT && INTVAL (op1) == 0)
+               || (code1 == CONST_DOUBLE
+                   && op1 == CONST0_RTX (GET_MODE (op1))))
+              && (TARGET_64BIT
+                  || offsettable_address_p (1, DFmode, XEXP (op0, 0))))
        {
-         if (TARGET_FLOAT64)
+         if (TARGET_64BIT)
            ret = "sd\t%.,%0";
          else
            {
@@ -1465,7 +1888,6 @@ mips_address_cost (addr)
       break;
 
     case LO_SUM:
-    case HIGH:
       return 1;
 
     case LABEL_REF:
@@ -1474,14 +1896,14 @@ mips_address_cost (addr)
     case CONST:
       {
        rtx offset = const0_rtx;
-       addr = eliminate_constant_term (addr, &offset);
+       addr = eliminate_constant_term (XEXP (addr, 0), &offset);
        if (GET_CODE (addr) == LABEL_REF)
          return 2;
 
        if (GET_CODE (addr) != SYMBOL_REF)
          return 4;
 
-       if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767)
+       if (! SMALL_INT (offset))
          return 2;
       }
       /* fall through */
@@ -1509,10 +1931,7 @@ mips_address_cost (addr)
            break;
 
          case CONST_INT:
-           {
-             int value = INTVAL (plus1);
-             return (value < -32768 || value > 32767) ? 2 : 1;
-           }
+           return (SMALL_INT (plus1) ? 1 : 2);
 
          case CONST:
          case SYMBOL_REF:
@@ -1527,6 +1946,22 @@ mips_address_cost (addr)
   return 4;
 }
 
+/* Return true if X is an address which needs a temporary register when 
+   reloaded while generating PIC code.  */
+
+int
+pic_address_needs_scratch (x)
+     rtx x;
+{
+  /* An address which is a symbolic plus a non SMALL_INT needs a temp reg.  */
+  if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
+      && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
+      && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+      && ! SMALL_INT (XEXP (XEXP (x, 0), 1)))
+    return 1;
+
+  return 0;
+}
 \f
 /* Make normal rtx_code into something we can index from an array */
 
@@ -1557,7 +1992,24 @@ map_test_to_internal_test (test_code)
 \f
 /* Generate the code to compare two integer values.  The return value is:
    (reg:SI xx)         The pseudo register the comparison is in
-   (rtx)0              No register, generate a simple branch.  */
+   (rtx)0              No register, generate a simple branch.
+
+   ??? This is called with result nonzero by the Scond patterns in
+   mips.md.  These patterns are called with a target in the mode of
+   the Scond instruction pattern.  Since this must be a constant, we
+   must use SImode.  This means that if RESULT is non-zero, it will
+   always be an SImode register, even if TARGET_64BIT is true.  We
+   cope with this by calling convert_move rather than emit_move_insn.
+   This will sometimes lead to an unnecessary extension of the result;
+   for example:
+
+   long long
+   foo (long long i)
+   {
+     return i < 5;
+   }
+
+   */
 
 rtx
 gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
@@ -1594,6 +2046,7 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
   };
 
   enum internal_test test;
+  enum machine_mode mode;
   struct cmp_info *p_info;
   int branch_p;
   int eqne_p;
@@ -1608,6 +2061,10 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
   p_info = &info[ (int)test ];
   eqne_p = (p_info->test_code == XOR);
 
+  mode = GET_MODE (cmp0);
+  if (mode == VOIDmode)
+    mode = GET_MODE (cmp1);
+
   /* Eliminate simple branches */
   branch_p = (result == (rtx)0);
   if (branch_p)
@@ -1624,18 +2081,28 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
        }
 
       /* allocate a pseudo to calculate the value in.  */
-      result = gen_reg_rtx (SImode);
+      result = gen_reg_rtx (mode);
     }
 
   /* Make sure we can handle any constants given to us.  */
   if (GET_CODE (cmp0) == CONST_INT)
-    cmp0 = force_reg (SImode, cmp0);
+    cmp0 = force_reg (mode, cmp0);
 
   if (GET_CODE (cmp1) == CONST_INT)
     {
       HOST_WIDE_INT value = INTVAL (cmp1);
-      if (value < p_info->const_low || value > p_info->const_high)
-       cmp1 = force_reg (SImode, cmp1);
+      if (value < p_info->const_low
+         || value > p_info->const_high
+         /* ??? Why?  And why wasn't the similar code below modified too?  */
+         || (TARGET_64BIT
+             && HOST_BITS_PER_WIDE_INT < 64
+             && p_info->const_add != 0
+             && ((p_info->unsignedp
+                  ? ((unsigned HOST_WIDE_INT) (value + p_info->const_add)
+                     > INTVAL (cmp1))
+                  : (value + p_info->const_add) > INTVAL (cmp1))
+                 != (p_info->const_add > 0))))
+       cmp1 = force_reg (mode, cmp1);
     }
 
   /* See if we need to invert the result.  */
@@ -1658,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))
@@ -1685,25 +2152,25 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
     reg = cmp0;
   else
     {
-      reg = (invert || eqne_p) ? gen_reg_rtx (SImode) : result;
-      emit_move_insn (reg, gen_rtx (p_info->test_code, SImode, cmp0, cmp1));
+      reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result;
+      convert_move (reg, gen_rtx (p_info->test_code, mode, cmp0, cmp1), 0);
     }
 
   if (test == ITEST_NE)
     {
-      emit_move_insn (result, gen_rtx (GTU, SImode, reg, const0_rtx));
+      convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0);
       invert = FALSE;
     }
 
   else if (test == ITEST_EQ)
     {
-      reg2 = (invert) ? gen_reg_rtx (SImode) : result;
-      emit_move_insn (reg2, gen_rtx (LTU, SImode, reg, const1_rtx));
+      reg2 = (invert) ? gen_reg_rtx (mode) : result;
+      convert_move (reg2, gen_rtx (LTU, mode, reg, const1_rtx), 0);
       reg = reg2;
     }
 
   if (invert)
-    emit_move_insn (result, gen_rtx (XOR, SImode, reg, const1_rtx));
+    convert_move (result, gen_rtx (XOR, mode, reg, const1_rtx), 0);
 
   return result;
 }
@@ -1711,106 +2178,74 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
 \f
 /* Emit the common code for doing conditional branches.
    operand[0] is the label to jump to.
-   The comparison operands are saved away by cmp{si,sf,df}.  */
+   The comparison operands are saved away by cmp{si,di,sf,df}.  */
 
 void
 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_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 = SImode;
-      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 (branch_type)
+  switch (type)
     {
     default:
-      goto fail;
+      abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test");
 
     case CMP_SI:
-      reg = gen_int_relational (test_code, (rtx)0, cmp0, cmp1, &invert);
-      if (reg != (rtx)0)
+    case CMP_DI:
+      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 (SImode, 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;
@@ -1823,164 +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
-#define UNITS_PER_SHORT (SHORT_TYPE_SIZE / BITS_PER_UNIT)
-
-/* Internal code to generate the load and store of one word/short/byte.
-   The load is emitted directly, and the store insn is returned.  */
+/* Emit the common code for conditional moves.  OPERANDS is the array
+   of operands passed to the conditional move defined_expand.  */
 
-#if 0
-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 */
+void
+gen_conditional_move (operands)
+     rtx *operands;
 {
-  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_WORD && align >= UNITS_PER_WORD)
-    {
-      mode = SImode;
-      size = UNITS_PER_WORD;
-      load_func = gen_movsi;
-      store_func = gen_movsi;
-    }
-
-#if 0
-  /* Don't generate unaligned moves here, rather defer those to the
-     general movestrsi_internal pattern.  */
-  else if (bytes >= UNITS_PER_WORD)
-    {
-      mode = SImode;
-      size = UNITS_PER_WORD;
-      load_func = gen_movsi_ulw;
-      store_func = gen_movsi_usw;
-    }
-#endif
-
-  else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT)
+  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_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:
 
@@ -2008,15 +2381,16 @@ block_move_sequence (dest_reg, src_reg, bytes, align, orig_src)
 #define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
 
 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;
@@ -2035,19 +2409,41 @@ block_move_loop (dest_reg, src_reg, bytes, align, orig_src)
 
   if (bytes > 0x7fff)
     {
-      emit_insn (gen_movsi (final_src, bytes_rtx));
-      emit_insn (gen_addsi3 (final_src, final_src, src_reg));
+      if (TARGET_LONG64)
+       {
+         emit_insn (gen_movdi (final_src, bytes_rtx));
+         emit_insn (gen_adddi3 (final_src, final_src, src_reg));
+       }
+      else
+       {
+         emit_insn (gen_movsi (final_src, bytes_rtx));
+         emit_insn (gen_addsi3 (final_src, final_src, src_reg));
+       }
     }
   else
-    emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx));
+    {
+      if (TARGET_LONG64)
+       emit_insn (gen_adddi3 (final_src, src_reg, bytes_rtx));
+      else
+       emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx));
+    }
 
   emit_label (label);
 
   bytes_rtx = GEN_INT (MAX_MOVE_BYTES);
   emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx));
-  emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx));
-  emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx));
-  emit_insn (gen_cmpsi (src_reg, final_src));
+  if (TARGET_LONG64)
+    {
+      emit_insn (gen_adddi3 (src_reg, src_reg, bytes_rtx));
+      emit_insn (gen_adddi3 (dest_reg, dest_reg, bytes_rtx));
+      emit_insn (gen_cmpdi (src_reg, final_src));
+    }
+  else
+    {
+      emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx));
+      emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx));
+      emit_insn (gen_cmpsi (src_reg, final_src));
+    }
   emit_jump_insn (gen_bne (label));
 
   if (leftover)
@@ -2055,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.  */
 
@@ -2065,18 +2460,29 @@ block_move_call (dest_reg, src_reg, bytes_rtx)
      rtx src_reg;
      rtx bytes_rtx;
 {
+  /* We want to pass the size as Pmode, which will normally be SImode
+     but will be DImode if we are using 64 bit longs and pointers.  */
+  if (GET_MODE (bytes_rtx) != VOIDmode
+      && GET_MODE (bytes_rtx) != Pmode)
+    bytes_rtx = convert_to_mode (Pmode, bytes_rtx, TRUE);
+
 #ifdef TARGET_MEM_FUNCTIONS
   emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0,
                     VOIDmode, 3,
                     dest_reg, Pmode,
                     src_reg, Pmode,
-                    bytes_rtx, SImode);
+                    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, SImode);
+                    convert_to_mode (TYPE_MODE (integer_type_node),
+                                      bytes_rtx,
+                                      TREE_UNSIGNED (integer_type_node)),
+                    TYPE_MODE (integer_type_node));
 #endif
 }
 
@@ -2098,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;
 
@@ -2108,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)
     {
@@ -2140,35 +2544,39 @@ expand_block_move (operands)
 
       bytes -= leftover;
 
-      emit_insn (gen_iorsi3 (temp, src_reg, dest_reg));
-      emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1)));
-      emit_insn (gen_cmpsi (temp, const0_rtx));
+      if (TARGET_LONG64)
+       {
+         emit_insn (gen_iordi3 (temp, src_reg, dest_reg));
+         emit_insn (gen_anddi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1)));
+         emit_insn (gen_cmpdi (temp, const0_rtx));
+       }
+      else
+       {
+         emit_insn (gen_iorsi3 (temp, src_reg, dest_reg));
+         emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1)));
+         emit_insn (gen_cmpsi (temp, const0_rtx));
+       }
       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
@@ -2258,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);
            }
        }
 
@@ -2272,25 +2683,98 @@ 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;
 
-      if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD)
+      if (TARGET_64BIT && bytes >= 8 && align >= 8)
+       {
+         load_store[num].load       = "ld\t%0,%1";
+         load_store[num].load_nop   = "ld\t%0,%1%#";
+         load_store[num].store      = "sd\t%0,%1";
+         load_store[num].last_store = "sd\t%0,%1";
+         load_store[num].final      = (char *)0;
+         load_store[num].mode       = DImode;
+         offset += 8;
+         bytes -= 8;
+       }
+
+      /* ??? 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";
+           }
+         load_store[num].mode = DImode;
+         offset += 8;
+         bytes -= 8;
+         use_lwl_lwr = TRUE;
+       }
+
+      else if (bytes >= 4 && align >= 4)
        {
          load_store[num].load       = "lw\t%0,%1";
          load_store[num].load_nop   = "lw\t%0,%1%#";
@@ -2298,42 +2782,44 @@ output_block_move (insn, operands, num_regs, move_type)
          load_store[num].last_store = "sw\t%0,%1";
          load_store[num].final      = (char *)0;
          load_store[num].mode       = SImode;
-         offset += UNITS_PER_WORD;
-         bytes -= UNITS_PER_WORD;
+         offset += 4;
+         bytes -= 4;
        }
 
-      else if (bytes >= UNITS_PER_WORD)
+      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 += UNITS_PER_WORD;
-         bytes -= UNITS_PER_WORD;
+         offset += 4;
+         bytes -= 4;
          use_lwl_lwr = TRUE;
        }
 
-      else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT)
+      else if (bytes >= 2 && align >= 2)
        {
          load_store[num].load       = "lh\t%0,%1";
          load_store[num].load_nop   = "lh\t%0,%1%#";
          load_store[num].store      = "sh\t%0,%1";
          load_store[num].last_store = "sh\t%0,%1";
          load_store[num].final      = (char *)0;
-         load_store[num].offset     = offset;
          load_store[num].mode       = HImode;
-         offset += UNITS_PER_SHORT;
-         bytes -= UNITS_PER_SHORT;
+         offset += 2;
+         bytes -= 2;
        }
 
       else
@@ -2391,8 +2877,14 @@ output_block_move (insn, operands, num_regs, move_type)
                                          plus_constant (src_reg, offset));
 
                  if (use_lwl_lwr)
-                   xoperands[2] = gen_rtx (MEM, load_store[i].mode,
-                                           plus_constant (src_reg, UNITS_PER_WORD-1+offset));
+                   {
+                     int extra_offset;
+                     extra_offset = GET_MODE_SIZE (load_store[i].mode) - 1;
+                     xoperands[2] = gen_rtx (MEM, load_store[i].mode,
+                                             plus_constant (src_reg,
+                                                            extra_offset
+                                                            + offset));
+                   }
 
                  output_asm_insn (load_store[i].load, xoperands);
                }
@@ -2409,8 +2901,14 @@ output_block_move (insn, operands, num_regs, move_type)
 
 
              if (use_lwl_lwr)
-               xoperands[2] = gen_rtx (MEM, load_store[i].mode,
-                                       plus_constant (dest_reg, UNITS_PER_WORD-1+offset));
+               {
+                 int extra_offset;
+                 extra_offset = GET_MODE_SIZE (load_store[i].mode) - 1;
+                 xoperands[2] = gen_rtx (MEM, load_store[i].mode,
+                                         plus_constant (dest_reg,
+                                                        extra_offset
+                                                        + offset));
+               }
 
              if (move_type == BLOCK_MOVE_NORMAL)
                output_asm_insn (load_store[i].store, xoperands);
@@ -2429,13 +2927,10 @@ output_block_move (insn, operands, num_regs, move_type)
            }
 
          num = 0;              /* reset load_store */
-         use_lwl_lwr = FALSE;  /* reset whether or not we used lwl/lwr */
+         use_lwl_lwr = FALSE;
        }
     }
 
-  if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && --set_noreorder == 0)
-    output_asm_insn (".set\treorder", operands);
-
   return "";
 }
 
@@ -2512,25 +3007,33 @@ function_arg_advance (cum, mode, type, named)
          && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
        abort ();
       cum->gp_reg_found = 1;
-      cum->arg_words += (GET_MODE_SIZE (mode) + 3) / 4;
+      cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
+                        / UNITS_PER_WORD);
       break;
 
     case BLKmode:
       cum->gp_reg_found = 1;
-      cum->arg_words += (int_size_in_bytes (type) + 3) / 4;
+      cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
+                        / UNITS_PER_WORD);
       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 += 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:
       cum->gp_reg_found = 1;
-      cum->arg_words += 2;
+      cum->arg_words += (TARGET_64BIT ? 1 : 2);
       break;
 
     case QImode:
@@ -2542,8 +3045,8 @@ function_arg_advance (cum, mode, type, named)
     }
 }
 
-/* Return a RTL expression containing the register for the given mode,
-   or 0 if the argument is too be passed on the stack.  */
+/* Return an RTL expression containing the register for the given mode,
+   or 0 if the argument is to be passed on the stack.  */
 
 struct rtx_def *
 function_arg (cum, mode, type, named)
@@ -2555,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));
@@ -2565,24 +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)
-       regbase = GP_ARG_FIRST;
-      else {
-       regbase = (TARGET_SOFT_FLOAT) ? GP_ARG_FIRST : FP_ARG_FIRST;
-       if (cum->arg_words == 1)        /* first arg was float */
-         bias = 1;                     /* use correct reg */
-      }
-
+      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;
+       }
+      else
+       regbase = (TARGET_SOFT_FLOAT || ! named ? GP_ARG_FIRST : FP_ARG_FIRST);
       break;
 
     case DFmode:
-      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 (! TARGET_64BIT)
+       {
+         if (mips_abi == ABI_EABI
+             && ! TARGET_SOFT_FLOAT
+             && ! TARGET_SINGLE_FLOAT)
+           cum->fp_arg_words += cum->fp_arg_words & 1;
+         else
+           cum->arg_words += cum->arg_words & 1;
+       }
+      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:
@@ -2592,9 +3135,9 @@ function_arg (cum, mode, type, named)
 
       /* Drops through.  */
     case BLKmode:
-      if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD)
+      if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD
+         && ! TARGET_64BIT && mips_abi != ABI_EABI)
        cum->arg_words += (cum->arg_words & 1);
-
       regbase = GP_ARG_FIRST;
       break;
 
@@ -2606,11 +3149,12 @@ function_arg (cum, mode, type, named)
       break;
 
     case DImode:
-      cum->arg_words += (cum->arg_words & 1);
+      if (! TARGET_64BIT)
+       cum->arg_words += (cum->arg_words & 1);
       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]" : "");
@@ -2622,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
@@ -2634,19 +3253,29 @@ function_arg (cum, mode, type, named)
         This also makes varargs work.  If we have such a structure,
         we save the adjustment RTL, and the call define expands will
         emit them.  For the VOIDmode argument (argument after the
-        last real argument, pass back a parallel vector holding each
+        last real argument), pass back a parallel vector holding each
         of the adjustments.  */
 
       /* ??? function_arg can be called more than once for each argument.
         As a result, we compute more adjustments than we need here.
         See the CUMULATIVE_ARGS definition in mips.h.  */
 
-      if (struct_p && int_size_in_bytes (type) < 4)
+      /* ??? This scheme requires everything smaller than the word size to
+        shifted to the left, but when TARGET_64BIT and ! TARGET_INT64,
+        that would mean every int needs to be shifted left, which is very
+        inefficient.  Let's not carry this compatibility to the 64 bit
+        calling convention for now.  */
+
+      if (struct_p && int_size_in_bytes (type) < UNITS_PER_WORD
+         && ! TARGET_64BIT && mips_abi != ABI_EABI)
        {
          rtx amount = GEN_INT (BITS_PER_WORD
                                - int_size_in_bytes (type) * BITS_PER_UNIT);
-         rtx reg = gen_rtx (REG, SImode, regbase + cum->arg_words + bias);
-         cum->adjust[ cum->num_adjusts++ ] = gen_ashlsi3 (reg, reg, amount);
+         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
+           cum->adjust[ cum->num_adjusts++ ] = gen_ashlsi3 (reg, reg, amount);
        }
     }
 
@@ -2667,13 +3296,15 @@ 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)
-       words = (int_size_in_bytes (type) + 3) / 4;
+       words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
+                / UNITS_PER_WORD);
       else
-       words = (GET_MODE_SIZE (mode) + 3) / 4;
+       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 */
@@ -2685,7 +3316,9 @@ function_arg_partial_nregs (cum, mode, type, named)
       return MAX_ARGS_IN_REGISTERS - cum->arg_words;
     }
 
-  else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1)
+  else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1
+          && ! TARGET_64BIT
+          && mips_abi != ABI_EABI)
     {
       if (TARGET_DEBUG_E_MODE)
        fprintf (stderr, "function_arg_partial_nregs = 1\n");
@@ -2695,82 +3328,6 @@ function_arg_partial_nregs (cum, mode, type, named)
 
   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;
-
-  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
-
-  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;
-       }
-    }
-
-  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++)
-    {
-      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;
-       }
-    }
-
-  fputs ("\n\n", out);
-}
-
 \f
 /* Abort after printing out a specific insn.  */
 
@@ -2821,19 +3378,143 @@ override_options ()
 
   mips_section_threshold = (g_switch_set) ? g_switch_value : MIPS_DEFAULT_GVALUE;
 
+  if (mips_section_threshold <= 0)
+    target_flags &= ~MASK_GPOPT;
+  else if (optimize)
+    target_flags |= MASK_GPOPT;
+
+  /* Get the architectural level.  */
+  if (mips_isa_string == (char *)0)
+    {
+#ifdef MIPS_ISA_DEFAULT
+      mips_isa = MIPS_ISA_DEFAULT;
+#else
+      mips_isa = 1;
+#endif
+    }
+
+  else if (isdigit (*mips_isa_string))
+    {
+      mips_isa = atoi (mips_isa_string);
+      if (mips_isa < 1 || mips_isa > 4)
+       {
+         error ("-mips%d not supported", mips_isa);
+         mips_isa = 1;
+       }
+    }
+
+  else
+    {
+      error ("bad value (%s) for -mips switch", mips_isa_string);
+      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")
       || !strcmp (mips_cpu_string, "DEFAULT"))
     {
-      mips_cpu_string = "default";
-      mips_cpu = PROCESSOR_DEFAULT;
+      switch (mips_isa)
+       {
+       default:
+         mips_cpu_string = "3000";
+         mips_cpu = PROCESSOR_R3000;
+         break;
+       case 2:
+         mips_cpu_string = "6000";
+         mips_cpu = PROCESSOR_R6000;
+         break;
+       case 3:
+         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++;
 
@@ -2856,14 +3537,53 @@ 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':
          if (!strcmp (p, "6000") || !strcmp (p, "6k") || !strcmp (p, "6K"))
            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);
@@ -2871,71 +3591,90 @@ override_options ()
        }
     }
 
-  /* Now get the architectural level.  */
-  if (mips_isa_string == (char *)0)
-    mips_isa = 1;
-
-  else if (isdigit (*mips_isa_string))
-    mips_isa = atoi (mips_isa_string);
-
-  else
-    {
-      error ("bad value (%s) for -mips switch", mips_isa_string);
-      mips_isa = 1;
-    }
-
-  if (mips_isa < 0 || mips_isa > 3)
-    error ("-mips%d not supported", mips_isa);
-
-  else if (mips_isa > 1
-          && (mips_cpu == PROCESSOR_DEFAULT || mips_cpu == PROCESSOR_R3000))
-    error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa);
-
-  else if (mips_cpu == PROCESSOR_R6000 && mips_isa > 2)
+  if ((mips_cpu == PROCESSOR_R3000 && mips_isa > 1)
+      || (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 the r4000 can support 64 bit ints");
+       fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit ints");
 
       else if (TARGET_LONG64)
-       fatal ("Only the r4000 can support 64 bit longs");
-
-      else if (TARGET_LLONG128)
-       fatal ("Only the r4000 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 the r4000 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 or MIPS-IV CPUs can support 64 bit gp registers");
     }
-  else if (TARGET_INT64 || TARGET_LONG64 || TARGET_LLONG128 || TARGET_FLOAT64)
-    warning ("r4000 64/128 bit types not yet supported");
+
+  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 ();
 
+  /* -fpic (-KPIC) is the default when TARGET_ABICALLS is defined.  We need
+     to set flag_pic so that the LEGITIMATE_PIC_OPERAND_P macro will work.  */
+  /* ??? -non_shared turns off pic code generation, but this is not
+     implemented.  */
   if (TARGET_ABICALLS)
-    mips_abicalls = MIPS_ABICALLS_YES;
+    {
+      mips_abicalls = MIPS_ABICALLS_YES;
+      flag_pic = 1;
+      if (mips_section_threshold > 0)
+       warning ("-G is incompatible with PIC code which is the default");
+    }
   else
     mips_abicalls = MIPS_ABICALLS_NO;
 
+  /* -membedded-pic is a form of PIC code suitable for embedded
+     systems.  All calls are made using PC relative addressing, and
+     all data is addressed using the $gp register.  This requires gas,
+     which does most of the work, and GNU ld, which automatically
+     expands PC relative calls which are out of range into a longer
+     instruction sequence.  All gcc really does differently is
+     generate a different sequence for a switch.  */
+  if (TARGET_EMBEDDED_PIC)
+    {
+      flag_pic = 1;
+      if (TARGET_ABICALLS)
+       warning ("-membedded-pic and -mabicalls are incompatible");
+      if (g_switch_set)
+       warning ("-G and -membedded-pic are incompatible");
+      /* Setting mips_section_threshold is not required, because gas
+        will force everything to be GP addressable anyhow, but
+        setting it will cause gcc to make better estimates of the
+        number of instructions required to access a particular data
+        item.  */
+      mips_section_threshold = 0x7fffffff;
+    }
+
+  /* ??? This 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).
+     names instead of the hardware names (ie, $a0 instead of $4).
      We do this by switching the names in mips_reg_names, which the
      reg_names points into via the REGISTER_NAMES macro.  */
 
   if (TARGET_NAME_REGS)
-    {
-      if (TARGET_GAS)
-       {
-         target_flags &= ~ MASK_NAME_REGS;
-         error ("Gas does not support the MIPS software register name convention.");
-       }
-      else
-       bcopy ((char *) mips_sw_reg_names, (char *) mips_reg_names, sizeof (mips_reg_names));
-    }
+    bcopy ((char *) mips_sw_reg_names, (char *) mips_reg_names, sizeof (mips_reg_names));
 
   /* If this is OSF/1, set up a SIGINFO handler so we can see what function
      is currently being compiled.  */
@@ -2958,23 +3697,13 @@ override_options ()
 #endif
 #endif
 
-  /* 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;
+  /* Initialize the high and low values for legitimate floating point
+     constants.  Rather than trying to get the accuracy down to the
+     last bit, just use approximate ranges.  */
+  dfhigh = REAL_VALUE_ATOF ("1.0e300", DFmode);
+  dflow = REAL_VALUE_ATOF ("1.0e-300", DFmode);
+  sfhigh = REAL_VALUE_ATOF ("1.0e38", SFmode);
+  sflow = REAL_VALUE_ATOF ("1.0e-38", SFmode);
 
   mips_print_operand_punct['?'] = TRUE;
   mips_print_operand_punct['#'] = TRUE;
@@ -2997,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;
 
@@ -3033,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));
@@ -3043,10 +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))
-           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;
@@ -3128,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
@@ -3203,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;
@@ -3274,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)
       {
@@ -3289,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')
@@ -3307,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]);
@@ -3329,30 +4105,28 @@ 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)
     {
-#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
-      union { double d; int i[2]; } u;
-      u.i[0] = CONST_DOUBLE_LOW (op);
-      u.i[1] = CONST_DOUBLE_HIGH (op);
-      if (GET_MODE (op) == SFmode)
-       {
-         float f;
-         f = u.d;
-         u.d = f;
-       }
-      fprintf (file, "%.20e", u.d);
-#else
-      fatal ("CONST_DOUBLE found in cross compilation");
-#endif
+      REAL_VALUE_TYPE d;
+      char s[30];
+
+      REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+      REAL_VALUE_TO_DECIMAL (d, "%.20e", s);
+      fprintf (file, s);
     }
 
   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)));
 
@@ -3364,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);
 }
@@ -3390,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:
@@ -3400,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;
@@ -3428,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)]);
@@ -3468,7 +4268,6 @@ mips_output_external (file, decl, name)
   int len;
 
   if (TARGET_GP_OPT
-      && mips_section_threshold != 0
       && ((TREE_CODE (decl)) != FUNCTION_DECL)
       && ((len = int_size_in_bytes (TREE_TYPE (decl))) > 0))
     {
@@ -3478,12 +4277,56 @@ 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.  */
 
+/* On MSDOS, write temp files in current dir
+   because there's no place else we can expect to use.  */
+#if __MSDOS__
+#ifndef P_tmpdir
+#define P_tmpdir "./"
+#endif
+#endif
+
 static FILE *
 make_temp_file ()
 {
@@ -3505,19 +4348,25 @@ make_temp_file ()
     }
 
   len = strlen (base);
-  temp_filename = (char *) alloca (len + sizeof("/ccXXXXXX"));
+  /* temp_filename is global, so we must use malloc, not alloca.  */
+  temp_filename = (char *) xmalloc (len + sizeof("/ctXXXXXX"));
   strcpy (temp_filename, base);
   if (len > 0 && temp_filename[len-1] != '/')
     temp_filename[len++] = '/';
 
-  strcpy (temp_filename + len, "ccXXXXXX");
+  strcpy (temp_filename + len, "ctXXXXXX");
   mktemp (temp_filename);
 
   stream = fopen (temp_filename, "w+");
   if (!stream)
     pfatal_with_name (temp_filename);
 
+#ifndef __MSDOS__
+  /* In MSDOS, we cannot unlink the temporary file until we are finished using
+     it.  Otherwise, we delete it now, so that it will be gone even if the
+     compiler happens to crash.  */
   unlink (temp_filename);
+#endif
   return stream;
 }
 
@@ -3539,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");
@@ -3566,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");
     }
 }
 
@@ -3595,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
@@ -3625,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;
@@ -3642,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)
@@ -3698,6 +4541,18 @@ 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.  */
+  /* ??? Current versions of gas do not require that externs occur before
+     symbol references.  This means that this code is unnecessary when
+     gas is being used.  This gas feature hasn't been well tested as yet
+     though.  */
   if (TARGET_GP_OPT)
     {
       asm_out_data_file = stream;
@@ -3706,10 +4561,10 @@ mips_asm_file_start (stream)
   else
     asm_out_data_file = asm_out_text_file = stream;
 
-  if (TARGET_NAME_REGS)
-    fprintf (asm_out_file, "#include <regdef.h>\n");
-
-  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
@@ -3731,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)
        {
@@ -3744,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))
@@ -3764,6 +4628,10 @@ mips_asm_file_end (file)
 
       if (fclose (asm_out_text_file) != 0)
        pfatal_with_name (temp_filename);
+
+#ifdef __MSDOS__
+      unlink (temp_filename);
+#endif
     }
 }
 
@@ -3784,7 +4652,7 @@ mips_declare_object (stream, name, init_string, final_string, size)
   assemble_name (stream, name);
   fprintf (stream, final_string, size);        /* ":\n", ",%u\n", ",%u\n" */
 
-  if (TARGET_GP_OPT && mips_section_threshold != 0)
+  if (TARGET_GP_OPT)
     {
       tree name_tree = get_identifier (name);
       TREE_ASM_WRITTEN (name_tree) = 1;
@@ -3895,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.
@@ -3996,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;
@@ -4011,15 +4878,19 @@ compute_frame_size (size)
     {
       if (regs_ever_live[regno] && !call_used_regs[regno])
        {
-         fp_reg_size += 2*UNITS_PER_WORD;
+         fp_reg_size += fp_inc * UNITS_PER_FPREG;
          fmask |= fp_bits << (regno - FP_REG_FIRST);
        }
     }
 
   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)
     {
@@ -4031,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;
@@ -4042,12 +4918,12 @@ compute_frame_size (size)
   current_frame_info.fmask      = fmask;
   current_frame_info.initialized = reload_completed;
   current_frame_info.num_gp     = gp_reg_size / UNITS_PER_WORD;
-  current_frame_info.num_fp     = fp_reg_size / (2*UNITS_PER_WORD);
+  current_frame_info.num_fp     = fp_reg_size / (fp_inc * UNITS_PER_FPREG);
 
   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;
     }
@@ -4060,10 +4936,11 @@ compute_frame_size (size)
 
   if (fmask)
     {
-      unsigned long offset = args_size + extra_size + var_size
-                            + gp_reg_rounded + fp_reg_size - 2*UNITS_PER_WORD;
+      unsigned long offset = (args_size + extra_size + var_size
+                             + gp_reg_rounded + fp_reg_size
+                             - fp_inc * UNITS_PER_FPREG);
       current_frame_info.fp_sp_offset = offset;
-      current_frame_info.fp_save_offset = offset - total_size + UNITS_PER_WORD;
+      current_frame_info.fp_save_offset = offset - total_size;
     }
   else
     {
@@ -4140,9 +5017,15 @@ save_restore_insns (store_p, large_reg, large_offset, file)
          base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
          base_offset  = large_offset;
          if (file == (FILE *)0)
-           emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+           {
+             if (TARGET_LONG64)
+               emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+             else
+               emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+           }
          else
-           fprintf (file, "\taddu\t%s,%s,%s\n",
+           fprintf (file, "\t%s\t%s,%s,%s\n",
+                    TARGET_LONG64 ? "daddu" : "addu",
                     reg_names[MIPS_TEMP2_REGNUM],
                     reg_names[REGNO (large_reg)],
                     reg_names[STACK_POINTER_REGNUM]);
@@ -4155,13 +5038,17 @@ save_restore_insns (store_p, large_reg, large_offset, file)
          if (file == (FILE *)0)
            {
              emit_move_insn (base_reg_rtx, GEN_INT (gp_offset));
-             emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
+             if (TARGET_LONG64)
+               emit_insn (gen_adddi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
+             else
+               emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
            }
          else
-           fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n",
+           fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\t%s\t%s,%s,%s\n",
                     reg_names[MIPS_TEMP2_REGNUM],
                     (long)base_offset,
                     (long)base_offset,
+                    TARGET_LONG64 ? "daddu" : "addu",
                     reg_names[MIPS_TEMP2_REGNUM],
                     reg_names[MIPS_TEMP2_REGNUM],
                     reg_names[STACK_POINTER_REGNUM]);
@@ -4173,23 +5060,28 @@ save_restore_insns (store_p, large_reg, large_offset, file)
            {
              if (file == (FILE *)0)
                {
-                 rtx reg_rtx = gen_rtx (REG, Pmode, regno);
-                 rtx mem_rtx = gen_rtx (MEM, Pmode,
+                 rtx reg_rtx = gen_rtx (REG, word_mode, regno);
+                 rtx mem_rtx = gen_rtx (MEM, word_mode,
                                         gen_rtx (PLUS, Pmode, base_reg_rtx,
                                                  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",
-                            (store_p) ? "sw" : "lw",
+                            (TARGET_64BIT
+                             ? (store_p) ? "sd" : "ld"
+                             : (store_p) ? "sw" : "lw"),
                             reg_names[regno],
                             gp_offset - base_offset,
                             reg_names[REGNO(base_reg_rtx)]);
@@ -4201,18 +5093,19 @@ 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.  */
       fp_offset  = current_frame_info.fp_sp_offset;
-      end_offset = fp_offset - (current_frame_info.fp_reg_size - 2*UNITS_PER_WORD);
+      end_offset = fp_offset - (current_frame_info.fp_reg_size - fp_size);
 
       if (fp_offset < 0 || end_offset < 0)
        fatal ("fp_offset (%ld) or end_offset (%ld) is less than zero.",
@@ -4238,9 +5131,15 @@ save_restore_insns (store_p, large_reg, large_offset, file)
          base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
          base_offset  = large_offset;
          if (file == (FILE *)0)
-           emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+           {
+             if (TARGET_LONG64)
+               emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+             else
+               emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+           }
          else
-           fprintf (file, "\taddu\t%s,%s,%s\n",
+           fprintf (file, "\t%s\t%s,%s,%s\n",
+                    TARGET_LONG64 ? "daddu" : "addu",
                     reg_names[MIPS_TEMP2_REGNUM],
                     reg_names[REGNO (large_reg)],
                     reg_names[STACK_POINTER_REGNUM]);
@@ -4253,13 +5152,17 @@ save_restore_insns (store_p, large_reg, large_offset, file)
          if (file == (FILE *)0)
            {
              emit_move_insn (base_reg_rtx, GEN_INT (fp_offset));
-             emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
+             if (TARGET_LONG64)
+               emit_insn (gen_adddi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
+             else
+               emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
            }
          else
-           fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n",
+           fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\t%s\t%s,%s,%s\n",
                     reg_names[MIPS_TEMP2_REGNUM],
                     (long)base_offset,
                     (long)base_offset,
+                    TARGET_LONG64 ? "daddu" : "addu",
                     reg_names[MIPS_TEMP2_REGNUM],
                     reg_names[MIPS_TEMP2_REGNUM],
                     reg_names[STACK_POINTER_REGNUM]);
@@ -4271,25 +5174,32 @@ 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)]);
 
 
-             fp_offset -= 2*UNITS_PER_WORD;
+             fp_offset -= fp_size;
            }
        }
     }
@@ -4303,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 ],
@@ -4334,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];
 
@@ -4342,7 +5263,9 @@ function_prologue (file, size)
               reg_names[PIC_FUNCTION_ADDR_REGNUM]);
       if (tsize > 0)
        {
-         fprintf (file, "\tsubu\t%s,%s,%d\n", sp_str, sp_str, tsize);
+         fprintf (file, "\t%s\t%s,%s,%d\n",
+                  (TARGET_LONG64 ? "dsubu" : "subu"),
+                  sp_str, sp_str, tsize);
          fprintf (file, "\t.cprestore %d\n", current_frame_info.args_size);
        }
     }
@@ -4360,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;
@@ -4383,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)
        {
@@ -4401,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;
 
@@ -4413,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)
@@ -4457,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;
@@ -4474,8 +5398,8 @@ mips_expand_prologue ()
        {
          if (offset != 0)
            ptr = gen_rtx (PLUS, Pmode, stack_pointer_rtx, GEN_INT (offset));
-         emit_move_insn (gen_rtx (MEM, Pmode, ptr),
-                         gen_rtx (REG, Pmode, regno));
+         emit_move_insn (gen_rtx (MEM, word_mode, ptr),
+                         gen_rtx (REG, word_mode, regno));
          offset += UNITS_PER_WORD;
        }
     }
@@ -4485,23 +5409,43 @@ 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, SImode, MIPS_TEMP1_REGNUM);
+             tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
              emit_move_insn (tmp_rtx, tsize_rtx);
              tsize_rtx = tmp_rtx;
            }
 
-         emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                tsize_rtx));
+         if (TARGET_LONG64)
+           insn = emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx,
+                                         tsize_rtx));
+         else
+           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)
-       emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
+       {
+         rtx insn;
+
+         if (TARGET_64BIT)
+           insn= emit_insn (gen_movdi (frame_pointer_rtx, stack_pointer_rtx));
+         else
+           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
@@ -4522,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;
@@ -4549,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)
@@ -4580,7 +5522,7 @@ function_epilogue (file, size)
   if (set_volatile != 0)
     {
       set_volatile = 0;
-      fprintf (file, "\t#.set\tnovolatile\n", (TARGET_MIPS_AS) ? "" : "#");
+      fprintf (file, "\t%s.set\tnovolatile\n", (TARGET_MIPS_AS) ? "" : "#");
       error ("internal gcc error: .set volatile left on in epilogue");
     }
 
@@ -4625,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);
 
@@ -4654,10 +5597,14 @@ function_epilogue (file, size)
          fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]);
 
          if (tsize > 32767)
-           fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str);
+           fprintf (file, "\t%s\t%s,%s,%s\n",
+                    TARGET_LONG64 ? "daddu" : "addu",
+                    sp_str, sp_str, t1_str);
 
          else if (tsize > 0)
-           fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize);
+           fprintf (file, "\t%s\t%s,%s,%d\n",
+                    TARGET_LONG64 ? "daddu" : "addu",
+                    sp_str, sp_str, tsize);
 
          else if (!load_only_r31 && epilogue_delay != 0)
            final_scan_insn (XEXP (epilogue_delay, 0),
@@ -4672,25 +5619,36 @@ function_epilogue (file, size)
       else
        {
          if (tsize > 32767)
-           fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str);
+           fprintf (file, "\t%s\t%s,%s,%s\n",
+                    TARGET_LONG64 ? "daddu" : "addu",
+                    sp_str, sp_str, t1_str);
 
          else if (tsize > 0)
-           fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize);
+           fprintf (file, "\t%s\t%s,%s,%d\n",
+                    TARGET_LONG64 ? "daddu" : "addu",
+                    sp_str, sp_str, tsize);
 
          fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]);
        }
     }
 
+#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++;
@@ -4777,7 +5735,7 @@ mips_expand_epilogue ()
 
   if (tsize > 32767)
     {
-      tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
+      tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
       emit_move_insn (tmp_rtx, tsize_rtx);
       tsize_rtx = tmp_rtx;
     }
@@ -4785,11 +5743,21 @@ mips_expand_epilogue ()
   if (tsize > 0)
     {
       if (frame_pointer_needed)
-       emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
+       {
+         if (TARGET_LONG64)
+           emit_insn (gen_movdi (stack_pointer_rtx, frame_pointer_rtx));
+         else
+           emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
+       }
 
       save_restore_insns (FALSE, tmp_rtx, tsize, (FILE *)0);
 
-      emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx));
+      if (TARGET_LONG64)
+       emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx,
+                              tsize_rtx));
+      else
+       emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+                              tsize_rtx));
     }
 
   emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode, GP_REG_FIRST+31)));
@@ -4832,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;
+}