OSDN Git Service

* rtlanal.c (dead_or_set_regno_p): Ignore REG_DEAD notes after
[pf3gnuchains/gcc-fork.git] / gcc / config / mips / mips.md
index 57ae3cb..f90d75b 100644 (file)
@@ -1,7 +1,9 @@
 ;;  Mips.md         Machine Description for MIPS based processors
 ;;  Contributed by   A. Lichnewsky, lich@inria.inria.fr
 ;;  Changes by       Michael Meissner, meissner@osf.org
-;;  Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+;;  64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
+;;  Brendan Eich, brendan@microunity.com.
+;;  Copyright (C) 1989, 90-97, 1998 Free Software Foundation, Inc.
 
 ;; This file is part of GNU CC.
 
 
 ;; 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.
+
+;; ??? Currently does not have define_function_unit support for the R8000.
+;; Must include new entries for fmadd in addition to existing entries.
 
 \f
 
@@ -43,6 +49,7 @@
 ;; icmp                integer compare
 ;; fadd                floating point add/subtract
 ;; fmul                floating point multiply
+;; fmadd       floating point multiply-add
 ;; fdiv                floating point divide
 ;; fabs                floating point absolute value
 ;; fneg                floating point negation
@@ -53,7 +60,7 @@
 ;; nop         no operation
 
 (define_attr "type"
-  "unknown,branch,jump,call,load,store,move,xfer,hilo,arith,darith,imul,idiv,icmp,fadd,fmul,fdiv,fabs,fneg,fcmp,fcvt,fsqrt,multi,nop"
+  "unknown,branch,jump,call,load,store,move,xfer,hilo,arith,darith,imul,idiv,icmp,fadd,fmul,fmadd,fdiv,fabs,fneg,fcmp,fcvt,fsqrt,multi,nop"
   (const_string "unknown"))
 
 ;; Main data type used by the insn
 
 ;; whether or not an instruction has a mandatory delay slot
 (define_attr "dslot" "no,yes"
-  (if_then_else (eq_attr "type" "branch,jump,call,load,xfer,hilo,fcmp")
+  (if_then_else (ior (eq_attr "type" "branch,jump,call,xfer,hilo,fcmp")
+                    (and (eq_attr "type" "load")
+                         (and (eq (symbol_ref "mips_isa") (const_int 1))
+                                  (eq (symbol_ref "mips16") (const_int 0)))))
                (const_string "yes")
                (const_string "no")))
 
 ;;           (eq (symbol_ref "mips_cpu") (symbol_ref "PROCESSOR_R6000"))   (const_string "r6000")]
 ;;          (const_string "default"))))
 
-(define_attr "cpu" "default,r3000,r6000,r4000"
+;; ??? Fix everything that tests this attribute.
+(define_attr "cpu"
+  "default,r3000,r3900,r6000,r4000,r4100,r4300,r4600,r4650,r5000,r8000"
   (const (symbol_ref "mips_cpu_attr")))
 
 ;; Attribute defining whether or not we can use the branch-likely instructions
-;; (MIPS ISA level 2)
 
 (define_attr "branch_likely" "no,yes"
   (const
-   (if_then_else (ge (symbol_ref "mips_isa") (const_int 2))
+   (if_then_else (ne (symbol_ref "GENERATE_BRANCHLIKELY") (const_int 0))
                 (const_string "yes")
                 (const_string "no"))))
 
 (define_asm_attributes
   [(set_attr "type" "multi")])
 
+;; whether or not generating calls to position independent functions
+(define_attr "abicalls" "no,yes"
+  (const (symbol_ref "mips_abicalls_attr")))
+
 \f
 
 ;; .........................
 ;;
 ;; .........................
 
-(define_delay (eq_attr "type" "branch")
+(define_delay (and (eq_attr "type" "branch")
+                  (eq (symbol_ref "mips16") (const_int 0)))
   [(and (eq_attr "dslot" "no") (eq_attr "length" "1"))
    (nil)
    (and (eq_attr "branch_likely" "yes") (and (eq_attr "dslot" "no") (eq_attr "length" "1")))])
 
-(define_delay (eq_attr "type" "call,jump")
+(define_delay (eq_attr "type" "jump")
+  [(and (eq_attr "dslot" "no") (eq_attr "length" "1"))
+   (nil)
+   (nil)])
+
+(define_delay (and (eq_attr "type" "call") (eq_attr "abicalls" "no"))
   [(and (eq_attr "dslot" "no") (eq_attr "length" "1"))
    (nil)
    (nil)])
 ;; Make the default case (PROCESSOR_DEFAULT) handle the worst case
 
 (define_function_unit "memory" 1 0
-  (and (eq_attr "type" "load") (eq_attr "cpu" "!r3000"))
+  (and (eq_attr "type" "load")
+       (eq_attr "cpu" "!r3000,r3900,r4600,r4650,r4100,r4300,r5000"))
   3 0)
 
 (define_function_unit "memory" 1 0
-  (and (eq_attr "type" "load") (eq_attr "cpu" "r3000"))
+  (and (eq_attr "type" "load")
+       (eq_attr "cpu" "r3000,r3900,r4600,r4650,r4100,r4300,r5000"))
   2 0)
 
 (define_function_unit "memory"   1 0 (eq_attr "type" "store") 1 0)
 
-(define_function_unit "addr"     1 0 (eq_attr "type" "fcmp") 2 0)
-
 (define_function_unit "memory"   1 0 (eq_attr "type" "xfer") 2 0)
-(define_function_unit "memory"   1 0 (eq_attr "type" "hilo") 3 0)
-
-(define_function_unit "imuldiv"  1 1
-  (and (eq_attr "type" "imul") (eq_attr "cpu" "!r3000,r4000"))
-  17 0)
 
-(define_function_unit "imuldiv"  1 1
-  (and (eq_attr "type" "imul") (eq_attr "cpu" "r3000"))
-  12 0)
-
-(define_function_unit "imuldiv" 1 1
-  (and (eq_attr "type" "imul") (eq_attr "cpu" "r4000"))
-  10 0)
+(define_function_unit "imuldiv"  1 0
+  (eq_attr "type" "hilo")
+  1 3)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "imul")
+       (eq_attr "cpu" "!r3000,r3900,r4000,r4600,r4650,r4100,r4300,r5000"))
+  17 17)
+
+;; On them mips16, we want to stronly discourage a mult from appearing
+;; after an mflo, since that requires explicit nop instructions.  We
+;; do this by pretending that mflo ties up the function unit for long
+;; enough that the scheduler will ignore load stalls and the like when
+;; selecting instructions to between the two instructions.
+
+(define_function_unit "imuldiv" 1 0
+  (and (eq_attr "type" "hilo") (ne (symbol_ref "mips16") (const_int 0)))
+  1 5)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "imul") (eq_attr "cpu" "r3000,r3900"))
+  12 12)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "imul") (eq_attr "cpu" "r4000,r4600"))
+  10 10)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "imul") (eq_attr "cpu" "r4650"))
+  4 4)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "imul")
+       (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4100")))
+  1 1)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "imul")
+       (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4100")))
+  4 4)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "imul")
+       (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4300,r5000")))
+  5 5)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "imul")
+       (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4300")))
+  8 8)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "imul")
+       (and (eq_attr "mode" "DI") (eq_attr "cpu" "r5000")))
+  9 9)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "idiv")
+       (eq_attr "cpu" "!r3000,r3900,r4000,r4600,r4650,r4100,r4300,r5000"))
+  38 38)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "idiv") (eq_attr "cpu" "r3000,r3900"))
+  35 35)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "idiv") (eq_attr "cpu" "r4600"))
+  42 42)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "idiv") (eq_attr "cpu" "r4650"))
+  36 36)
+
+(define_function_unit "imuldiv"  1 0
+  (and (eq_attr "type" "idiv") (eq_attr "cpu" "r4000"))
+  69 69)
+
+(define_function_unit "imuldiv" 1 0
+  (and (eq_attr "type" "idiv")
+       (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4100")))
+  35 35)
+
+(define_function_unit "imuldiv" 1 0
+  (and (eq_attr "type" "idiv")
+       (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4100")))
+  67 67)
+
+(define_function_unit "imuldiv" 1 0
+  (and (eq_attr "type" "idiv")
+       (and (eq_attr "mode" "SI") (eq_attr "cpu" "r4300")))
+  37 37)
+
+(define_function_unit "imuldiv" 1 0
+  (and (eq_attr "type" "idiv")
+       (and (eq_attr "mode" "DI") (eq_attr "cpu" "r4300")))
+  69 69)
+
+(define_function_unit "imuldiv" 1 0
+  (and (eq_attr "type" "idiv")
+       (and (eq_attr "mode" "SI") (eq_attr "cpu" "r5000")))
+  36 36)
+
+(define_function_unit "imuldiv" 1 0
+  (and (eq_attr "type" "idiv")
+       (and (eq_attr "mode" "DI") (eq_attr "cpu" "r5000")))
+  68 68)
+
+;; The R4300 does *NOT* have a separate Floating Point Unit, instead
+;; the FP hardware is part of the normal ALU circuitry.  This means FP
+;; instructions affect the pipe-line, and no functional unit
+;; parallelism can occur on R4300 processors.  To force GCC into coding
+;; for only a single functional unit, we force the R4300 FP
+;; instructions to be processed in the "imuldiv" unit.
 
-(define_function_unit "imuldiv"  1 1
-  (and (eq_attr "type" "idiv") (eq_attr "cpu" "!r3000,r4000"))
-  38 0)
+(define_function_unit "adder" 1 1
+  (and (eq_attr "type" "fcmp") (eq_attr "cpu" "!r3000,r3900,r6000,r4300,r5000"))
+  3 0)
 
-(define_function_unit "imuldiv"  1 1
-  (and (eq_attr "type" "idiv") (eq_attr "cpu" "r3000"))
-  35 0)
+(define_function_unit "adder" 1 1
+  (and (eq_attr "type" "fcmp") (eq_attr "cpu" "r3000,r3900,r6000"))
+  2 0)
 
-(define_function_unit "imuldiv" 1 1
-  (and (eq_attr "type" "idiv") (eq_attr "cpu" "r4000"))
-  69 0)
+(define_function_unit "adder" 1 1
+  (and (eq_attr "type" "fcmp") (eq_attr "cpu" "r5000"))
+  1 0)
 
 (define_function_unit "adder" 1 1
-  (and (eq_attr "type" "fadd") (eq_attr "cpu" "!r3000,r6000"))
+  (and (eq_attr "type" "fadd") (eq_attr "cpu" "!r3000,r3900,r6000,r4300"))
   4 0)
 
 (define_function_unit "adder" 1 1
-  (and (eq_attr "type" "fadd") (eq_attr "cpu" "r3000"))
+  (and (eq_attr "type" "fadd") (eq_attr "cpu" "r3000,r3900"))
   2 0)
 
 (define_function_unit "adder" 1 1
   3 0)
 
 (define_function_unit "adder" 1 1
-  (and (eq_attr "type" "fabs,fneg") (eq_attr "cpu" "!r3000"))
+  (and (eq_attr "type" "fabs,fneg")
+       (eq_attr "cpu" "!r3000,r3900,r4600,r4650,r4300,r5000"))
   2 0)
 
 (define_function_unit "adder" 1 1
-  (and (eq_attr "type" "fabs,fneg") (eq_attr "cpu" "r3000"))
+  (and (eq_attr "type" "fabs,fneg") (eq_attr "cpu" "r3000,r3900,r4600,r4650,r5000"))
   1 0)
 
 (define_function_unit "mult" 1 1
-  (and (eq_attr "type" "fmul") (and (eq_attr "mode" "SF") (eq_attr "cpu" "!r3000,r6000")))
+  (and (eq_attr "type" "fmul")
+       (and (eq_attr "mode" "SF")
+           (eq_attr "cpu" "!r3000,r3900,r6000,r4600,r4650,r4300,r5000")))
   7 0)
 
 (define_function_unit "mult" 1 1
-  (and (eq_attr "type" "fmul") (and (eq_attr "mode" "SF") (eq_attr "cpu" "r3000")))
+  (and (eq_attr "type" "fmul")
+       (and (eq_attr "mode" "SF") (eq_attr "cpu" "r3000,r3900,r5000")))
   4 0)
 
 (define_function_unit "mult" 1 1
-  (and (eq_attr "type" "fmul") (and (eq_attr "mode" "SF") (eq_attr "cpu" "r6000")))
+  (and (eq_attr "type" "fmul")
+       (and (eq_attr "mode" "SF") (eq_attr "cpu" "r6000")))
   5 0)
 
 (define_function_unit "mult" 1 1
-  (and (eq_attr "type" "fmul") (and (eq_attr "mode" "DF") (eq_attr "cpu" "!r3000,r6000")))
+  (and (eq_attr "type" "fmul")
+       (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4600,r4650")))
+  8 0)
+
+(define_function_unit "mult" 1 1
+  (and (eq_attr "type" "fmul")
+       (and (eq_attr "mode" "DF") (eq_attr "cpu" "!r3000,r3900,r6000,r4300,r5000")))
   8 0)
 
 (define_function_unit "mult" 1 1
-  (and (eq_attr "type" "fmul") (and (eq_attr "mode" "DF") (eq_attr "cpu" "r3000")))
+  (and (eq_attr "type" "fmul")
+       (and (eq_attr "mode" "DF") (eq_attr "cpu" "r3000,r3900,r5000")))
   5 0)
 
 (define_function_unit "mult" 1 1
-  (and (eq_attr "type" "fmul") (and (eq_attr "mode" "DF") (eq_attr "cpu" "r6000")))
+  (and (eq_attr "type" "fmul")
+       (and (eq_attr "mode" "DF") (eq_attr "cpu" "r6000")))
   6 0)
 
 (define_function_unit "divide" 1 1
-  (and (eq_attr "type" "fdiv") (and (eq_attr "mode" "SF") (eq_attr "cpu" "!r3000,r6000")))
+  (and (eq_attr "type" "fdiv")
+       (and (eq_attr "mode" "SF")
+           (eq_attr "cpu" "!r3000,r3900,r6000,r4600,r4650,r4300,r5000")))
   23 0)
 
 (define_function_unit "divide" 1 1
-  (and (eq_attr "type" "fdiv") (and (eq_attr "mode" "SF") (eq_attr "cpu" "r3000")))
+  (and (eq_attr "type" "fdiv")
+       (and (eq_attr "mode" "SF") (eq_attr "cpu" "r3000,r3900")))
   12 0)
 
 (define_function_unit "divide" 1 1
-  (and (eq_attr "type" "fdiv") (and (eq_attr "mode" "SF") (eq_attr "cpu" "r6000")))
+  (and (eq_attr "type" "fdiv")
+       (and (eq_attr "mode" "SF") (eq_attr "cpu" "r6000")))
   15 0)
 
 (define_function_unit "divide" 1 1
-  (and (eq_attr "type" "fdiv") (and (eq_attr "mode" "DF") (eq_attr "cpu" "!r3000,r6000")))
+  (and (eq_attr "type" "fdiv")
+       (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4600,r4650")))
+  32 0)
+
+(define_function_unit "divide" 1 1
+  (and (eq_attr "type" "fdiv")
+       (and (eq_attr "mode" "SF") (eq_attr "cpu" "r5000")))
+  21 0)
+
+(define_function_unit "divide" 1 1
+  (and (eq_attr "type" "fdiv")
+       (and (eq_attr "mode" "DF")
+           (eq_attr "cpu" "!r3000,r3900,r6000,r4600,r4650,r4300")))
   36 0)
 
 (define_function_unit "divide" 1 1
-  (and (eq_attr "type" "fdiv") (and (eq_attr "mode" "DF") (eq_attr "cpu" "r3000")))
+  (and (eq_attr "type" "fdiv")
+       (and (eq_attr "mode" "DF") (eq_attr "cpu" "r3000,r3900")))
   19 0)
 
 (define_function_unit "divide" 1 1
-  (and (eq_attr "type" "fdiv") (and (eq_attr "mode" "DF") (eq_attr "cpu" "r6000")))
+  (and (eq_attr "type" "fdiv")
+       (and (eq_attr "mode" "DF") (eq_attr "cpu" "r6000")))
   16 0)
 
-(define_function_unit "divide" 1 1 (and (eq_attr "type" "fsqrt") (eq_attr "mode" "SF"))  54 0)
-(define_function_unit "divide" 1 1 (and (eq_attr "type" "fsqrt") (eq_attr "mode" "DF")) 112 0)
+(define_function_unit "divide" 1 1
+  (and (eq_attr "type" "fdiv")
+       (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4600,r4650")))
+  61 0)
+
+;;; ??? Is this number right?
+(define_function_unit "divide" 1 1
+  (and (eq_attr "type" "fsqrt")
+       (and (eq_attr "mode" "SF") (eq_attr "cpu" "!r4600,r4650,r4300,r5000")))
+  54 0)
+
+(define_function_unit "divide" 1 1
+  (and (eq_attr "type" "fsqrt")
+       (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4600,r4650")))
+  31 0)
+
+(define_function_unit "divide" 1 1
+  (and (eq_attr "type" "fsqrt")
+       (and (eq_attr "mode" "SF") (eq_attr "cpu" "r5000")))
+  21 0)
+
+;;; ??? Is this number right?
+(define_function_unit "divide" 1 1
+  (and (eq_attr "type" "fsqrt")
+       (and (eq_attr "mode" "DF") (eq_attr "cpu" "!r4600,r4650,r4300,r5000")))
+  112 0)
+
+(define_function_unit "divide" 1 1
+  (and (eq_attr "type" "fsqrt")
+       (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4600,r4650")))
+  60 0)
+
+(define_function_unit "divide" 1 1
+  (and (eq_attr "type" "fsqrt")
+       (and (eq_attr "mode" "DF") (eq_attr "cpu" "r5000")))
+  36 0)
 
+;; R4300 FP instruction classes treated as part of the "imuldiv"
+;; functional unit:
+
+(define_function_unit "imuldiv" 1 0
+  (and (eq_attr "type" "fadd") (eq_attr "cpu" "r4300"))
+  3 3)
+
+(define_function_unit "imuldiv" 1 0
+  (and (eq_attr "type" "fcmp,fabs,fneg") (eq_attr "cpu" "r4300"))
+  1 1)
+
+(define_function_unit "imuldiv" 1 0
+  (and (eq_attr "type" "fmul") (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4300")))
+  5 5)
+(define_function_unit "imuldiv" 1 0
+  (and (eq_attr "type" "fmul") (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4300")))
+  8 8)
+
+(define_function_unit "imuldiv" 1 0
+  (and (and (eq_attr "type" "fdiv") (eq_attr "type" "fsqrt"))
+       (and (eq_attr "mode" "SF") (eq_attr "cpu" "r4300")))
+  29 29)
+(define_function_unit "imuldiv" 1 0
+  (and (and (eq_attr "type" "fdiv") (eq_attr "type" "fsqrt"))
+       (and (eq_attr "mode" "DF") (eq_attr "cpu" "r4300")))
+  58 58)
 \f
 ;; The following functional units do not use the cpu type, and use
 ;; much less memory in genattrtab.c.
   [(set (match_operand:DF 0 "register_operand" "=f")
        (plus:DF (match_operand:DF 1 "register_operand" "f")
                 (match_operand:DF 2 "register_operand" "f")))]
-  "TARGET_HARD_FLOAT"
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "add.d\\t%0,%1,%2"
   [(set_attr "type"    "fadd")
    (set_attr "mode"    "DF")
   ""
   "
 {
-  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == -32768)
+  /* The mips16 assembler handles -32768 correctly, and so does gas,
+     but some other MIPS assemblers think that -32768 needs to be
+     loaded into a register before it can be added in.  */
+  if (! TARGET_MIPS16
+      && ! TARGET_GAS
+      && GET_CODE (operands[2]) == CONST_INT
+      && INTVAL (operands[2]) == -32768)
     operands[2] = force_reg (SImode, operands[2]);
 }")
 
   [(set (match_operand:SI 0 "register_operand" "=d")
        (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
                 (match_operand:SI 2 "arith_operand" "dI")))]
-  "GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768"
+  "! TARGET_MIPS16
+   && (TARGET_GAS
+       || GET_CODE (operands[2]) != CONST_INT
+       || INTVAL (operands[2]) != -32768)"
+  "addu\\t%0,%z1,%2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+;; For the mips16, we need to recognize stack pointer additions
+;; explicitly, since we don't have a constraint for $sp.  These insns
+;; will be generated by the save_restore_insns functions.
+
+(define_insn ""
+  [(set (reg:SI 29)
+       (plus:SI (reg:SI 29)
+                (match_operand:SI 0 "small_int" "I")))]
+  "TARGET_MIPS16"
+  "addu\\t%$,%$,%0"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set (attr "length")        (if_then_else (match_operand:VOID 0 "m16_simm8_8" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (plus:SI (reg:SI 29)
+                (match_operand:SI 1 "small_int" "I")))]
+  "TARGET_MIPS16"
+  "addu\\t%0,%$,%1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set (attr "length")        (if_then_else (match_operand:VOID 1 "m16_uimm8_4" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
+       (plus:SI (match_operand:SI 1 "register_operand" "0,d,d")
+                (match_operand:SI 2 "arith_operand" "IQ,O,d")))]
+  "TARGET_MIPS16
+   && (GET_CODE (operands[1]) != REG
+       || REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER
+       || M16_REG_P (REGNO (operands[1]))
+       || REGNO (operands[1]) == ARG_POINTER_REGNUM
+       || REGNO (operands[1]) == FRAME_POINTER_REGNUM
+       || REGNO (operands[1]) == STACK_POINTER_REGNUM)
+   && (GET_CODE (operands[2]) != REG
+       || REGNO (operands[2]) >= FIRST_PSEUDO_REGISTER
+       || M16_REG_P (REGNO (operands[2]))
+       || REGNO (operands[2]) == ARG_POINTER_REGNUM
+       || REGNO (operands[2]) == FRAME_POINTER_REGNUM
+       || REGNO (operands[2]) == STACK_POINTER_REGNUM)"
   "*
 {
-  return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
-    ? \"subu\\t%0,%z1,%n2\"
-    : \"addu\\t%0,%z1,%2\";
+  if (REGNO (operands[0]) == REGNO (operands[1]))
+    return \"addu\\t%0,%2\";
+  return \"addu\\t%0,%1,%2\";
 }"
   [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "1")])
+   (set_attr_alternative "length"
+               [(if_then_else (match_operand:VOID 2 "m16_simm8_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (if_then_else (match_operand:VOID 2 "m16_simm4_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (const_int 1)])])
+
+
+;; On the mips16, we can sometimes split an add of a constant which is
+;; a 4 byte instruction into two adds which are both 2 byte
+;; instructions.  There are two cases: one where we are adding a
+;; constant plus a register to another register, and one where we are
+;; simply adding a constant to a register.
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (plus:SI (match_dup 0)
+                (match_operand:SI 1 "const_int_operand" "")))]
+  "TARGET_MIPS16 && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == CONST_INT
+   && ((INTVAL (operands[1]) > 0x7f
+       && INTVAL (operands[1]) <= 0x7f + 0x7f)
+       || (INTVAL (operands[1]) < - 0x80
+          && INTVAL (operands[1]) >= - 0x80 - 0x80))"
+  [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1)))
+   (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 2)))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[1]);
+
+  if (val >= 0)
+    {
+      operands[1] = GEN_INT (0x7f);
+      operands[2] = GEN_INT (val - 0x7f);
+    }
+  else
+    {
+      operands[1] = GEN_INT (- 0x80);
+      operands[2] = GEN_INT (val + 0x80);
+    }
+}")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (plus:SI (match_operand:SI 1 "register_operand" "")
+                (match_operand:SI 2 "const_int_operand" "")))]
+  "TARGET_MIPS16 && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == REG
+   && M16_REG_P (REGNO (operands[1]))
+   && REGNO (operands[0]) != REGNO (operands[1])
+   && GET_CODE (operands[2]) == CONST_INT
+   && ((INTVAL (operands[2]) > 0x7
+       && INTVAL (operands[2]) <= 0x7 + 0x7f)
+       || (INTVAL (operands[2]) < - 0x8
+          && INTVAL (operands[2]) >= - 0x8 - 0x80))"
+  [(set (match_dup 0) (plus:SI (match_dup 1) (match_dup 2)))
+   (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 3)))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[2]);
+
+  if (val >= 0)
+    {
+      operands[2] = GEN_INT (0x7);
+      operands[3] = GEN_INT (val - 0x7);
+    }
+  else
+    {
+      operands[2] = GEN_INT (- 0x8);
+      operands[3] = GEN_INT (val + 0x8);
+    }
+}")
 
 (define_expand "adddi3"
   [(parallel [(set (match_operand:DI 0 "register_operand" "")
-                  (plus:DI (match_operand:DI 1 "register_operand" "")
-                           (match_operand:DI 2 "arith_operand" "")))
+                  (plus:DI (match_operand:DI 1 "se_register_operand" "")
+                           (match_operand:DI 2 "se_arith_operand" "")))
              (clobber (match_dup 3))])]
-  "!TARGET_DEBUG_G_MODE"
+  "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
   "
 {
-  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == -32768)
-    operands[2] = force_reg (SImode, operands[2]);
+  /* The mips16 assembler handles -32768 correctly, and so does gas,
+     but some other MIPS assemblers think that -32768 needs to be
+     loaded into a register before it can be added in.  */
+  if (! TARGET_MIPS16
+      && ! TARGET_GAS
+      && GET_CODE (operands[2]) == CONST_INT
+      && INTVAL (operands[2]) == -32768)
+    operands[2] = force_reg (DImode, operands[2]);
+
+  if (TARGET_64BIT)
+    {
+      emit_insn (gen_adddi3_internal_3 (operands[0], operands[1],
+                                       operands[2]));
+      DONE;
+    }
 
   operands[3] = gen_reg_rtx (SImode);
 }")
        (plus:DI (match_operand:DI 1 "register_operand" "0,d")
                 (match_operand:DI 2 "register_operand" "d,d")))
    (clobber (match_operand:SI 3 "register_operand" "=d,d"))]
-  "!TARGET_DEBUG_G_MODE"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
   "*
 {
   return (REGNO (operands[0]) == REGNO (operands[1])
        (plus:DI (match_operand:DI 1 "register_operand" "")
                 (match_operand:DI 2 "register_operand" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
    && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))
        (plus:DI (match_operand:DI 1 "register_operand" "")
                 (match_operand:DI 2 "register_operand" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
    && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))
        (plus:DI (match_operand:DI 1 "register_operand" "%d,%d,%d")
                 (match_operand:DI 2 "small_int" "P,J,N")))
    (clobber (match_operand:SI 3 "register_operand" "=d,d,d"))]
-  "!TARGET_DEBUG_G_MODE && INTVAL (operands[2]) != -32768"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+   && INTVAL (operands[2]) != -32768"
   "@
    addu\\t%L0,%L1,%2\;sltu\\t%3,%L0,%2\;addu\\t%M0,%M1,%3
    move\\t%L0,%L1\;move\\t%M0,%M1
        (plus:DI (match_operand:DI 1 "register_operand" "")
                 (match_operand:DI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
    && INTVAL (operands[2]) > 0"
        (plus:DI (match_operand:DI 1 "register_operand" "")
                 (match_operand:DI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
    && INTVAL (operands[2]) > 0"
        (plus:SI (subreg:SI (match_dup 1) 0)
                 (match_dup 3)))]
   "")
+
+(define_insn "adddi3_internal_3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (plus:DI (match_operand:DI 1 "se_reg_or_0_operand" "dJ")
+                (match_operand:DI 2 "se_arith_operand" "dI")))]
+  "TARGET_64BIT
+   && !TARGET_MIPS16
+   && (TARGET_GAS
+       || GET_CODE (operands[2]) != CONST_INT
+       || INTVAL (operands[2]) != -32768)"
+  "*
+{
+  return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+    ? \"dsubu\\t%0,%z1,%n2\"
+    : \"daddu\\t%0,%z1,%2\";
+}"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
+;; For the mips16, we need to recognize stack pointer additions
+;; explicitly, since we don't have a constraint for $sp.  These insns
+;; will be generated by the save_restore_insns functions.
+
+(define_insn ""
+  [(set (reg:DI 29)
+       (plus:DI (reg:DI 29)
+                (match_operand:DI 0 "small_int" "I")))]
+  "TARGET_MIPS16 && TARGET_64BIT"
+  "daddu\\t%$,%$,%0"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set (attr "length")        (if_then_else (match_operand:VOID 0 "m16_simm8_8" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (plus:DI (reg:DI 29)
+                (match_operand:DI 1 "small_int" "I")))]
+  "TARGET_MIPS16 && TARGET_64BIT"
+  "daddu\\t%0,%$,%1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set (attr "length")        (if_then_else (match_operand:VOID 0 "m16_uimm5_4" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+       (plus:DI (match_operand:DI 1 "register_operand" "0,d,d")
+                (match_operand:DI 2 "arith_operand" "IQ,O,d")))]
+  "TARGET_MIPS16 && TARGET_64BIT
+   && (GET_CODE (operands[1]) != REG
+       || REGNO (operands[1]) >= FIRST_PSEUDO_REGISTER
+       || M16_REG_P (REGNO (operands[1]))
+       || REGNO (operands[1]) == ARG_POINTER_REGNUM
+       || REGNO (operands[1]) == FRAME_POINTER_REGNUM
+       || REGNO (operands[1]) == STACK_POINTER_REGNUM)
+   && (GET_CODE (operands[2]) != REG
+       || REGNO (operands[2]) >= FIRST_PSEUDO_REGISTER
+       || M16_REG_P (REGNO (operands[2]))
+       || REGNO (operands[2]) == ARG_POINTER_REGNUM
+       || REGNO (operands[2]) == FRAME_POINTER_REGNUM
+       || REGNO (operands[2]) == STACK_POINTER_REGNUM)"
+  "*
+{
+  if (REGNO (operands[0]) == REGNO (operands[1]))
+    return \"daddu\\t%0,%2\";
+  return \"daddu\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr_alternative "length"
+               [(if_then_else (match_operand:VOID 2 "m16_simm5_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (if_then_else (match_operand:VOID 2 "m16_simm4_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (const_int 1)])])
+
+
+;; On the mips16, we can sometimes split an add of a constant which is
+;; a 4 byte instruction into two adds which are both 2 byte
+;; instructions.  There are two cases: one where we are adding a
+;; constant plus a register to another register, and one where we are
+;; simply adding a constant to a register.
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (plus:DI (match_dup 0)
+                (match_operand:DI 1 "const_int_operand" "")))]
+  "TARGET_MIPS16 && TARGET_64BIT && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == CONST_INT
+   && ((INTVAL (operands[1]) > 0xf
+       && INTVAL (operands[1]) <= 0xf + 0xf)
+       || (INTVAL (operands[1]) < - 0x10
+          && INTVAL (operands[1]) >= - 0x10 - 0x10))"
+  [(set (match_dup 0) (plus:DI (match_dup 0) (match_dup 1)))
+   (set (match_dup 0) (plus:DI (match_dup 0) (match_dup 2)))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[1]);
+
+  if (val >= 0)
+    {
+      operands[1] = GEN_INT (0xf);
+      operands[2] = GEN_INT (val - 0xf);
+    }
+  else
+    {
+      operands[1] = GEN_INT (- 0x10);
+      operands[2] = GEN_INT (val + 0x10);
+    }
+}")
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (plus:DI (match_operand:DI 1 "register_operand" "")
+                (match_operand:DI 2 "const_int_operand" "")))]
+  "TARGET_MIPS16 && TARGET_64BIT && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == REG
+   && M16_REG_P (REGNO (operands[1]))
+   && REGNO (operands[0]) != REGNO (operands[1])
+   && GET_CODE (operands[2]) == CONST_INT
+   && ((INTVAL (operands[2]) > 0x7
+       && INTVAL (operands[2]) <= 0x7 + 0xf)
+       || (INTVAL (operands[2]) < - 0x8
+          && INTVAL (operands[2]) >= - 0x8 - 0x10))"
+  [(set (match_dup 0) (plus:DI (match_dup 1) (match_dup 2)))
+   (set (match_dup 0) (plus:DI (match_dup 0) (match_dup 3)))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[2]);
+
+  if (val >= 0)
+    {
+      operands[2] = GEN_INT (0x7);
+      operands[3] = GEN_INT (val - 0x7);
+    }
+  else
+    {
+      operands[2] = GEN_INT (- 0x8);
+      operands[3] = GEN_INT (val + 0x8);
+    }
+}")
+
+(define_insn "addsi3_internal_2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (sign_extend:DI (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
+                                (match_operand:SI 2 "arith_operand" "dI"))))]
+  "TARGET_64BIT
+   && !TARGET_MIPS16
+   && (TARGET_GAS
+       || GET_CODE (operands[2]) != CONST_INT
+       || INTVAL (operands[2]) != -32768)"
+  "*
+{
+  return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+    ? \"subu\\t%0,%z1,%n2\"
+    : \"addu\\t%0,%z1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+       (sign_extend:DI (plus:SI (match_operand:SI 1 "register_operand" "0,d,d")
+                                (match_operand:SI 2 "arith_operand" "I,O,d"))))]
+  "TARGET_MIPS16 && TARGET_64BIT"
+  "*
+{
+  if (REGNO (operands[0]) == REGNO (operands[1]))
+    return \"addu\\t%0,%2\";
+  return \"addu\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr_alternative "length"
+               [(if_then_else (match_operand:VOID 2 "m16_simm8_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (if_then_else (match_operand:VOID 2 "m16_simm4_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (const_int 1)])])
+
 \f
 ;;
 ;;  ....................
   [(set (match_operand:DF 0 "register_operand" "=f")
        (minus:DF (match_operand:DF 1 "register_operand" "f")
                  (match_operand:DF 2 "register_operand" "f")))]
-  "TARGET_HARD_FLOAT"
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "sub.d\\t%0,%1,%2"
   [(set_attr "type"    "fadd")
    (set_attr "mode"    "DF")
   ""
   "
 {
-  if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) == -32768)
+  if (GET_CODE (operands[2]) == CONST_INT
+      && (INTVAL (operands[2]) == -32768
+         || (TARGET_MIPS16
+             && INTVAL (operands[2]) == -0x4000)))
     operands[2] = force_reg (SImode, operands[2]);
 }")
 
   [(set (match_operand:SI 0 "register_operand" "=d")
        (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
                  (match_operand:SI 2 "arith_operand" "dI")))]
-  "GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768"
+  "!TARGET_MIPS16
+   && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+  "subu\\t%0,%z1,%2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+;; For the mips16, we need to recognize stack pointer subtractions
+;; explicitly, since we don't have a constraint for $sp.  These insns
+;; will be generated by the save_restore_insns functions.
+
+(define_insn ""
+  [(set (reg:SI 29)
+       (minus:SI (reg:SI 29)
+                 (match_operand:SI 0 "small_int" "I")))]
+  "TARGET_MIPS16
+   && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+  "addu\\t%$,%$,%n0"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set (attr "length")        (if_then_else (match_operand:VOID 0 "m16_nsimm8_8" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (minus:SI (reg:SI 29)
+                 (match_operand:SI 1 "small_int" "I")))]
+  "TARGET_MIPS16
+   && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+  "addu\\t%0,%$,%n1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set (attr "length")        (if_then_else (match_operand:VOID 1 "m16_nuimm8_4" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
+
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
+       (minus:SI (match_operand:SI 1 "register_operand" "0,d,d")
+                 (match_operand:SI 2 "arith_operand" "I,O,d")))]
+  "TARGET_MIPS16
+   && (GET_CODE (operands[2]) != CONST_INT
+       || (INTVAL (operands[2]) != -32768 && INTVAL (operands[2]) != -0x4000))"
   "*
 {
-  return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
-    ? \"addu\\t%0,%z1,%n2\"
-    : \"subu\\t%0,%z1,%2\";
+  if (REGNO (operands[0]) == REGNO (operands[1]))
+    return \"subu\\t%0,%2\";
+  return \"subu\\t%0,%1,%2\";
 }"
   [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "1")])
+   (set_attr_alternative "length"
+               [(if_then_else (match_operand:VOID 2 "m16_nsimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (if_then_else (match_operand:VOID 2 "m16_nsimm4_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (const_int 1)])])
+
+;; On the mips16, we can sometimes split an subtract of a constant
+;; which is a 4 byte instruction into two adds which are both 2 byte
+;; instructions.  There are two cases: one where we are setting a
+;; register to a register minus a constant, and one where we are
+;; simply subtracting a constant from a register.
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (minus:SI (match_dup 0)
+                 (match_operand:SI 1 "const_int_operand" "")))]
+  "TARGET_MIPS16 && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == CONST_INT
+   && ((INTVAL (operands[1]) > 0x80
+       && INTVAL (operands[1]) <= 0x80 + 0x80)
+       || (INTVAL (operands[1]) < - 0x7f
+          && INTVAL (operands[1]) >= - 0x7f - 0x7f))"
+  [(set (match_dup 0) (minus:SI (match_dup 0) (match_dup 1)))
+   (set (match_dup 0) (minus:SI (match_dup 0) (match_dup 2)))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[1]);
+
+  if (val >= 0)
+    {
+      operands[1] = GEN_INT (0x80);
+      operands[2] = GEN_INT (val - 0x80);
+    }
+  else
+    {
+      operands[1] = GEN_INT (- 0x7f);
+      operands[2] = GEN_INT (val + 0x7f);
+    }
+}")
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (minus:SI (match_operand:SI 1 "register_operand" "")
+                 (match_operand:SI 2 "const_int_operand" "")))]
+  "TARGET_MIPS16 && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == REG
+   && M16_REG_P (REGNO (operands[1]))
+   && REGNO (operands[0]) != REGNO (operands[1])
+   && GET_CODE (operands[2]) == CONST_INT
+   && ((INTVAL (operands[2]) > 0x8
+       && INTVAL (operands[2]) <= 0x8 + 0x80)
+       || (INTVAL (operands[2]) < - 0x7
+          && INTVAL (operands[2]) >= - 0x7 - 0x7f))"
+  [(set (match_dup 0) (minus:SI (match_dup 1) (match_dup 2)))
+   (set (match_dup 0) (minus:SI (match_dup 0) (match_dup 3)))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[2]);
+
+  if (val >= 0)
+    {
+      operands[2] = GEN_INT (0x8);
+      operands[3] = GEN_INT (val - 0x8);
+    }
+  else
+    {
+      operands[2] = GEN_INT (- 0x7);
+      operands[3] = GEN_INT (val + 0x7);
+    }
+}")
 
 (define_expand "subdi3"
   [(parallel [(set (match_operand:DI 0 "register_operand" "=d")
-                  (minus:DI (match_operand:DI 1 "register_operand" "d")
-                            (match_operand:DI 2 "register_operand" "d")))
+                  (minus:DI (match_operand:DI 1 "se_register_operand" "d")
+                            (match_operand:DI 2 "se_register_operand" "d")))
              (clobber (match_dup 3))])]
-  "!TARGET_DEBUG_G_MODE"
-  "operands[3] = gen_reg_rtx (SImode);")
+  "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
+  "
+{
+  if (TARGET_64BIT)
+    {
+      emit_insn (gen_subdi3_internal_3 (operands[0], operands[1],
+                                       operands[2]));
+      DONE;
+    }
+
+  operands[3] = gen_reg_rtx (SImode);
+}")
 
 (define_insn "subdi3_internal"
   [(set (match_operand:DI 0 "register_operand" "=d")
        (minus:DI (match_operand:DI 1 "register_operand" "d")
                  (match_operand:DI 2 "register_operand" "d")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "!TARGET_DEBUG_G_MODE"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
   "sltu\\t%3,%L1,%L2\;subu\\t%L0,%L1,%L2\;subu\\t%M0,%M1,%M2\;subu\\t%M0,%M0,%3"
   [(set_attr "type"    "darith")
    (set_attr "mode"    "DI")
        (minus:DI (match_operand:DI 1 "register_operand" "")
                  (match_operand:DI 2 "register_operand" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
    && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
        (minus:DI (match_operand:DI 1 "register_operand" "")
                  (match_operand:DI 2 "register_operand" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
    && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
        (minus:DI (match_operand:DI 1 "register_operand" "d,d,d")
                  (match_operand:DI 2 "small_int" "P,J,N")))
    (clobber (match_operand:SI 3 "register_operand" "=d,d,d"))]
-  "!TARGET_DEBUG_G_MODE && INTVAL (operands[2]) != -32768"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+   && INTVAL (operands[2]) != -32768"
   "@
    sltu\\t%3,%L1,%2\;subu\\t%L0,%L1,%2\;subu\\t%M0,%M1,%3
    move\\t%L0,%L1\;move\\t%M0,%M1
        (minus:DI (match_operand:DI 1 "register_operand" "")
                  (match_operand:DI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
    && INTVAL (operands[2]) > 0"
        (minus:DI (match_operand:DI 1 "register_operand" "")
                  (match_operand:DI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
    && INTVAL (operands[2]) > 0"
                  (match_dup 3)))]
   "")
 
-\f
-;;
-;;  ....................
-;;
-;;     MULTIPLICATION
-;;
-;;  ....................
-;;
-
-(define_insn "muldf3"
-  [(set (match_operand:DF 0 "register_operand" "=f")
-       (mult:DF (match_operand:DF 1 "register_operand" "f")
-                (match_operand:DF 2 "register_operand" "f")))]
-  "TARGET_HARD_FLOAT"
-  "mul.d\\t%0,%1,%2"
-  [(set_attr "type"    "fmul")
-   (set_attr "mode"    "DF")
+(define_insn "subdi3_internal_3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (minus:DI (match_operand:DI 1 "se_reg_or_0_operand" "dJ")
+                 (match_operand:DI 2 "se_arith_operand" "dI")))]
+  "TARGET_64BIT && !TARGET_MIPS16
+   && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+  "*
+{
+  return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+    ? \"daddu\\t%0,%z1,%n2\"
+    : \"dsubu\\t%0,%z1,%2\";
+}"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "DI")
    (set_attr "length"  "1")])
 
-(define_insn "mulsf3"
-  [(set (match_operand:SF 0 "register_operand" "=f")
-       (mult:SF (match_operand:SF 1 "register_operand" "f")
-                (match_operand:SF 2 "register_operand" "f")))]
-  "TARGET_HARD_FLOAT"
-  "mul.s\\t%0,%1,%2"
-  [(set_attr "type"    "fmul")
-   (set_attr "mode"    "SF")
-   (set_attr "length"  "1")])
+;; For the mips16, we need to recognize stack pointer subtractions
+;; explicitly, since we don't have a constraint for $sp.  These insns
+;; will be generated by the save_restore_insns functions.
+
+(define_insn ""
+  [(set (reg:DI 29)
+       (minus:DI (reg:DI 29)
+                 (match_operand:DI 0 "small_int" "I")))]
+  "TARGET_MIPS16
+   && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+  "daddu\\t%$,%$,%n0"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set (attr "length")        (if_then_else (match_operand:VOID 0 "m16_nsimm8_8" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
 
-(define_insn "mulsi3"
-  [(set (match_operand:SI 0 "register_operand" "=d")
-       (mult:SI (match_operand:SI 1 "register_operand" "d")
-                (match_operand:SI 2 "register_operand" "d")))
-   (clobber (reg:SI 64))
-   (clobber (reg:SI 65))]
-  ""
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (minus:DI (reg:DI 29)
+                 (match_operand:DI 1 "small_int" "I")))]
+  "TARGET_MIPS16
+   && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+  "daddu\\t%0,%$,%n1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set (attr "length")        (if_then_else (match_operand:VOID 0 "m16_nuimm5_4" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+       (minus:DI (match_operand:DI 1 "register_operand" "0,d,d")
+                 (match_operand:DI 2 "arith_operand" "I,O,d")))]
+  "TARGET_MIPS16
+   && (GET_CODE (operands[2]) != CONST_INT
+       || (INTVAL (operands[2]) != -32768 && INTVAL (operands[2]) != -0x4000))"
   "*
 {
-  rtx xoperands[10];
+  if (REGNO (operands[0]) == REGNO (operands[1]))
+    return \"dsubu\\t%0,%2\";
+  return \"dsubu\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr_alternative "length"
+               [(if_then_else (match_operand:VOID 2 "m16_nsimm5_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (if_then_else (match_operand:VOID 2 "m16_nsimm4_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (const_int 1)])])
+
+;; On the mips16, we can sometimes split an add of a constant which is
+;; a 4 byte instruction into two adds which are both 2 byte
+;; instructions.  There are two cases: one where we are adding a
+;; constant plus a register to another register, and one where we are
+;; simply adding a constant to a register.
 
-  xoperands[0] = operands[0];
-  xoperands[1] = gen_rtx (REG, SImode, LO_REGNUM);
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (minus:DI (match_dup 0)
+                 (match_operand:DI 1 "const_int_operand" "")))]
+  "TARGET_MIPS16 && TARGET_64BIT && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == CONST_INT
+   && ((INTVAL (operands[1]) > 0x10
+       && INTVAL (operands[1]) <= 0x10 + 0x10)
+       || (INTVAL (operands[1]) < - 0xf
+          && INTVAL (operands[1]) >= - 0xf - 0xf))"
+  [(set (match_dup 0) (minus:DI (match_dup 0) (match_dup 1)))
+   (set (match_dup 0) (minus:DI (match_dup 0) (match_dup 2)))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[1]);
 
-  output_asm_insn (\"mult\\t%1,%2\", operands);
-  output_asm_insn (mips_move_1word (xoperands, insn), xoperands);
-  return \"\";
-}"
-  [(set_attr "type"    "imul")
-   (set_attr "mode"    "SI")
-   (set_attr "length"  "3")])          ;; mult + mflo + delay
+  if (val >= 0)
+    {
+      operands[1] = GEN_INT (0xf);
+      operands[2] = GEN_INT (val - 0xf);
+    }
+  else
+    {
+      operands[1] = GEN_INT (- 0x10);
+      operands[2] = GEN_INT (val + 0x10);
+    }
+}")
 
 (define_split
-  [(set (match_operand:SI 0 "register_operand" "")
-       (mult:SI (match_operand:SI 1 "register_operand" "")
-                (match_operand:SI 2 "register_operand" "")))
-   (clobber (reg:SI 64))
-   (clobber (reg:SI 65))]
-  "!TARGET_DEBUG_D_MODE"
-  [(parallel [(set (reg:SI 65)         ;; low register
-                  (mult:SI (match_dup 1)
-                           (match_dup 2)))
-             (clobber (reg:SI 64))])
-   (set (match_dup 0)
-       (reg:SI 65))]
-  "")
+  [(set (match_operand:DI 0 "register_operand" "")
+       (minus:DI (match_operand:DI 1 "register_operand" "")
+                 (match_operand:DI 2 "const_int_operand" "")))]
+  "TARGET_MIPS16 && TARGET_64BIT && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == REG
+   && M16_REG_P (REGNO (operands[1]))
+   && REGNO (operands[0]) != REGNO (operands[1])
+   && GET_CODE (operands[2]) == CONST_INT
+   && ((INTVAL (operands[2]) > 0x8
+       && INTVAL (operands[2]) <= 0x8 + 0x10)
+       || (INTVAL (operands[2]) < - 0x7
+          && INTVAL (operands[2]) >= - 0x7 - 0xf))"
+  [(set (match_dup 0) (minus:DI (match_dup 1) (match_dup 2)))
+   (set (match_dup 0) (minus:DI (match_dup 0) (match_dup 3)))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[2]);
 
-(define_insn "mulsi3_internal"
-  [(set (reg:SI 65)            ;; low register
-       (mult:SI (match_operand:SI 0 "register_operand" "d")
-                (match_operand:SI 1 "register_operand" "d")))
-   (clobber (reg:SI 64))]
-  ""
-  "mult\\t%0,%1"
-  [(set_attr "type"    "imul")
-   (set_attr "mode"    "SI")
-   (set_attr "length"  "1")])
+  if (val >= 0)
+    {
+      operands[2] = GEN_INT (0x8);
+      operands[3] = GEN_INT (val - 0x8);
+    }
+  else
+    {
+      operands[2] = GEN_INT (- 0x7);
+      operands[3] = GEN_INT (val + 0x7);
+    }
+}")
 
-(define_insn "mulsidi3"
+(define_insn "subsi3_internal_2"
   [(set (match_operand:DI 0 "register_operand" "=d")
-       (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d"))
-                (sign_extend:DI (match_operand:SI 2 "register_operand" "d"))))
-   (clobber (reg:DI 64))]
-  ""
+       (sign_extend:DI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
+                                 (match_operand:SI 2 "arith_operand" "dI"))))]
+  "TARGET_64BIT && !TARGET_MIPS16
+   && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
   "*
 {
-  rtx xoperands[10];
-
-  xoperands[0] = operands[0];
-  xoperands[1] = gen_rtx (REG, DImode, MD_REG_FIRST);
-
-  output_asm_insn (\"mult\\t%1,%2\", operands);
-  output_asm_insn (mips_move_2words (xoperands, insn), xoperands);
-  return \"\";
+  return (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) < 0)
+    ? \"addu\\t%0,%z1,%n2\"
+    : \"subu\\t%0,%z1,%2\";
 }"
-  [(set_attr "type"    "imul")
-   (set_attr "mode"    "SI")
-   (set_attr "length"  "4")])          ;; mult + mflo + mfhi + delay
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
 
-(define_insn "umulsidi3"
-  [(set (match_operand:DI 0 "register_operand" "=d")
-       (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "d"))
-                (zero_extend:DI (match_operand:SI 2 "register_operand" "d"))))
-   (clobber (reg:DI 64))]
-  ""
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+       (sign_extend:DI (minus:SI (match_operand:SI 1 "register_operand" "0,d,d")
+                                 (match_operand:SI 2 "arith_operand" "I,O,d"))))]
+  "TARGET_64BIT && TARGET_MIPS16
+   && (GET_CODE (operands[2]) != CONST_INT
+       || (INTVAL (operands[2]) != -32768 && INTVAL (operands[2]) != -0x4000))"
   "*
 {
-  rtx xoperands[10];
-
-  xoperands[0] = operands[0];
-  xoperands[1] = gen_rtx (REG, DImode, MD_REG_FIRST);
-
-  output_asm_insn (\"multu\\t%1,%2\", operands);
-  output_asm_insn (mips_move_2words (xoperands, insn), xoperands);
-  return \"\";
+  if (REGNO (operands[0]) == REGNO (operands[1]))
+    return \"subu\\t%0,%2\";
+  return \"subu\\t%0,%1,%2\";
 }"
-  [(set_attr "type"    "imul")
+  [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "4")])          ;; mult + mflo + mfhi + delay
+   (set_attr_alternative "length"
+               [(if_then_else (match_operand:VOID 2 "m16_nsimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (if_then_else (match_operand:VOID 2 "m16_nsimm4_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (const_int 1)])])
+  
 
 \f
 ;;
 ;;  ....................
 ;;
-;;     DIVISION and REMAINDER
+;;     MULTIPLICATION
 ;;
 ;;  ....................
 ;;
 
-(define_insn "divdf3"
+;; Early Vr4300 silicon has a CPU bug where multiplies with certain
+;; operands may corrupt immediately following multiplies. This is a
+;; simple fix to insert NOPs.
+
+(define_expand "muldf3"
   [(set (match_operand:DF 0 "register_operand" "=f")
-       (div:DF (match_operand:DF 1 "register_operand" "f")
-               (match_operand:DF 2 "register_operand" "f")))]
-  "TARGET_HARD_FLOAT"
-  "div.d\\t%0,%1,%2"
-  [(set_attr "type"    "fdiv")
+       (mult:DF (match_operand:DF 1 "register_operand" "f")
+                (match_operand:DF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "
+{
+  if (mips_cpu != PROCESSOR_R4300)
+    emit_insn (gen_muldf3_internal (operands[0], operands[1], operands[2]));
+  else
+    emit_insn (gen_muldf3_r4300 (operands[0], operands[1], operands[2]));
+  DONE;
+}")
+
+(define_insn "muldf3_internal"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (mult:DF (match_operand:DF 1 "register_operand" "f")
+                (match_operand:DF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && mips_cpu != PROCESSOR_R4300"
+  "mul.d\\t%0,%1,%2"
+  [(set_attr "type"    "fmul")
    (set_attr "mode"    "DF")
    (set_attr "length"  "1")])
 
-(define_insn "divsf3"
+(define_insn "muldf3_r4300"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (mult:DF (match_operand:DF 1 "register_operand" "f")
+                (match_operand:DF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && mips_cpu == PROCESSOR_R4300"
+  "*
+{
+  output_asm_insn (\"mul.d\\t%0,%1,%2\", operands);
+  if (TARGET_4300_MUL_FIX)
+    output_asm_insn (\"nop\", operands);
+  return \"\";
+}"
+  [(set_attr "type"    "fmul")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "2")])  ;; mul.d + nop
+
+(define_expand "mulsf3"
   [(set (match_operand:SF 0 "register_operand" "=f")
-       (div:SF (match_operand:SF 1 "register_operand" "f")
-               (match_operand:SF 2 "register_operand" "f")))]
+       (mult:SF (match_operand:SF 1 "register_operand" "f")
+                (match_operand:SF 2 "register_operand" "f")))]
   "TARGET_HARD_FLOAT"
-  "div.s\\t%0,%1,%2"
-  [(set_attr "type"    "fdiv")
+  "
+{
+  if (mips_cpu != PROCESSOR_R4300)
+    emit_insn( gen_mulsf3_internal (operands[0], operands[1], operands[2]));
+  else
+    emit_insn( gen_mulsf3_r4300 (operands[0], operands[1], operands[2]));
+  DONE;
+}")
+
+(define_insn "mulsf3_internal"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (mult:SF (match_operand:SF 1 "register_operand" "f")
+                (match_operand:SF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && mips_cpu != PROCESSOR_R4300"
+  "mul.s\\t%0,%1,%2"
+  [(set_attr "type"    "fmul")
    (set_attr "mode"    "SF")
    (set_attr "length"  "1")])
 
-;; If optimizing, prefer the divmod functions over separate div and
-;; mod functions, since this will allow using one instruction for both
-;; the quotient and remainder.  At present, the divmod is not moved out
-;; of loops if it is constant within the loop, so allow -mdebugc to
-;; use the old method of doing things.
-
-;; 64 is the multiply/divide hi register
-;; 65 is the multiply/divide lo register
-
-(define_insn "divmodsi4"
-  [(set (match_operand:SI 0 "register_operand" "=d")
-       (div:SI (match_operand:SI 1 "register_operand" "d")
-               (match_operand:SI 2 "register_operand" "d")))
-   (set (match_operand:SI 3 "register_operand" "=d")
-       (mod:SI (match_dup 1)
-               (match_dup 2)))
-   (clobber (reg:SI 64))
-   (clobber (reg:SI 65))]
-  "optimize"
+(define_insn "mulsf3_r4300"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (mult:SF (match_operand:SF 1 "register_operand" "f")
+                (match_operand:SF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && mips_cpu == PROCESSOR_R4300"
   "*
 {
-  if (find_reg_note (insn, REG_UNUSED, operands[3]))
-    return \"div\\t%0,%1,%2\";
-
-  if (find_reg_note (insn, REG_UNUSED, operands[0]))
-    return \"rem\\t%3,%1,%2\";
-
-  return \"div\\t%0,%1,%2\;mfhi\\t%3\";
+  output_asm_insn (\"mul.s\\t%0,%1,%2\", operands);
+  if (TARGET_4300_MUL_FIX)
+    output_asm_insn (\"nop\", operands);
+  return \"\";
 }"
-  [(set_attr "type"    "idiv")
-   (set_attr "mode"    "SI")
-   (set_attr "length"  "13")])         ;; various tests for dividing by 0 and such
+  [(set_attr "type"    "fmul")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "2")])  ;; mul.s + nop
 
-(define_insn "udivmodsi4"
-  [(set (match_operand:SI 0 "register_operand" "=d")
-       (udiv:SI (match_operand:SI 1 "register_operand" "d")
+;; ??? The R4000 (only) has a cpu bug.  If a double-word shift executes while
+;; a multiply is in progress, it may give an incorrect result.  Avoid
+;; this by keeping the mflo with the mult on the R4000.
+
+(define_expand "mulsi3"
+  [(set (match_operand:SI 0 "register_operand" "=l")
+       (mult:SI (match_operand:SI 1 "register_operand" "d")
                 (match_operand:SI 2 "register_operand" "d")))
-   (set (match_operand:SI 3 "register_operand" "=d")
-       (umod:SI (match_dup 1)
-                (match_dup 2)))
-   (clobber (reg:SI 64))
-   (clobber (reg:SI 65))]
-  "optimize"
-  "*
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=a"))]
+  ""
+  "
 {
-  if (find_reg_note (insn, REG_UNUSED, operands[3]))
-    return \"divu\\t%0,%1,%2\";
-
-  if (find_reg_note (insn, REG_UNUSED, operands[0]))
-    return \"remu\\t%3,%1,%2\";
-
-  return \"divu\\t%0,%1,%2\;mfhi\\t%3\";
-}"
-  [(set_attr "type"    "idiv")
-   (set_attr "mode"    "SI")
-   (set_attr "length"  "13")])         ;; various tests for dividing by 0 and such
+  if (GENERATE_MULT3)
+    emit_insn (gen_mulsi3_mult3 (operands[0], operands[1], operands[2]));
+  else if (TARGET_MAD)
+    emit_insn (gen_mulsi3_r4650 (operands[0], operands[1], operands[2]));
+  else if (mips_cpu != PROCESSOR_R4000 || TARGET_MIPS16)
+    emit_insn (gen_mulsi3_internal (operands[0], operands[1], operands[2]));
+  else
+    emit_insn (gen_mulsi3_r4000 (operands[0], operands[1], operands[2]));
+  DONE;
+}")
 
-(define_insn "divsi3"
+(define_insn "mulsi3_mult3"
   [(set (match_operand:SI 0 "register_operand" "=d")
-       (div:SI (match_operand:SI 1 "register_operand" "d")
-               (match_operand:SI 2 "register_operand" "d")))
-   (clobber (reg:SI 64))
-   (clobber (reg:SI 65))]
-  "!optimize"
-  "div\\t%0,%1,%2"
-  [(set_attr "type"    "idiv")
+       (mult:SI (match_operand:SI 1 "register_operand" "d")
+                (match_operand:SI 2 "register_operand" "d")))
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=l"))
+   (clobber (match_scratch:SI 5 "=a"))]
+  "GENERATE_MULT3"
+  "mult\\t%0,%1,%2"
+  [(set_attr "type"    "imul")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "13")])         ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
-(define_insn "modsi3"
-  [(set (match_operand:SI 0 "register_operand" "=d")
-       (mod:SI (match_operand:SI 1 "register_operand" "d")
-               (match_operand:SI 2 "register_operand" "d")))
-   (clobber (reg:SI 64))
-   (clobber (reg:SI 65))]
-  "!optimize"
-  "rem\\t%0,%1,%2"
-  [(set_attr "type"    "idiv")
+(define_insn "mulsi3_internal"
+  [(set (match_operand:SI 0 "register_operand" "=l")
+       (mult:SI (match_operand:SI 1 "register_operand" "d")
+                (match_operand:SI 2 "register_operand" "d")))
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=a"))]
+  "mips_cpu != PROCESSOR_R4000 || TARGET_MIPS16"
+  "mult\\t%1,%2"
+  [(set_attr "type"    "imul")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "14")])         ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
-(define_insn "udivsi3"
+(define_insn "mulsi3_r4000"
   [(set (match_operand:SI 0 "register_operand" "=d")
-       (udiv:SI (match_operand:SI 1 "register_operand" "d")
+       (mult:SI (match_operand:SI 1 "register_operand" "d")
                 (match_operand:SI 2 "register_operand" "d")))
-   (clobber (reg:SI 64))
-   (clobber (reg:SI 65))]
-  "!optimize"
-  "divu\\t%0,%1,%2"
-  [(set_attr "type"    "idiv")
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=l"))
+   (clobber (match_scratch:SI 5 "=a"))]
+  "mips_cpu == PROCESSOR_R4000 && !TARGET_MIPS16"
+  "*
+{
+  rtx xoperands[10];
+
+  xoperands[0] = operands[0];
+  xoperands[1] = gen_rtx (REG, SImode, LO_REGNUM);
+
+  output_asm_insn (\"mult\\t%1,%2\", operands);
+  output_asm_insn (mips_move_1word (xoperands, insn, FALSE), xoperands);
+  return \"\";
+}"
+  [(set_attr "type"    "imul")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "14")])         ;; various tests for dividing by 0 and such
+   (set_attr "length"  "3")])          ;; mult + mflo + delay
 
-(define_insn "umodsi3"
+(define_insn "mulsi3_r4650"
   [(set (match_operand:SI 0 "register_operand" "=d")
-       (umod:SI (match_operand:SI 1 "register_operand" "d")
+       (mult:SI (match_operand:SI 1 "register_operand" "d")
                 (match_operand:SI 2 "register_operand" "d")))
-   (clobber (reg:SI 64))
-   (clobber (reg:SI 65))]
-  "!optimize"
-  "remu\\t%0,%1,%2"
-  [(set_attr "type"    "idiv")
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=l"))
+   (clobber (match_scratch:SI 5 "=a"))]
+  "TARGET_MAD"
+  "mul\\t%0,%1,%2"
+  [(set_attr "type"    "imul")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "14")])         ;; various tests for dividing by 0 and such
+   (set_attr "length"  "1")])
 
-\f
-;;
-;;  ....................
-;;
-;;     SQUARE ROOT
-;;
-;;  ....................
+(define_expand "muldi3"
+  [(set (match_operand:DI 0 "register_operand" "=l")
+       (mult:DI (match_operand:DI 1 "se_register_operand" "d")
+                (match_operand:DI 2 "register_operand" "d")))
+   (clobber (match_scratch:DI 3 "=h"))
+   (clobber (match_scratch:DI 4 "=a"))]
+  "TARGET_64BIT"
+  "
+{
+  if (GENERATE_MULT3 || mips_cpu == PROCESSOR_R4000 || TARGET_MIPS16)
+    emit_insn (gen_muldi3_internal2 (operands[0], operands[1], operands[2]));
+  else
+    emit_insn (gen_muldi3_internal (operands[0], operands[1], operands[2]));
+  DONE;
+}")
 
-(define_insn "sqrtdf2"
-  [(set (match_operand:DF 0 "register_operand" "=f")
-       (sqrt:DF (match_operand:DF 1 "register_operand" "f")))]
-  "TARGET_HARD_FLOAT && HAVE_SQRT_P()"
-  "sqrt.d\\t%0,%1"
-  [(set_attr "type"    "fabs")
-   (set_attr "mode"    "DF")
+;; Don't accept both operands using se_register_operand, because if
+;; both operands are sign extended we would prefer to use mult in the
+;; mulsidi3 pattern.  Commutativity should permit either operand to be
+;; sign extended.
+
+(define_insn "muldi3_internal"
+  [(set (match_operand:DI 0 "register_operand" "=l")
+       (mult:DI (match_operand:DI 1 "se_register_operand" "d")
+                (match_operand:DI 2 "register_operand" "d")))
+   (clobber (match_scratch:DI 3 "=h"))
+   (clobber (match_scratch:DI 4 "=a"))]
+  "TARGET_64BIT && mips_cpu != PROCESSOR_R4000 && !TARGET_MIPS16"
+  "dmult\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "DI")
    (set_attr "length"  "1")])
 
-(define_insn "sqrtsf2"
-  [(set (match_operand:SF 0 "register_operand" "=f")
-       (sqrt:SF (match_operand:SF 1 "register_operand" "f")))]
-  "TARGET_HARD_FLOAT && HAVE_SQRT_P()"
-  "sqrt.s\\t%0,%1"
-  [(set_attr "type"    "fabs")
-   (set_attr "mode"    "SF")
-   (set_attr "length"  "1")])
+(define_insn "muldi3_internal2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (mult:DI (match_operand:DI 1 "se_register_operand" "d")
+                (match_operand:DI 2 "register_operand" "d")))
+   (clobber (match_scratch:DI 3 "=h"))
+   (clobber (match_scratch:DI 4 "=l"))
+   (clobber (match_scratch:DI 5 "=a"))]
+  "TARGET_64BIT && (GENERATE_MULT3 || mips_cpu == PROCESSOR_R4000 || TARGET_MIPS16)"
+  "*
+{
+  if (GENERATE_MULT3)
+    output_asm_insn (\"dmult\\t%0,%1,%2\", operands);
+  else 
+    {
+    rtx xoperands[10];
 
-\f
-;;
-;;  ....................
-;;
-;;     ABSOLUTE VALUE
-;;
-;;  ....................
+    xoperands[0] = operands[0];
+    xoperands[1] = gen_rtx (REG, DImode, LO_REGNUM);
 
-;; Do not use the integer abs macro instruction, since that signals an
-;; exception on -2147483648 (sigh).
+    output_asm_insn (\"dmult\\t%1,%2\", operands);
+    output_asm_insn (mips_move_1word (xoperands, insn, FALSE), xoperands);
+    }
+  return \"\";
+}"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "DI")
+   (set (attr "length")
+       (if_then_else (ne (symbol_ref "GENERATE_MULT3") (const_int 0))
+                      (const_int 1)
+                      (const_int 3)))])        ;; mult + mflo + delay
+
+;; ??? We could define a mulditi3 pattern when TARGET_64BIT.
+
+(define_expand "mulsidi3"
+  [(set (match_operand:DI 0 "register_operand" "=x")
+       (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d"))
+                (sign_extend:DI (match_operand:SI 2 "register_operand" "d"))))]
+  ""
+  "
+{
+  if (TARGET_64BIT)
+    emit_insn (gen_mulsidi3_64bit (operands[0], operands[1], operands[2]));
+  else
+    emit_insn (gen_mulsidi3_internal (operands[0], operands[1], operands[2]));
+  DONE;
+}")
+
+(define_insn "mulsidi3_internal"
+  [(set (match_operand:DI 0 "register_operand" "=x")
+       (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d"))
+                (sign_extend:DI (match_operand:SI 2 "register_operand" "d"))))
+   (clobber (match_scratch:SI 3 "=a"))]
+  "!TARGET_64BIT"
+  "mult\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn "mulsidi3_64bit"
+  [(set (match_operand:DI 0 "register_operand" "=a")
+       (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d"))
+                (sign_extend:DI (match_operand:SI 2 "register_operand" "d"))))
+   (clobber (match_scratch:DI 3 "=l"))
+   (clobber (match_scratch:DI 4 "=h"))]
+  "TARGET_64BIT"
+  "mult\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn "smulsi3_highpart"
+  [(set (match_operand:SI 0 "register_operand" "=h")
+       (truncate:SI
+        (lshiftrt:DI (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "d"))
+                              (sign_extend:DI (match_operand:SI 2 "register_operand" "d")))
+                     (const_int 32))))
+   (clobber (match_scratch:SI 3 "=l"))
+   (clobber (match_scratch:SI 4 "=a"))]
+  ""
+  "mult\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_expand "umulsidi3"
+  [(set (match_operand:DI 0 "register_operand" "=x")
+       (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "d"))
+                (zero_extend:DI (match_operand:SI 2 "register_operand" "d"))))]
+  ""
+  "
+{
+  if (TARGET_64BIT)
+    emit_insn (gen_umulsidi3_64bit (operands[0], operands[1], operands[2]));
+  else
+    emit_insn (gen_umulsidi3_internal (operands[0], operands[1], operands[2]));
+  DONE;
+}")
+
+(define_insn "umulsidi3_internal"
+  [(set (match_operand:DI 0 "register_operand" "=x")
+       (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "d"))
+                (zero_extend:DI (match_operand:SI 2 "register_operand" "d"))))
+   (clobber (match_scratch:SI 3 "=a"))]
+  "!TARGET_64BIT"
+  "multu\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn "umulsidi3_64bit"
+  [(set (match_operand:DI 0 "register_operand" "=a")
+       (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "d"))
+                (zero_extend:DI (match_operand:SI 2 "register_operand" "d"))))
+   (clobber (match_scratch:DI 3 "=l"))
+   (clobber (match_scratch:DI 4 "=h"))]
+  "TARGET_64BIT"
+  "multu\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn "umulsi3_highpart"
+  [(set (match_operand:SI 0 "register_operand" "=h")
+       (truncate:SI
+        (lshiftrt:DI (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "d"))
+                              (zero_extend:DI (match_operand:SI 2 "register_operand" "d")))
+                     (const_int 32))))
+   (clobber (match_scratch:SI 3 "=l"))
+   (clobber (match_scratch:SI 4 "=a"))]
+  ""
+  "multu\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn "smuldi3_highpart"
+  [(set (match_operand:DI 0 "register_operand" "=h")
+       (truncate:DI
+        (lshiftrt:TI (mult:TI (sign_extend:TI (match_operand:DI 1 "se_register_operand" "d"))
+                              (sign_extend:TI (match_operand:DI 2 "se_register_operand" "d")))
+                     (const_int 64))))
+   (clobber (match_scratch:DI 3 "=l"))
+   (clobber (match_scratch:DI 4 "=a"))]
+  "TARGET_64BIT"
+  "dmult\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
+(define_insn "umuldi3_highpart"
+  [(set (match_operand:DI 0 "register_operand" "=h")
+       (truncate:DI
+        (lshiftrt:TI (mult:TI (zero_extend:TI (match_operand:DI 1 "se_register_operand" "d"))
+                              (zero_extend:TI (match_operand:DI 2 "se_register_operand" "d")))
+                     (const_int 64))))
+   (clobber (match_scratch:DI 3 "=l"))
+   (clobber (match_scratch:DI 4 "=a"))]
+  "TARGET_64BIT"
+  "dmultu\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
+;; The R4650 supports a 32 bit multiply/ 64 bit accumulate
+;; instruction.  The HI/LO registers are used as a 64 bit accumulator.
+
+(define_insn "madsi"
+  [(set (match_operand:SI 0 "register_operand" "+l")
+       (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d")
+                         (match_operand:SI 2 "register_operand" "d"))
+                (match_dup 0)))
+   (clobber (match_scratch:SI 3 "=h"))
+   (clobber (match_scratch:SI 4 "=a"))]
+  "TARGET_MAD || GENERATE_MADD"
+  "*
+{
+  if (TARGET_MAD)
+    return \"mad\\t%1,%2\";
+  else
+    return \"madd\\t%1,%2\";
+}"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"   "1")])
+
+(define_insn "maddi"
+  [(set (match_operand:DI 0 "register_operand" "+x")
+       (plus:DI (mult:DI (sign_extend:DI
+                          (match_operand:SI 1 "register_operand" "d"))
+                         (sign_extend:DI
+                          (match_operand:SI 2 "register_operand" "d")))
+                (match_dup 0)))
+   (clobber (match_scratch:SI 3 "=a"))]
+  "TARGET_MAD && ! TARGET_64BIT"
+  "mad\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"   "1")])
+
+(define_insn "maddi_64bit"
+  [(set (match_operand:DI 0 "register_operand" "+a")
+       (plus:DI (mult:DI (sign_extend:DI
+                          (match_operand:SI 1 "register_operand" "d"))
+                         (sign_extend:DI
+                          (match_operand:SI 2 "register_operand" "d")))
+                (match_dup 0)))
+   (clobber (match_scratch:DI 3 "=l"))
+   (clobber (match_scratch:DI 4 "=h"))]
+  "TARGET_MAD && TARGET_64BIT"
+  "mad\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"   "1")])
+
+(define_insn "umaddi"
+  [(set (match_operand:DI 0 "register_operand" "+x")
+       (plus:DI (mult:DI (zero_extend:DI
+                          (match_operand:SI 1 "register_operand" "d"))
+                         (zero_extend:DI
+                          (match_operand:SI 2 "register_operand" "d")))
+                (match_dup 0)))
+   (clobber (match_scratch:SI 3 "=a"))]
+  "TARGET_MAD && ! TARGET_64BIT"
+  "madu\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"   "1")])
+
+(define_insn "umaddi_64bit"
+  [(set (match_operand:DI 0 "register_operand" "+a")
+       (plus:DI (mult:DI (zero_extend:DI
+                          (match_operand:SI 1 "register_operand" "d"))
+                         (zero_extend:DI
+                          (match_operand:SI 2 "register_operand" "d")))
+                (match_dup 0)))
+   (clobber (match_scratch:DI 3 "=l"))
+   (clobber (match_scratch:DI 4 "=h"))]
+  "TARGET_MAD && TARGET_64BIT"
+  "madu\\t%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"   "1")])
+
+(define_insn "madd3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d")
+                         (match_operand:SI 2 "register_operand" "d"))
+                (match_operand:SI 3 "register_operand" "l")))
+   (clobber (match_scratch:SI 4 "=l"))
+   (clobber (match_scratch:SI 5 "=h"))
+   (clobber (match_scratch:SI 6 "=a"))]
+  "GENERATE_MADD"
+  "madd\\t%0,%1,%2"
+  [(set_attr "type"    "imul")
+   (set_attr "mode"    "SI")
+   (set_attr "length"   "1")])
+
+;; Floating point multiply accumulate instructions.
+
+(define_insn ""
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (plus:DF (mult:DF (match_operand:DF 1 "register_operand" "f")
+                         (match_operand:DF 2 "register_operand" "f"))
+                (match_operand:DF 3 "register_operand" "f")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "madd.d\\t%0,%3,%1,%2"
+  [(set_attr "type"    "fmadd")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (plus:SF (mult:SF (match_operand:SF 1 "register_operand" "f")
+                         (match_operand:SF 2 "register_operand" "f"))
+                (match_operand:SF 3 "register_operand" "f")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "madd.s\\t%0,%3,%1,%2"
+  [(set_attr "type"    "fmadd")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (minus:DF (mult:DF (match_operand:DF 1 "register_operand" "f")
+                          (match_operand:DF 2 "register_operand" "f"))
+                 (match_operand:DF 3 "register_operand" "f")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "msub.d\\t%0,%3,%1,%2"
+  [(set_attr "type"    "fmadd")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (minus:SF (mult:SF (match_operand:SF 1 "register_operand" "f")
+                          (match_operand:SF 2 "register_operand" "f"))
+                 (match_operand:SF 3 "register_operand" "f")))]
+                 
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "msub.s\\t%0,%3,%1,%2"
+  [(set_attr "type"    "fmadd")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (neg:DF (plus:DF (mult:DF (match_operand:DF 1 "register_operand" "f")
+                                 (match_operand:DF 2 "register_operand" "f"))
+                        (match_operand:DF 3 "register_operand" "f"))))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "nmadd.d\\t%0,%3,%1,%2"
+  [(set_attr "type"    "fmadd")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (neg:SF (plus:SF (mult:SF (match_operand:SF 1 "register_operand" "f")
+                                 (match_operand:SF 2 "register_operand" "f"))
+                        (match_operand:SF 3 "register_operand" "f"))))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "nmadd.s\\t%0,%3,%1,%2"
+  [(set_attr "type"    "fmadd")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (minus:DF (match_operand:DF 1 "register_operand" "f")
+                 (mult:DF (match_operand:DF 2 "register_operand" "f")
+                          (match_operand:DF 3 "register_operand" "f"))))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "nmsub.d\\t%0,%1,%2,%3"
+  [(set_attr "type"    "fmadd")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (minus:SF (match_operand:SF 1 "register_operand" "f")
+                 (mult:SF (match_operand:SF 2 "register_operand" "f")
+                          (match_operand:SF 3 "register_operand" "f"))))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "nmsub.s\\t%0,%1,%2,%3"
+  [(set_attr "type"    "fmadd")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+\f
+;;
+;;  ....................
+;;
+;;     DIVISION and REMAINDER
+;;
+;;  ....................
+;;
+
+(define_insn "divdf3"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (div:DF (match_operand:DF 1 "register_operand" "f")
+               (match_operand:DF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "div.d\\t%0,%1,%2"
+  [(set_attr "type"    "fdiv")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
+
+(define_insn "divsf3"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (div:SF (match_operand:SF 1 "register_operand" "f")
+               (match_operand:SF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
+  "div.s\\t%0,%1,%2"
+  [(set_attr "type"    "fdiv")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (div:DF (match_operand:DF 1 "const_float_1_operand" "")
+               (match_operand:DF 2 "register_operand" "f")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && flag_fast_math"
+  "recip.d\\t%0,%2"
+  [(set_attr "type"    "fdiv")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (div:SF (match_operand:SF 1 "const_float_1_operand" "")
+               (match_operand:SF 2 "register_operand" "f")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && flag_fast_math"
+  "recip.s\\t%0,%2"
+  [(set_attr "type"    "fdiv")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+
+;; If optimizing, prefer the divmod functions over separate div and
+;; mod functions, since this will allow using one instruction for both
+;; the quotient and remainder.  At present, the divmod is not moved out
+;; of loops if it is constant within the loop, so allow -mdebugc to
+;; use the old method of doing things.
+
+;; 64 is the multiply/divide hi register
+;; 65 is the multiply/divide lo register
+
+;; ??? We can't accept constants here, because the MIPS assembler will replace
+;; a divide by power of 2 with a shift, and then the remainder is no longer
+;; available.
+
+(define_insn "divmodsi4"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (div:SI (match_operand:SI 1 "register_operand" "d")
+               (match_operand:SI 2 "register_operand" "d")))
+   (set (match_operand:SI 3 "register_operand" "=d")
+       (mod:SI (match_dup 1)
+               (match_dup 2)))
+   (clobber (match_scratch:SI 4 "=l"))
+   (clobber (match_scratch:SI 5 "=h"))
+   (clobber (match_scratch:SI 6 "=a"))]
+  "optimize"
+  "*
+{
+  if (find_reg_note (insn, REG_UNUSED, operands[3]))
+    return \"div\\t%0,%1,%2\";
+
+  if (find_reg_note (insn, REG_UNUSED, operands[0]))
+    return \"rem\\t%3,%1,%2\";
+
+  return \"div\\t%0,%1,%2\;mfhi\\t%3\";
+}"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "14")])         ;; various tests for dividing by 0 and such
+
+(define_insn "divmoddi4"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (div:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))
+   (set (match_operand:DI 3 "register_operand" "=d")
+       (mod:DI (match_dup 1)
+               (match_dup 2)))
+   (clobber (match_scratch:DI 4 "=l"))
+   (clobber (match_scratch:DI 5 "=h"))
+   (clobber (match_scratch:DI 6 "=a"))]
+  "TARGET_64BIT && optimize"
+  "*
+{
+  if (find_reg_note (insn, REG_UNUSED, operands[3]))
+    return \"ddiv\\t%0,%1,%2\";
+
+  if (find_reg_note (insn, REG_UNUSED, operands[0]))
+    return \"drem\\t%3,%1,%2\";
+
+  return \"ddiv\\t%0,%1,%2\;mfhi\\t%3\";
+}"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "15")])         ;; various tests for dividing by 0 and such
+
+(define_insn "udivmodsi4"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (udiv:SI (match_operand:SI 1 "register_operand" "d")
+                (match_operand:SI 2 "register_operand" "d")))
+   (set (match_operand:SI 3 "register_operand" "=d")
+       (umod:SI (match_dup 1)
+                (match_dup 2)))
+   (clobber (match_scratch:SI 4 "=l"))
+   (clobber (match_scratch:SI 5 "=h"))
+   (clobber (match_scratch:SI 6 "=a"))]
+  "optimize"
+  "*
+{
+  if (find_reg_note (insn, REG_UNUSED, operands[3]))
+    return \"divu\\t%0,%1,%2\";
+
+  if (find_reg_note (insn, REG_UNUSED, operands[0]))
+    return \"remu\\t%3,%1,%2\";
+
+  return \"divu\\t%0,%1,%2\;mfhi\\t%3\";
+}"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "8")])          ;; various tests for dividing by 0 and such
+
+(define_insn "udivmoddi4"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (udiv:DI (match_operand:DI 1 "se_register_operand" "d")
+                (match_operand:DI 2 "se_register_operand" "d")))
+   (set (match_operand:DI 3 "register_operand" "=d")
+       (umod:DI (match_dup 1)
+                (match_dup 2)))
+   (clobber (match_scratch:DI 4 "=l"))
+   (clobber (match_scratch:DI 5 "=h"))
+   (clobber (match_scratch:DI 6 "=a"))]
+  "TARGET_64BIT && optimize"
+  "*
+{
+  if (find_reg_note (insn, REG_UNUSED, operands[3]))
+    return \"ddivu\\t%0,%1,%2\";
+
+  if (find_reg_note (insn, REG_UNUSED, operands[0]))
+    return \"dremu\\t%3,%1,%2\";
+
+  return \"ddivu\\t%0,%1,%2\;mfhi\\t%3\";
+}"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "8")])          ;; various tests for dividing by 0 and such
+
+(define_expand "divsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (div:SI (match_operand:SI 1 "register_operand" "d")
+               (match_operand:SI 2 "nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=l"))
+   (clobber (match_scratch:SI 4 "=h"))
+   (clobber (match_scratch:SI 6 "=a"))]
+  "!optimize"
+  "
+{
+  /* MIPS16 needs div/rem ops in registers. */
+  if (TARGET_MIPS16)
+    operands[2] = force_reg (SImode, operands[2]);
+}")
+
+(define_insn "divsi3_internal"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (div:SI (match_operand:SI 1 "register_operand" "d")
+               (match_operand:SI 2 "nonmemory_operand" "di")))]
+  "!optimize"
+  "div\\t%0,%1,%2"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "13")]) ;; various tests for dividing by 0 and such
+
+(define_expand "divdi3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (div:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_nonmemory_operand" "di")))
+   (clobber (match_scratch:DI 3 "=l"))
+   (clobber (match_scratch:DI 4 "=h"))
+   (clobber (match_scratch:DI 6 "=a"))]
+  "TARGET_64BIT && !optimize"
+  "
+{
+  /* MIPS16 needs div/rem ops in registers. */
+  if (TARGET_MIPS16)
+    operands[2] = force_reg (DImode, operands[2]);
+}")
+
+(define_insn "divdi3_internal"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (div:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_nonmemory_operand" "di")))]
+  "TARGET_64BIT && !optimize"
+  "ddiv\\t%0,%1,%2"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "14")]) ;; various tests for dividing by 0 and such
+
+(define_expand "modsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (mod:SI (match_operand:SI 1 "register_operand" "d")
+               (match_operand:SI 2 "nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=l"))
+   (clobber (match_scratch:SI 4 "=h"))
+   (clobber (match_scratch:SI 6 "=a"))]
+  "!optimize"
+  "
+{
+  /* MIPS16 needs div/rem ops in registers. */
+  if (TARGET_MIPS16)
+    operands[2] = force_reg (SImode, operands[2]);
+}")
+
+(define_insn "modsi3_internal"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (mod:SI (match_operand:SI 1 "register_operand" "d")
+               (match_operand:SI 2 "nonmemory_operand" "di")))]
+  "!optimize"
+  "rem\\t%0,%1,%2"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "13")]) ;; various tests for dividing by 0 and such
+
+(define_expand "moddi3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (mod:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_nonmemory_operand" "di")))
+   (clobber (match_scratch:DI 3 "=l"))
+   (clobber (match_scratch:DI 4 "=h"))
+   (clobber (match_scratch:DI 6 "=a"))]
+  "TARGET_64BIT && !optimize"
+  "
+{
+  /* MIPS16 needs div/rem ops in registers. */
+  if (TARGET_MIPS16)
+    operands[2] = force_reg (DImode, operands[2]);
+}")
+
+(define_insn "moddi3_internal"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (mod:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_nonmemory_operand" "di")))]
+  "TARGET_64BIT && !optimize"
+  "drem\\t%0,%1,%2"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "14")]) ;; various tests for dividing by 0 and such
+
+(define_expand "udivsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (udiv:SI (match_operand:SI 1 "register_operand" "d")
+                (match_operand:SI 2 "nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=l"))
+   (clobber (match_scratch:SI 4 "=h"))
+   (clobber (match_scratch:SI 6 "=a"))]
+  "!optimize"
+  "
+{
+  /* MIPS16 needs div/rem ops in registers. */
+  if (TARGET_MIPS16)
+    operands[2] = force_reg (SImode, operands[2]);
+}")
+
+(define_insn "udivsi3_internal"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (udiv:SI (match_operand:SI 1 "register_operand" "d")
+                (match_operand:SI 2 "nonmemory_operand" "di")))]
+  "!optimize"
+  "divu\\t%0,%1,%2"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "7")])  ;; various tests for dividing by 0 and such
+
+(define_expand "udivdi3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (udiv:DI (match_operand:DI 1 "se_register_operand" "d")
+                (match_operand:DI 2 "se_nonmemory_operand" "di")))
+   (clobber (match_scratch:DI 3 "=l"))
+   (clobber (match_scratch:DI 4 "=h"))
+   (clobber (match_scratch:DI 6 "=a"))]
+  "TARGET_64BIT && !optimize"
+  "
+{
+  /* MIPS16 needs div/rem ops in registers. */
+  if (TARGET_MIPS16)
+    operands[2] = force_reg (DImode, operands[2]);
+}")
+
+(define_insn "udivdi3_internal"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (udiv:DI (match_operand:DI 1 "se_register_operand" "d")
+                (match_operand:DI 2 "se_nonmemory_operand" "di")))]
+  "TARGET_64BIT && !optimize"
+  "ddivu\\t%0,%1,%2"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "7")])  ;; various tests for dividing by 0 and such
+
+(define_expand "umodsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (umod:SI (match_operand:SI 1 "register_operand" "d")
+                (match_operand:SI 2 "nonmemory_operand" "di")))
+   (clobber (match_scratch:SI 3 "=l"))
+   (clobber (match_scratch:SI 4 "=h"))
+   (clobber (match_scratch:SI 6 "=a"))]
+  "!optimize"
+  "
+{
+  /* MIPS16 needs div/rem ops in registers. */
+  if (TARGET_MIPS16)
+    operands[2] = force_reg (SImode, operands[2]);
+}")
+
+(define_insn "umodsi3_internal"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (umod:SI (match_operand:SI 1 "register_operand" "d")
+                (match_operand:SI 2 "nonmemory_operand" "di")))]
+  "!optimize"
+  "remu\\t%0,%1,%2"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "7")])  ;; various tests for dividing by 0 and such
+
+(define_expand "umoddi3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (umod:DI (match_operand:DI 1 "se_register_operand" "d")
+                (match_operand:DI 2 "se_nonmemory_operand" "di")))
+   (clobber (match_scratch:DI 3 "=l"))
+   (clobber (match_scratch:DI 4 "=h"))
+   (clobber (match_scratch:DI 6 "=a"))]
+  "TARGET_64BIT && !optimize"
+  "
+{
+  /* MIPS16 needs div/rem ops in registers. */
+  if (TARGET_MIPS16)
+    operands[2] = force_reg (DImode, operands[2]);
+}")
+
+(define_insn "umoddi3_internal"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (umod:DI (match_operand:DI 1 "se_register_operand" "d")
+                (match_operand:DI 2 "se_nonmemory_operand" "di")))]
+  "TARGET_64BIT && !optimize"
+  "dremu\\t%0,%1,%2"
+  [(set_attr "type"    "idiv")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "7")])  ;; various tests for dividing by 0 and such
+
+\f
+;;
+;;  ....................
+;;
+;;     SQUARE ROOT
+;;
+;;  ....................
+
+(define_insn "sqrtdf2"
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (sqrt:DF (match_operand:DF 1 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && HAVE_SQRT_P() && TARGET_DOUBLE_FLOAT"
+  "sqrt.d\\t%0,%1"
+  [(set_attr "type"    "fsqrt")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
+
+(define_insn "sqrtsf2"
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (sqrt:SF (match_operand:SF 1 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && HAVE_SQRT_P()"
+  "sqrt.s\\t%0,%1"
+  [(set_attr "type"    "fsqrt")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (div:DF (match_operand:DF 1 "const_float_1_operand" "")
+               (sqrt:DF (match_operand:DF 2 "register_operand" "f"))))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT && flag_fast_math"
+  "rsqrt.d\\t%0,%2"
+  [(set_attr "type"    "fsqrt")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (div:SF (match_operand:SF 1 "const_float_1_operand" "")
+               (sqrt:SF (match_operand:SF 2 "register_operand" "f"))))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && flag_fast_math"
+  "rsqrt.s\\t%0,%2"
+  [(set_attr "type"    "fsqrt")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+
+\f
+;;
+;;  ....................
+;;
+;;     ABSOLUTE VALUE
+;;
+;;  ....................
+
+;; Do not use the integer abs macro instruction, since that signals an
+;; exception on -2147483648 (sigh).
 
 (define_insn "abssi2"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (abs:SI (match_operand:SI 1 "register_operand" "d")))]
-  ""
+  "!TARGET_MIPS16"
   "*
 {
   dslots_jump_total++;
   dslots_jump_filled++;
   operands[2] = const0_rtx;
 
-  return (REGNO (operands[0]) == REGNO (operands[1]))
-               ? \"bgez\\t%1,1f%#\\n\\tsubu\\t%0,%z2,%0\\n1:\"
-               : \"%(bgez\\t%1,1f\\n\\tmove\\t%0,%1\\n\\tsubu\\t%0,%z2,%0\\n1:%)\";
+  if (REGNO (operands[0]) == REGNO (operands[1]))
+    {
+      if (GENERATE_BRANCHLIKELY)
+       return \"%(bltzl\\t%1,1f\\n\\tsubu\\t%0,%z2,%0\\n1:%)\";
+      else
+       return \"bgez\\t%1,1f%#\\n\\tsubu\\t%0,%z2,%0\\n1:\";
+    }    
+  else
+    return \"%(bgez\\t%1,1f\\n\\tmove\\t%0,%1\\n\\tsubu\\t%0,%z2,%0\\n1:%)\";
 }"
   [(set_attr "type"    "multi")
    (set_attr "mode"    "SI")
    (set_attr "length"  "3")])
 
+(define_insn "absdi2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (abs:DI (match_operand:DI 1 "se_register_operand" "d")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "*
+{
+  dslots_jump_total++;
+  dslots_jump_filled++;
+  operands[2] = const0_rtx;
+
+  if (REGNO (operands[0]) == REGNO (operands[1]))
+    return \"%(bltzl\\t%1,1f\\n\\tdsubu\\t%0,%z2,%0\\n1:%)\";
+  else
+    return \"%(bgez\\t%1,1f\\n\\tmove\\t%0,%1\\n\\tdsubu\\t%0,%z2,%0\\n1:%)\";
+}"
+  [(set_attr "type"    "multi")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "3")])
+
 (define_insn "absdf2"
   [(set (match_operand:DF 0 "register_operand" "=f")
        (abs:DF (match_operand:DF 1 "register_operand" "f")))]
-  "TARGET_HARD_FLOAT"
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "abs.d\\t%0,%1"
   [(set_attr "type"    "fabs")
    (set_attr "mode"    "DF")
        (ffs:SI (match_operand:SI 1 "register_operand" "d")))
    (clobber (match_scratch:SI 2 "=&d"))
    (clobber (match_scratch:SI 3 "=&d"))]
-  ""
+  "!TARGET_MIPS16"
   "*
 {
   dslots_jump_total += 2;
@@ -1026,6 +2616,42 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "6")])
 
+(define_insn "ffsdi2"
+  [(set (match_operand:DI 0 "register_operand" "=&d")
+       (ffs:DI (match_operand:DI 1 "se_register_operand" "d")))
+   (clobber (match_scratch:DI 2 "=&d"))
+   (clobber (match_scratch:DI 3 "=&d"))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "*
+{
+  dslots_jump_total += 2;
+  dslots_jump_filled += 2;
+  operands[4] = const0_rtx;
+
+  if (optimize && find_reg_note (insn, REG_DEAD, operands[1]))
+    return \"%(\\
+move\\t%0,%z4\\n\\
+\\tbeq\\t%1,%z4,2f\\n\\
+1:\\tand\\t%2,%1,0x0001\\n\\
+\\tdaddu\\t%0,%0,1\\n\\
+\\tbeq\\t%2,%z4,1b\\n\\
+\\tdsrl\\t%1,%1,1\\n\\
+2:%)\";
+
+  return \"%(\\
+move\\t%0,%z4\\n\\
+\\tmove\\t%3,%1\\n\\
+\\tbeq\\t%3,%z4,2f\\n\\
+1:\\tand\\t%2,%3,0x0001\\n\\
+\\tdaddu\\t%0,%0,1\\n\\
+\\tbeq\\t%2,%z4,1b\\n\\
+\\tdsrl\\t%3,%3,1\\n\\
+2:%)\";
+}"
+  [(set_attr "type"    "multi")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "6")])
+
 \f
 ;;
 ;;  ....................
@@ -1040,6 +2666,8 @@ move\\t%0,%z4\\n\\
   ""
   "*
 {
+  if (TARGET_MIPS16)
+    return \"neg\\t%0,%1\";
   operands[2] = const0_rtx;
   return \"subu\\t%0,%z2,%1\";
 }"
@@ -1047,18 +2675,27 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
-(define_expand "negdi3"
+(define_expand "negdi2"
   [(parallel [(set (match_operand:DI 0 "register_operand" "=d")
-                  (neg:DI (match_operand:DI 1 "register_operand" "d")))
+                  (neg:DI (match_operand:DI 1 "se_register_operand" "d")))
              (clobber (match_dup 2))])]
-  "!TARGET_DEBUG_G_MODE"
-  "operands[2] = gen_reg_rtx (SImode);")
+  "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16"
+  "
+{
+  if (TARGET_64BIT)
+    {
+      emit_insn (gen_negdi2_internal_2 (operands[0], operands[1]));
+      DONE;
+    }
+
+  operands[2] = gen_reg_rtx (SImode);
+}")
 
-(define_insn "negdi3_internal"
+(define_insn "negdi2_internal"
   [(set (match_operand:DI 0 "register_operand" "=d")
        (neg:DI (match_operand:DI 1 "register_operand" "d")))
    (clobber (match_operand:SI 2 "register_operand" "=d"))]
-  "!TARGET_DEBUG_G_MODE"
+  "! TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
   "*
 {
   operands[3] = const0_rtx;
@@ -1068,10 +2705,23 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "DI")
    (set_attr "length"  "4")])
 
+(define_insn "negdi2_internal_2"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (neg:DI (match_operand:DI 1 "se_register_operand" "d")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "*
+{
+  operands[2] = const0_rtx;
+  return \"dsubu\\t%0,%z2,%1\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
 (define_insn "negdf2"
   [(set (match_operand:DF 0 "register_operand" "=f")
        (neg:DF (match_operand:DF 1 "register_operand" "f")))]
-  "TARGET_HARD_FLOAT"
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "neg.d\\t%0,%1"
   [(set_attr "type"    "fneg")
    (set_attr "mode"    "DF")
@@ -1092,6 +2742,8 @@ move\\t%0,%z4\\n\\
   ""
   "*
 {
+  if (TARGET_MIPS16)
+    return \"not\\t%0,%1\";
   operands[2] = const0_rtx;
   return \"nor\\t%0,%z2,%1\";
 }"
@@ -1101,21 +2753,32 @@ move\\t%0,%z4\\n\\
 
 (define_insn "one_cmpldi2"
   [(set (match_operand:DI 0 "register_operand" "=d")
-       (not:SI (match_operand:DI 1 "register_operand" "d")))]
+       (not:DI (match_operand:DI 1 "se_register_operand" "d")))]
   ""
   "*
 {
+  if (TARGET_MIPS16)
+    {
+      if (TARGET_64BIT)
+       return \"not\\t%0,%1\";
+      return \"not\\t%M0,%M1\;not\\t%L0,%L1\";
+    }
   operands[2] = const0_rtx;
+  if (TARGET_64BIT)
+    return \"nor\\t%0,%z2,%1\";
   return \"nor\\t%M0,%z2,%M1\;nor\\t%L0,%z2,%L1\";
 }"
   [(set_attr "type"    "darith")
    (set_attr "mode"    "DI")
-   (set_attr "length"  "2")])
+   (set (attr "length")
+       (if_then_else (ge (symbol_ref "mips_isa") (const_int 3))
+                      (const_int 1)
+                      (const_int 2)))])
 
 (define_split
   [(set (match_operand:DI 0 "register_operand" "")
        (not:DI (match_operand:DI 1 "register_operand" "")))]
-  "reload_completed && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))"
 
@@ -1123,43 +2786,6 @@ move\\t%0,%z4\\n\\
    (set (subreg:SI (match_dup 0) 1) (not:SI (subreg:SI (match_dup 1) 1)))]
   "")
 
-;; Simple hack to recognize the "nor" instruction on the MIPS
-;; This must appear before the normal or patterns, so that the
-;; combiner will correctly fold things.
-
-(define_insn "norsi3"
-  [(set (match_operand:SI 0 "register_operand" "=d")
-       (not:SI (ior:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
-                       (match_operand:SI 2 "reg_or_0_operand" "dJ"))))]
-  ""
-  "nor\\t%0,%z1,%z2"
-  [(set_attr "type"    "arith")
-   (set_attr "mode"    "SI")
-   (set_attr "length"  "1")])
-
-(define_insn "nordi3"
-  [(set (match_operand:DI 0 "register_operand" "=d")
-       (not:DI (ior:DI (match_operand:DI 1 "register_operand" "d")
-                       (match_operand:DI 2 "register_operand" "d"))))]
-  ""
-  "nor\\t%M0,%M1,%M2\;nor\\t%L0,%L1,%L2"
-  [(set_attr "type"    "darith")
-   (set_attr "mode"    "DI")
-   (set_attr "length"  "2")])
-
-(define_split
-  [(set (match_operand:DI 0 "register_operand" "")
-       (not:DI (ior:DI (match_operand:DI 1 "register_operand" "")
-                       (match_operand:DI 2 "register_operand" ""))))]
-  "reload_completed && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
-   && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
-   && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
-   && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
-
-  [(set (subreg:SI (match_dup 0) 0) (not:SI (ior:SI (subreg:SI (match_dup 1) 0) (subreg:SI (match_dup 2) 0))))
-   (set (subreg:SI (match_dup 0) 1) (not:SI (ior:SI (subreg:SI (match_dup 1) 1) (subreg:SI (match_dup 2) 1))))]
-  "")
-
 \f
 ;;
 ;;  ....................
@@ -1169,11 +2795,25 @@ move\\t%0,%z4\\n\\
 ;;  ....................
 ;;
 
-(define_insn "andsi3"
+;; Many of these instructions uses trivial define_expands, because we
+;; want to use a different set of constraints when TARGET_MIPS16.
+
+(define_expand "andsi3"
   [(set (match_operand:SI 0 "register_operand" "=d,d")
        (and:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
                (match_operand:SI 2 "uns_arith_operand" "d,K")))]
   ""
+  "
+{
+  if (TARGET_MIPS16)
+    operands[2] = force_reg (SImode, operands[2]);
+}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (and:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+               (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+  "!TARGET_MIPS16"
   "@
    and\\t%0,%1,%2
    andi\\t%0,%1,%x2"
@@ -1181,21 +2821,68 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
-(define_insn "anddi3"
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (and:SI (match_operand:SI 1 "register_operand" "%0")
+               (match_operand:SI 2 "register_operand" "d")))]
+  "TARGET_MIPS16"
+  "and\\t%0,%2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_expand "anddi3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (and:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+  "
+{
+  if (TARGET_MIPS16)
+    operands[2] = force_reg (DImode, operands[2]);
+}")
+
+(define_insn ""
   [(set (match_operand:DI 0 "register_operand" "=d")
-       (and:DI (match_operand:DI 1 "register_operand" "d")
-               (match_operand:DI 2 "register_operand" "d")))]
-  "!TARGET_DEBUG_G_MODE"
-  "and\\t%M0,%M1,%M2\;and\\t%L0,%L1,%L2"
+       (and:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16"
+  "*
+{
+  if (TARGET_64BIT)
+    return \"and\\t%0,%1,%2\";
+  return \"and\\t%M0,%M1,%M2\;and\\t%L0,%L1,%L2\";
+}"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "DI")
+   (set (attr "length")
+       (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0))
+                      (const_int 1)
+                      (const_int 2)))])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (and:DI (match_operand:DI 1 "se_register_operand" "0")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && TARGET_MIPS16"
+  "*
+{
+  if (TARGET_64BIT)
+    return \"and\\t%0,%2\";
+  return \"and\\t%M0,%M2\;and\\t%L0,%L2\";
+}"
   [(set_attr "type"    "darith")
    (set_attr "mode"    "DI")
-   (set_attr "length"  "2")])
+   (set (attr "length")
+       (if_then_else (ge (symbol_ref "mips_isa") (const_int 3))
+                      (const_int 1)
+                      (const_int 2)))])
 
 (define_split
   [(set (match_operand:DI 0 "register_operand" "")
        (and:DI (match_operand:DI 1 "register_operand" "")
                (match_operand:DI 2 "register_operand" "")))]
-  "reload_completed && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
    && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
@@ -1204,11 +2891,34 @@ move\\t%0,%z4\\n\\
    (set (subreg:SI (match_dup 0) 1) (and:SI (subreg:SI (match_dup 1) 1) (subreg:SI (match_dup 2) 1)))]
   "")
 
-(define_insn "iorsi3"
+(define_insn "anddi3_internal1"
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (and:DI (match_operand:DI 1 "se_register_operand" "%d,d")
+               (match_operand:DI 2 "se_uns_arith_operand" "d,K")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "@
+   and\\t%0,%1,%2
+   andi\\t%0,%1,%x2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
+(define_expand "iorsi3"
   [(set (match_operand:SI 0 "register_operand" "=d,d")
        (ior:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
                (match_operand:SI 2 "uns_arith_operand" "d,K")))]
   ""
+  "
+{
+  if (TARGET_MIPS16)
+    operands[2] = force_reg (SImode, operands[2]);
+}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (ior:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+               (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+  "!TARGET_MIPS16"
   "@
    or\\t%0,%1,%2
    ori\\t%0,%1,%x2"
@@ -1216,21 +2926,67 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
-(define_insn "iordi3"
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ior:SI (match_operand:SI 1 "register_operand" "%0")
+               (match_operand:SI 2 "register_operand" "d")))]
+  "TARGET_MIPS16"
+  "or\\t%0,%2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+;;; ??? There is no iordi3 pattern which accepts 'K' constants when
+;;; TARGET_64BIT
+
+(define_expand "iordi3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (ior:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+  "")
+
+(define_insn ""
   [(set (match_operand:DI 0 "register_operand" "=d")
-       (ior:DI (match_operand:DI 1 "register_operand" "d")
-               (match_operand:DI 2 "register_operand" "d")))]
-  "!TARGET_DEBUG_G_MODE"
-  "or\\t%M0,%M1,%M2\;or\\t%L0,%L1,%L2"
+       (ior:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16"
+  "*
+{
+  if (TARGET_64BIT)
+    return \"or\\t%0,%1,%2\";
+  return \"or\\t%M0,%M1,%M2\;or\\t%L0,%L1,%L2\";
+}"
   [(set_attr "type"    "darith")
    (set_attr "mode"    "DI")
-   (set_attr "length"  "2")])
+   (set (attr "length")
+       (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0))
+                      (const_int 1)
+                      (const_int 2)))])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (ior:DI (match_operand:DI 1 "se_register_operand" "0")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && TARGET_MIPS16"
+  "*
+{
+  if (TARGET_64BIT)
+    return \"or\\t%0,%2\";
+  return \"or\\t%M0,%M2\;or\\t%L0,%L2\";
+}"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "DI")
+   (set (attr "length")
+       (if_then_else (ge (symbol_ref "mips_isa") (const_int 3))
+                      (const_int 1)
+                      (const_int 2)))])
 
 (define_split
   [(set (match_operand:DI 0 "register_operand" "")
        (ior:DI (match_operand:DI 1 "register_operand" "")
                (match_operand:DI 2 "register_operand" "")))]
-  "reload_completed && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
    && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
@@ -1239,11 +2995,18 @@ move\\t%0,%z4\\n\\
    (set (subreg:SI (match_dup 0) 1) (ior:SI (subreg:SI (match_dup 1) 1) (subreg:SI (match_dup 2) 1)))]
   "")
 
-(define_insn "xorsi3"
+(define_expand "xorsi3"
   [(set (match_operand:SI 0 "register_operand" "=d,d")
        (xor:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
                (match_operand:SI 2 "uns_arith_operand" "d,K")))]
   ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (xor:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+               (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+  "!TARGET_MIPS16"
   "@
    xor\\t%0,%1,%2
    xori\\t%0,%1,%x2"
@@ -1251,21 +3014,84 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
-(define_insn "xordi3"
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,t,t")
+       (xor:SI (match_operand:SI 1 "uns_arith_operand" "%0,d,d")
+               (match_operand:SI 2 "uns_arith_operand" "d,K,d")))]
+  "TARGET_MIPS16"
+  "@
+   xor\\t%0,%2
+   cmpi\\t%1,%2
+   cmp\\t%1,%2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (const_int 1)])])
+
+;; ??? If delete the 32-bit long long patterns, then could merge this with
+;; the following xordi3_internal pattern.
+(define_expand "xordi3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (xor:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+  "")
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (xor:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16"
+  "*
+{
+  if (TARGET_64BIT)
+    return \"xor\\t%0,%1,%2\";
+  return \"xor\\t%M0,%M1,%M2\;xor\\t%L0,%L1,%L2\";
+}"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "DI")
+   (set (attr "length")
+       (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0))
+                      (const_int 1)
+                      (const_int 2)))])
+
+(define_insn ""
   [(set (match_operand:DI 0 "register_operand" "=d")
-       (xor:DI (match_operand:DI 1 "register_operand" "d")
-               (match_operand:DI 2 "register_operand" "d")))]
-  "!TARGET_DEBUG_G_MODE"
-  "xor\\t%M0,%M1,%M2\;xor\\t%L0,%L1,%L2"
+       (xor:DI (match_operand:DI 1 "se_register_operand" "0")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "!TARGET_64BIT && TARGET_MIPS16"
+  "xor\\t%M0,%M2\;xor\\t%L0,%L2"
   [(set_attr "type"    "darith")
    (set_attr "mode"    "DI")
    (set_attr "length"  "2")])
 
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,t,t")
+       (xor:DI (match_operand:DI 1 "se_register_operand" "%0,d,d")
+               (match_operand:DI 2 "se_uns_arith_operand" "d,K,d")))]
+  "TARGET_64BIT && TARGET_MIPS16"
+  "@
+   xor\\t%0,%2
+   cmpi\\t%1,%2
+   cmp\\t%1,%2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (const_int 1)])])
+
 (define_split
   [(set (match_operand:DI 0 "register_operand" "")
        (xor:DI (match_operand:DI 1 "register_operand" "")
                (match_operand:DI 2 "register_operand" "")))]
-  "reload_completed && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
    && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
@@ -1274,6 +3100,56 @@ move\\t%0,%z4\\n\\
    (set (subreg:SI (match_dup 0) 1) (xor:SI (subreg:SI (match_dup 1) 1) (subreg:SI (match_dup 2) 1)))]
   "")
 
+(define_insn "xordi3_immed"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (xor:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_uns_arith_operand" "K")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "xori\\t%0,%1,%x2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
+(define_insn "*norsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (and:SI (not:SI (match_operand:SI 1 "register_operand" "d"))
+               (not:SI (match_operand:SI 2 "register_operand" "d"))))]
+  "!TARGET_MIPS16"
+  "nor\\t%0,%z1,%z2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn "*nordi3"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (and:DI (not:DI (match_operand:DI 1 "se_register_operand" "d"))
+               (not:DI (match_operand:DI 2 "se_register_operand" "d"))))]
+  "!TARGET_MIPS16"
+  "*
+{
+  if (TARGET_64BIT)
+    return \"nor\\t%0,%z1,%z2\";
+  return \"nor\\t%M0,%M1,%M2\;nor\\t%L0,%L1,%L2\";
+}"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "DI")
+   (set (attr "length")
+       (if_then_else (ne (symbol_ref "TARGET_64BIT") (const_int 0))
+                      (const_int 1)
+                      (const_int 2)))])
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (and:DI (not:DI (match_operand:DI 1 "register_operand" ""))
+               (not:DI (match_operand:DI 2 "register_operand" ""))))]
+  "reload_completed && !TARGET_MIPS16 && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+   && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))
+   && GET_CODE (operands[2]) == REG && GP_REG_P (REGNO (operands[2]))"
+
+  [(set (subreg:SI (match_dup 0) 0) (and:SI (not:SI (subreg:SI (match_dup 1) 0)) (not:SI (subreg:SI (match_dup 2) 0))))
+   (set (subreg:SI (match_dup 0) 1) (and:SI (not:SI (subreg:SI (match_dup 1) 1)) (not:SI (subreg:SI (match_dup 2) 1))))]
+  "")
 \f
 ;;
 ;;  ....................
@@ -1285,12 +3161,151 @@ move\\t%0,%z4\\n\\
 (define_insn "truncdfsf2"
   [(set (match_operand:SF 0 "register_operand" "=f")
        (float_truncate:SF (match_operand:DF 1 "register_operand" "f")))]
-  "TARGET_HARD_FLOAT"
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "cvt.s.d\\t%0,%1"
   [(set_attr "type"    "fcvt")
    (set_attr "mode"    "SF")
    (set_attr "length"  "1")])
 
+(define_insn "truncdisi2"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (truncate:SI (match_operand:DI 1 "se_register_operand" "d")))]
+  "TARGET_64BIT"
+  "*
+{
+  if (TARGET_MIPS16)
+    return \"dsll\\t%0,%1,32\;dsra\\t%0,32\";
+  return \"dsll\\t%0,%1,32\;dsra\\t%0,%0,32\";
+}"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "SI")
+   (set (attr "length")        (if_then_else (eq (symbol_ref "mips16") (const_int 0))
+                                     (const_int 2)
+                                     (const_int 4)))])
+
+(define_insn "truncdihi2"
+  [(set (match_operand:HI 0 "register_operand" "=d")
+       (truncate:HI (match_operand:DI 1 "se_register_operand" "d")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "andi\\t%0,%1,0xffff"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "HI")
+   (set_attr "length"  "1")])
+
+(define_insn "truncdiqi2"
+  [(set (match_operand:QI 0 "register_operand" "=d")
+       (truncate:QI (match_operand:DI 1 "se_register_operand" "d")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "andi\\t%0,%1,0x00ff"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "QI")
+   (set_attr "length"  "1")])
+
+;; Combiner patterns to optimize shift/truncate combinations.
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (truncate:SI (ashiftrt:DI (match_operand:DI 1 "se_register_operand" "d")
+                                 (match_operand:DI 2 "small_int" "I"))))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "*
+{
+  int shift_amt = INTVAL (operands[2]) & 0x3f;
+
+  if (shift_amt < 32)
+    {
+      operands[2] = GEN_INT (32 - shift_amt);
+      return \"dsll\\t%0,%1,%2\;dsra\\t%0,%0,32\";
+    }
+  else
+    {
+      operands[2] = GEN_INT (shift_amt);
+      return \"dsra\\t%0,%1,%2\";
+    }
+}"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "2")])
+       
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (truncate:SI (lshiftrt:DI (match_operand:DI 1 "se_register_operand" "d")
+                                 (match_operand:DI 2 "small_int" "I"))))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "*
+{
+  int shift_amt = INTVAL (operands[2]) & 0x3f;
+
+  if (shift_amt < 32)
+    {
+      operands[2] = GEN_INT (32 - shift_amt);
+      return \"dsll\\t%0,%1,%2\;dsra\\t%0,%0,32\";
+    }
+  else if (shift_amt == 32)
+    return \"dsra\\t%0,%1,32\";
+  else
+    {
+      operands[2] = GEN_INT (shift_amt);
+      return \"dsrl\\t%0,%1,%2\";
+    }
+}"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (truncate:SI (ashift:DI (match_operand:DI 1 "se_register_operand" "d")
+                               (match_operand:DI 2 "small_int" "I"))))]
+  "TARGET_64BIT"
+  "*
+{
+  int shift_amt = INTVAL (operands[2]) & 0x3f;
+
+  if (shift_amt < 32)
+    {
+      operands[2] = GEN_INT (32 + shift_amt);
+      if (TARGET_MIPS16)
+       return \"dsll\\t%0,%1,%2\;dsra\\t%0,32\";
+      return \"dsll\\t%0,%1,%2\;dsra\\t%0,%0,32\";
+    }
+  else
+    return \"move\\t%0,%.\";
+}"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "2")])
+
+;; Combiner patterns to optimize truncate/zero_extend combinations.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (zero_extend:SI (truncate:HI
+                        (match_operand:DI 1 "se_register_operand" "d"))))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "andi\\t%0,%1,0xffff"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (zero_extend:SI (truncate:QI
+                        (match_operand:DI 1 "se_register_operand" "d"))))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "andi\\t%0,%1,0xff"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:HI 0 "register_operand" "=d")
+       (zero_extend:HI (truncate:QI
+                        (match_operand:DI 1 "se_register_operand" "d"))))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "andi\\t%0,%1,0xff"
+  [(set_attr "type"    "darith")
+   (set_attr "mode"    "HI")
+   (set_attr "length"  "1")])
 \f
 ;;
 ;;  ....................
@@ -1299,18 +3314,184 @@ move\\t%0,%z4\\n\\
 ;;
 ;;  ....................
 
-;; Extension insns.
-;; Those for integer source operand
-;; are ordered widest source type first.
+;; Extension insns.
+;; Those for integer source operand are ordered widest source type first.
+
+(define_expand "zero_extendsidi2"
+  [(set (match_operand:DI 0 "register_operand" "")
+       (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand" "")))]
+  "TARGET_64BIT"
+  "
+{
+  if (optimize && GET_CODE (operands[1]) == MEM)
+    operands[1] = force_not_mem (operands[1]);
+
+  if (GET_CODE (operands[1]) != MEM)
+    {
+      rtx op1   = gen_lowpart (DImode, operands[1]);
+      rtx temp  = gen_reg_rtx (DImode);
+      rtx shift = gen_rtx (CONST_INT, VOIDmode, 32);
+
+      emit_insn (gen_ashldi3 (temp, op1, shift));
+      emit_insn (gen_lshrdi3 (operands[0], temp, shift));
+      DONE;
+    }
+}")
+
+(define_insn "zero_extendsidi2_internal"
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (zero_extend:DI (match_operand:SI 1 "memory_operand" "R,m")))]
+  "TARGET_64BIT"
+  "* return mips_move_1word (operands, insn, TRUE);"
+  [(set_attr "type"    "load")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1,2")])
+
+(define_expand "zero_extendhisi2"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
+  ""
+  "
+{
+  if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM)
+    {
+      rtx op = gen_lowpart (SImode, operands[1]);
+      rtx temp = force_reg (SImode, GEN_INT (0xffff));
+
+      emit_insn (gen_andsi3 (operands[0], op, temp));
+      DONE;
+    }
+}")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
+       (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d,R,m")))]
+  "!TARGET_MIPS16"
+  "*
+{
+  if (which_alternative == 0)
+    return \"andi\\t%0,%1,0xffff\";
+  else
+    return mips_move_1word (operands, insn, TRUE);
+}"
+  [(set_attr "type"    "arith,load,load")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1,1,2")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (zero_extend:SI (match_operand:HI 1 "memory_operand" "R,m")))]
+  "TARGET_MIPS16"
+  "* return mips_move_1word (operands, insn, TRUE);"
+  [(set_attr "type"    "load,load")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1,2")])
+
+(define_expand "zero_extendhidi2"
+  [(set (match_operand:DI 0 "register_operand" "")
+       (zero_extend:DI (match_operand:HI 1 "nonimmediate_operand" "")))]
+  "TARGET_64BIT"
+  "
+{
+  if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM)
+    {
+      rtx op = gen_lowpart (DImode, operands[1]);
+      rtx temp = force_reg (DImode, GEN_INT (0xffff));
+
+      emit_insn (gen_anddi3 (operands[0], op, temp));
+      DONE;
+    }
+}")
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+       (zero_extend:DI (match_operand:HI 1 "nonimmediate_operand" "d,R,m")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "*
+{
+  if (which_alternative == 0)
+    return \"andi\\t%0,%1,0xffff\";
+  else
+    return mips_move_1word (operands, insn, TRUE);
+}"
+  [(set_attr "type"    "arith,load,load")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1,1,2")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (zero_extend:DI (match_operand:HI 1 "memory_operand" "R,m")))]
+  "TARGET_64BIT && TARGET_MIPS16"
+  "* return mips_move_1word (operands, insn, TRUE);"
+  [(set_attr "type"    "load,load")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1,2")])
+
+(define_expand "zero_extendqihi2"
+  [(set (match_operand:HI 0 "register_operand" "")
+       (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))]
+  ""
+  "
+{
+  if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM)
+    {
+      rtx op0 = gen_lowpart (SImode, operands[0]);
+      rtx op1 = gen_lowpart (SImode, operands[1]);
+      rtx temp = force_reg (SImode, GEN_INT (0xff));
+
+      emit_insn (gen_andsi3 (op0, op1, temp));
+      DONE;
+    }
+}")
+
+(define_insn ""
+  [(set (match_operand:HI 0 "register_operand" "=d,d,d")
+       (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
+  "!TARGET_MIPS16"
+  "*
+{
+  if (which_alternative == 0)
+    return \"andi\\t%0,%1,0x00ff\";
+  else
+    return mips_move_1word (operands, insn, TRUE);
+}"
+  [(set_attr "type"    "arith,load,load")
+   (set_attr "mode"    "HI")
+   (set_attr "length"  "1,1,2")])
+
+(define_insn ""
+  [(set (match_operand:HI 0 "register_operand" "=d,d")
+       (zero_extend:HI (match_operand:QI 1 "memory_operand" "R,m")))]
+  "TARGET_MIPS16"
+  "* return mips_move_1word (operands, insn, TRUE);"
+  [(set_attr "type"    "load,load")
+   (set_attr "mode"    "HI")
+   (set_attr "length"  "1,2")])
+
+(define_expand "zero_extendqisi2"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
+  ""
+  "
+{
+  if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM)
+    {
+      rtx op = gen_lowpart (SImode, operands[1]);
+      rtx temp = force_reg (SImode, GEN_INT (0xff));
+
+      emit_insn (gen_andsi3 (operands[0], op, temp));
+      DONE;
+    }
+}")
 
-(define_insn "zero_extendhisi2"
+(define_insn ""
   [(set (match_operand:SI 0 "register_operand" "=d,d,d")
-       (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d,R,m")))]
-  ""
+       (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
+  "!TARGET_MIPS16"
   "*
 {
   if (which_alternative == 0)
-    return \"andi\\t%0,%1,0xffff\";
+    return \"andi\\t%0,%1,0x00ff\";
   else
     return mips_move_1word (operands, insn, TRUE);
 }"
@@ -1318,10 +3499,35 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1,1,2")])
 
-(define_insn "zero_extendqihi2"
-  [(set (match_operand:HI 0 "register_operand" "=d,d,d")
-       (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
-  ""
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (zero_extend:SI (match_operand:QI 1 "memory_operand" "R,m")))]
+  "TARGET_MIPS16"
+  "* return mips_move_1word (operands, insn, TRUE);"
+  [(set_attr "type"    "load,load")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1,2")])
+
+(define_expand "zero_extendqidi2"
+  [(set (match_operand:DI 0 "register_operand" "")
+       (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))]
+  "TARGET_64BIT"
+  "
+{
+  if (TARGET_MIPS16 && GET_CODE (operands[1]) != MEM)
+    {
+      rtx op = gen_lowpart (DImode, operands[1]);
+      rtx temp = force_reg (DImode, GEN_INT (0xff));
+
+      emit_insn (gen_anddi3 (operands[0], op, temp));
+      DONE;
+    }
+}")
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d,d")
+       (zero_extend:DI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
   "*
 {
   if (which_alternative == 0)
@@ -1330,24 +3536,35 @@ move\\t%0,%z4\\n\\
     return mips_move_1word (operands, insn, TRUE);
 }"
   [(set_attr "type"    "arith,load,load")
-   (set_attr "mode"    "HI")
+   (set_attr "mode"    "DI")
    (set_attr "length"  "1,1,2")])
 
-(define_insn "zero_extendqisi2"
-  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
-       (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
-  ""
+;; These can be created when a paradoxical subreg operand with an implicit
+;; sign_extend operator is reloaded.  Because of the subreg, this is really
+;; a zero extend.
+;; ??? It might be possible to eliminate the need for these patterns by adding
+;; more support to reload for implicit sign_extend operators.
+(define_insn "*paradoxical_extendhidi2"
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (sign_extend:DI
+        (subreg:SI (match_operand:HI 1 "memory_operand" "R,m") 0)))]
+  "TARGET_64BIT"
   "*
 {
-  if (which_alternative == 0)
-    return \"andi\\t%0,%1,0x00ff\";
-  else
-    return mips_move_1word (operands, insn, TRUE);
+  return mips_move_1word (operands, insn, TRUE);
 }"
-  [(set_attr "type"    "arith,load,load")
-   (set_attr "mode"    "SI")
-   (set_attr "length"  "1,1,2")])
+  [(set_attr "type"    "load,load")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1,2")])
 
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (zero_extend:DI (match_operand:QI 1 "memory_operand" "R,m")))]
+  "TARGET_64BIT && TARGET_MIPS16"
+  "* return mips_move_1word (operands, insn, TRUE);"
+  [(set_attr "type"    "load,load")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1,2")])
 \f
 ;;
 ;;  ....................
@@ -1357,13 +3574,56 @@ move\\t%0,%z4\\n\\
 ;;  ....................
 
 ;; Extension insns.
-;; Those for integer source operand
-;; are ordered widest source type first.
+;; Those for integer source operand are ordered widest source type first.
+
+;; In 64 bit mode, 32 bit values in general registers are always
+;; correctly sign extended.  That means that if the target is a
+;; general register, we can sign extend from SImode to DImode just by
+;; doing a move.
+
+(define_insn "extendsidi2"
+  [(set (match_operand:DI 0 "register_operand" "=d,*d,d,d")
+       (sign_extend:DI (match_operand:SI 1 "nonimmediate_operand" "d,*x,R,m")))]
+  "TARGET_64BIT"
+  "* return mips_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "move,hilo,load,load")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1,1,1,2")])
 
 ;; These patterns originally accepted general_operands, however, slightly
 ;; better code is generated by only accepting register_operands, and then
 ;; letting combine generate the lh and lb insns.
 
+(define_expand "extendhidi2"
+  [(set (match_operand:DI 0 "register_operand" "")
+       (sign_extend:DI (match_operand:HI 1 "nonimmediate_operand" "")))]
+  "TARGET_64BIT"
+  "
+{
+  if (optimize && GET_CODE (operands[1]) == MEM)
+    operands[1] = force_not_mem (operands[1]);
+
+  if (GET_CODE (operands[1]) != MEM)
+    {
+      rtx op1   = gen_lowpart (DImode, operands[1]);
+      rtx temp  = gen_reg_rtx (DImode);
+      rtx shift = gen_rtx (CONST_INT, VOIDmode, 48);
+
+      emit_insn (gen_ashldi3 (temp, op1, shift));
+      emit_insn (gen_ashrdi3 (operands[0], temp, shift));
+      DONE;
+    }
+}")
+
+(define_insn "extendhidi2_internal"
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (sign_extend:DI (match_operand:HI 1 "memory_operand" "R,m")))]
+  "TARGET_64BIT"
+  "* return mips_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "load")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1,2")])
+
 (define_expand "extendhisi2"
   [(set (match_operand:SI 0 "register_operand" "")
        (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
@@ -1456,11 +3716,41 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1,2")])
 
+(define_expand "extendqidi2"
+  [(set (match_operand:DI 0 "register_operand" "")
+       (sign_extend:DI (match_operand:QI 1 "nonimmediate_operand" "")))]
+  "TARGET_64BIT"
+  "
+{
+  if (optimize && GET_CODE (operands[1]) == MEM)
+    operands[1] = force_not_mem (operands[1]);
+
+  if (GET_CODE (operands[1]) != MEM)
+    {
+      rtx op1   = gen_lowpart (DImode, operands[1]);
+      rtx temp  = gen_reg_rtx (DImode);
+      rtx shift = gen_rtx (CONST_INT, VOIDmode, 56);
+
+      emit_insn (gen_ashldi3 (temp, op1, shift));
+      emit_insn (gen_ashrdi3 (operands[0], temp, shift));
+      DONE;
+    }
+}")
+
+(define_insn "extendqidi2_insn"
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (sign_extend:DI (match_operand:QI 1 "memory_operand" "R,m")))]
+  "TARGET_64BIT"
+  "* return mips_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "load")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1,2")])
+
 
 (define_insn "extendsfdf2"
   [(set (match_operand:DF 0 "register_operand" "=f")
        (float_extend:DF (match_operand:SF 1 "register_operand" "f")))]
-  "TARGET_HARD_FLOAT"
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "cvt.d.s\\t%0,%1"
   [(set_attr "type"    "fcvt")
    (set_attr "mode"    "DF")
@@ -1475,12 +3765,22 @@ move\\t%0,%z4\\n\\
 ;;
 ;;  ....................
 
+;; The SImode scratch register can not be shared with address regs used for
+;; operand zero, because then the address in the move instruction will be
+;; clobbered.  We mark the scratch register as early clobbered to prevent this.
+
+;; We need the ?X in alternative 1 so that it will be choosen only if the
+;; destination is a floating point register.  Otherwise, alternative 1 can
+;; have lower cost than alternative 0 (because there is one less loser), and
+;; can be choosen when it won't work (because integral reloads into FP
+;; registers are not supported).
+
 (define_insn "fix_truncdfsi2"
-  [(set (match_operand:SI 0 "general_operand" "=d,*f,R,o")
+  [(set (match_operand:SI 0 "general_operand" "=d,*f,R,To")
        (fix:SI (match_operand:DF 1 "register_operand" "f,*f,f,f")))
-   (clobber (match_scratch:SI 2 "=d,*d,d,d"))
-   (clobber (match_scratch:DF 3 "=f,*X,f,f"))]
-  "TARGET_HARD_FLOAT"
+   (clobber (match_scratch:SI 2 "=d,*d,&d,&d"))
+   (clobber (match_scratch:DF 3 "=f,?*X,f,f"))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "*
 {
   rtx xoperands[10];
@@ -1501,10 +3801,10 @@ move\\t%0,%z4\\n\\
 
 
 (define_insn "fix_truncsfsi2"
-  [(set (match_operand:SI 0 "general_operand" "=d,*f,R,o")
+  [(set (match_operand:SI 0 "general_operand" "=d,*f,R,To")
        (fix:SI (match_operand:SF 1 "register_operand" "f,*f,f,f")))
-   (clobber (match_scratch:SI 2 "=d,*d,d,d"))
-   (clobber (match_scratch:SF 3 "=f,*X,f,f"))]
+   (clobber (match_scratch:SI 2 "=d,*d,&d,&d"))
+   (clobber (match_scratch:SF 3 "=f,?*X,f,f"))]
   "TARGET_HARD_FLOAT"
   "*
 {
@@ -1525,10 +3825,70 @@ move\\t%0,%z4\\n\\
    (set_attr "length"  "11,9,10,11")])
 
 
+;;; ??? trunc.l.d is mentioned in the appendix of the 1993 r4000/r4600 manuals
+;;; but not in the chapter that describes the FPU.  It is not mentioned at all
+;;; in the 1991 manuals.  The r4000 at Cygnus does not have this instruction.
+
+;;; Deleting this means that we now need two libgcc2.a libraries.  One for
+;;; the 32 bit calling convention and one for the 64 bit calling convention.
+
+;;; If this is disabled, then fixuns_truncdfdi2 must be disabled also.
+
+(define_insn "fix_truncdfdi2"
+  [(set (match_operand:DI 0 "general_operand" "=d,*f,R,To")
+       (fix:DI (match_operand:DF 1 "register_operand" "f,*f,f,f")))
+   (clobber (match_scratch:DF 2 "=f,?*X,f,f"))]
+  "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+  "*
+{
+  rtx xoperands[10];
+
+  if (which_alternative == 1)
+    return \"trunc.l.d %0,%1\";
+
+  output_asm_insn (\"trunc.l.d %2,%1\", operands);
+
+  xoperands[0] = operands[0];
+  xoperands[1] = operands[2];
+  output_asm_insn (mips_move_2words (xoperands, insn, FALSE), xoperands);
+  return \"\";
+}"
+  [(set_attr "type"    "fcvt")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "2,1,2,3")])
+
+
+;;; ??? trunc.l.s is mentioned in the appendix of the 1993 r4000/r4600 manuals
+;;; but not in the chapter that describes the FPU.  It is not mentioned at all
+;;; in the 1991 manuals.  The r4000 at Cygnus does not have this instruction.
+(define_insn "fix_truncsfdi2"
+  [(set (match_operand:DI 0 "general_operand" "=d,*f,R,To")
+       (fix:DI (match_operand:SF 1 "register_operand" "f,*f,f,f")))
+   (clobber (match_scratch:DF 2 "=f,?*X,f,f"))]
+  "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+  "*
+{
+  rtx xoperands[10];
+
+  if (which_alternative == 1)
+    return \"trunc.l.s %0,%1\";
+
+  output_asm_insn (\"trunc.l.s %2,%1\", operands);
+
+  xoperands[0] = operands[0];
+  xoperands[1] = operands[2];
+  output_asm_insn (mips_move_2words (xoperands, insn, FALSE), xoperands);
+  return \"\";
+}"
+  [(set_attr "type"    "fcvt")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "2,1,2,3")])
+
+
 (define_insn "floatsidf2"
   [(set (match_operand:DF 0 "register_operand" "=f,f,f")
        (float:DF (match_operand:SI 1 "nonimmediate_operand" "d,R,m")))]
-  "TARGET_HARD_FLOAT"
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "*
 {
   dslots_load_total++;
@@ -1542,6 +3902,23 @@ move\\t%0,%z4\\n\\
    (set_attr "length"  "3,4,3")])
 
 
+(define_insn "floatdidf2"
+  [(set (match_operand:DF 0 "register_operand" "=f,f,f")
+       (float:DF (match_operand:DI 1 "se_nonimmediate_operand" "d,R,m")))]
+  "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+  "*
+{
+  dslots_load_total++;
+  if (GET_CODE (operands[1]) == MEM)
+    return \"l.d\\t%0,%1%#\;cvt.d.l\\t%0,%0\";
+
+  return \"dmtc1\\t%1,%0%#\;cvt.d.l\\t%0,%0\";
+}"
+  [(set_attr "type"    "fcvt")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "3,4,3")])
+
+
 (define_insn "floatsisf2"
   [(set (match_operand:SF 0 "register_operand" "=f,f,f")
        (float:SF (match_operand:SI 1 "nonimmediate_operand" "d,R,m")))]
@@ -1559,10 +3936,27 @@ move\\t%0,%z4\\n\\
    (set_attr "length"  "3,4,3")])
 
 
+(define_insn "floatdisf2"
+  [(set (match_operand:SF 0 "register_operand" "=f,f,f")
+       (float:SF (match_operand:DI 1 "se_nonimmediate_operand" "d,R,m")))]
+  "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+  "*
+{
+  dslots_load_total++;
+  if (GET_CODE (operands[1]) == MEM)
+    return \"l.d\\t%0,%1%#\;cvt.s.l\\t%0,%0\";
+
+  return \"dmtc1\\t%1,%0%#\;cvt.s.l\\t%0,%0\";
+}"
+  [(set_attr "type"    "fcvt")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "3,4,3")])
+
+
 (define_expand "fixuns_truncdfsi2"
   [(set (match_operand:SI 0 "register_operand" "")
        (unsigned_fix:SI (match_operand:DF 1 "register_operand" "")))]
-  "TARGET_HARD_FLOAT"
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "
 {
   rtx reg1 = gen_reg_rtx (DFmode);
@@ -1602,6 +3996,50 @@ move\\t%0,%z4\\n\\
 }")
 
 
+(define_expand "fixuns_truncdfdi2"
+  [(set (match_operand:DI 0 "register_operand" "")
+       (unsigned_fix:DI (match_operand:DF 1 "register_operand" "")))]
+  "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+  "
+{
+  rtx reg1 = gen_reg_rtx (DFmode);
+  rtx reg2 = gen_reg_rtx (DFmode);
+  rtx reg3 = gen_reg_rtx (DImode);
+  rtx label1 = gen_label_rtx ();
+  rtx label2 = gen_label_rtx ();
+  REAL_VALUE_TYPE offset = REAL_VALUE_LDEXP (1.0, 63);
+
+  if (reg1)                    /* turn off complaints about unreached code */
+    {
+      emit_move_insn (reg1, immed_real_const_1 (offset, DFmode));
+      do_pending_stack_adjust ();
+
+      emit_insn (gen_cmpdf (operands[1], reg1));
+      emit_jump_insn (gen_bge (label1));
+
+      emit_insn (gen_fix_truncdfdi2 (operands[0], operands[1]));
+      emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx,
+                              gen_rtx (LABEL_REF, VOIDmode, label2)));
+      emit_barrier ();
+
+      emit_label (label1);
+      emit_move_insn (reg2, gen_rtx (MINUS, DFmode, operands[1], reg1));
+      emit_move_insn (reg3, gen_rtx (CONST_INT, VOIDmode, 0x80000000));
+      emit_insn (gen_ashldi3 (reg3, reg3, GEN_INT (32)));
+
+      emit_insn (gen_fix_truncdfdi2 (operands[0], reg2));
+      emit_insn (gen_iordi3 (operands[0], operands[0], reg3));
+
+      emit_label (label2);
+
+      /* allow REG_NOTES to be set on last insn (labels don't have enough
+        fields, and can't be used for REG_NOTES anyway).  */
+      emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx));
+      DONE;
+    }
+}")
+
+
 (define_expand "fixuns_truncsfsi2"
   [(set (match_operand:SI 0 "register_operand" "")
        (unsigned_fix:SI (match_operand:SF 1 "register_operand" "")))]
@@ -1644,6 +4082,50 @@ move\\t%0,%z4\\n\\
     }
 }")
 
+
+(define_expand "fixuns_truncsfdi2"
+  [(set (match_operand:DI 0 "register_operand" "")
+       (unsigned_fix:DI (match_operand:SF 1 "register_operand" "")))]
+  "TARGET_HARD_FLOAT && TARGET_64BIT && TARGET_DOUBLE_FLOAT"
+  "
+{
+  rtx reg1 = gen_reg_rtx (SFmode);
+  rtx reg2 = gen_reg_rtx (SFmode);
+  rtx reg3 = gen_reg_rtx (DImode);
+  rtx label1 = gen_label_rtx ();
+  rtx label2 = gen_label_rtx ();
+  REAL_VALUE_TYPE offset = REAL_VALUE_LDEXP (1.0, 63);
+
+  if (reg1)                    /* turn off complaints about unreached code */
+    {
+      emit_move_insn (reg1, immed_real_const_1 (offset, SFmode));
+      do_pending_stack_adjust ();
+
+      emit_insn (gen_cmpsf (operands[1], reg1));
+      emit_jump_insn (gen_bge (label1));
+
+      emit_insn (gen_fix_truncsfdi2 (operands[0], operands[1]));
+      emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx,
+                              gen_rtx (LABEL_REF, VOIDmode, label2)));
+      emit_barrier ();
+
+      emit_label (label1);
+      emit_move_insn (reg2, gen_rtx (MINUS, SFmode, operands[1], reg1));
+      emit_move_insn (reg3, gen_rtx (CONST_INT, VOIDmode, 0x80000000));
+      emit_insn (gen_ashldi3 (reg3, reg3, GEN_INT (32)));
+
+      emit_insn (gen_fix_truncsfdi2 (operands[0], reg2));
+      emit_insn (gen_iordi3 (operands[0], operands[0], reg3));
+
+      emit_label (label2);
+
+      /* allow REG_NOTES to be set on last insn (labels don't have enough
+        fields, and can't be used for REG_NOTES anyway).  */
+      emit_insn (gen_rtx (USE, VOIDmode, stack_pointer_rtx));
+      DONE;
+    }
+}")
+
 \f
 ;;
 ;;  ....................
@@ -1652,54 +4134,108 @@ move\\t%0,%z4\\n\\
 ;;
 ;;  ....................
 
-;; unaligned word moves generated by the block moves.
+;; Bit field extract patterns which use lwl/lwr.
 
-(define_expand "movsi_unaligned"
-  [(set (match_operand:SI 0 "general_operand" "")
-       (unspec [(match_operand:SI 1 "general_operand" "")] 0))]
-  ""
+;; ??? There should be DImode variants for 64 bit code, but the current
+;; bitfield scheme can't handle that.  We would need to add new optabs
+;; in order to make that work.
+
+;; ??? There could be HImode variants for the ulh/ulhu/ush macros.
+;; It isn't clear whether this will give better code.
+
+(define_expand "extv"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (sign_extract:SI (match_operand:QI 1 "memory_operand" "")
+                        (match_operand:SI 2 "immediate_operand" "")
+                        (match_operand:SI 3 "immediate_operand" "")))]
+  "!TARGET_MIPS16"
   "
 {
-  /* Handle loads.  */
-  if (GET_CODE (operands[0]) == MEM)
-    {
-      rtx reg = gen_reg_rtx (SImode);
-      rtx insn = emit_insn (gen_movsi_ulw (reg, operands[1]));
-      rtx addr = XEXP (operands[0], 0);
-      if (CONSTANT_P (addr))
-       REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUIV, addr, REG_NOTES (insn));
+  /* If this isn't a 32 bit field, and it doesn't start on a byte boundary
+     then fail.  */
+  if (INTVAL (operands[2]) != 32 || (INTVAL (operands[3]) % 8) != 0)
+    FAIL;
 
-      if (reg_or_0_operand (operands[1], SImode))
-       DONE;
+  /* This can happen for a 64 bit target, when extracting a value from
+     a 64 bit union member.  extract_bit_field doesn't verify that our
+     source matches the predicate, so we force it to be a MEM here.  */
+  if (GET_CODE (operands[1]) != MEM)
+    FAIL;
 
-      operands[1] = reg;
-    }
+  /* Change the mode to BLKmode for aliasing purposes.  */
+  operands[1] = change_address (operands[1], BLKmode, XEXP (operands[1], 0));
 
-  /* Generate appropriate load, store.  If not a load or store,
-     do a normal movsi.  */
-  if (GET_CODE (operands[0]) != MEM && GET_CODE (operands[1]) != MEM)
-    {
-      emit_insn (gen_movsi (operands[0], operands[1]));
-      DONE;
-    }
+  /* Otherwise, emit a lwl/lwr pair to load the value.  */
+  emit_insn (gen_movsi_ulw (operands[0], operands[1]));
+  DONE;
+}")
+
+(define_expand "extzv"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (zero_extract:SI (match_operand:QI 1 "memory_operand" "")
+                        (match_operand:SI 2 "immediate_operand" "")
+                        (match_operand:SI 3 "immediate_operand" "")))]
+  "!TARGET_MIPS16"
+  "
+{
+  /* If this isn't a 32 bit field, and it doesn't start on a byte boundary
+     then fail.  */
+  if (INTVAL (operands[2]) != 32 || (INTVAL (operands[3]) % 8) != 0)
+    FAIL;
+
+  /* This can happen for a 64 bit target, when extracting a value from
+     a 64 bit union member.  extract_bit_field doesn't verify that our
+     source matches the predicate, so we force it to be a MEM here.  */
+  if (GET_CODE (operands[1]) != MEM)
+    FAIL;
+
+  /* Change the mode to BLKmode for aliasing purposes.  */
+  operands[1] = change_address (operands[1], BLKmode, XEXP (operands[1], 0));
+
+  /* Otherwise, emit a lwl/lwr pair to load the value.  */
+  emit_insn (gen_movsi_ulw (operands[0], operands[1]));
+  DONE;
+}")
+
+(define_expand "insv"
+  [(set (zero_extract:SI (match_operand:QI 0 "memory_operand" "")
+                        (match_operand:SI 1 "immediate_operand" "")
+                        (match_operand:SI 2 "immediate_operand" ""))
+       (match_operand:SI 3 "register_operand" ""))]
+  "!TARGET_MIPS16"
+  "
+{
+  /* If this isn't a 32 bit field, and it doesn't start on a byte boundary
+     then fail.  */
+  if (INTVAL (operands[1]) != 32 || (INTVAL (operands[2]) % 8) != 0)
+    FAIL;
+
+  /* This can happen for a 64 bit target, when storing into a 32 bit union
+     member.  store_bit_field doesn't verify that our target matches the
+     predicate, so we force it to be a MEM here.  */
+  if (GET_CODE (operands[0]) != MEM)
+    FAIL;
+
+  /* Change the mode to BLKmode for aliasing purposes.  */
+  operands[0] = change_address (operands[0], BLKmode, XEXP (operands[0], 0));
 
-  /* Fall through and generate normal code.  */
+  /* Otherwise, emit a swl/swr pair to load the value.  */
+  emit_insn (gen_movsi_usw (operands[0], operands[3]));
+  DONE;
 }")
 
+;; unaligned word moves generated by the bit field patterns
+
 (define_insn "movsi_ulw"
-  [(set (match_operand:SI 0 "register_operand" "=&d,&d,d,d")
-       (unspec [(match_operand:SI 1 "general_operand" "R,o,dIKL,M")] 0))]
-  ""
+  [(set (match_operand:SI 0 "register_operand" "=&d,&d")
+       (unspec:SI [(match_operand:BLK 1 "general_operand" "R,o")] 0))]
+  "!TARGET_MIPS16"
   "*
 {
-  enum rtx_code code;
+  rtx offset = const0_rtx;
+  rtx addr = XEXP (operands[1], 0);
+  rtx mem_addr = eliminate_constant_term (addr, &offset);
   char *ret;
-  rtx offset;
-  rtx addr;
-  rtx mem_addr;
-
-  if (which_alternative != 0)
-    return mips_move_1word (operands, insn, FALSE);
 
   if (TARGET_STATS)
     mips_count_memory_refs (operands[1], 2);
@@ -1707,39 +4243,22 @@ move\\t%0,%z4\\n\\
   /* The stack/frame pointers are always aligned, so we can convert
      to the faster lw if we are referencing an aligned stack location.  */
 
-  offset = const0_rtx;
-  addr = XEXP (operands[1], 0);
-  mem_addr = eliminate_constant_term (addr, &offset);
-
-  if ((INTVAL (offset) & (UNITS_PER_WORD-1)) == 0
+  if ((INTVAL (offset) & 3) == 0
       && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx))
     ret = \"lw\\t%0,%1\";
-
   else
-    {
-      ret = \"ulw\\t%0,%1\";
-      if (TARGET_GAS)
-       {
-         enum rtx_code code = GET_CODE (addr);
-
-         if (code == CONST || code == SYMBOL_REF || code == LABEL_REF)
-           {
-             operands[2] = gen_rtx (REG, SImode, GP_REG_FIRST + 1);
-             ret = \"%[la\\t%2,%1\;ulw\\t%0,0(%2)%]\";
-           }
-       }
-    }
+    ret = \"ulw\\t%0,%1\";
 
   return mips_fill_delay_slot (ret, DELAY_LOAD, operands, insn);
 }"
-  [(set_attr "type"    "load,load,move,arith")
+  [(set_attr "type"    "load,load")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "2,4,1,2")])
+   (set_attr "length"  "2,4")])
 
 (define_insn "movsi_usw"
-  [(set (match_operand:SI 0 "memory_operand" "=R,o")
-       (unspec [(match_operand:SI 1 "reg_or_0_operand" "dJ,dJ")] 0))]
-  ""
+  [(set (match_operand:BLK 0 "memory_operand" "=R,o")
+       (unspec:BLK [(match_operand:SI 1 "reg_or_0_operand" "dJ,dJ")] 1))]
+  "!TARGET_MIPS16"
   "*
 {
   rtx offset = const0_rtx;
@@ -1752,28 +4271,39 @@ move\\t%0,%z4\\n\\
   /* The stack/frame pointers are always aligned, so we can convert
      to the faster sw if we are referencing an aligned stack location.  */
 
-  if ((INTVAL (offset) & (UNITS_PER_WORD-1)) == 0
+  if ((INTVAL (offset) & 3) == 0
       && (mem_addr == stack_pointer_rtx || mem_addr == frame_pointer_rtx))
     return \"sw\\t%1,%0\";
 
-
-  if (TARGET_GAS)
-    {
-      enum rtx_code code = GET_CODE (XEXP (operands[0], 0));
-
-      if (code == CONST || code == SYMBOL_REF || code == LABEL_REF)
-       {
-         operands[2] = gen_rtx (REG, SImode, GP_REG_FIRST + 1);
-         return \"%[la\\t%2,%0\;usw\\t%z1,0(%2)%]\";
-       }
-    }
-
   return \"usw\\t%z1,%0\";
 }"
   [(set_attr "type"    "store")
    (set_attr "mode"    "SI")
    (set_attr "length"  "2,4")])
 
+;; These two patterns support loading addresses with two instructions instead
+;; of using the macro instruction la.
+
+;; ??? mips_move_1word has support for HIGH, so this pattern may be
+;; unnecessary.
+
+(define_insn "high"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (high:SI (match_operand:SI 1 "immediate_operand" "")))]
+  "mips_split_addresses && !TARGET_MIPS16"
+  "lui\\t%0,%%hi(%1) # high"
+  [(set_attr "type"    "move")
+   (set_attr "length"  "1")])
+
+(define_insn "low"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
+                  (match_operand:SI 2 "immediate_operand" "")))]
+  "mips_split_addresses && !TARGET_MIPS16"
+  "addiu\\t%0,%1,%%lo(%2) # low"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
 
 ;; 64-bit integer moves
 
@@ -1787,11 +4317,96 @@ move\\t%0,%z4\\n\\
   ""
   "
 {
+  if (mips_split_addresses && mips_check_split (operands[1], DImode))
+    {
+      enum machine_mode mode = GET_MODE (operands[0]);
+      rtx tem = ((reload_in_progress | reload_completed)
+                ? operands[0] : gen_reg_rtx (mode));
+
+      emit_insn (gen_rtx (SET, VOIDmode, tem,
+                         gen_rtx (HIGH, mode, operands[1])));
+
+      operands[1] = gen_rtx (LO_SUM, mode, tem, operands[1]);
+    }
+
+  /* If we are generating embedded PIC code, and we are referring to a
+     symbol in the .text section, we must use an offset from the start
+     of the function.  */
+  if (TARGET_EMBEDDED_PIC
+      && (GET_CODE (operands[1]) == LABEL_REF
+         || (GET_CODE (operands[1]) == SYMBOL_REF
+             && ! SYMBOL_REF_FLAG (operands[1]))))
+    {
+      rtx temp;
+
+      temp = embedded_pic_offset (operands[1]);
+      temp = gen_rtx (PLUS, Pmode, embedded_pic_fnaddr_rtx,
+                     force_reg (DImode, temp));
+      emit_move_insn (operands[0], force_reg (DImode, temp));
+      DONE;
+    }
+
+  /* If operands[1] is a constant address illegal for pic, then we need to
+     handle it just like LEGITIMIZE_ADDRESS does.  */
+  if (flag_pic && pic_address_needs_scratch (operands[1]))
+    {
+      rtx temp = force_reg (DImode, XEXP (XEXP (operands[1], 0), 0));
+      rtx temp2 = XEXP (XEXP (operands[1], 0), 1);
+
+      if (! SMALL_INT (temp2))
+       temp2 = force_reg (DImode, temp2);
+
+      emit_move_insn (operands[0], gen_rtx (PLUS, DImode, temp, temp2));
+      DONE;
+    }
+
+  /* On the mips16, we can handle a GP relative reference by adding in
+     $gp.  We need to check the name to see whether this is a string
+     constant.  */
+  if (TARGET_MIPS16
+      && register_operand (operands[0], DImode)
+      && GET_CODE (operands[1]) == SYMBOL_REF
+      && SYMBOL_REF_FLAG (operands[1]))
+    {
+      char *name = XSTR (operands[1], 0);
+
+      if (name[0] != '*'
+         || strncmp (name + 1, LOCAL_LABEL_PREFIX,
+                     sizeof LOCAL_LABEL_PREFIX - 1) != 0)
+       {
+         rtx base_reg;
+
+         if (reload_in_progress || reload_completed)
+           {
+             /* In movsi we use the constant table here.  However, in
+                 this case, we're better off copying $28 into a
+                 register and adding, because the constant table entry
+                 would be 8 bytes.  */
+             base_reg = operands[0];
+             emit_move_insn (base_reg,
+                             gen_rtx (CONST, DImode,
+                                      gen_rtx (REG, DImode,
+                                               GP_REG_FIRST + 28)));
+           }
+         else
+           {
+             base_reg = gen_reg_rtx (Pmode);
+             emit_move_insn (base_reg, mips16_gp_pseudo_reg ());
+           }
+
+         emit_move_insn (operands[0],
+                         gen_rtx (PLUS, SImode, base_reg,
+                                  mips16_gp_offset (operands[1])));
+         DONE;
+       }
+    }
+
   if ((reload_in_progress | reload_completed) == 0
       && !register_operand (operands[0], DImode)
       && !register_operand (operands[1], DImode)
-      && (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
-      && operands[1] != CONST0_RTX (DImode))
+      && (TARGET_MIPS16
+         || (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
+             && operands[1] != CONST0_RTX (DImode)))
     {
       rtx temp = force_reg (DImode, operands[1]);
       emit_move_insn (operands[0], temp);
@@ -1799,22 +4414,51 @@ move\\t%0,%z4\\n\\
     }
 }")
 
+;; For mips16, we need a special case to handle storing $31 into
+;; memory, since we don't have a constraint to match $31.  This
+;; instruction can be generated by save_restore_insns.
+
+(define_insn ""
+  [(set (match_operand:DI 0 "memory_operand" "R,m")
+       (reg:DI 31))]
+  "TARGET_MIPS16 && TARGET_64BIT"
+  "*
+{
+  operands[1] = gen_rtx (REG, DImode, 31);
+  return mips_move_2words (operands, insn);
+}"
+  [(set_attr "type"    "store")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1,2")])
+
 (define_insn "movdi_internal"
-  [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,R,o,*d,*x")
-       (match_operand:DI 1 "general_operand" "d,iF,R,o,d,d,*x,*d"))]
-  "register_operand (operands[0], DImode)
-   || register_operand (operands[1], DImode)
-   || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
-   || operands[1] == CONST0_RTX (DImode)"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,R,o,*x,*d,*x")
+       (match_operand:DI 1 "general_operand" "d,iF,R,o,d,d,J,*x,*d"))]
+  "!TARGET_64BIT && !TARGET_MIPS16
+   && (register_operand (operands[0], DImode)
+       || register_operand (operands[1], DImode)
+       || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
+       || operands[1] == CONST0_RTX (DImode))"
   "* return mips_move_2words (operands, insn); "
-  [(set_attr "type"    "move,arith,load,load,store,store,hilo,hilo")
+  [(set_attr "type"    "move,arith,load,load,store,store,hilo,hilo,hilo")
    (set_attr "mode"    "DI")
-   (set_attr "length"  "2,4,2,4,2,4,2,2")])
+   (set_attr "length"   "2,4,2,4,2,4,2,2,2")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,R,To,*d")
+       (match_operand:DI 1 "general_operand" "d,d,y,K,N,R,To,d,d,*x"))]
+  "!TARGET_64BIT && TARGET_MIPS16
+   && (register_operand (operands[0], DImode)
+       || register_operand (operands[1], DImode))"
+  "* return mips_move_2words (operands, insn);"
+  [(set_attr "type"    "move,move,move,arith,arith,load,load,store,store,hilo")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "2,2,2,2,3,2,4,2,4,2")])
 
 (define_split
   [(set (match_operand:DI 0 "register_operand" "")
        (match_operand:DI 1 "register_operand" ""))]
-  "reload_completed && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))"
 
@@ -1822,13 +4466,228 @@ move\\t%0,%z4\\n\\
    (set (subreg:SI (match_dup 0) 1) (subreg:SI (match_dup 1) 1))]
   "")
 
+(define_insn "movdi_internal2"
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*x,*d,*x,*a")
+       (match_operand:DI 1 "movdi_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,J,*x,*d,*J"))]
+  "TARGET_64BIT && !TARGET_MIPS16
+   && (register_operand (operands[0], DImode)
+       || se_register_operand (operands[1], DImode)
+       || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
+       || operands[1] == CONST0_RTX (DImode))"
+  "* return mips_move_2words (operands, insn); "
+  [(set_attr "type"    "move,load,arith,arith,load,load,store,store,hilo,hilo,hilo,hilo")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1,2,1,2,1,2,1,2,1,1,1,2")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,d,R,m,*d")
+       (match_operand:DI 1 "movdi_operand" "d,d,y,K,N,s,R,m,d,d,*x"))]
+  "TARGET_64BIT && TARGET_MIPS16
+   && (register_operand (operands[0], DImode)
+       || se_register_operand (operands[1], DImode))"
+  "* return mips_move_2words (operands, insn);"
+  [(set_attr "type"    "move,move,move,arith,arith,arith,load,load,store,store,hilo")
+   (set_attr "mode"    "DI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (const_int 1)
+                (const_int 1)
+                (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "")
+                              (const_int 2)
+                              (const_int 3))
+                (if_then_else (match_operand:VOID 1 "m16_usym5_4" "")
+                              (const_int 1)
+                              (const_int 2))
+                (const_int 1)
+                (const_int 2)
+                (const_int 1)
+                (const_int 2)
+                (const_int 1)])])
+
+;; On the mips16, we can split ld $r,N($r) into an add and a load,
+;; when the original load is a 4 byte instruction but the add and the
+;; load are 2 2 byte instructions.
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (mem:DI (plus:DI (match_dup 0)
+                        (match_operand:DI 1 "const_int_operand" ""))))]
+  "TARGET_64BIT && TARGET_MIPS16 && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == CONST_INT
+   && ((INTVAL (operands[1]) < 0
+       && INTVAL (operands[1]) >= -0x10)
+       || (INTVAL (operands[1]) >= 32 * 8
+          && INTVAL (operands[1]) <= 31 * 8 + 0x8)
+       || (INTVAL (operands[1]) >= 0
+          && INTVAL (operands[1]) < 32 * 8
+          && (INTVAL (operands[1]) & 7) != 0))"
+  [(set (match_dup 0) (plus:DI (match_dup 0) (match_dup 1)))
+   (set (match_dup 0) (mem:DI (plus:DI (match_dup 0) (match_dup 2))))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[1]);
+
+  if (val < 0)
+    operands[2] = GEN_INT (0);
+  else if (val >= 32 * 8)
+    {
+      int off = val & 7;
+
+      operands[1] = GEN_INT (0x8 + off);
+      operands[2] = GEN_INT (val - off - 0x8);
+    }
+  else
+    {
+      int off = val & 7;
+
+      operands[1] = GEN_INT (off);
+      operands[2] = GEN_INT (val - off);
+    }
+}")
+
+;; Handle input reloads in DImode.
+;; This is mainly to handle reloading HILO_REGNUM.  Note that we may
+;; see it as the source or the destination, depending upon which way
+;; reload handles the instruction.
+;; Making the second operand TImode is a trick.  The compiler may
+;; reuse the same register for operand 0 and operand 2.  Using TImode
+;; gives us two registers, so we can always use the one which is not
+;; used.
+
+(define_expand "reload_indi"
+  [(set (match_operand:DI 0 "register_operand" "=b")
+       (match_operand:DI 1 "movdi_operand" "b"))
+   (clobber (match_operand:TI 2 "register_operand" "=&d"))]
+  "TARGET_64BIT"
+  "
+{
+  rtx scratch = gen_rtx (REG, DImode,
+                        (REGNO (operands[0]) == REGNO (operands[2]) 
+                         ? REGNO (operands[2]) + 1
+                         : REGNO (operands[2])));
+
+  if (GET_CODE (operands[0]) == REG && REGNO (operands[0]) == HILO_REGNUM)
+    {
+      if (GET_CODE (operands[1]) == MEM)
+       {
+         rtx memword, offword, hiword, loword;
+
+         scratch = gen_rtx (REG, SImode, REGNO (scratch));
+         memword = change_address (operands[1], SImode, NULL_RTX);
+         offword = change_address (adj_offsettable_operand (operands[1], 4),
+                                   SImode, NULL_RTX);
+         if (BYTES_BIG_ENDIAN)
+           {
+             hiword = memword;
+             loword = offword;
+           }
+         else
+           {
+             hiword = offword;
+             loword = memword;
+           }
+         emit_move_insn (scratch, hiword);
+         emit_move_insn (gen_rtx (REG, SImode, 64), scratch);
+         emit_move_insn (scratch, loword);
+         emit_move_insn (gen_rtx (REG, SImode, 65), scratch);
+       }
+      else
+       {
+         emit_insn (gen_ashrdi3 (scratch, operands[1], GEN_INT (32)));
+         emit_insn (gen_movdi (gen_rtx (REG, DImode, 64), scratch));
+         emit_insn (gen_ashldi3 (scratch, operands[1], GEN_INT (32)));
+         emit_insn (gen_ashrdi3 (scratch, scratch, GEN_INT (32)));
+         emit_insn (gen_movdi (gen_rtx (REG, DImode, 65), scratch));
+       }
+      DONE;
+    }
+  if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == HILO_REGNUM)
+    {
+      emit_insn (gen_movdi (scratch, gen_rtx (REG, DImode, 65)));
+      emit_insn (gen_ashldi3 (scratch, scratch, GEN_INT (32)));
+      emit_insn (gen_lshrdi3 (scratch, scratch, GEN_INT (32)));
+      emit_insn (gen_movdi (operands[0], gen_rtx (REG, DImode, 64)));
+      emit_insn (gen_ashldi3 (operands[0], operands[0], GEN_INT (32)));
+      emit_insn (gen_iordi3 (operands[0], operands[0], scratch));
+      DONE;
+    }
+  /* This handles moves between a float register and HI/LO.  */
+  emit_move_insn (scratch, operands[1]);
+  emit_move_insn (operands[0], scratch);
+  DONE;
+}")
+
+;; Handle output reloads in DImode.
+
+(define_expand "reload_outdi"
+  [(set (match_operand:DI 0 "general_operand" "=b")
+       (match_operand:DI 1 "se_register_operand" "b"))
+   (clobber (match_operand:DI 2 "register_operand" "=&d"))]
+  "TARGET_64BIT"
+  "
+{
+  if (GET_CODE (operands[0]) == REG && REGNO (operands[0]) == HILO_REGNUM)
+    {
+      emit_insn (gen_ashrdi3 (operands[2], operands[1], GEN_INT (32)));
+      emit_insn (gen_movdi (gen_rtx (REG, DImode, 64), operands[2]));
+      emit_insn (gen_ashldi3 (operands[2], operands[1], GEN_INT (32)));
+      emit_insn (gen_ashrdi3 (operands[2], operands[2], GEN_INT (32)));
+      emit_insn (gen_movdi (gen_rtx (REG, DImode, 65), operands[2]));
+      DONE;
+    }
+  if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == HILO_REGNUM)
+    {
+      if (GET_CODE (operands[0]) == MEM)
+       {
+         rtx scratch, memword, offword, hiword, loword;
+
+         scratch = gen_rtx (REG, SImode, REGNO (operands[2]));
+         memword = change_address (operands[0], SImode, NULL_RTX);
+         offword = change_address (adj_offsettable_operand (operands[0], 4),
+                                   SImode, NULL_RTX);
+         if (BYTES_BIG_ENDIAN)
+           {
+             hiword = memword;
+             loword = offword;
+           }
+         else
+           {
+             hiword = offword;
+             loword = memword;
+           }
+         emit_move_insn (scratch, gen_rtx (REG, SImode, 64));
+         emit_move_insn (hiword, scratch);
+         emit_move_insn (scratch, gen_rtx (REG, SImode, 65));
+         emit_move_insn (loword, scratch);
+       }
+      else
+       {
+         emit_insn (gen_movdi (operands[2], gen_rtx (REG, DImode, 65)));
+         emit_insn (gen_ashldi3 (operands[2], operands[2], GEN_INT (32)));
+         emit_insn (gen_lshrdi3 (operands[2], operands[2], GEN_INT (32)));
+         emit_insn (gen_movdi (operands[0], gen_rtx (REG, DImode, 64)));
+         emit_insn (gen_ashldi3 (operands[0], operands[0], GEN_INT (32)));
+         emit_insn (gen_iordi3 (operands[0], operands[0], operands[2]));
+       }
+      DONE;
+    }
+  /* This handles moves between a float register and HI/LO.  */
+  emit_move_insn (operands[2], operands[1]);
+  emit_move_insn (operands[0], operands[2]);
+  DONE;
+}")
 
 ;; 32-bit Integer moves
 
 (define_split
   [(set (match_operand:SI 0 "register_operand" "")
        (match_operand:SI 1 "large_int" ""))]
-  "!TARGET_DEBUG_D_MODE"
+  "!TARGET_DEBUG_D_MODE && !TARGET_MIPS16"
   [(set (match_dup 0)
        (match_dup 2))
    (set (match_dup 0)
@@ -1850,44 +4709,537 @@ move\\t%0,%z4\\n\\
   ""
   "
 {
-  if ((reload_in_progress | reload_completed) == 0
-      && !register_operand (operands[0], SImode)
-      && !register_operand (operands[1], SImode)
-      && (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0))
-    {
-      rtx temp = force_reg (SImode, operands[1]);
-      emit_move_insn (operands[0], temp);
-      DONE;
-    }
+  if (mips_split_addresses && mips_check_split (operands[1], SImode))
+    {
+      enum machine_mode mode = GET_MODE (operands[0]);
+      rtx tem = ((reload_in_progress | reload_completed)
+                ? operands[0] : gen_reg_rtx (mode));
+
+      emit_insn (gen_rtx (SET, VOIDmode, tem,
+                         gen_rtx (HIGH, mode, operands[1])));
+
+      operands[1] = gen_rtx (LO_SUM, mode, tem, operands[1]);
+    }
+
+  /* If we are generating embedded PIC code, and we are referring to a
+     symbol in the .text section, we must use an offset from the start
+     of the function.  */
+  if (TARGET_EMBEDDED_PIC
+      && (GET_CODE (operands[1]) == LABEL_REF
+         || (GET_CODE (operands[1]) == SYMBOL_REF
+             && ! SYMBOL_REF_FLAG (operands[1]))))
+    {
+      rtx temp;
+
+      temp = embedded_pic_offset (operands[1]);
+      temp = gen_rtx (PLUS, Pmode, embedded_pic_fnaddr_rtx,
+                     force_reg (SImode, temp));
+      emit_move_insn (operands[0], force_reg (SImode, temp));
+      DONE;
+    }
+
+  /* If operands[1] is a constant address invalid for pic, then we need to
+     handle it just like LEGITIMIZE_ADDRESS does.  */
+  if (flag_pic && pic_address_needs_scratch (operands[1]))
+    {
+      rtx temp = force_reg (SImode, XEXP (XEXP (operands[1], 0), 0));
+      rtx temp2 = XEXP (XEXP (operands[1], 0), 1);
+
+      if (! SMALL_INT (temp2))
+       temp2 = force_reg (SImode, temp2);
+
+      emit_move_insn (operands[0], gen_rtx (PLUS, SImode, temp, temp2));
+      DONE;
+    }
+
+  /* On the mips16, we can handle a GP relative reference by adding in
+     $gp.  We need to check the name to see whether this is a string
+     constant.  */
+  if (TARGET_MIPS16
+      && register_operand (operands[0], SImode)
+      && GET_CODE (operands[1]) == SYMBOL_REF
+      && SYMBOL_REF_FLAG (operands[1]))
+    {
+      char *name = XSTR (operands[1], 0);
+
+      if (name[0] != '*'
+         || strncmp (name + 1, LOCAL_LABEL_PREFIX,
+                     sizeof LOCAL_LABEL_PREFIX - 1) != 0)
+       {
+         rtx base_reg;
+
+         if (reload_in_progress || reload_completed)
+           {
+             /* We need to reload this address.  In this case we
+                 aren't going to have a chance to combine loading the
+                 address with the load or store.  That means that we
+                 can either generate a 2 byte move followed by a 4
+                 byte addition, or a 2 byte load with a 4 byte entry
+                 in the constant table.  Since the entry in the
+                 constant table might be shared, we're better off, on
+                 average, loading the address from the constant table.  */
+             emit_move_insn (operands[0],
+                             force_const_mem (SImode, operands[1]));
+             DONE;
+           }
+
+         base_reg = gen_reg_rtx (Pmode);
+         emit_move_insn (base_reg, mips16_gp_pseudo_reg ());
+
+         emit_move_insn (operands[0],
+                         gen_rtx (PLUS, SImode, base_reg,
+                                  mips16_gp_offset (operands[1])));
+         DONE;
+       }
+    }
+
+  if ((reload_in_progress | reload_completed) == 0
+      && !register_operand (operands[0], SImode)
+      && !register_operand (operands[1], SImode)
+      && (TARGET_MIPS16
+         || GET_CODE (operands[1]) != CONST_INT
+         || INTVAL (operands[1]) != 0))
+    {
+      rtx temp = force_reg (SImode, operands[1]);
+      emit_move_insn (operands[0], temp);
+      DONE;
+    }
+}")
+
+;; For mips16, we need a special case to handle storing $31 into
+;; memory, since we don't have a constraint to match $31.  This
+;; instruction can be generated by save_restore_insns.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "memory_operand" "R,m")
+       (reg:SI 31))]
+  "TARGET_MIPS16"
+  "*
+{
+  operands[1] = gen_rtx (REG, SImode, 31);
+  return mips_move_1word (operands, insn, FALSE);
+}"
+  [(set_attr "type"    "store")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1,2")])
+
+;; The difference between these two is whether or not ints are allowed
+;; in FP registers (off by default, use -mdebugh to enable).
+
+(define_insn "movsi_internal1"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*d,*f*z,*f,*f,*f,*R,*m,*x,*x,*d,*d")
+       (match_operand:SI 1 "move_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,*f*z,*d,*f,*R,*m,*f,*f,J,*d,*x,*a"))]
+  "TARGET_DEBUG_H_MODE && !TARGET_MIPS16
+   && (register_operand (operands[0], SImode)
+       || register_operand (operands[1], SImode)
+       || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
+  "* return mips_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "move,load,arith,arith,load,load,store,store,xfer,xfer,move,load,load,store,store,hilo,hilo,hilo,hilo")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1,2,1,2,1,2,1,2,1,1,1,1,2,1,2,1,1,1,1")])
+
+(define_insn "movsi_internal2"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*d,*z,*x,*d,*x,*d")
+       (match_operand:SI 1 "move_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,*z,*d,J,*x,*d,*a"))]
+  "!TARGET_DEBUG_H_MODE && !TARGET_MIPS16
+   && (register_operand (operands[0], SImode)
+       || register_operand (operands[1], SImode)
+       || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
+  "* return mips_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "move,load,arith,arith,load,load,store,store,xfer,xfer,hilo,hilo,hilo,hilo")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1,2,1,2,1,2,1,2,1,1,1,1,1,1")])
+
+;; This is the mips16 movsi instruction.  We accept a small integer as
+;; the source if the destination is a GP memory reference.  This is
+;; because we want the combine pass to turn adding a GP reference to a
+;; register into a direct GP reference, but the combine pass will pass
+;; in the source as a constant if it finds an equivalent one.  If the
+;; instruction is recognized, reload will force the constant back out
+;; into a register.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,d,d,R,m,*d,*d")
+       (match_operand:SI 1 "move_operand" "d,d,y,S,K,N,s,R,m,d,d,*x,*a"))]
+  "TARGET_MIPS16
+   && (register_operand (operands[0], SImode)
+       || register_operand (operands[1], SImode)
+       || (GET_CODE (operands[0]) == MEM
+          && GET_CODE (XEXP (operands[0], 0)) == PLUS
+          && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == CONST
+          && mips16_gp_offset_p (XEXP (XEXP (operands[0], 0), 1))
+          && GET_CODE (operands[1]) == CONST_INT
+          && (SMALL_INT (operands[1])
+              || SMALL_INT_UNSIGNED (operands[1]))))"
+  "* return mips_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "move,move,move,load,arith,arith,arith,load,load,store,store,hilo,hilo")
+   (set_attr "mode"    "SI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (const_int 1)
+                (const_int 1)
+                (const_int 2)
+                (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "")
+                              (const_int 2)
+                              (const_int 3))
+                (if_then_else (match_operand:VOID 1 "m16_usym8_4" "")
+                              (const_int 1)
+                              (const_int 2))
+                (const_int 1)
+                (const_int 2)
+                (const_int 1)
+                (const_int 2)
+                (const_int 1)
+                (const_int 1)])])
+
+;; On the mips16, we can split lw $r,N($r) into an add and a load,
+;; when the original load is a 4 byte instruction but the add and the
+;; load are 2 2 byte instructions.
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (mem:SI (plus:SI (match_dup 0)
+                        (match_operand:SI 1 "const_int_operand" ""))))]
+  "TARGET_MIPS16 && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == CONST_INT
+   && ((INTVAL (operands[1]) < 0
+       && INTVAL (operands[1]) >= -0x80)
+       || (INTVAL (operands[1]) >= 32 * 4
+          && INTVAL (operands[1]) <= 31 * 4 + 0x7c)
+       || (INTVAL (operands[1]) >= 0
+          && INTVAL (operands[1]) < 32 * 4
+          && (INTVAL (operands[1]) & 3) != 0))"
+  [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1)))
+   (set (match_dup 0) (mem:SI (plus:SI (match_dup 0) (match_dup 2))))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[1]);
+
+  if (val < 0)
+    operands[2] = GEN_INT (0);
+  else if (val >= 32 * 4)
+    {
+      int off = val & 3;
+
+      operands[1] = GEN_INT (0x7c + off);
+      operands[2] = GEN_INT (val - off - 0x7c);
+    }
+  else
+    {
+      int off = val & 3;
+
+      operands[1] = GEN_INT (off);
+      operands[2] = GEN_INT (val - off);
+    }
+}")
+
+;; On the mips16, we can split a load of certain constants into a load
+;; and an add.  This turns a 4 byte instruction into 2 2 byte
+;; instructions.
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (match_operand:SI 1 "const_int_operand" ""))]
+  "TARGET_MIPS16 && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == CONST_INT
+   && INTVAL (operands[1]) >= 0x100
+   && INTVAL (operands[1]) <= 0xff + 0x7f"
+  [(set (match_dup 0) (match_dup 1))
+   (set (match_dup 0) (plus:SI (match_dup 0) (match_dup 2)))]
+  "
+{
+  int val = INTVAL (operands[1]);
+
+  operands[1] = GEN_INT (0xff);
+  operands[2] = GEN_INT (val - 0xff);
+}")
+
+;; On the mips16, we can split a load of a negative constant into a
+;; load and a neg.  That's what mips_move_1word will generate anyhow.
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (match_operand:SI 1 "const_int_operand" ""))]
+  "TARGET_MIPS16 && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == CONST_INT
+   && INTVAL (operands[1]) < 0
+   && INTVAL (operands[1]) > - 0x8000"
+  [(set (match_dup 0) (match_dup 1))
+   (set (match_dup 0) (neg:SI (match_dup 0)))]
+  "
+{
+  operands[1] = GEN_INT (- INTVAL (operands[1]));
+}")
+
+;; Reload HILO_REGNUM in SI mode.  This needs a scratch register in
+;; order to set the sign bit correctly in the HI register.
+
+(define_expand "reload_outsi"
+  [(set (match_operand:SI 0 "general_operand" "=b")
+       (match_operand:SI 1 "register_operand" "b"))
+   (clobber (match_operand:SI 2 "register_operand" "=&d"))]
+  "TARGET_64BIT || TARGET_MIPS16"
+  "
+{
+  if (TARGET_64BIT
+      && GET_CODE (operands[0]) == REG && REGNO (operands[0]) == HILO_REGNUM)
+    {
+      emit_insn (gen_movsi (gen_rtx (REG, SImode, 65), operands[1]));
+      emit_insn (gen_ashrsi3 (operands[2], operands[1], GEN_INT (31)));
+      emit_insn (gen_movsi (gen_rtx (REG, SImode, 64), operands[2]));
+      DONE;
+    }
+  /* Use a mult to reload LO on mips16.  ??? This is hideous.  */
+  if (TARGET_MIPS16
+      && GET_CODE (operands[0]) == REG && REGNO (operands[0]) == LO_REGNUM)
+    {
+      emit_insn (gen_movsi (operands[2], GEN_INT (1)));
+      /* This is gen_mulsi3_internal, but we need to fill in the
+        scratch registers.  */
+      emit_insn (gen_rtx (PARALLEL, VOIDmode,
+                         gen_rtvec (3,
+                                    gen_rtx (SET, VOIDmode,
+                                             operands[0],
+                                             gen_rtx (MULT, SImode,
+                                                      operands[1],
+                                                      operands[2])),
+                                    gen_rtx (CLOBBER, VOIDmode,
+                                             gen_rtx (REG, SImode, 64)),
+                                    gen_rtx (CLOBBER, VOIDmode,
+                                             gen_rtx (REG, SImode, 66)))));
+      DONE;
+    }
+  /* FIXME: I don't know how to get a value into the HI register.  */
+  if (GET_CODE (operands[0]) == REG && GP_REG_P (operands[0]))
+    {
+      emit_move_insn (operands[0], operands[1]);
+      DONE;
+    }
+  /* This handles moves between a float register and HI/LO.  */
+  emit_move_insn (operands[2], operands[1]);
+  emit_move_insn (operands[0], operands[2]);
+  DONE;
+}")
+
+;; Reload a value into HI or LO.  There is no mthi or mtlo on mips16,
+;; so we use a mult.  ??? This is hideous, and we ought to figure out
+;; something better.
+
+(define_expand "reload_insi"
+  [(set (match_operand:SI 0 "register_operand" "=b")
+       (match_operand:SI 1 "register_operand" "b"))
+   (clobber (match_operand:SI 2 "register_operand" "=&d"))]
+  "TARGET_MIPS16"
+  "
+{
+  if (TARGET_MIPS16
+      && GET_CODE (operands[0]) == REG && REGNO (operands[0]) == LO_REGNUM)
+    {
+      emit_insn (gen_movsi (operands[2], GEN_INT (1)));
+      /* This is gen_mulsi3_internal, but we need to fill in the
+        scratch registers.  */
+      emit_insn (gen_rtx (PARALLEL, VOIDmode,
+                         gen_rtvec (3,
+                                    gen_rtx (SET, VOIDmode,
+                                             operands[0],
+                                             gen_rtx (MULT, SImode,
+                                                      operands[1],
+                                                      operands[2])),
+                                    gen_rtx (CLOBBER, VOIDmode,
+                                             gen_rtx (REG, SImode, 64)),
+                                    gen_rtx (CLOBBER, VOIDmode,
+                                             gen_rtx (REG, SImode, 66)))));
+      DONE;
+    }
+  /* FIXME: I don't know how to get a value into the HI register.  */
+  emit_move_insn (operands[0], operands[1]);
+  DONE;
+}")
+
+;; This insn handles moving CCmode values.  It's really just a
+;; slightly simplified copy of movsi_internal2, with additional cases
+;; to move a condition register to a general register and to move
+;; between the general registers and the floating point registers.
+
+(define_insn "movcc"
+  [(set (match_operand:CC 0 "nonimmediate_operand" "=d,*d,*d,*d,*R,*m,*d,*f,*f,*f,*f,*R,*m")
+       (match_operand:CC 1 "general_operand" "z,*d,*R,*m,*d,*d,*f,*d,*f,*R,*m,*f,*f"))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "* return mips_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "move,move,load,load,store,store,xfer,xfer,move,load,load,store,store")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "2,1,1,2,1,2,1,1,1,1,2,1,2")])
+
+;; Reload condition code registers.  These need scratch registers.
+
+(define_expand "reload_incc"
+  [(set (match_operand:CC 0 "register_operand" "=z")
+       (match_operand:CC 1 "general_operand" "z"))
+   (clobber (match_operand:TF 2 "register_operand" "=&f"))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "
+{
+  rtx source;
+  rtx fp1, fp2;
+
+  /* This is called when are copying some value into a condition code
+     register.  Operand 0 is the condition code register.  Operand 1
+     is the source.  Operand 2 is a scratch register; we use TFmode
+     because we actually need two floating point registers.  */
+  if (! ST_REG_P (true_regnum (operands[0]))
+      || ! FP_REG_P (true_regnum (operands[2])))
+    abort ();
+
+  /* We need to get the source in SFmode so that the insn is
+     recognized.  */
+  if (GET_CODE (operands[1]) == MEM)
+    source = change_address (operands[1], SFmode, NULL_RTX);
+  else if (GET_CODE (operands[1]) == REG || GET_CODE (operands[1]) == SUBREG)
+    source = gen_rtx (REG, SFmode, true_regnum (operands[1]));
+  else
+    source = operands[1];
+
+  fp1 = gen_rtx (REG, SFmode, REGNO (operands[2]));
+  fp2 = gen_rtx (REG, SFmode, REGNO (operands[2]) + 1);
+
+  emit_insn (gen_move_insn (fp1, source));
+  emit_insn (gen_move_insn (fp2, gen_rtx (REG, SFmode, 0)));
+  emit_insn (gen_rtx (SET, VOIDmode, operands[0],
+                     gen_rtx (LT, CCmode, fp2, fp1)));
+
+  DONE;
+}")
+
+(define_expand "reload_outcc"
+  [(set (match_operand:CC 0 "general_operand" "=z")
+       (match_operand:CC 1 "register_operand" "z"))
+   (clobber (match_operand:CC 2 "register_operand" "=&d"))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "
+{
+  /* This is called when we are copying a condition code register out
+     to save it somewhere.  Operand 0 should be the location we are
+     going to save it to.  Operand 1 should be the condition code
+     register.  Operand 2 should be a scratch general purpose register
+     created for us by reload.  The mips_secondary_reload_class
+     function should have told reload that we don't need a scratch
+     register if the destination is a general purpose register anyhow.  */
+  if (ST_REG_P (true_regnum (operands[0]))
+      || GP_REG_P (true_regnum (operands[0]))
+      || ! ST_REG_P (true_regnum (operands[1]))
+      || ! GP_REG_P (true_regnum (operands[2])))
+    abort ();
+
+  /* All we have to do is copy the value from the condition code to
+     the data register, which movcc can handle, and then store the
+     value into the real final destination.  */
+  emit_insn (gen_move_insn (operands[2], operands[1]));
+  emit_insn (gen_move_insn (operands[0], operands[2]));
+
+  DONE;
 }")
 
-;; The difference between these two is whether or not ints are allowed
-;; in FP registers (off by default, use -mdebugh to enable).
+;; MIPS4 supports loading and storing a floating point register from
+;; the sum of two general registers.  We use two versions for each of
+;; these four instructions: one where the two general registers are
+;; SImode, and one where they are DImode.  This is because general
+;; registers will be in SImode when they hold 32 bit values, but,
+;; since the 32 bit values are always sign extended, the [ls][wd]xc1
+;; instructions will still work correctly.
 
-(define_insn "movsi_internal1"
-  [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*d,*f*z,*f,*f,*f,*R,*m,*x,*d")
-       (match_operand:SI 1 "general_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,*f*z,*d,*f,*R,*m,*f,*f,*d,*x"))]
-  "TARGET_DEBUG_H_MODE
-   && (register_operand (operands[0], SImode)
-       || register_operand (operands[1], SImode)
-       || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
-  "* return mips_move_1word (operands, insn, TRUE);"
-  [(set_attr "type"    "move,load,arith,arith,load,load,store,store,xfer,xfer,move,load,load,store,store,hilo,hilo")
-   (set_attr "mode"    "SI")
-   (set_attr "length"  "1,2,1,2,1,2,1,2,1,1,1,1,2,1,2,1,1")])
+;; ??? Perhaps it would be better to support these instructions by
+;; modifying GO_IF_LEGITIMATE_ADDRESS and friends.  However, since
+;; these instructions can only be used to load and store floating
+;; point registers, that would probably cause trouble in reload.
 
-(define_insn "movsi_internal2"
-  [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*d,*z,*d,*x")
-       (match_operand:SI 1 "general_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,*z,*d,*x,*d"))]
-  "!TARGET_DEBUG_H_MODE
-   && (register_operand (operands[0], SImode)
-       || register_operand (operands[1], SImode)
-       || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
-  "* return mips_move_1word (operands, insn, TRUE);"
-  [(set_attr "type"    "move,load,arith,arith,load,load,store,store,xfer,xfer,hilo,hilo")
-   (set_attr "mode"    "SI")
-   (set_attr "length"  "1,2,1,2,1,2,1,2,1,1,1,1")])
+(define_insn ""
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (mem:SF (plus:SI (match_operand:SI 1 "register_operand" "d")
+                        (match_operand:SI 2 "register_operand" "d"))))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "lwxc1\\t%0,%1(%2)"
+  [(set_attr "type"    "load")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SF 0 "register_operand" "=f")
+       (mem:SF (plus:DI (match_operand:DI 1 "se_register_operand" "d")
+                        (match_operand:DI 2 "se_register_operand" "d"))))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "lwxc1\\t%0,%1(%2)"
+  [(set_attr "type"    "load")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (mem:DF (plus:SI (match_operand:SI 1 "register_operand" "d")
+                        (match_operand:SI 2 "register_operand" "d"))))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "ldxc1\\t%0,%1(%2)"
+  [(set_attr "type"    "load")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DF 0 "register_operand" "=f")
+       (mem:DF (plus:DI (match_operand:DI 1 "se_register_operand" "d")
+                        (match_operand:DI 2 "se_register_operand" "d"))))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "ldxc1\\t%0,%1(%2)"
+  [(set_attr "type"    "load")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (mem:SF (plus:SI (match_operand:SI 1 "register_operand" "d")
+                        (match_operand:SI 2 "register_operand" "d")))
+       (match_operand:SF 0 "register_operand" "=f"))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "swxc1\\t%0,%1(%2)"
+  [(set_attr "type"    "store")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (mem:SF (plus:DI (match_operand:DI 1 "se_register_operand" "d")
+                        (match_operand:DI 2 "se_register_operand" "d")))
+       (match_operand:SF 0 "register_operand" "=f"))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "swxc1\\t%0,%1(%2)"
+  [(set_attr "type"    "store")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (mem:DF (plus:SI (match_operand:SI 1 "register_operand" "d")
+                        (match_operand:SI 2 "register_operand" "d")))
+       (match_operand:DF 0 "register_operand" "=f"))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "sdxc1\\t%0,%1(%2)"
+  [(set_attr "type"    "store")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
 
+(define_insn ""
+  [(set (mem:DF (plus:DI (match_operand:DI 1 "se_register_operand" "d")
+                        (match_operand:DI 2 "se_register_operand" "d")))
+       (match_operand:DF 0 "register_operand" "=f"))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "sdxc1\\t%0,%1(%2)"
+  [(set_attr "type"    "store")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1")])
 
 ;; 16-bit Integer moves
 
@@ -1905,7 +5257,9 @@ move\\t%0,%z4\\n\\
   if ((reload_in_progress | reload_completed) == 0
       && !register_operand (operands[0], HImode)
       && !register_operand (operands[1], HImode)
-      && (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0))
+      && (TARGET_MIPS16
+         || (GET_CODE (operands[1]) != CONST_INT
+         || INTVAL (operands[1]) != 0)))
     {
       rtx temp = force_reg (HImode, operands[1]);
       emit_move_insn (operands[0], temp);
@@ -1919,7 +5273,7 @@ move\\t%0,%z4\\n\\
 (define_insn "movhi_internal1"
   [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*f,*f*z,*x,*d")
        (match_operand:HI 1 "general_operand"       "d,IK,R,m,dJ,dJ,*f*z,*d,*f,*d,*x"))]
-  "TARGET_DEBUG_H_MODE
+  "TARGET_DEBUG_H_MODE && !TARGET_MIPS16
    && (register_operand (operands[0], HImode)
        || register_operand (operands[1], HImode)
        || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
@@ -1931,7 +5285,7 @@ move\\t%0,%z4\\n\\
 (define_insn "movhi_internal2"
   [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*z,*x,*d")
        (match_operand:HI 1 "general_operand"       "d,IK,R,m,dJ,dJ,*z,*d,*d,*x"))]
-  "!TARGET_DEBUG_H_MODE
+  "!TARGET_DEBUG_H_MODE && !TARGET_MIPS16
    && (register_operand (operands[0], HImode)
        || register_operand (operands[1], HImode)
        || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
@@ -1940,6 +5294,74 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "HI")
    (set_attr "length"  "1,1,1,2,1,2,1,1,1,1")])
 
+(define_insn ""
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,R,m,*d")
+       (match_operand:HI 1 "general_operand"      "d,d,y,K,N,R,m,d,d,*x"))]
+  "TARGET_MIPS16
+   && (register_operand (operands[0], HImode)
+       || register_operand (operands[1], HImode))"
+  "* return mips_move_1word (operands, insn, TRUE);"
+  [(set_attr "type"    "move,move,move,arith,arith,load,load,store,store,hilo")
+   (set_attr "mode"    "HI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (const_int 1)
+                (const_int 1)
+                (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "")
+                              (const_int 2)
+                              (const_int 3))
+                (const_int 1)
+                (const_int 2)
+                (const_int 1)
+                (const_int 2)
+                (const_int 1)])])
+
+
+;; On the mips16, we can split lh $r,N($r) into an add and a load,
+;; when the original load is a 4 byte instruction but the add and the
+;; load are 2 2 byte instructions.
+
+(define_split
+  [(set (match_operand:HI 0 "register_operand" "")
+       (mem:SI (plus:SI (match_dup 0)
+                        (match_operand:SI 1 "const_int_operand" ""))))]
+  "TARGET_MIPS16 && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == CONST_INT
+   && ((INTVAL (operands[1]) < 0
+       && INTVAL (operands[1]) >= -0x80)
+       || (INTVAL (operands[1]) >= 32 * 2
+          && INTVAL (operands[1]) <= 31 * 2 + 0x7e)
+       || (INTVAL (operands[1]) >= 0
+          && INTVAL (operands[1]) < 32 * 2
+          && (INTVAL (operands[1]) & 1) != 0))"
+  [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1)))
+   (set (match_dup 0) (mem:HI (plus:SI (match_dup 0) (match_dup 2))))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[1]);
+
+  if (val < 0)
+    operands[2] = GEN_INT (0);
+  else if (val >= 32 * 2)
+    {
+      int off = val & 1;
+
+      operands[1] = GEN_INT (0x7e + off);
+      operands[2] = GEN_INT (val - off - 0x7e);
+    }
+  else
+    {
+      int off = val & 1;
+
+      operands[1] = GEN_INT (off);
+      operands[2] = GEN_INT (val - off);
+    }
+}")
 
 ;; 8-bit Integer moves
 
@@ -1957,7 +5379,9 @@ move\\t%0,%z4\\n\\
   if ((reload_in_progress | reload_completed) == 0
       && !register_operand (operands[0], QImode)
       && !register_operand (operands[1], QImode)
-      && (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0))
+      && (TARGET_MIPS16
+         || (GET_CODE (operands[1]) != CONST_INT
+         || INTVAL (operands[1]) != 0)))
     {
       rtx temp = force_reg (QImode, operands[1]);
       emit_move_insn (operands[0], temp);
@@ -1971,7 +5395,7 @@ move\\t%0,%z4\\n\\
 (define_insn "movqi_internal1"
   [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*f*z,*f,*x,*d")
        (match_operand:QI 1 "general_operand"       "d,IK,R,m,dJ,dJ,*f*z,*d,*f,*d,*x"))]
-  "TARGET_DEBUG_H_MODE
+  "TARGET_DEBUG_H_MODE && !TARGET_MIPS16
    && (register_operand (operands[0], QImode)
        || register_operand (operands[1], QImode)
        || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
@@ -1983,7 +5407,7 @@ move\\t%0,%z4\\n\\
 (define_insn "movqi_internal2"
   [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*z,*x,*d")
        (match_operand:QI 1 "general_operand"       "d,IK,R,m,dJ,dJ,*z,*d,*d,*x"))]
-  "!TARGET_DEBUG_H_MODE
+  "!TARGET_DEBUG_H_MODE && !TARGET_MIPS16
    && (register_operand (operands[0], QImode)
        || register_operand (operands[1], QImode)
        || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
@@ -1992,6 +5416,62 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "QI")
    (set_attr "length"  "1,1,1,2,1,2,1,1,1,1")])
 
+(define_insn ""
+  [(set (match_operand:QI 0 "nonimmediate_operand" "=d,y,d,d,d,d,d,R,m,*d")
+       (match_operand:QI 1 "general_operand"      "d,d,y,K,N,R,m,d,d,*x"))]
+  "TARGET_MIPS16
+   && (register_operand (operands[0], QImode)
+       || register_operand (operands[1], QImode))"
+  "* return mips_move_1word (operands, insn, TRUE);"
+  [(set_attr "type"    "move,move,move,arith,arith,load,load,store,store,hilo")
+   (set_attr "mode"    "QI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (const_int 1)
+                (const_int 1)
+                (if_then_else (match_operand:VOID 1 "m16_uimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))
+                (if_then_else (match_operand:VOID 1 "m16_nuimm8_1" "")
+                              (const_int 2)
+                              (const_int 3))
+                (const_int 1)
+                (const_int 2)
+                (const_int 1)
+                (const_int 2)
+                (const_int 1)])])
+
+
+;; On the mips16, we can split lb $r,N($r) into an add and a load,
+;; when the original load is a 4 byte instruction but the add and the
+;; load are 2 2 byte instructions.
+
+(define_split
+  [(set (match_operand:QI 0 "register_operand" "")
+       (mem:QI (plus:SI (match_dup 0)
+                        (match_operand:SI 1 "const_int_operand" ""))))]
+  "TARGET_MIPS16 && reload_completed
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && GET_CODE (operands[1]) == CONST_INT
+   && ((INTVAL (operands[1]) < 0
+       && INTVAL (operands[1]) >= -0x80)
+       || (INTVAL (operands[1]) >= 32
+          && INTVAL (operands[1]) <= 31 + 0x7f))"
+  [(set (match_dup 0) (plus:SI (match_dup 0) (match_dup 1)))
+   (set (match_dup 0) (mem:QI (plus:SI (match_dup 0) (match_dup 2))))]
+  "
+{
+  HOST_WIDE_INT val = INTVAL (operands[1]);
+
+  if (val < 0)
+    operands[2] = GEN_INT (0);
+  else
+    {
+      operands[1] = GEN_INT (0x7f);
+      operands[2] = GEN_INT (val - 0x7f);
+    }
+}")
 
 ;; 32-bit floating point moves
 
@@ -2004,8 +5484,9 @@ move\\t%0,%z4\\n\\
   if ((reload_in_progress | reload_completed) == 0
       && !register_operand (operands[0], SFmode)
       && !register_operand (operands[1], SFmode)
-      && (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
-      && operands[1] != CONST0_RTX (SFmode))
+      && (TARGET_MIPS16
+         || (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
+             && operands[1] != CONST0_RTX (SFmode)))
     {
       rtx temp = force_reg (SFmode, operands[1]);
       emit_move_insn (operands[0], temp);
@@ -2015,7 +5496,7 @@ move\\t%0,%z4\\n\\
 
 (define_insn "movsf_internal1"
   [(set (match_operand:SF 0 "nonimmediate_operand" "=f,f,f,f,R,m,*f,*d,*d,*d,*d,*R,*m")
-       (match_operand:SF 1 "general_operand" "f,G,R,Em,fG,fG,*d,*f,*G*d,*R,*E*m,*d,*d"))]
+       (match_operand:SF 1 "general_operand" "f,G,R,Fm,fG,fG,*d,*f,*G*d,*R,*F*m,*d,*d"))]
   "TARGET_HARD_FLOAT
    && (register_operand (operands[0], SFmode)
        || register_operand (operands[1], SFmode)
@@ -2029,8 +5510,8 @@ move\\t%0,%z4\\n\\
 
 (define_insn "movsf_internal2"
   [(set (match_operand:SF 0 "nonimmediate_operand" "=d,d,d,R,m")
-       (match_operand:SF 1 "general_operand" "      Gd,R,Em,d,d"))]
-  "TARGET_SOFT_FLOAT
+       (match_operand:SF 1 "general_operand" "      Gd,R,Fm,d,d"))]
+  "TARGET_SOFT_FLOAT && !TARGET_MIPS16
    && (register_operand (operands[0], SFmode)
        || register_operand (operands[1], SFmode)
        || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
@@ -2040,6 +5521,17 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SF")
    (set_attr "length"  "1,1,2,1,2")])
 
+(define_insn ""
+  [(set (match_operand:SF 0 "nonimmediate_operand" "=d,y,d,d,d,R,m")
+       (match_operand:SF 1 "general_operand"      "d,d,y,R,Fm,d,d"))]
+  "TARGET_MIPS16
+   && (register_operand (operands[0], SFmode)
+       || register_operand (operands[1], SFmode))"
+  "* return mips_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "move,move,move,load,load,store,store")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "1,1,1,1,2,1,2")])
+
 
 ;; 64-bit floating point moves
 
@@ -2052,8 +5544,9 @@ move\\t%0,%z4\\n\\
   if ((reload_in_progress | reload_completed) == 0
       && !register_operand (operands[0], DFmode)
       && !register_operand (operands[1], DFmode)
-      && (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
-      && operands[1] != CONST0_RTX (DFmode))
+      && (TARGET_MIPS16
+         || (GET_CODE (operands[1]) != CONST_INT || INTVAL (operands[1]) != 0)
+             && operands[1] != CONST0_RTX (DFmode)))
     {
       rtx temp = force_reg (DFmode, operands[1]);
       emit_move_insn (operands[0], temp);
@@ -2062,9 +5555,10 @@ move\\t%0,%z4\\n\\
 }")
 
 (define_insn "movdf_internal1"
-  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,R,o,f,*f,*d,*d,*d,*d,*R,*o")
-       (match_operand:DF 1 "general_operand" "f,R,o,fG,fG,E,*d,*f,*d*G,*R,*o*E,*d,*d"))]
-  "TARGET_HARD_FLOAT
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,f,R,To,f,*f,*d,*d,*d,*d,*R,*T")
+       (match_operand:DF 1 "general_operand" "f,R,To,fG,fG,F,*d,*f,*d*G,*R,*T*F,*d,*d"))]
+  "TARGET_HARD_FLOAT && !(TARGET_FLOAT64 && !TARGET_64BIT)
+   && TARGET_DOUBLE_FLOAT
    && (register_operand (operands[0], DFmode)
        || register_operand (operands[1], DFmode)
        || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
@@ -2074,10 +5568,26 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "DF")
    (set_attr "length"  "1,2,4,2,4,4,2,2,2,2,4,2,4")])
 
+(define_insn "movdf_internal1a"
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=f,f,R,R,To,To,f,*d,*d,*d,*To,*R")
+       (match_operand:DF 1 "general_operand"      " f,To,f,G,f,G,F,*F,*To,*R,*d,*d"))]
+  "TARGET_HARD_FLOAT && (TARGET_FLOAT64 && !TARGET_64BIT)
+   && TARGET_DOUBLE_FLOAT
+   && (register_operand (operands[0], DFmode)
+       || register_operand (operands[1], DFmode))
+       || (GET_CODE (operands [0]) == MEM
+          && ((GET_CODE (operands[1]) == CONST_INT
+               && INTVAL (operands[1]) == 0)
+              || operands[1] == CONST0_RTX (DFmode)))"
+  "* return mips_move_2words (operands, insn); "
+  [(set_attr "type"    "move,load,store,store,store,store,load,load,load,load,store,store")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "1,2,1,1,2,2,2,2,2,1,2,1")])
+
 (define_insn "movdf_internal2"
-  [(set (match_operand:DF 0 "nonimmediate_operand" "=d,d,d,R,o")
-       (match_operand:DF 1 "general_operand" "dG,R,oE,d,d"))]
-  "TARGET_SOFT_FLOAT
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=d,d,d,R,To")
+       (match_operand:DF 1 "general_operand" "dG,R,ToF,d,d"))]
+  "(TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT) && !TARGET_MIPS16
    && (register_operand (operands[0], DFmode)
        || register_operand (operands[1], DFmode)
        || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
@@ -2087,17 +5597,40 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "DF")
    (set_attr "length"  "2,2,4,2,4")])
 
+(define_insn ""
+  [(set (match_operand:DF 0 "nonimmediate_operand" "=d,y,d,d,d,R,To")
+       (match_operand:DF 1 "general_operand" "d,d,y,R,ToF,d,d"))]
+  "TARGET_MIPS16
+   && (register_operand (operands[0], DFmode)
+       || register_operand (operands[1], DFmode))"
+  "* return mips_move_2words (operands, insn);"
+  [(set_attr "type"    "move,move,move,load,load,store,store")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "2,2,2,2,4,2,4")])
+
 (define_split
   [(set (match_operand:DF 0 "register_operand" "")
        (match_operand:DF 1 "register_operand" ""))]
-  "reload_completed && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
    && GET_CODE (operands[0]) == REG && GP_REG_P (REGNO (operands[0]))
    && GET_CODE (operands[1]) == REG && GP_REG_P (REGNO (operands[1]))"
-
   [(set (subreg:SI (match_dup 0) 0) (subreg:SI (match_dup 1) 0))
    (set (subreg:SI (match_dup 0) 1) (subreg:SI (match_dup 1) 1))]
   "")
 
+;; Instructions to load the global pointer register.
+;; This is volatile to make sure that the scheduler won't move any symbol_ref
+;; uses in front of it.  All symbol_refs implicitly use the gp reg.
+
+(define_insn "loadgp"
+  [(set (reg:DI 28)
+       (unspec_volatile:DI [(match_operand:DI 0 "address_operand" "")] 2))
+   (clobber (reg:DI 1))]
+  ""
+  "%[lui\\t$1,%%hi(%%neg(%%gp_rel(%a0)))\\n\\taddiu\\t$1,$1,%%lo(%%neg(%%gp_rel(%a0)))\\n\\tdaddu\\t$gp,$1,$25%]"
+  [(set_attr "type"    "move")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "3")])
 \f
 ;; Block moves, see mips.c for more details.
 ;; Argument 0 is the destination
@@ -2106,11 +5639,11 @@ move\\t%0,%z4\\n\\
 ;; Argument 3 is the alignment
 
 (define_expand "movstrsi"
-  [(parallel [(set (mem:BLK (match_operand:BLK 0 "general_operand" ""))
-                  (mem:BLK (match_operand:BLK 1 "general_operand" "")))
+  [(parallel [(set (match_operand:BLK 0 "general_operand" "")
+                  (match_operand:BLK 1 "general_operand" ""))
              (use (match_operand:SI 2 "arith32_operand" ""))
              (use (match_operand:SI 3 "immediate_operand" ""))])]
-  ""
+  "!TARGET_MIPS16"
   "
 {
   if (operands[0])             /* avoid unused code messages */
@@ -2123,8 +5656,8 @@ move\\t%0,%z4\\n\\
 ;; Insn generated by block moves
 
 (define_insn "movstrsi_internal"
-  [(set (match_operand:BLK 0 "memory_operand" "=Ro")   ;; destination
-       (match_operand:BLK 1 "memory_operand" "Ro"))    ;; source
+  [(set (match_operand:BLK 0 "memory_operand" "=o")    ;; destination
+       (match_operand:BLK 1 "memory_operand" "o"))     ;; source
    (clobber (match_scratch:SI 4 "=&d"))                        ;; temp 1
    (clobber (match_scratch:SI 5 "=&d"))                        ;; temp 2
    (clobber (match_scratch:SI 6 "=&d"))                        ;; temp 3
@@ -2134,6 +5667,58 @@ move\\t%0,%z4\\n\\
    (use (const_int 0))]                                        ;; normal block move
   ""
   "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);"
+  [(set_attr "type"    "store")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "20")])
+
+;; We need mips16 versions, because an offset from the stack pointer
+;; is not offsettable, since the stack pointer can only handle 4 and 8
+;; byte loads.
+
+(define_insn ""
+  [(set (match_operand:BLK 0 "memory_operand" "=d")    ;; destination
+       (match_operand:BLK 1 "memory_operand" "d"))     ;; source
+   (clobber (match_scratch:SI 4 "=&d"))                        ;; temp 1
+   (clobber (match_scratch:SI 5 "=&d"))                        ;; temp 2
+   (clobber (match_scratch:SI 6 "=&d"))                        ;; temp 3
+   (clobber (match_scratch:SI 7 "=&d"))                        ;; temp 4
+   (use (match_operand:SI 2 "small_int" "I"))          ;; # bytes to move
+   (use (match_operand:SI 3 "small_int" "I"))          ;; alignment
+   (use (const_int 0))]                                        ;; normal block move
+  "TARGET_MIPS16"
+  "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);"
+  [(set_attr "type"    "multi")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "20")])
+
+(define_insn ""
+  [(set (match_operand:BLK 0 "memory_operand" "=d")    ;; destination
+       (match_operand:BLK 1 "memory_operand" "o"))     ;; source
+   (clobber (match_scratch:SI 4 "=&d"))                        ;; temp 1
+   (clobber (match_scratch:SI 5 "=&d"))                        ;; temp 2
+   (clobber (match_scratch:SI 6 "=&d"))                        ;; temp 3
+   (clobber (match_scratch:SI 7 "=&d"))                        ;; temp 4
+   (use (match_operand:SI 2 "small_int" "I"))          ;; # bytes to move
+   (use (match_operand:SI 3 "small_int" "I"))          ;; alignment
+   (use (const_int 0))]                                        ;; normal block move
+  "TARGET_MIPS16"
+  "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);"
+  [(set_attr "type"    "multi")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "20")])
+
+(define_insn ""
+  [(set (match_operand:BLK 0 "memory_operand" "=o")    ;; destination
+       (match_operand:BLK 1 "memory_operand" "d"))     ;; source
+   (clobber (match_scratch:SI 4 "=&d"))                        ;; temp 1
+   (clobber (match_scratch:SI 5 "=&d"))                        ;; temp 2
+   (clobber (match_scratch:SI 6 "=&d"))                        ;; temp 3
+   (clobber (match_scratch:SI 7 "=&d"))                        ;; temp 4
+   (use (match_operand:SI 2 "small_int" "I"))          ;; # bytes to move
+   (use (match_operand:SI 3 "small_int" "I"))          ;; alignment
+   (use (const_int 0))]                                        ;; normal block move
+  "TARGET_MIPS16"
+  "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NORMAL);"
   [(set_attr "type"    "multi")
    (set_attr "mode"    "none")
    (set_attr "length"  "20")])
@@ -2182,8 +5767,8 @@ move\\t%0,%z4\\n\\
   "")
 
 (define_insn "movstrsi_internal2"
-  [(set (match_operand:BLK 0 "memory_operand" "=Ro")   ;; destination
-       (match_operand:BLK 1 "memory_operand" "Ro"))    ;; source
+  [(set (match_operand:BLK 0 "memory_operand" "=o")    ;; destination
+       (match_operand:BLK 1 "memory_operand" "o"))     ;; source
    (clobber (match_scratch:SI 4 "=&d"))                        ;; temp 1
    (clobber (match_scratch:SI 5 "=&d"))                        ;; temp 2
    (clobber (match_scratch:SI 6 "=&d"))                        ;; temp 3
@@ -2193,6 +5778,22 @@ move\\t%0,%z4\\n\\
    (use (const_int 1))]                                        ;; all but last store
   ""
   "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NOT_LAST);"
+  [(set_attr "type"    "store")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "20")])
+
+(define_insn ""
+  [(set (match_operand:BLK 0 "memory_operand" "=d")    ;; destination
+       (match_operand:BLK 1 "memory_operand" "d"))     ;; source
+   (clobber (match_scratch:SI 4 "=&d"))                        ;; temp 1
+   (clobber (match_scratch:SI 5 "=&d"))                        ;; temp 2
+   (clobber (match_scratch:SI 6 "=&d"))                        ;; temp 3
+   (clobber (match_scratch:SI 7 "=&d"))                        ;; temp 4
+   (use (match_operand:SI 2 "small_int" "I"))          ;; # bytes to move
+   (use (match_operand:SI 3 "small_int" "I"))          ;; alignment
+   (use (const_int 1))]                                        ;; all but last store
+  "TARGET_MIPS16"
+  "* return output_block_move (insn, operands, 4, BLOCK_MOVE_NOT_LAST);"
   [(set_attr "type"    "multi")
    (set_attr "mode"    "none")
    (set_attr "length"  "20")])
@@ -2206,13 +5807,29 @@ move\\t%0,%z4\\n\\
    (clobber (match_scratch:SI 7 "=&d"))                        ;; temp 4
    (use (match_operand:SI 2 "small_int" "I"))          ;; # bytes to move
    (use (match_operand:SI 3 "small_int" "I"))          ;; alignment
-   (use (const_int 2))]                                        ;; just last store of block mvoe
+   (use (const_int 2))]                                        ;; just last store of block move
   ""
   "* return output_block_move (insn, operands, 4, BLOCK_MOVE_LAST);"
   [(set_attr "type"    "store")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
+(define_insn ""
+  [(set (match_operand:BLK 0 "memory_operand" "=d")    ;; destination
+       (match_operand:BLK 1 "memory_operand" "d"))     ;; source
+   (clobber (match_scratch:SI 4 "=&d"))                        ;; temp 1
+   (clobber (match_scratch:SI 5 "=&d"))                        ;; temp 2
+   (clobber (match_scratch:SI 6 "=&d"))                        ;; temp 3
+   (clobber (match_scratch:SI 7 "=&d"))                        ;; temp 4
+   (use (match_operand:SI 2 "small_int" "I"))          ;; # bytes to move
+   (use (match_operand:SI 3 "small_int" "I"))          ;; alignment
+   (use (const_int 2))]                                        ;; just last store of block move
+  "TARGET_MIPS16"
+  "* return output_block_move (insn, operands, 4, BLOCK_MOVE_LAST);"
+  [(set_attr "type"    "store")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "1")])
+
 \f
 ;;
 ;;  ....................
@@ -2221,15 +5838,49 @@ move\\t%0,%z4\\n\\
 ;;
 ;;  ....................
 
-(define_insn "ashlsi3"
+;; Many of these instructions uses trivial define_expands, because we
+;; want to use a different set of constraints when TARGET_MIPS16.
+
+(define_expand "ashlsi3"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (ashift:SI (match_operand:SI 1 "register_operand" "d")
                   (match_operand:SI 2 "arith_operand" "dI")))]
   ""
+  "
+{
+  /* On the mips16, a shift of more than 8 is a four byte instruction,
+     so, for a shift between 8 and 16, it is just as fast to do two
+     shifts of 8 or less.  If there is a lot of shifting going on, we
+     may win in CSE.  Otherwise combine will put the shifts back
+     together again.  This can be called by function_arg, so we must
+     be careful not to allocate a new register if we've reached the
+     reload pass.  */
+  if (TARGET_MIPS16
+      && optimize
+      && GET_CODE (operands[2]) == CONST_INT
+      && INTVAL (operands[2]) > 8
+      && INTVAL (operands[2]) <= 16
+      && ! reload_in_progress
+      && ! reload_completed)
+    {
+      rtx temp = gen_reg_rtx (SImode);
+
+      emit_insn (gen_ashlsi3_internal2 (temp, operands[1], GEN_INT (8)));
+      emit_insn (gen_ashlsi3_internal2 (operands[0], temp,
+                                       GEN_INT (INTVAL (operands[2]) - 8)));
+      DONE;
+    }
+}")
+
+(define_insn "ashlsi3_internal1"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ashift:SI (match_operand:SI 1 "register_operand" "d")
+                  (match_operand:SI 2 "arith_operand" "dI")))]
+  "!TARGET_MIPS16"
   "*
 {
   if (GET_CODE (operands[2]) == CONST_INT)
-    operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);
+    operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);
 
   return \"sll\\t%0,%1,%2\";
 }"
@@ -2237,14 +5888,87 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
+(define_insn "ashlsi3_internal2"
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (ashift:SI (match_operand:SI 1 "register_operand" "0,d")
+                  (match_operand:SI 2 "arith_operand" "d,I")))]
+  "TARGET_MIPS16"
+  "*
+{
+  if (which_alternative == 0)
+    return \"sll\\t%0,%2\";
+
+  if (GET_CODE (operands[2]) == CONST_INT)
+    operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);
+
+  return \"sll\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+                              (const_int 1)
+                              (const_int 2))])])
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (ashift:SI (match_operand:SI 1 "register_operand" "")
+                  (match_operand:SI 2 "const_int_operand" "")))]
+  "TARGET_MIPS16
+   && reload_completed
+   && GET_CODE (operands[2]) == CONST_INT
+   && INTVAL (operands[2]) > 8
+   && INTVAL (operands[2]) <= 16"
+  [(set (match_dup 0) (ashift:SI (match_dup 1) (const_int 8)))
+   (set (match_dup 0) (ashift:SI (match_dup 0) (match_dup 2)))]
+"
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+}")
 
 (define_expand "ashldi3"
   [(parallel [(set (match_operand:DI 0 "register_operand" "")
-                  (ashift:DI (match_operand:DI 1 "register_operand" "")
+                  (ashift:DI (match_operand:DI 1 "se_register_operand" "")
                              (match_operand:SI 2 "arith_operand" "")))
              (clobber (match_dup  3))])]
-  "!TARGET_DEBUG_G_MODE"
-  "operands[3] = gen_reg_rtx (SImode);")
+  "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
+  "
+{
+  if (TARGET_64BIT)
+    {
+      /* On the mips16, a shift of more than 8 is a four byte
+        instruction, so, for a shift between 8 and 16, it is just as
+        fast to do two shifts of 8 or less.  If there is a lot of
+        shifting going on, we may win in CSE.  Otherwise combine will
+        put the shifts back together again.  This can be called by
+        function_arg, so we must be careful not to allocate a new
+        register if we've reached the reload pass.  */
+      if (TARGET_MIPS16
+         && optimize
+         && GET_CODE (operands[2]) == CONST_INT
+         && INTVAL (operands[2]) > 8
+         && INTVAL (operands[2]) <= 16
+         && ! reload_in_progress
+         && ! reload_completed)
+       {
+         rtx temp = gen_reg_rtx (DImode);
+
+         emit_insn (gen_ashldi3_internal4 (temp, operands[1], GEN_INT (8)));
+         emit_insn (gen_ashldi3_internal4 (operands[0], temp,
+                                           GEN_INT (INTVAL (operands[2]) - 8)));
+         DONE;
+       }
+
+      emit_insn (gen_ashldi3_internal4 (operands[0], operands[1],
+                                       operands[2]));
+      DONE;
+    }
+
+  operands[3] = gen_reg_rtx (SImode);
+}")
 
 
 (define_insn "ashldi3_internal"
@@ -2252,7 +5976,7 @@ move\\t%0,%z4\\n\\
        (ashift:DI (match_operand:DI 1 "register_operand" "d")
                   (match_operand:SI 2 "register_operand" "d")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "!TARGET_DEBUG_G_MODE"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
   "* 
 {
   operands[4] = const0_rtx;
@@ -2286,10 +6010,11 @@ move\\t%0,%z4\\n\\
        (ashift:DI (match_operand:DI 1 "register_operand" "d")
                   (match_operand:SI 2 "small_int" "IJK")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "!TARGET_DEBUG_G_MODE && (INTVAL (operands[2]) & 32) != 0"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+   && (INTVAL (operands[2]) & 32) != 0"
   "*
 {
-  operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);
+  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);
   operands[4] = const0_rtx;
   return \"sll\\t%M0,%L1,%2\;move\\t%L0,%z4\";
 }"
@@ -2303,7 +6028,8 @@ move\\t%0,%z4\\n\\
        (ashift:DI (match_operand:DI 1 "register_operand" "")
                   (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 32) != 0"
@@ -2311,7 +6037,7 @@ move\\t%0,%z4\\n\\
   [(set (subreg:SI (match_dup 0) 1) (ashift:SI (subreg:SI (match_dup 1) 0) (match_dup 2)))
    (set (subreg:SI (match_dup 0) 0) (const_int 0))]
 
-  "operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);")
+  "operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);")
 
 
 (define_split
@@ -2319,7 +6045,8 @@ move\\t%0,%z4\\n\\
        (ashift:DI (match_operand:DI 1 "register_operand" "")
                   (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 32) != 0"
@@ -2327,7 +6054,7 @@ move\\t%0,%z4\\n\\
   [(set (subreg:SI (match_dup 0) 0) (ashift:SI (subreg:SI (match_dup 1) 1) (match_dup 2)))
    (set (subreg:SI (match_dup 0) 1) (const_int 0))]
 
-  "operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);")
+  "operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);")
 
 
 (define_insn "ashldi3_internal3"
@@ -2335,7 +6062,7 @@ move\\t%0,%z4\\n\\
        (ashift:DI (match_operand:DI 1 "register_operand" "d")
                   (match_operand:SI 2 "small_int" "IJK")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "!TARGET_DEBUG_G_MODE
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && (INTVAL (operands[2]) & 63) < 32
    && (INTVAL (operands[2]) & 63) != 0"
   "*
@@ -2358,7 +6085,8 @@ move\\t%0,%z4\\n\\
        (ashift:DI (match_operand:DI 1 "register_operand" "")
                   (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 63) < 32
@@ -2392,7 +6120,8 @@ move\\t%0,%z4\\n\\
        (ashift:DI (match_operand:DI 1 "register_operand" "")
                   (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 63) < 32
@@ -2421,30 +6150,185 @@ move\\t%0,%z4\\n\\
 }")
 
 
-(define_insn "ashrsi3"
+(define_insn "ashldi3_internal4"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (ashift:DI (match_operand:DI 1 "se_register_operand" "d")
+                  (match_operand:SI 2 "arith_operand" "dI")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "*
+{
+  if (GET_CODE (operands[2]) == CONST_INT)
+    operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+
+  return \"dsll\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (ashift:DI (match_operand:DI 1 "se_register_operand" "0,d")
+                  (match_operand:SI 2 "arith_operand" "d,I")))]
+  "TARGET_64BIT && TARGET_MIPS16"
+  "*
+{
+  if (which_alternative == 0)
+    return \"dsll\\t%0,%2\";
+
+  if (GET_CODE (operands[2]) == CONST_INT)
+    operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+
+  return \"dsll\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+                              (const_int 1)
+                              (const_int 2))])])
+
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (ashift:DI (match_operand:DI 1 "register_operand" "")
+                  (match_operand:SI 2 "const_int_operand" "")))]
+  "TARGET_MIPS16 && TARGET_64BIT
+   && reload_completed
+   && GET_CODE (operands[2]) == CONST_INT
+   && INTVAL (operands[2]) > 8
+   && INTVAL (operands[2]) <= 16"
+  [(set (match_dup 0) (ashift:DI (match_dup 1) (const_int 8)))
+   (set (match_dup 0) (ashift:DI (match_dup 0) (match_dup 2)))]
+"
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+}")
+
+(define_expand "ashrsi3"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (ashiftrt:SI (match_operand:SI 1 "register_operand" "d")
                     (match_operand:SI 2 "arith_operand" "dI")))]
   ""
+  "
+{
+  /* On the mips16, a shift of more than 8 is a four byte instruction,
+     so, for a shift between 8 and 16, it is just as fast to do two
+     shifts of 8 or less.  If there is a lot of shifting going on, we
+     may win in CSE.  Otherwise combine will put the shifts back
+     together again.  */
+  if (TARGET_MIPS16
+      && optimize
+      && GET_CODE (operands[2]) == CONST_INT
+      && INTVAL (operands[2]) > 8
+      && INTVAL (operands[2]) <= 16)
+    {
+      rtx temp = gen_reg_rtx (SImode);
+
+      emit_insn (gen_ashrsi3_internal2 (temp, operands[1], GEN_INT (8)));
+      emit_insn (gen_ashrsi3_internal2 (operands[0], temp,
+                                       GEN_INT (INTVAL (operands[2]) - 8)));
+      DONE;
+    }
+}")
+
+(define_insn "ashrsi3_internal1"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ashiftrt:SI (match_operand:SI 1 "register_operand" "d")
+                    (match_operand:SI 2 "arith_operand" "dI")))]
+  "!TARGET_MIPS16"
+  "*
+{
+  if (GET_CODE (operands[2]) == CONST_INT)
+    operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+
+  return \"sra\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn "ashrsi3_internal2"
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (ashiftrt:SI (match_operand:SI 1 "register_operand" "0,d")
+                    (match_operand:SI 2 "arith_operand" "d,I")))]
+  "TARGET_MIPS16"
   "*
 {
+  if (which_alternative == 0)
+    return \"sra\\t%0,%2\";
+
   if (GET_CODE (operands[2]) == CONST_INT)
-    operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);
+    operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);
 
   return \"sra\\t%0,%1,%2\";
 }"
   [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
-   (set_attr "length"  "1")])
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+                              (const_int 1)
+                              (const_int 2))])])
+
 
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (ashiftrt:SI (match_operand:SI 1 "register_operand" "")
+                    (match_operand:SI 2 "const_int_operand" "")))]
+  "TARGET_MIPS16
+   && reload_completed
+   && GET_CODE (operands[2]) == CONST_INT
+   && INTVAL (operands[2]) > 8
+   && INTVAL (operands[2]) <= 16"
+  [(set (match_dup 0) (ashiftrt:SI (match_dup 1) (const_int 8)))
+   (set (match_dup 0) (ashiftrt:SI (match_dup 0) (match_dup 2)))]
+"
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+}")
 
 (define_expand "ashrdi3"
   [(parallel [(set (match_operand:DI 0 "register_operand" "")
-                  (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
+                  (ashiftrt:DI (match_operand:DI 1 "se_register_operand" "")
                                (match_operand:SI 2 "arith_operand" "")))
              (clobber (match_dup  3))])]
-  "!TARGET_DEBUG_G_MODE"
-  "operands[3] = gen_reg_rtx (SImode);")
+  "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
+  "
+{
+  if (TARGET_64BIT)
+    {
+      /* On the mips16, a shift of more than 8 is a four byte
+        instruction, so, for a shift between 8 and 16, it is just as
+        fast to do two shifts of 8 or less.  If there is a lot of
+        shifting going on, we may win in CSE.  Otherwise combine will
+        put the shifts back together again.  */
+      if (TARGET_MIPS16
+         && optimize
+         && GET_CODE (operands[2]) == CONST_INT
+         && INTVAL (operands[2]) > 8
+         && INTVAL (operands[2]) <= 16)
+       {
+         rtx temp = gen_reg_rtx (DImode);
+
+         emit_insn (gen_ashrdi3_internal4 (temp, operands[1], GEN_INT (8)));
+         emit_insn (gen_ashrdi3_internal4 (operands[0], temp,
+                                           GEN_INT (INTVAL (operands[2]) - 8)));
+         DONE;
+       }
+
+      emit_insn (gen_ashrdi3_internal4 (operands[0], operands[1],
+                                       operands[2]));
+      DONE;
+    }
+
+  operands[3] = gen_reg_rtx (SImode);
+}")
 
 
 (define_insn "ashrdi3_internal"
@@ -2452,7 +6336,7 @@ move\\t%0,%z4\\n\\
        (ashiftrt:DI (match_operand:DI 1 "register_operand" "d")
                     (match_operand:SI 2 "register_operand" "d")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "!TARGET_DEBUG_G_MODE"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
   "* 
 {
   operands[4] = const0_rtx;
@@ -2486,10 +6370,10 @@ move\\t%0,%z4\\n\\
        (ashiftrt:DI (match_operand:DI 1 "register_operand" "d")
                     (match_operand:SI 2 "small_int" "IJK")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "!TARGET_DEBUG_G_MODE && (INTVAL (operands[2]) & 32) != 0"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && (INTVAL (operands[2]) & 32) != 0"
   "*
 {
-  operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);
+  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);
   return \"sra\\t%L0,%M1,%2\;sra\\t%M0,%M1,31\";
 }"
   [(set_attr "type"    "darith")
@@ -2502,7 +6386,7 @@ move\\t%0,%z4\\n\\
        (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
                     (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 32) != 0"
@@ -2510,7 +6394,7 @@ move\\t%0,%z4\\n\\
   [(set (subreg:SI (match_dup 0) 0) (ashiftrt:SI (subreg:SI (match_dup 1) 1) (match_dup 2)))
    (set (subreg:SI (match_dup 0) 1) (ashiftrt:SI (subreg:SI (match_dup 1) 1) (const_int 31)))]
 
-  "operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);")
+  "operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);")
 
 
 (define_split
@@ -2518,7 +6402,7 @@ move\\t%0,%z4\\n\\
        (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
                     (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 32) != 0"
@@ -2526,7 +6410,7 @@ move\\t%0,%z4\\n\\
   [(set (subreg:SI (match_dup 0) 1) (ashiftrt:SI (subreg:SI (match_dup 1) 0) (match_dup 2)))
    (set (subreg:SI (match_dup 0) 0) (ashiftrt:SI (subreg:SI (match_dup 1) 0) (const_int 31)))]
 
-  "operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);")
+  "operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);")
 
 
 (define_insn "ashrdi3_internal3"
@@ -2534,7 +6418,7 @@ move\\t%0,%z4\\n\\
        (ashiftrt:DI (match_operand:DI 1 "register_operand" "d")
                     (match_operand:SI 2 "small_int" "IJK")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "!TARGET_DEBUG_G_MODE
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && (INTVAL (operands[2]) & 63) < 32
    && (INTVAL (operands[2]) & 63) != 0"
   "*
@@ -2556,7 +6440,8 @@ move\\t%0,%z4\\n\\
        (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
                     (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 63) < 32
@@ -2590,7 +6475,8 @@ move\\t%0,%z4\\n\\
        (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
                     (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 63) < 32
@@ -2619,15 +6505,96 @@ move\\t%0,%z4\\n\\
 }")
 
 
-(define_insn "lshrsi3"
+(define_insn "ashrdi3_internal4"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (ashiftrt:DI (match_operand:DI 1 "se_register_operand" "d")
+                    (match_operand:SI 2 "arith_operand" "dI")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "*
+{
+  if (GET_CODE (operands[2]) == CONST_INT)
+    operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+
+  return \"dsra\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (ashiftrt:DI (match_operand:DI 1 "se_register_operand" "0,0")
+                    (match_operand:SI 2 "arith_operand" "d,I")))]
+  "TARGET_64BIT && TARGET_MIPS16"
+  "*
+{
+  if (GET_CODE (operands[2]) == CONST_INT)
+    operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+
+  return \"dsra\\t%0,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+                              (const_int 1)
+                              (const_int 2))])])
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (ashiftrt:DI (match_operand:DI 1 "register_operand" "")
+                    (match_operand:SI 2 "const_int_operand" "")))]
+  "TARGET_MIPS16 && TARGET_64BIT
+   && reload_completed
+   && GET_CODE (operands[2]) == CONST_INT
+   && INTVAL (operands[2]) > 8
+   && INTVAL (operands[2]) <= 16"
+  [(set (match_dup 0) (ashiftrt:DI (match_dup 1) (const_int 8)))
+   (set (match_dup 0) (ashiftrt:DI (match_dup 0) (match_dup 2)))]
+"
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+}")
+
+(define_expand "lshrsi3"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (lshiftrt:SI (match_operand:SI 1 "register_operand" "d")
                     (match_operand:SI 2 "arith_operand" "dI")))]
   ""
+  "
+{
+  /* On the mips16, a shift of more than 8 is a four byte instruction,
+     so, for a shift between 8 and 16, it is just as fast to do two
+     shifts of 8 or less.  If there is a lot of shifting going on, we
+     may win in CSE.  Otherwise combine will put the shifts back
+     together again.  */
+  if (TARGET_MIPS16
+      && optimize
+      && GET_CODE (operands[2]) == CONST_INT
+      && INTVAL (operands[2]) > 8
+      && INTVAL (operands[2]) <= 16)
+    {
+      rtx temp = gen_reg_rtx (SImode);
+
+      emit_insn (gen_lshrsi3_internal2 (temp, operands[1], GEN_INT (8)));
+      emit_insn (gen_lshrsi3_internal2 (operands[0], temp,
+                                       GEN_INT (INTVAL (operands[2]) - 8)));
+      DONE;
+    }
+}")
+
+(define_insn "lshrsi3_internal1"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (lshiftrt:SI (match_operand:SI 1 "register_operand" "d")
+                    (match_operand:SI 2 "arith_operand" "dI")))]
+  "!TARGET_MIPS16"
   "*
 {
   if (GET_CODE (operands[2]) == CONST_INT)
-    operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);
+    operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);
 
   return \"srl\\t%0,%1,%2\";
 }"
@@ -2635,14 +6602,116 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
+(define_insn "lshrsi3_internal2"
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (lshiftrt:SI (match_operand:SI 1 "register_operand" "0,d")
+                    (match_operand:SI 2 "arith_operand" "d,I")))]
+  "TARGET_MIPS16"
+  "*
+{
+  if (which_alternative == 0)
+    return \"srl\\t%0,%2\";
+
+  if (GET_CODE (operands[2]) == CONST_INT)
+    operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);
+
+  return \"srl\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+                              (const_int 1)
+                              (const_int 2))])])
+
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (lshiftrt:SI (match_operand:SI 1 "register_operand" "")
+                    (match_operand:SI 2 "const_int_operand" "")))]
+  "TARGET_MIPS16
+   && reload_completed
+   && GET_CODE (operands[2]) == CONST_INT
+   && INTVAL (operands[2]) > 8
+   && INTVAL (operands[2]) <= 16"
+  [(set (match_dup 0) (lshiftrt:SI (match_dup 1) (const_int 8)))
+   (set (match_dup 0) (lshiftrt:SI (match_dup 0) (match_dup 2)))]
+"
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+}")
+
+;; If we load a byte on the mips16 as a bitfield, the resulting
+;; sequence of instructions is too complicated for combine, because it
+;; involves four instructions: a load, a shift, a constant load into a
+;; register, and an and (the key problem here is that the mips16 does
+;; not have and immediate).  We recognize a shift of a load in order
+;; to make it simple enough for combine to understand.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "d,d")
+       (lshiftrt:SI (match_operand:SI 1 "memory_operand" "R,m")
+                    (match_operand:SI 2 "immediate_operand" "I,I")))]
+  "TARGET_MIPS16"
+  "lw\\t%0,%1\;srl\\t%0,%2"
+  [(set_attr "type"    "load")
+   (set_attr "mode"    "SI")
+   (set_attr_alternative "length"
+               [(if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+                              (const_int 2)
+                              (const_int 3))
+                (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+                              (const_int 3)
+                              (const_int 4))])])
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (lshiftrt:SI (match_operand:SI 1 "memory_operand" "")
+                    (match_operand:SI 2 "immediate_operand" "")))]
+  "TARGET_MIPS16"
+  [(set (match_dup 0) (match_dup 1))
+   (set (match_dup 0) (lshiftrt:SI (match_dup 0) (match_dup 2)))]
+  "")
 
 (define_expand "lshrdi3"
   [(parallel [(set (match_operand:DI 0 "register_operand" "")
-                  (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
+                  (lshiftrt:DI (match_operand:DI 1 "se_register_operand" "")
                                (match_operand:SI 2 "arith_operand" "")))
              (clobber (match_dup  3))])]
-  "!TARGET_DEBUG_G_MODE"
-  "operands[3] = gen_reg_rtx (SImode);")
+  "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
+  "
+{
+  if (TARGET_64BIT)
+    {
+      /* On the mips16, a shift of more than 8 is a four byte
+        instruction, so, for a shift between 8 and 16, it is just as
+        fast to do two shifts of 8 or less.  If there is a lot of
+        shifting going on, we may win in CSE.  Otherwise combine will
+        put the shifts back together again.  */
+      if (TARGET_MIPS16
+         && optimize
+         && GET_CODE (operands[2]) == CONST_INT
+         && INTVAL (operands[2]) > 8
+         && INTVAL (operands[2]) <= 16)
+       {
+         rtx temp = gen_reg_rtx (DImode);
+
+         emit_insn (gen_lshrdi3_internal4 (temp, operands[1], GEN_INT (8)));
+         emit_insn (gen_lshrdi3_internal4 (operands[0], temp,
+                                           GEN_INT (INTVAL (operands[2]) - 8)));
+         DONE;
+       }
+
+      emit_insn (gen_lshrdi3_internal4 (operands[0], operands[1],
+                                       operands[2]));
+      DONE;
+    }
+
+  operands[3] = gen_reg_rtx (SImode);
+}")
 
 
 (define_insn "lshrdi3_internal"
@@ -2650,7 +6719,7 @@ move\\t%0,%z4\\n\\
        (lshiftrt:DI (match_operand:DI 1 "register_operand" "d")
                     (match_operand:SI 2 "register_operand" "d")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "!TARGET_DEBUG_G_MODE"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
   "* 
 {
   operands[4] = const0_rtx;
@@ -2684,10 +6753,11 @@ move\\t%0,%z4\\n\\
        (lshiftrt:DI (match_operand:DI 1 "register_operand" "d")
                     (match_operand:SI 2 "small_int" "IJK")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "!TARGET_DEBUG_G_MODE && (INTVAL (operands[2]) & 32) != 0"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
+   && (INTVAL (operands[2]) & 32) != 0"
   "*
 {
-  operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);
+  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);
   operands[4] = const0_rtx;
   return \"srl\\t%L0,%M1,%2\;move\\t%M0,%z4\";
 }"
@@ -2701,7 +6771,8 @@ move\\t%0,%z4\\n\\
        (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
                     (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 32) != 0"
@@ -2709,7 +6780,7 @@ move\\t%0,%z4\\n\\
   [(set (subreg:SI (match_dup 0) 0) (lshiftrt:SI (subreg:SI (match_dup 1) 1) (match_dup 2)))
    (set (subreg:SI (match_dup 0) 1) (const_int 0))]
 
-  "operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);")
+  "operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);")
 
 
 (define_split
@@ -2717,7 +6788,8 @@ move\\t%0,%z4\\n\\
        (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
                     (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 32) != 0"
@@ -2725,7 +6797,7 @@ move\\t%0,%z4\\n\\
   [(set (subreg:SI (match_dup 0) 1) (lshiftrt:SI (subreg:SI (match_dup 1) 0) (match_dup 2)))
    (set (subreg:SI (match_dup 0) 0) (const_int 0))]
 
-  "operands[2] = gen_rtx (CONST_INT, VOIDmode, (XINT (operands[2], 0))& 0x1f);")
+  "operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);")
 
 
 (define_insn "lshrdi3_internal3"
@@ -2733,7 +6805,7 @@ move\\t%0,%z4\\n\\
        (lshiftrt:DI (match_operand:DI 1 "register_operand" "d")
                   (match_operand:SI 2 "small_int" "IJK")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "!TARGET_DEBUG_G_MODE
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && (INTVAL (operands[2]) & 63) < 32
    && (INTVAL (operands[2]) & 63) != 0"
   "*
@@ -2755,7 +6827,8 @@ move\\t%0,%z4\\n\\
        (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
                     (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && !WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 63) < 32
@@ -2789,7 +6862,8 @@ move\\t%0,%z4\\n\\
        (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
                     (match_operand:SI 2 "small_int" "")))
    (clobber (match_operand:SI 3 "register_operand" ""))]
-  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "reload_completed && WORDS_BIG_ENDIAN && !TARGET_64BIT
+   && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && GET_CODE (operands[0]) == REG && REGNO (operands[0]) < FIRST_PSEUDO_REGISTER
    && GET_CODE (operands[1]) == REG && REGNO (operands[1]) < FIRST_PSEUDO_REGISTER
    && (INTVAL (operands[2]) & 63) < 32
@@ -2817,6 +6891,61 @@ move\\t%0,%z4\\n\\
   operands[4] = gen_rtx (CONST_INT, VOIDmode, ((-amount) & 31));
 }")
 
+
+(define_insn "lshrdi3_internal4"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (lshiftrt:DI (match_operand:DI 1 "se_register_operand" "d")
+                    (match_operand:SI 2 "arith_operand" "dI")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "*
+{
+  if (GET_CODE (operands[2]) == CONST_INT)
+    operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+
+  return \"dsrl\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (lshiftrt:DI (match_operand:DI 1 "se_register_operand" "0,0")
+                    (match_operand:SI 2 "arith_operand" "d,I")))]
+  "TARGET_64BIT && TARGET_MIPS16"
+  "*
+{
+  if (GET_CODE (operands[2]) == CONST_INT)
+    operands[2] = GEN_INT (INTVAL (operands[2]) & 0x3f);
+
+  return \"dsrl\\t%0,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm3_b" "")
+                              (const_int 1)
+                              (const_int 2))])])
+
+;; On the mips16, we can split a 4 byte shift into 2 2 byte shifts.
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (lshiftrt:DI (match_operand:DI 1 "register_operand" "")
+                    (match_operand:SI 2 "const_int_operand" "")))]
+  "TARGET_MIPS16
+   && reload_completed
+   && GET_CODE (operands[2]) == CONST_INT
+   && INTVAL (operands[2]) > 8
+   && INTVAL (operands[2]) <= 16"
+  [(set (match_dup 0) (lshiftrt:DI (match_dup 1) (const_int 8)))
+   (set (match_dup 0) (lshiftrt:DI (match_dup 0) (match_dup 2)))]
+"
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) - 8);
+}")
+
 \f
 ;;
 ;;  ....................
@@ -2827,7 +6956,7 @@ move\\t%0,%z4\\n\\
 
 ;; Flow here is rather complex:
 ;;
-;;  1) The cmp{si,sf,df} routine is called.  It deposits the
+;;  1) The cmp{si,di,sf,df} routine is called.  It deposits the
 ;;     arguments into the branch_cmp array, and the type into
 ;;     branch_type.  No RTL is generated.
 ;;
@@ -2872,11 +7001,42 @@ move\\t%0,%z4\\n\\
     }
 }")
 
+(define_expand "cmpdi"
+  [(set (cc0)
+       (compare:CC (match_operand:DI 0 "se_register_operand" "")
+                   (match_operand:DI 1 "se_arith_operand" "")))]
+  "TARGET_64BIT"
+  "
+{
+  if (operands[0])             /* avoid unused code message */
+    {
+      branch_cmp[0] = operands[0];
+      branch_cmp[1] = operands[1];
+      branch_type = CMP_DI;
+      DONE;
+    }
+}")
+
+(define_expand "tstdi"
+  [(set (cc0)
+       (match_operand:DI 0 "se_register_operand" ""))]
+  "TARGET_64BIT"
+  "
+{
+  if (operands[0])             /* avoid unused code message */
+    {
+      branch_cmp[0] = operands[0];
+      branch_cmp[1] = const0_rtx;
+      branch_type = CMP_DI;
+      DONE;
+    }
+}")
+
 (define_expand "cmpdf"
   [(set (cc0)
-       (compare:CC_FP (match_operand:DF 0 "register_operand" "")
-                      (match_operand:DF 1 "register_operand" "")))]
-  "TARGET_HARD_FLOAT"
+       (compare:CC (match_operand:DF 0 "register_operand" "")
+                   (match_operand:DF 1 "register_operand" "")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "
 {
   if (operands[0])             /* avoid unused code message */
@@ -2890,8 +7050,8 @@ move\\t%0,%z4\\n\\
 
 (define_expand "cmpsf"
   [(set (cc0)
-       (compare:CC_FP (match_operand:SF 0 "register_operand" "")
-                      (match_operand:SF 1 "register_operand" "")))]
+       (compare:CC (match_operand:SF 0 "register_operand" "")
+                   (match_operand:SF 1 "register_operand" "")))]
   "TARGET_HARD_FLOAT"
   "
 {
@@ -2914,77 +7074,118 @@ move\\t%0,%z4\\n\\
 
 (define_insn "branch_fp_ne"
   [(set (pc)
-       (if_then_else (ne:CC_FP (reg:CC_FP 66)
-                               (const_int 0))
-                     (match_operand 0 "pc_or_label_operand" "")
-                     (match_operand 1 "pc_or_label_operand" "")))]
-  ""
+       (if_then_else (ne:CC (match_operand:CC 0 "register_operand" "z")
+                            (const_int 0))
+                     (match_operand 1 "pc_or_label_operand" "")
+                     (match_operand 2 "pc_or_label_operand" "")))]
+  "TARGET_HARD_FLOAT"
   "*
 {
   mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
-  return (operands[0] != pc_rtx) ? \"%*bc1t%?\\t%0\" : \"%*bc1f%?\\t%1\";
+  return (operands[1] != pc_rtx) ? \"%*bc1t%?\\t%Z0%1\" : \"%*bc1f%?\\t%Z0%2\";
 }"
   [(set_attr "type"    "branch")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
-(define_insn "branch_fp_ne_rev"
+(define_insn "branch_fp_eq"
   [(set (pc)
-       (if_then_else (ne:CC_REV_FP (reg:CC_REV_FP 66)
-                                   (const_int 0))
-                     (match_operand 0 "pc_or_label_operand" "")
-                     (match_operand 1 "pc_or_label_operand" "")))]
-  ""
+       (if_then_else (eq:CC (match_operand:CC 0 "register_operand" "z")
+                            (const_int 0))
+                     (match_operand 1 "pc_or_label_operand" "")
+                     (match_operand 2 "pc_or_label_operand" "")))]
+  "TARGET_HARD_FLOAT"
   "*
 {
   mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
-  return (operands[0] != pc_rtx) ? \"%*bc1f%?\\t%0\" : \"%*bc1t%?\\t%1\";
+  return (operands[1] != pc_rtx) ? \"%*bc1f%?\\t%Z0%1\" : \"%*bc1t%?\\t%Z0%2\";
 }"
   [(set_attr "type"    "branch")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
-(define_insn "branch_fp_eq"
+(define_insn "branch_zero"
   [(set (pc)
-       (if_then_else (eq:CC_FP (reg:CC_FP 66)
-                               (const_int 0))
-                     (match_operand 0 "pc_or_label_operand" "")
-                     (match_operand 1 "pc_or_label_operand" "")))]
-  ""
+       (if_then_else (match_operator:SI 0 "cmp_op"
+                                        [(match_operand:SI 1 "register_operand" "d")
+                                         (const_int 0)])
+       (match_operand 2 "pc_or_label_operand" "")
+       (match_operand 3 "pc_or_label_operand" "")))]
+  "!TARGET_MIPS16"
   "*
 {
   mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
-  return (operands[0] != pc_rtx) ? \"%*bc1f%?\\t%0\" : \"%*bc1t%?\\t%1\";
+  if (operands[2] != pc_rtx)
+    {                          /* normal jump */
+      switch (GET_CODE (operands[0]))
+       {
+       case EQ:  return \"%*beq%?\\t%z1,%.,%2\";
+       case NE:  return \"%*bne%?\\t%z1,%.,%2\";
+       case GTU: return \"%*bne%?\\t%z1,%.,%2\";
+       case LEU: return \"%*beq%?\\t%z1,%.,%2\";
+       case GEU: return \"%*j\\t%2\";
+       case LTU: return \"%*bne%?\\t%.,%.,%2\";
+       }
+
+      return \"%*b%C0z%?\\t%z1,%2\";
+    }
+  else
+    {                          /* inverted jump */
+      switch (GET_CODE (operands[0]))
+       {
+       case EQ:  return \"%*bne%?\\t%z1,%.,%3\";
+       case NE:  return \"%*beq%?\\t%z1,%.,%3\";
+       case GTU: return \"%*beq%?\\t%z1,%.,%3\";
+       case LEU: return \"%*bne%?\\t%z1,%.,%3\";
+       case GEU: return \"%*beq%?\\t%.,%.,%3\";
+       case LTU: return \"%*j\\t%3\";
+       }
+
+      return \"%*b%N0z%?\\t%z1,%3\";
+    }
 }"
   [(set_attr "type"    "branch")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
-(define_insn "branch_fp_eq_rev"
+
+(define_insn ""
   [(set (pc)
-       (if_then_else (eq:CC_REV_FP (reg:CC_REV_FP 66)
-                                   (const_int 0))
-                     (match_operand 0 "pc_or_label_operand" "")
-                     (match_operand 1 "pc_or_label_operand" "")))]
-  ""
+       (if_then_else (match_operator:SI 0 "equality_op"
+                                        [(match_operand:SI 1 "register_operand" "d,t")
+                                         (const_int 0)])
+       (match_operand 2 "pc_or_label_operand" "")
+       (match_operand 3 "pc_or_label_operand" "")))]
+  "TARGET_MIPS16"
   "*
 {
-  mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
-  return (operands[0] != pc_rtx) ? \"%*bc1t%?\\t%0\" : \"%*bc1f%?\\t%1\";
+  if (operands[2] != pc_rtx)
+    {
+      if (which_alternative == 0)
+       return \"%*b%C0z\\t%1,%2\";
+      else
+       return \"%*bt%C0z\\t%2\";
+    }
+  else
+    {
+      if (which_alternative == 0)
+       return \"%*b%N0z\\t%1,%3\";
+      else
+       return \"%*bt%N0z\\t%3\";
+    }
 }"
   [(set_attr "type"    "branch")
    (set_attr "mode"    "none")
-   (set_attr "length"  "1")])
-
+   (set_attr "length"  "2")])
 
-(define_insn "branch_zero"
+(define_insn "branch_zero_di"
   [(set (pc)
-       (if_then_else (match_operator:SI 0 "cmp_op"
-                                        [(match_operand:SI 1 "register_operand" "d")
+       (if_then_else (match_operator:DI 0 "cmp_op"
+                                        [(match_operand:DI 1 "se_register_operand" "d")
                                          (const_int 0)])
        (match_operand 2 "pc_or_label_operand" "")
        (match_operand 3 "pc_or_label_operand" "")))]
-  ""
+  "!TARGET_MIPS16"
   "*
 {
   mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
@@ -3021,6 +7222,35 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
+(define_insn ""
+  [(set (pc)
+       (if_then_else (match_operator:DI 0 "equality_op"
+                                        [(match_operand:DI 1 "se_register_operand" "d,t")
+                                         (const_int 0)])
+       (match_operand 2 "pc_or_label_operand" "")
+       (match_operand 3 "pc_or_label_operand" "")))]
+  "TARGET_MIPS16"
+  "*
+{
+  if (operands[2] != pc_rtx)
+    {
+      if (which_alternative == 0)
+       return \"%*b%C0z\\t%1,%2\";
+      else
+       return \"%*bt%C0z\\t%2\";
+    }
+  else
+    {
+      if (which_alternative == 0)
+       return \"%*b%N0z\\t%1,%3\";
+      else
+       return \"%*bt%N0z\\t%3\";
+    }
+}"
+  [(set_attr "type"    "branch")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
 
 (define_insn "branch_equality"
   [(set (pc)
@@ -3029,7 +7259,27 @@ move\\t%0,%z4\\n\\
                                          (match_operand:SI 2 "register_operand" "d")])
        (match_operand 3 "pc_or_label_operand" "")
        (match_operand 4 "pc_or_label_operand" "")))]
-  ""
+  "!TARGET_MIPS16"
+  "*
+{
+  mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
+  return (operands[3] != pc_rtx)
+       ? \"%*b%C0%?\\t%z1,%z2,%3\"
+       : \"%*b%N0%?\\t%z1,%z2,%4\";
+}"
+  [(set_attr "type"    "branch")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "1")])
+
+
+(define_insn "branch_equality_di"
+  [(set (pc)
+       (if_then_else (match_operator:DI 0 "equality_op"
+                                        [(match_operand:DI 1 "se_register_operand" "d")
+                                         (match_operand:DI 2 "se_register_operand" "d")])
+       (match_operand 3 "pc_or_label_operand" "")
+       (match_operand 4 "pc_or_label_operand" "")))]
+  "!TARGET_MIPS16"
   "*
 {
   mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
@@ -3044,8 +7294,8 @@ move\\t%0,%z4\\n\\
 
 (define_expand "beq"
   [(set (pc)
-       (if_then_else (eq:CC_EQ (cc0)
-                               (const_int 0))
+       (if_then_else (eq:CC (cc0)
+                            (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
   ""
@@ -3060,8 +7310,8 @@ move\\t%0,%z4\\n\\
 
 (define_expand "bne"
   [(set (pc)
-       (if_then_else (ne:CC_EQ (cc0)
-                               (const_int 0))
+       (if_then_else (ne:CC (cc0)
+                            (const_int 0))
                      (label_ref (match_operand 0 "" ""))
                      (pc)))]
   ""
@@ -3218,14 +7468,14 @@ move\\t%0,%z4\\n\\
   ""
   "
 {
-  if (branch_type != CMP_SI)
+  if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
     FAIL;
 
   /* set up operands from compare.  */
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (!TARGET_DEBUG_C_MODE)
+  if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
     {
       gen_int_relational (EQ, operands[0], operands[1], operands[2], (int *)0);
       DONE;
@@ -3238,25 +7488,55 @@ move\\t%0,%z4\\n\\
 }")
 
 
-(define_insn "seq_si_zero"
-  [(set (match_operand:SI 0 "register_operand" "=d")
+(define_insn "seq_si_zero"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (eq:SI (match_operand:SI 1 "register_operand" "d")
+              (const_int 0)))]
+  "!TARGET_MIPS16"
+  "sltu\\t%0,%1,1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=t")
        (eq:SI (match_operand:SI 1 "register_operand" "d")
               (const_int 0)))]
-  ""
-  "sltu\\t%0,%1,1"
[(set_attr "type"     "arith")
+  "TARGET_MIPS16"
+  "sltu\\t%1,1"
 [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
+(define_insn "seq_di_zero"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (eq:DI (match_operand:DI 1 "se_register_operand" "d")
+              (const_int 0)))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "sltu\\t%0,%1,1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=t")
+       (eq:DI (match_operand:DI 1 "se_register_operand" "d")
+              (const_int 0)))]
+  "TARGET_64BIT && TARGET_MIPS16"
+  "sltu\\t%1,1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
 (define_insn "seq_si"
   [(set (match_operand:SI 0 "register_operand" "=d,d")
        (eq:SI (match_operand:SI 1 "register_operand" "%d,d")
               (match_operand:SI 2 "uns_arith_operand" "d,K")))]
-  "TARGET_DEBUG_C_MODE"
+  "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
   "@
    xor\\t%0,%1,%2\;sltu\\t%0,%0,1
    xori\\t%0,%1,%2\;sltu\\t%0,%0,1"
[(set_attr "type"     "arith")
 [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
    (set_attr "length"  "2")])
 
@@ -3264,7 +7544,7 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "")
        (eq:SI (match_operand:SI 1 "register_operand" "")
               (match_operand:SI 2 "uns_arith_operand" "")))]
-  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
     && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)"
   [(set (match_dup 0)
        (xor:SI (match_dup 1)
@@ -3274,21 +7554,50 @@ move\\t%0,%z4\\n\\
                (const_int 1)))]
   "")
 
+(define_insn "seq_di"
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (eq:DI (match_operand:DI 1 "se_register_operand" "%d,d")
+              (match_operand:DI 2 "se_uns_arith_operand" "d,K")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+  "@
+   xor\\t%0,%1,%2\;sltu\\t%0,%0,1
+   xori\\t%0,%1,%2\;sltu\\t%0,%0,1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "2")])
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (eq:DI (match_operand:DI 1 "se_register_operand" "")
+              (match_operand:DI 2 "se_uns_arith_operand" "")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+    && !TARGET_MIPS16
+    && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)"
+  [(set (match_dup 0)
+       (xor:DI (match_dup 1)
+               (match_dup 2)))
+   (set (match_dup 0)
+       (ltu:DI (match_dup 0)
+               (const_int 1)))]
+  "")
+
+;; On the mips16 the default code is better than using sltu.
+
 (define_expand "sne"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (ne:SI (match_dup 1)
               (match_dup 2)))]
-  ""
+  "!TARGET_MIPS16"
   "
 {
-  if (branch_type != CMP_SI)
+  if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
     FAIL;
 
   /* set up operands from compare.  */
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (!TARGET_DEBUG_C_MODE)
+  if (TARGET_64BIT || !TARGET_DEBUG_C_MODE)
     {
       gen_int_relational (NE, operands[0], operands[1], operands[2], (int *)0);
       DONE;
@@ -3304,21 +7613,31 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "=d")
        (ne:SI (match_operand:SI 1 "register_operand" "d")
               (const_int 0)))]
-  ""
+  "!TARGET_MIPS16"
   "sltu\\t%0,%.,%1"
[(set_attr "type"     "arith")
 [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
+(define_insn "sne_di_zero"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (ne:DI (match_operand:DI 1 "se_register_operand" "d")
+              (const_int 0)))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "sltu\\t%0,%.,%1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
 (define_insn "sne_si"
   [(set (match_operand:SI 0 "register_operand" "=d,d")
        (ne:SI (match_operand:SI 1 "register_operand" "%d,d")
               (match_operand:SI 2 "uns_arith_operand" "d,K")))]
-  "TARGET_DEBUG_C_MODE"
+  "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
   "@
     xor\\t%0,%1,%2\;sltu\\t%0,%.,%0
     xori\\t%0,%1,%x2\;sltu\\t%0,%.,%0"
[(set_attr "type"     "arith")
 [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
    (set_attr "length"  "2")])
 
@@ -3326,7 +7645,7 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "")
        (ne:SI (match_operand:SI 1 "register_operand" "")
               (match_operand:SI 2 "uns_arith_operand" "")))]
-  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16
     && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)"
   [(set (match_dup 0)
        (xor:SI (match_dup 1)
@@ -3336,6 +7655,33 @@ move\\t%0,%z4\\n\\
                (const_int 0)))]
   "")
 
+(define_insn "sne_di"
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (ne:DI (match_operand:DI 1 "se_register_operand" "%d,d")
+              (match_operand:DI 2 "se_uns_arith_operand" "d,K")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+  "@
+    xor\\t%0,%1,%2\;sltu\\t%0,%.,%0
+    xori\\t%0,%1,%x2\;sltu\\t%0,%.,%0"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "2")])
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (ne:DI (match_operand:DI 1 "se_register_operand" "")
+              (match_operand:DI 2 "se_uns_arith_operand" "")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+    && !TARGET_MIPS16
+    && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != 0)"
+  [(set (match_dup 0)
+       (xor:DI (match_dup 1)
+               (match_dup 2)))
+   (set (match_dup 0)
+       (gtu:DI (match_dup 0)
+               (const_int 0)))]
+  "")
+
 (define_expand "sgt"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (gt:SI (match_dup 1)
@@ -3343,14 +7689,14 @@ move\\t%0,%z4\\n\\
   ""
   "
 {
-  if (branch_type != CMP_SI)
+  if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
     FAIL;
 
   /* set up operands from compare.  */
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (!TARGET_DEBUG_C_MODE)
+  if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
     {
       gen_int_relational (GT, operands[0], operands[1], operands[2], (int *)0);
       DONE;
@@ -3366,12 +7712,42 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "=d")
        (gt:SI (match_operand:SI 1 "register_operand" "d")
               (match_operand:SI 2 "reg_or_0_operand" "dJ")))]
-  ""
+  "!TARGET_MIPS16"
   "slt\\t%0,%z2,%1"
- [(set_attr "type"     "arith")
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=t")
+       (gt:SI (match_operand:SI 1 "register_operand" "d")
+              (match_operand:SI 2 "register_operand" "d")))]
+  "TARGET_MIPS16"
+  "slt\\t%2,%1"
+  [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
+(define_insn "sgt_di"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (gt:DI (match_operand:DI 1 "se_register_operand" "d")
+              (match_operand:DI 2 "se_reg_or_0_operand" "dJ")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "slt\\t%0,%z2,%1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (gt:DI (match_operand:DI 1 "se_register_operand" "d")
+              (match_operand:DI 2 "se_register_operand" "d")))]
+  "TARGET_64BIT && TARGET_MIPS16"
+  "slt\\t%2,%1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
 (define_expand "sge"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (ge:SI (match_dup 1)
@@ -3379,14 +7755,14 @@ move\\t%0,%z4\\n\\
   ""
   "
 {
-  if (branch_type != CMP_SI)
+  if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
     FAIL;
 
   /* set up operands from compare.  */
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (!TARGET_DEBUG_C_MODE)
+  if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
     {
       gen_int_relational (GE, operands[0], operands[1], operands[2], (int *)0);
       DONE;
@@ -3399,9 +7775,9 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "=d")
        (ge:SI (match_operand:SI 1 "register_operand" "d")
               (match_operand:SI 2 "arith_operand" "dI")))]
-  "TARGET_DEBUG_C_MODE"
+  "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
   "slt\\t%0,%1,%2\;xori\\t%0,%0,0x0001"
[(set_attr "type"     "arith")
 [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
    (set_attr "length"  "2")])
 
@@ -3409,7 +7785,7 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "")
        (ge:SI (match_operand:SI 1 "register_operand" "")
               (match_operand:SI 2 "arith_operand" "")))]
-  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE"
+  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16"
   [(set (match_dup 0)
        (lt:SI (match_dup 1)
               (match_dup 2)))
@@ -3418,6 +7794,30 @@ move\\t%0,%z4\\n\\
                (const_int 1)))]
   "")
 
+(define_insn "sge_di"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (ge:DI (match_operand:DI 1 "se_register_operand" "d")
+              (match_operand:DI 2 "se_arith_operand" "dI")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+  "slt\\t%0,%1,%2\;xori\\t%0,%0,0x0001"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "2")])
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (ge:DI (match_operand:DI 1 "se_register_operand" "")
+              (match_operand:DI 2 "se_arith_operand" "")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+   && !TARGET_MIPS16"
+  [(set (match_dup 0)
+       (lt:DI (match_dup 1)
+              (match_dup 2)))
+   (set (match_dup 0)
+       (xor:DI (match_dup 0)
+               (const_int 1)))]
+  "")
+
 (define_expand "slt"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (lt:SI (match_dup 1)
@@ -3425,14 +7825,14 @@ move\\t%0,%z4\\n\\
   ""
   "
 {
-  if (branch_type != CMP_SI)
+  if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
     FAIL;
 
   /* set up operands from compare.  */
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (!TARGET_DEBUG_C_MODE)
+  if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
     {
       gen_int_relational (LT, operands[0], operands[1], operands[2], (int *)0);
       DONE;
@@ -3445,12 +7845,50 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "=d")
        (lt:SI (match_operand:SI 1 "register_operand" "d")
               (match_operand:SI 2 "arith_operand" "dI")))]
-  ""
+  "!TARGET_MIPS16"
   "slt\\t%0,%1,%2"
- [(set_attr "type"     "arith")
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=t,t")
+       (lt:SI (match_operand:SI 1 "register_operand" "d,d")
+              (match_operand:SI 2 "arith_operand" "d,I")))]
+  "TARGET_MIPS16"
+  "slt\\t%1,%2"
+  [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))])])
+
+(define_insn "slt_di"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (lt:DI (match_operand:DI 1 "se_register_operand" "d")
+              (match_operand:DI 2 "se_arith_operand" "dI")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "slt\\t%0,%1,%2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
    (set_attr "length"  "1")])
 
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=t,t")
+       (lt:DI (match_operand:DI 1 "se_register_operand" "d,d")
+              (match_operand:DI 2 "se_arith_operand" "d,I")))]
+  "TARGET_64BIT && TARGET_MIPS16"
+  "slt\\t%1,%2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))])])
+
 (define_expand "sle"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (le:SI (match_dup 1)
@@ -3458,14 +7896,14 @@ move\\t%0,%z4\\n\\
   ""
   "
 {
-  if (branch_type != CMP_SI)
+  if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
     FAIL;
 
   /* set up operands from compare.  */
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (!TARGET_DEBUG_C_MODE)
+  if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
     {
       gen_int_relational (LE, operands[0], operands[1], operands[2], (int *)0);
       DONE;
@@ -3481,23 +7919,69 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "=d")
        (le:SI (match_operand:SI 1 "register_operand" "d")
               (match_operand:SI 2 "small_int" "I")))]
-  "INTVAL (operands[2]) < 32767"
+  "!TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
   "*
 {
   operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2])+1);
   return \"slt\\t%0,%1,%2\";
 }"
- [(set_attr "type"     "arith")
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=t")
+       (le:SI (match_operand:SI 1 "register_operand" "d")
+              (match_operand:SI 2 "small_int" "I")))]
+  "TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+  "*
+{
+  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2])+1);
+  return \"slt\\t%1,%2\";
+}"
+  [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
+   (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
+
+(define_insn "sle_di_const"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (le:DI (match_operand:DI 1 "se_register_operand" "d")
+              (match_operand:DI 2 "small_int" "I")))]
+  "TARGET_64BIT && !TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+  "*
+{
+  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2])+1);
+  return \"slt\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
    (set_attr "length"  "1")])
 
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=t")
+       (le:DI (match_operand:DI 1 "se_register_operand" "d")
+              (match_operand:DI 2 "small_int" "I")))]
+  "TARGET_64BIT && TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+  "*
+{
+  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2])+1);
+  return \"slt\\t%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
+
 (define_insn "sle_si_reg"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (le:SI (match_operand:SI 1 "register_operand" "d")
               (match_operand:SI 2 "register_operand" "d")))]
-  "TARGET_DEBUG_C_MODE"
+  "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
   "slt\\t%0,%z2,%1\;xori\\t%0,%0,0x0001"
[(set_attr "type"     "arith")
 [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
    (set_attr "length"  "2")])
 
@@ -3505,7 +7989,7 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "")
        (le:SI (match_operand:SI 1 "register_operand" "")
               (match_operand:SI 2 "register_operand" "")))]
-  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE"
+  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16"
   [(set (match_dup 0)
        (lt:SI (match_dup 2)
               (match_dup 1)))
@@ -3514,6 +7998,30 @@ move\\t%0,%z4\\n\\
                (const_int 1)))]
   "")
 
+(define_insn "sle_di_reg"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (le:DI (match_operand:DI 1 "se_register_operand" "d")
+              (match_operand:DI 2 "se_register_operand" "d")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+  "slt\\t%0,%z2,%1\;xori\\t%0,%0,0x0001"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "2")])
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (le:DI (match_operand:DI 1 "se_register_operand" "")
+              (match_operand:DI 2 "se_register_operand" "")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+   && !TARGET_MIPS16"
+  [(set (match_dup 0)
+       (lt:DI (match_dup 2)
+              (match_dup 1)))
+   (set (match_dup 0)
+       (xor:DI (match_dup 0)
+               (const_int 1)))]
+  "")
+
 (define_expand "sgtu"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (gtu:SI (match_dup 1)
@@ -3521,14 +8029,14 @@ move\\t%0,%z4\\n\\
   ""
   "
 {
-  if (branch_type != CMP_SI)
+  if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
     FAIL;
 
   /* set up operands from compare.  */
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (!TARGET_DEBUG_C_MODE)
+  if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
     {
       gen_int_relational (GTU, operands[0], operands[1], operands[2], (int *)0);
       DONE;
@@ -3546,10 +8054,40 @@ move\\t%0,%z4\\n\\
                (match_operand:SI 2 "reg_or_0_operand" "dJ")))]
   ""
   "sltu\\t%0,%z2,%1"
- [(set_attr "type"     "arith")
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=t")
+       (gtu:SI (match_operand:SI 1 "register_operand" "d")
+               (match_operand:SI 2 "register_operand" "d")))]
+  ""
+  "sltu\\t%2,%1"
+  [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
+(define_insn "sgtu_di"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (gtu:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_reg_or_0_operand" "dJ")))]
+  "TARGET_64BIT"
+  "sltu\\t%0,%z2,%1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=t")
+       (gtu:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "TARGET_64BIT"
+  "sltu\\t%2,%1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "1")])
+
 (define_expand "sgeu"
   [(set (match_operand:SI 0 "register_operand" "=d")
         (geu:SI (match_dup 1)
@@ -3557,14 +8095,14 @@ move\\t%0,%z4\\n\\
   ""
   "
 {
-  if (branch_type != CMP_SI)
+  if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
     FAIL;
 
   /* set up operands from compare.  */
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (!TARGET_DEBUG_C_MODE)
+  if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
     {
       gen_int_relational (GEU, operands[0], operands[1], operands[2], (int *)0);
       DONE;
@@ -3577,9 +8115,9 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "=d")
        (geu:SI (match_operand:SI 1 "register_operand" "d")
                (match_operand:SI 2 "arith_operand" "dI")))]
-  "TARGET_DEBUG_C_MODE"
+  "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
   "sltu\\t%0,%1,%2\;xori\\t%0,%0,0x0001"
[(set_attr "type"     "arith")
 [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
    (set_attr "length"  "2")])
 
@@ -3587,7 +8125,7 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "")
        (geu:SI (match_operand:SI 1 "register_operand" "")
                (match_operand:SI 2 "arith_operand" "")))]
-  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE"
+  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16"
   [(set (match_dup 0)
        (ltu:SI (match_dup 1)
                (match_dup 2)))
@@ -3596,6 +8134,30 @@ move\\t%0,%z4\\n\\
                (const_int 1)))]
   "")
 
+(define_insn "sgeu_di"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (geu:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_arith_operand" "dI")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+  "sltu\\t%0,%1,%2\;xori\\t%0,%0,0x0001"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "2")])
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (geu:DI (match_operand:DI 1 "se_register_operand" "")
+               (match_operand:DI 2 "se_arith_operand" "")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+   && !TARGET_MIPS16"
+  [(set (match_dup 0)
+       (ltu:DI (match_dup 1)
+               (match_dup 2)))
+   (set (match_dup 0)
+       (xor:DI (match_dup 0)
+               (const_int 1)))]
+  "")
+
 (define_expand "sltu"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (ltu:SI (match_dup 1)
@@ -3603,14 +8165,14 @@ move\\t%0,%z4\\n\\
   ""
   "
 {
-  if (branch_type != CMP_SI)
+  if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
     FAIL;
 
   /* set up operands from compare.  */
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (!TARGET_DEBUG_C_MODE)
+  if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
     {
       gen_int_relational (LTU, operands[0], operands[1], operands[2], (int *)0);
       DONE;
@@ -3623,12 +8185,50 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "=d")
        (ltu:SI (match_operand:SI 1 "register_operand" "d")
                (match_operand:SI 2 "arith_operand" "dI")))]
-  ""
+  "!TARGET_MIPS16"
   "sltu\\t%0,%1,%2"
- [(set_attr "type"     "arith")
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=t,t")
+       (ltu:SI (match_operand:SI 1 "register_operand" "d,d")
+               (match_operand:SI 2 "arith_operand" "d,I")))]
+  "TARGET_MIPS16"
+  "sltu\\t%1,%2"
+  [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))])])
+
+(define_insn "sltu_di"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (ltu:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_arith_operand" "dI")))]
+  "TARGET_64BIT && !TARGET_MIPS16"
+  "sltu\\t%0,%1,%2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
    (set_attr "length"  "1")])
 
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=t,t")
+       (ltu:DI (match_operand:DI 1 "se_register_operand" "d,d")
+               (match_operand:DI 2 "se_arith_operand" "d,I")))]
+  "TARGET_64BIT && TARGET_MIPS16"
+  "sltu\\t%1,%2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr_alternative "length"
+               [(const_int 1)
+                (if_then_else (match_operand:VOID 2 "m16_uimm8_1" "")
+                              (const_int 1)
+                              (const_int 2))])])
+
 (define_expand "sleu"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (leu:SI (match_dup 1)
@@ -3636,14 +8236,14 @@ move\\t%0,%z4\\n\\
   ""
   "
 {
-  if (branch_type != CMP_SI)
+  if (branch_type != CMP_SI && (!TARGET_64BIT || branch_type != CMP_DI))
     FAIL;
 
   /* set up operands from compare.  */
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (!TARGET_DEBUG_C_MODE)
+  if (TARGET_64BIT || !TARGET_DEBUG_C_MODE || TARGET_MIPS16)
     {
       gen_int_relational (LEU, operands[0], operands[1], operands[2], (int *)0);
       DONE;
@@ -3659,23 +8259,69 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "=d")
        (leu:SI (match_operand:SI 1 "register_operand" "d")
                (match_operand:SI 2 "small_int" "I")))]
-  "INTVAL (operands[2]) < 32767"
+  "!TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
   "*
 {
   operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2])+1);
   return \"sltu\\t%0,%1,%2\";
 }"
- [(set_attr "type"     "arith")
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "1")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=t")
+       (leu:SI (match_operand:SI 1 "register_operand" "d")
+               (match_operand:SI 2 "small_int" "I")))]
+  "TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+  "*
+{
+  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2])+1);
+  return \"sltu\\t%1,%2\";
+}"
+  [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
+   (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
+
+(define_insn "sleu_di_const"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (leu:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "small_int" "I")))]
+  "TARGET_64BIT && !TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+  "*
+{
+  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2])+1);
+  return \"sltu\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
    (set_attr "length"  "1")])
 
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=t")
+       (leu:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "small_int" "I")))]
+  "TARGET_64BIT && TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
+  "*
+{
+  operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2])+1);
+  return \"sltu\\t%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set (attr "length") (if_then_else (match_operand:VOID 2 "m16_uimm8_m1_1" "")
+                                     (const_int 1)
+                                     (const_int 2)))])
+
 (define_insn "sleu_si_reg"
   [(set (match_operand:SI 0 "register_operand" "=d")
        (leu:SI (match_operand:SI 1 "register_operand" "d")
                (match_operand:SI 2 "register_operand" "d")))]
-  "TARGET_DEBUG_C_MODE"
+  "TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
   "sltu\\t%0,%z2,%1\;xori\\t%0,%0,0x0001"
[(set_attr "type"     "arith")
 [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
    (set_attr "length"  "2")])
 
@@ -3683,7 +8329,7 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "")
        (leu:SI (match_operand:SI 1 "register_operand" "")
                (match_operand:SI 2 "register_operand" "")))]
-  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE"
+  "TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE && !TARGET_MIPS16"
   [(set (match_dup 0)
        (ltu:SI (match_dup 2)
                (match_dup 1)))
@@ -3692,6 +8338,30 @@ move\\t%0,%z4\\n\\
                (const_int 1)))]
   "")
 
+(define_insn "sleu_di_reg"
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (leu:DI (match_operand:DI 1 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_MIPS16"
+  "sltu\\t%0,%z2,%1\;xori\\t%0,%0,0x0001"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "2")])
+
+(define_split
+  [(set (match_operand:DI 0 "register_operand" "")
+       (leu:DI (match_operand:DI 1 "se_register_operand" "")
+               (match_operand:DI 2 "se_register_operand" "")))]
+  "TARGET_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+   && !TARGET_MIPS16"
+  [(set (match_dup 0)
+       (ltu:DI (match_dup 2)
+               (match_dup 1)))
+   (set (match_dup 0)
+       (xor:DI (match_dup 0)
+               (const_int 1)))]
+  "")
+
 \f
 ;;
 ;;  ....................
@@ -3701,216 +8371,130 @@ move\\t%0,%z4\\n\\
 ;;  ....................
 
 (define_insn "seq_df"
-  [(set (reg:CC_FP 66)
-       (eq:CC_FP (match_operand:DF 0 "register_operand" "f")
-                 (match_operand:DF 1 "register_operand" "f")))]
-  ""
-  "*
-{
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.eq.d\\t%0,%1\", DELAY_FCMP, xoperands, insn);
-}"
- [(set_attr "type"     "fcmp")
-  (set_attr "mode"     "FPSW")
-  (set_attr "length"   "1")])
-
-(define_insn "sne_df"
-  [(set (reg:CC_REV_FP 66)
-       (ne:CC_REV_FP (match_operand:DF 0 "register_operand" "f")
-                     (match_operand:DF 1 "register_operand" "f")))]
-  ""
+  [(set (match_operand:CC 0 "register_operand" "=z")
+       (eq:CC (match_operand:DF 1 "register_operand" "f")
+              (match_operand:DF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "*
-{
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.eq.d\\t%0,%1\", DELAY_FCMP, xoperands, insn);
+{
+  return mips_fill_delay_slot (\"c.eq.d\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
 }"
  [(set_attr "type"     "fcmp")
   (set_attr "mode"     "FPSW")
   (set_attr "length"   "1")])
 
 (define_insn "slt_df"
-  [(set (reg:CC_FP 66)
-       (lt:CC_FP (match_operand:DF 0 "register_operand" "f")
-                 (match_operand:DF 1 "register_operand" "f")))]
-  ""
+  [(set (match_operand:CC 0 "register_operand" "=z")
+       (lt:CC (match_operand:DF 1 "register_operand" "f")
+              (match_operand:DF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "*
 {
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.lt.d\\t%0,%1\", DELAY_FCMP, xoperands, insn);
+  return mips_fill_delay_slot (\"c.lt.d\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
 }"
  [(set_attr "type"     "fcmp")
   (set_attr "mode"     "FPSW")
   (set_attr "length"   "1")])
 
 (define_insn "sle_df"
-  [(set (reg:CC_FP 66)
-       (le:CC_FP (match_operand:DF 0 "register_operand" "f")
-                 (match_operand:DF 1 "register_operand" "f")))]
-  ""
+  [(set (match_operand:CC 0 "register_operand" "=z")
+       (le:CC (match_operand:DF 1 "register_operand" "f")
+              (match_operand:DF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "*
 {
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.le.d\\t%0,%1\", DELAY_FCMP, xoperands, insn);
+  return mips_fill_delay_slot (\"c.le.d\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
 }"
  [(set_attr "type"     "fcmp")
   (set_attr "mode"     "FPSW")
   (set_attr "length"   "1")])
 
 (define_insn "sgt_df"
-  [(set (reg:CC_FP 66)
-       (gt:CC_FP (match_operand:DF 0 "register_operand" "f")
-                 (match_operand:DF 1 "register_operand" "f")))]
-  ""
+  [(set (match_operand:CC 0 "register_operand" "=z")
+       (gt:CC (match_operand:DF 1 "register_operand" "f")
+              (match_operand:DF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "*
 {
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.lt.d\\t%1,%0\", DELAY_FCMP, xoperands, insn);
+  return mips_fill_delay_slot (\"c.lt.d\\t%Z0%2,%1\", DELAY_FCMP, operands, insn);
 }"
  [(set_attr "type"     "fcmp")
   (set_attr "mode"     "FPSW")
   (set_attr "length"   "1")])
 
 (define_insn "sge_df"
-  [(set (reg:CC_FP 66)
-       (ge:CC_FP (match_operand:DF 0 "register_operand" "f")
-                 (match_operand:DF 1 "register_operand" "f")))]
-  ""
+  [(set (match_operand:CC 0 "register_operand" "=z")
+       (ge:CC (match_operand:DF 1 "register_operand" "f")
+              (match_operand:DF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
   "*
 {
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.le.d\\t%1,%0\", DELAY_FCMP, xoperands, insn);
+  return mips_fill_delay_slot (\"c.le.d\\t%Z0%2,%1\", DELAY_FCMP, operands, insn);
 }"
  [(set_attr "type"     "fcmp")
   (set_attr "mode"     "FPSW")
   (set_attr "length"   "1")])
 
 (define_insn "seq_sf"
-  [(set (reg:CC_FP 66)
-       (eq:CC_FP (match_operand:SF 0 "register_operand" "f")
-                 (match_operand:SF 1 "register_operand" "f")))]
-  ""
-  "*
-{
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.eq.s\\t%0,%1\", DELAY_FCMP, xoperands, insn);
-}"
- [(set_attr "type"     "fcmp")
-  (set_attr "mode"     "FPSW")
-  (set_attr "length"   "1")])
-
-(define_insn "sne_sf"
-  [(set (reg:CC_REV_FP 66)
-       (ne:CC_REV_FP (match_operand:SF 0 "register_operand" "f")
-                     (match_operand:SF 1 "register_operand" "f")))]
-  ""
+  [(set (match_operand:CC 0 "register_operand" "=z")
+       (eq:CC (match_operand:SF 1 "register_operand" "f")
+              (match_operand:SF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
   "*
 {
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.eq.s\\t%0,%1\", DELAY_FCMP, xoperands, insn);
+  return mips_fill_delay_slot (\"c.eq.s\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
 }"
  [(set_attr "type"     "fcmp")
   (set_attr "mode"     "FPSW")
   (set_attr "length"   "1")])
 
 (define_insn "slt_sf"
-  [(set (reg:CC_FP 66)
-       (lt:CC_FP (match_operand:SF 0 "register_operand" "f")
-                 (match_operand:SF 1 "register_operand" "f")))]
-  ""
+  [(set (match_operand:CC 0 "register_operand" "=z")
+       (lt:CC (match_operand:SF 1 "register_operand" "f")
+              (match_operand:SF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
   "*
 {
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.lt.s\\t%0,%1\", DELAY_FCMP, xoperands, insn);
+  return mips_fill_delay_slot (\"c.lt.s\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
 }"
  [(set_attr "type"     "fcmp")
   (set_attr "mode"     "FPSW")
   (set_attr "length"   "1")])
 
 (define_insn "sle_sf"
-  [(set (reg:CC_FP 66)
-       (le:CC_FP (match_operand:SF 0 "register_operand" "f")
-                 (match_operand:SF 1 "register_operand" "f")))]
-  ""
+  [(set (match_operand:CC 0 "register_operand" "=z")
+       (le:CC (match_operand:SF 1 "register_operand" "f")
+              (match_operand:SF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
   "*
 {
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.le.s\\t%0,%1\", DELAY_FCMP, xoperands, insn);
+  return mips_fill_delay_slot (\"c.le.s\\t%Z0%1,%2\", DELAY_FCMP, operands, insn);
 }"
  [(set_attr "type"     "fcmp")
   (set_attr "mode"     "FPSW")
   (set_attr "length"   "1")])
 
 (define_insn "sgt_sf"
-  [(set (reg:CC_FP 66)
-       (gt:CC_FP (match_operand:SF 0 "register_operand" "f")
-                 (match_operand:SF 1 "register_operand" "f")))]
-  ""
+  [(set (match_operand:CC 0 "register_operand" "=z")
+       (gt:CC (match_operand:SF 1 "register_operand" "f")
+              (match_operand:SF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
   "*
 {
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.lt.s\\t%1,%0\", DELAY_FCMP, xoperands, insn);
+  return mips_fill_delay_slot (\"c.lt.s\\t%Z0%2,%1\", DELAY_FCMP, operands, insn);
 }"
  [(set_attr "type"     "fcmp")
   (set_attr "mode"     "FPSW")
   (set_attr "length"   "1")])
 
 (define_insn "sge_sf"
-  [(set (reg:CC_FP 66)
-       (ge:CC_FP (match_operand:SF 0 "register_operand" "f")
-                 (match_operand:SF 1 "register_operand" "f")))]
-  ""
+  [(set (match_operand:CC 0 "register_operand" "=z")
+       (ge:CC (match_operand:SF 1 "register_operand" "f")
+              (match_operand:SF 2 "register_operand" "f")))]
+  "TARGET_HARD_FLOAT"
   "*
 {
-  rtx xoperands[10];
-  xoperands[0] = gen_rtx (REG, CC_FPmode, FPSW_REGNUM);
-  xoperands[1] = operands[0];
-  xoperands[2] = operands[1];
-
-  return mips_fill_delay_slot (\"c.le.s\\t%1,%0\", DELAY_FCMP, xoperands, insn);
+  return mips_fill_delay_slot (\"c.le.s\\t%Z0%2,%1\", DELAY_FCMP, operands, insn);
 }"
  [(set_attr "type"     "fcmp")
   (set_attr "mode"     "FPSW")
@@ -3929,59 +8513,341 @@ move\\t%0,%z4\\n\\
 (define_insn "jump"
   [(set (pc)
        (label_ref (match_operand 0 "" "")))]
-  ""
+  "!TARGET_MIPS16"
   "*
 {
   if (GET_CODE (operands[0]) == REG)
     return \"%*j\\t%0\";
-  else
+  /* ??? I don't know why this is necessary.  This works around an
+     assembler problem that appears when a label is defined, then referenced
+     in a switch table, then used in a `j' instruction.  */
+  else if (mips_abi != ABI_32)
+    return \"%*b\\t%l0\";
+  else 
     return \"%*j\\t%l0\";
 }"
   [(set_attr "type"    "jump")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
-(define_insn "indirect_jump"
-  [(set (pc) (match_operand:SI 0 "register_operand" "d"))]
+;; We need a different insn for the mips16, because a mips16 branch
+;; does not have a delay slot.
+
+(define_insn ""
+  [(set (pc)
+       (label_ref (match_operand 0 "" "")))]
+  "TARGET_MIPS16 && GET_CODE (operands[0]) != REG"
+  "b\\t%l0"
+  [(set_attr "type"    "branch")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
+(define_expand "indirect_jump"
+  [(set (pc) (match_operand 0 "register_operand" "d"))]
   ""
+  "
+{
+  rtx dest;
+
+  if (operands[0])             /* eliminate unused code warnings */
+    {
+      dest = operands[0];
+      if (GET_CODE (dest) != REG || GET_MODE (dest) != Pmode)
+       operands[0] = copy_to_mode_reg (Pmode, dest);
+
+      if (!TARGET_LONG64)
+       emit_jump_insn (gen_indirect_jump_internal1 (operands[0]));
+      else
+       emit_jump_insn (gen_indirect_jump_internal2 (operands[0]));
+
+      DONE;
+    }
+}")
+
+(define_insn "indirect_jump_internal1"
+  [(set (pc) (match_operand:SI 0 "register_operand" "d"))]
+  "!TARGET_LONG64"
+  "%*j\\t%0"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "1")])
+
+(define_insn "indirect_jump_internal2"
+  [(set (pc) (match_operand:DI 0 "se_register_operand" "d"))]
+  "TARGET_LONG64"
   "%*j\\t%0"
   [(set_attr "type"    "jump")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
-(define_insn "tablejump"
+(define_expand "tablejump"
   [(set (pc)
-       (match_operand:SI 0 "register_operand" "d"))
+       (match_operand 0 "register_operand" "d"))
    (use (label_ref (match_operand 1 "" "")))]
   ""
+  "
+{
+  rtx dest;
+
+  if (operands[0])             /* eliminate unused code warnings */
+    {
+      if (TARGET_MIPS16)
+       {
+         if (GET_MODE (operands[0]) != HImode)
+           abort ();
+         if (!TARGET_LONG64)
+           emit_jump_insn (gen_tablejump_mips161 (operands[0], operands[1]));
+         else
+           emit_jump_insn (gen_tablejump_mips162 (operands[0], operands[1]));
+         DONE;
+       }
+
+      if (GET_MODE (operands[0]) != Pmode)
+       abort ();
+
+      if (! flag_pic)
+       {
+         if (!TARGET_LONG64)
+           emit_jump_insn (gen_tablejump_internal1 (operands[0], operands[1]));
+         else
+           emit_jump_insn (gen_tablejump_internal2 (operands[0], operands[1]));
+       }
+      else
+       {
+         if (!TARGET_LONG64)
+           emit_jump_insn (gen_tablejump_internal3 (operands[0], operands[1]));
+         else
+           emit_jump_insn (gen_tablejump_internal4 (operands[0], operands[1]));
+       }
+
+      DONE;
+    }
+}")
+
+(define_insn "tablejump_internal1"
+  [(set (pc)
+       (match_operand:SI 0 "register_operand" "d"))
+   (use (label_ref (match_operand 1 "" "")))]
+  "!TARGET_LONG64"
   "%*j\\t%0"
   [(set_attr "type"    "jump")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
-;; Function return, only allow after optimization, so that we can
-;; eliminate jumps to jumps if no stack space is used.
-
-;; (define_expand "return"
-;;   [(set (pc) (reg:SI 31))]
-;;   "simple_epilogue_p ()"
-;;   "")
+(define_insn "tablejump_internal2"
+  [(set (pc)
+       (match_operand:DI 0 "se_register_operand" "d"))
+   (use (label_ref (match_operand 1 "" "")))]
+  "TARGET_LONG64"
+  "%*j\\t%0"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "1")])
 
-(define_expand "return"
-  [(parallel [(return)
-             (use (reg:SI 31))])]
-  "simple_epilogue_p ()"
+(define_expand "tablejump_internal3"
+  [(set (pc)
+       (plus:SI (match_operand:SI 0 "register_operand" "d")
+                (label_ref:SI (match_operand:SI 1 "" ""))))]
+  ""
   "")
 
-(define_insn "return_internal"
-  [(parallel [(return)
-              (use (match_operand:SI 0 "register_operand" "d"))])]
+(define_expand "tablejump_mips161"
+  [(set (pc) (plus:SI (sign_extend:SI
+                      (match_operand:HI 0 "register_operand" "d"))
+                     (label_ref:SI (match_operand:SI 1 "" ""))))]
+  "TARGET_MIPS16 && !TARGET_LONG64"
+  "
+{
+  if (operands[0])     /* eliminate unused code warnings.  */
+    {
+      rtx t1, t2, t3;
+
+      t1 = gen_reg_rtx (SImode);
+      t2 = gen_reg_rtx (SImode);
+      t3 = gen_reg_rtx (SImode);
+      emit_insn (gen_extendhisi2 (t1, operands[0]));
+      emit_move_insn (t2, gen_rtx (LABEL_REF, SImode, operands[1]));
+      emit_insn (gen_addsi3 (t3, t1, t2));
+      emit_insn (gen_tablejump_internal1 (t3, operands[1]));
+      DONE;
+    }
+}")
+
+(define_expand "tablejump_mips162"
+  [(set (pc) (plus:DI (sign_extend:DI
+                      (match_operand:HI 0 "register_operand" "d"))
+                     (label_ref:DI (match_operand:SI 1 "" ""))))]
+  "TARGET_MIPS16 && TARGET_LONG64"
+  "
+{
+  if (operands[0])     /* eliminate unused code warnings.  */
+    {
+      rtx t1, t2, t3;
+
+      t1 = gen_reg_rtx (DImode);
+      t2 = gen_reg_rtx (DImode);
+      t3 = gen_reg_rtx (DImode);
+      emit_insn (gen_extendhidi2 (t1, operands[0]));
+      emit_move_insn (t2, gen_rtx (LABEL_REF, DImode, operands[1]));
+      emit_insn (gen_adddi3 (t3, t1, t2));
+      emit_insn (gen_tablejump_internal2 (t3, operands[1]));
+      DONE;
+    }
+}")
+
+;;; Make sure that this only matches the insn before ADDR_DIFF_VEC.  Otherwise
+;;; it is not valid.
+
+;;; ??? The length depends on the ABI.  It is two for o32, and one for n32.
+;;; We just use the conservative number here.
+
+(define_insn ""
+  [(set (pc)
+       (plus:SI (match_operand:SI 0 "register_operand" "d")
+                (label_ref:SI (match_operand:SI 1 "" ""))))]
+  "!TARGET_LONG64 && next_active_insn (insn) != 0
+   && GET_CODE (PATTERN (next_active_insn (insn))) == ADDR_DIFF_VEC
+   && PREV_INSN (next_active_insn (insn)) == operands[1]"
+  "*
+{
+  /* .cpadd expands to add REG,REG,$gp when pic, and nothing when not pic.  */
+  if (mips_abi == ABI_32)
+    output_asm_insn (\".cpadd\\t%0\", operands);
+  return \"%*j\\t%0\";
+}"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
+(define_expand "tablejump_internal4"
+  [(set (pc)
+       (plus:DI (match_operand:DI 0 "se_register_operand" "d")
+                (label_ref:DI (match_operand:SI 1 "" ""))))]
   ""
+  "")
+
+;;; Make sure that this only matches the insn before ADDR_DIFF_VEC.  Otherwise
+;;; it is not valid.
+
+(define_insn ""
+  [(set (pc)
+       (plus:DI (match_operand:DI 0 "se_register_operand" "d")
+                (label_ref:DI (match_operand:SI 1 "" ""))))]
+  "TARGET_LONG64 && next_active_insn (insn) != 0
+   && GET_CODE (PATTERN (next_active_insn (insn))) == ADDR_DIFF_VEC
+   && PREV_INSN (next_active_insn (insn)) == operands[1]"
   "%*j\\t%0"
   [(set_attr "type"    "jump")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
+;; Implement a switch statement when generating embedded PIC code.
+;; Switches are implemented by `tablejump' when not using -membedded-pic.
+
+(define_expand "casesi"
+  [(set (match_dup 5)
+       (minus:SI (match_operand:SI 0 "register_operand" "d")
+                 (match_operand:SI 1 "arith_operand" "dI")))
+   (set (cc0)
+       (compare:CC (match_dup 5)
+                   (match_operand:SI 2 "arith_operand" "")))
+   (set (pc)
+       (if_then_else (gtu (cc0)
+                          (const_int 0))
+                     (label_ref (match_operand 4 "" ""))
+                     (pc)))
+   (parallel
+    [(set (pc)
+         (mem:SI (plus:SI (mult:SI (match_dup 5)
+                                   (const_int 4))
+                          (label_ref (match_operand 3 "" "")))))
+     (clobber (match_scratch:SI 6 ""))
+     (clobber (reg:SI 31))])]
+  "TARGET_EMBEDDED_PIC"
+  "
+{
+  /* We need slightly different code for eight byte table entries.  */
+  if (TARGET_LONG64)
+    abort ();
+
+  if (operands[0])
+    {
+      rtx reg = gen_reg_rtx (SImode);
+
+      /* If the index is too large, go to the default label.  */
+      emit_insn (gen_subsi3 (reg, operands[0], operands[1]));
+      emit_insn (gen_cmpsi (reg, operands[2]));
+      emit_insn (gen_bgtu (operands[4]));
+
+      /* Do the PIC jump.  */
+      emit_insn (gen_casesi_internal (reg, operands[3], gen_reg_rtx (SImode)));
+
+      DONE;
+    }
+}")
+
+;; An embedded PIC switch statement looks like this:
+;;     bal     $LS1
+;;     sll     $reg,$index,2
+;; $LS1:
+;;     addu    $reg,$reg,$31
+;;     lw      $reg,$L1-$LS1($reg)
+;;     addu    $reg,$reg,$31
+;;     j       $reg
+;; $L1:
+;;     .word   case1-$LS1
+;;     .word   case2-$LS1
+;;     ...
+
+(define_insn "casesi_internal"
+  [(set (pc)
+       (mem:SI (plus:SI (mult:SI (match_operand:SI 0 "register_operand" "d")
+                                 (const_int 4))
+                        (label_ref (match_operand 1 "" "")))))
+   (clobber (match_operand:SI 2 "register_operand" "d"))
+   (clobber (reg:SI 31))]
+  "TARGET_EMBEDDED_PIC"
+  "*
+{
+  output_asm_insn (\"%(bal\\t%S1\;sll\\t%0,2\\n%S1:\", operands);
+  output_asm_insn (\"addu\\t%0,%0,$31%)\", operands);
+  output_asm_insn (\"lw\\t%0,%1-%S1(%0)\;addu\\t%0,%0,$31\", operands);
+  return \"j\\t%0\";
+}"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "6")])
+
+;; ??? This is a hack to work around a problem with expand_builtin_setjmp.
+;; It restores the frame pointer, and then does a call to restore the global
+;; pointer (gp) register.  The call insn implicitly (via the assembler) reloads
+;; gp from the stack.  However, call insns do not depend on $fp, so it is
+;; possible for the instruction scheduler to move the fp restore after the
+;; call, which then causes gp to be corrupted.  We fix this by emitting a
+;; scheduler barrier.  A better fix is to put code here that restores the
+;; $gp, and then the call is unnecessary.  This is only a problem when PIC
+;; (TARGET_ABICALLS), and only when the gp register is caller-saved
+;; (irix5/o32, but not irix6/n32/n64).
+
+(define_expand "nonlocal_goto_receiver"
+  [(const_int 0)]
+  ""
+  "
+{
+  emit_insn (gen_blockage ());
+}")
+
+;; For n32/n64, we need to restore gp after a builtin setjmp.   We do this
+;; by making use of the fact that we've just called __dummy.
+
+(define_expand "builtin_setjmp_receiver"
+  [(const_int 0)]
+  "TARGET_ABICALLS && mips_abi != ABI_32"
+  "
+{
+  emit_insn (gen_loadgp (gen_rtx (SYMBOL_REF, Pmode, \"__dummy\")));
+  emit_insn (gen_blockage ());
+}")
 \f
 ;;
 ;;  ....................
@@ -4015,20 +8881,56 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "none")
    (set_attr "length"  "0")])
 
-;; At present, don't expand the epilogue, reorg.c will clobber the
-;; return register in compiling gen_lowpart (emit-rtl.c).
-;; 
-;; (define_expand "epilogue"
-;;   [(const_int 2)]
-;;   ""
-;;   "
-;; {
-;;   if (mips_isa >= 0)            /* avoid unused code warnings */
-;;     {
-;;       mips_expand_epilogue ();
-;;       DONE;
-;;     }
-;; }")
+(define_expand "epilogue"
+  [(const_int 2)]
+  ""
+  "
+{
+  if (mips_isa >= 0)            /* avoid unused code warnings */
+    {
+      mips_expand_epilogue ();
+      DONE;
+    }
+}")
+
+;; Trivial return.  Make it look like a normal return insn as that
+;; allows jump optimizations to work better .
+(define_insn "return"
+  [(return)]
+  "mips_can_use_return_insn ()"
+  "%*j\\t$31"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "1")])
+
+;; Normal return.
+;; We match any mode for the return address, so that this will work with
+;; both 32 bit and 64 bit targets.
+(define_insn "return_internal"
+  [(use (match_operand 0 "register_operand" ""))
+   (return)]
+  ""
+  "*
+{
+  return \"%*j\\t%0\";
+}"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "1")])
+  
+;; When generating embedded PIC code we need to get the address of the
+;; current function.  This specialized instruction does just that.
+
+(define_insn "get_fnaddr"
+  [(set (match_operand 0 "register_operand" "=d")
+       (unspec [(match_operand 1 "" "")] 1))
+   (clobber (reg:SI 31))]
+  "TARGET_EMBEDDED_PIC
+   && GET_CODE (operands[1]) == SYMBOL_REF"
+  "%($LF%= = . + 8\;bal\\t$LF%=\;la\\t%0,%1-$LF%=%)\;addu\\t%0,%0,$31"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "4")])
 
 \f
 ;;
@@ -4054,8 +8956,9 @@ move\\t%0,%z4\\n\\
   if (operands[0])             /* eliminate unused code warnings */
     {
       addr = XEXP (operands[0], 0);
-      if (GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr) || TARGET_LONG_CALLS))
-       XEXP (operands[0], 0) = force_reg (FUNCTION_MODE, addr);
+      if ((GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr) || TARGET_LONG_CALLS))
+         || ! call_insn_operand (addr, VOIDmode))
+       XEXP (operands[0], 0) = copy_to_mode_reg (Pmode, addr);
 
       /* In order to pass small structures by value in registers
         compatibly with the MIPS compiler, we need to shift the value
@@ -4074,50 +8977,142 @@ move\\t%0,%z4\\n\\
            emit_insn (RTVEC_ELT (adjust, i));
        }
 
-      emit_call_insn (gen_call_internal1 (operands[0], operands[1],
-                                         gen_rtx (REG, Pmode, GP_REG_FIRST + 31)));
+      if (TARGET_MIPS16
+         && mips16_hard_float
+         && operands[2] != 0
+         && (int) GET_MODE (operands[2]) != 0)
+       {
+         if (build_mips16_call_stub (NULL_RTX, operands[0], operands[1],
+                                     (int) GET_MODE (operands[2])))
+           DONE;
+       }
+
+      emit_call_insn (gen_call_internal0 (operands[0], operands[1],
+                                         gen_rtx (REG, SImode, GP_REG_FIRST + 31)));
+
       DONE;
     }
 }")
 
+(define_expand "call_internal0"
+  [(parallel [(call (match_operand 0 "" "")
+                   (match_operand 1 "" ""))
+             (clobber (match_operand:SI 2 "" ""))])]
+  ""
+  "")
+
+;; We need to recognize reg:SI 31 specially for the mips16, because we
+;; don't have a constraint letter for it.
+
+(define_insn ""
+  [(call (mem (match_operand 0 "call_insn_operand" "ei"))
+        (match_operand 1 "" "i"))
+   (clobber (match_operand:SI 2 "register_operand" "=y"))]
+  "TARGET_MIPS16 && !TARGET_ABICALLS && !TARGET_LONG_CALLS
+   && GET_CODE (operands[2]) == REG && REGNO (operands[2]) == 31"
+  "%*jal\\t%0"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
 (define_insn "call_internal1"
-  [(call (match_operand 0 "memory_operand" "m")
+  [(call (mem (match_operand 0 "call_insn_operand" "ri"))
         (match_operand 1 "" "i"))
    (clobber (match_operand:SI 2 "register_operand" "=d"))]
-  "!TARGET_LONG_CALLS"
+  "!TARGET_ABICALLS && !TARGET_LONG_CALLS"
   "*
 {
-  register rtx target = XEXP (operands[0], 0);
+  register rtx target = operands[0];
 
   if (GET_CODE (target) == SYMBOL_REF)
     return \"%*jal\\t%0\";
-
   else if (GET_CODE (target) == CONST_INT)
-    {
-      operands[0] = target;
-      return \"%*%[li\\t%@,%0\\n\\tjal\\t%2,%@%]\";
-    }
-
+    return \"%[li\\t%@,%0\\n\\t%*jal\\t%2,%@%]\";
   else
-    {
-      operands[0] = target;
-      return \"%*jal\\t%2,%0\";
-    }
+    return \"%*jal\\t%2,%0\";
 }"
   [(set_attr "type"    "call")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
 (define_insn "call_internal2"
+  [(call (mem (match_operand 0 "call_insn_operand" "ri"))
+        (match_operand 1 "" "i"))
+   (clobber (match_operand:SI 2 "register_operand" "=d"))]
+  "TARGET_ABICALLS && !TARGET_LONG_CALLS"
+  "*
+{
+  register rtx target = operands[0];
+
+  if (GET_CODE (target) == SYMBOL_REF)
+    {
+      if (GET_MODE (target) == SImode)
+       return \"la\\t%^,%0\\n\\tjal\\t%2,%^\";
+      else
+       return \"dla\\t%^,%0\\n\\tjal\\t%2,%^\";
+    }
+  else if (GET_CODE (target) == CONST_INT)
+    return \"li\\t%^,%0\\n\\tjal\\t%2,%^\";
+  else if (REGNO (target) != PIC_FUNCTION_ADDR_REGNUM)
+    return \"move\\t%^,%0\\n\\tjal\\t%2,%^\";
+  else
+    return \"jal\\t%2,%0\";
+}"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
+(define_insn "call_internal3a"
   [(call (mem:SI (match_operand:SI 0 "register_operand" "r"))
         (match_operand 1 "" "i"))
    (clobber (match_operand:SI 2 "register_operand" "=d"))]
-  "TARGET_LONG_CALLS"
+  "!TARGET_LONG64 && !TARGET_ABICALLS && TARGET_LONG_CALLS"
   "%*jal\\t%2,%0"
   [(set_attr "type"    "call")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
+(define_insn "call_internal3b"
+  [(call (mem:DI (match_operand:DI 0 "se_register_operand" "r"))
+        (match_operand 1 "" "i"))
+   (clobber (match_operand:SI 2 "register_operand" "=d"))]
+  "TARGET_LONG64 && !TARGET_ABICALLS && TARGET_LONG_CALLS"
+  "%*jal\\t%2,%0"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "1")])
+
+(define_insn "call_internal4a"
+  [(call (mem:SI (match_operand:SI 0 "register_operand" "r"))
+        (match_operand 1 "" "i"))
+   (clobber (match_operand:SI 2 "register_operand" "=d"))]
+  "!TARGET_LONG64 && TARGET_ABICALLS && TARGET_LONG_CALLS"
+  "*
+{
+  if (REGNO (operands[0]) != PIC_FUNCTION_ADDR_REGNUM)
+    return \"move\\t%^,%0\\n\\tjal\\t%2,%^\";
+  else
+    return \"jal\\t%2,%0\";
+}"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
+(define_insn "call_internal4b"
+  [(call (mem:DI (match_operand:DI 0 "se_register_operand" "r"))
+        (match_operand 1 "" "i"))
+   (clobber (match_operand:SI 2 "register_operand" "=d"))]
+  "TARGET_LONG64 && TARGET_ABICALLS && TARGET_LONG_CALLS"
+  "*
+{
+  if (REGNO (operands[0]) != PIC_FUNCTION_ADDR_REGNUM)
+    return \"move\\t%^,%0\\n\\tjal\\t%2,%^\";
+  else
+    return \"jal\\t%2,%0\";
+}"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
 
 ;; calls.c now passes a fourth argument, make saber happy
 
@@ -4135,8 +9130,9 @@ move\\t%0,%z4\\n\\
   if (operands[0])             /* eliminate unused code warning */
     {
       addr = XEXP (operands[1], 0);
-      if (GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr) || TARGET_LONG_CALLS))
-       XEXP (operands[1], 0) = force_reg (FUNCTION_MODE, addr);
+      if ((GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr) || TARGET_LONG_CALLS))
+         || ! call_insn_operand (addr, VOIDmode))
+       XEXP (operands[1], 0) = copy_to_mode_reg (Pmode, addr);
 
       /* In order to pass small structures by value in registers
         compatibly with the MIPS compiler, we need to shift the value
@@ -4151,42 +9147,85 @@ move\\t%0,%z4\\n\\
          int num = GET_NUM_ELEM (adjust);
          int i;
 
-         for (i = 0; i < num; i++)
-           emit_insn (RTVEC_ELT (adjust, i));
+         for (i = 0; i < num; i++)
+           emit_insn (RTVEC_ELT (adjust, i));
+       }
+
+      if (TARGET_MIPS16
+         && mips16_hard_float
+         && ((operands[3] != 0
+              && (int) GET_MODE (operands[3]) != 0)
+             || GET_MODE_CLASS (GET_MODE (operands[0])) == MODE_FLOAT))
+       {
+         if (build_mips16_call_stub (operands[0], operands[1], operands[2],
+                                     (operands[3] == 0 ? 0
+                                      : (int) GET_MODE (operands[3]))))
+           DONE;
+       }
+
+      /* Handle Irix6 function calls that have multiple non-contiguous
+        results.  */
+      if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) > 1)
+       {
+         emit_call_insn (gen_call_value_multiple_internal0
+                         (XEXP (XVECEXP (operands[0], 0, 0), 0),
+                          operands[1], operands[2],
+                          XEXP (XVECEXP (operands[0], 0, 1), 0),
+                          gen_rtx (REG, SImode, GP_REG_FIRST + 31)));
+         DONE;
        }
 
-      emit_call_insn (gen_call_value_internal1 (operands[0], operands[1], operands[2],
-                                               gen_rtx (REG, Pmode, GP_REG_FIRST + 31)));
+      /* We have a call returning a DImode structure in an FP reg.
+        Strip off the now unnecessary PARALLEL.  */
+      if (GET_CODE (operands[0]) == PARALLEL)
+       operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
+
+      emit_call_insn (gen_call_value_internal0 (operands[0], operands[1], operands[2],
+                                               gen_rtx (REG, SImode, GP_REG_FIRST + 31)));
 
       DONE;
     }
-
 }")
 
+(define_expand "call_value_internal0"
+  [(parallel [(set (match_operand 0 "" "")
+                  (call (match_operand 1 "" "")
+                        (match_operand 2 "" "")))
+             (clobber (match_operand:SI 3 "" ""))])]
+  ""
+  "")
+
+;; Recognize $31 specially on the mips16, because we don't have a
+;; constraint letter for it.
+
+(define_insn ""
+  [(set (match_operand 0 "register_operand" "=d")
+        (call (mem (match_operand 1 "call_insn_operand" "ei"))
+              (match_operand 2 "" "i")))
+   (clobber (match_operand:SI 3 "register_operand" "=y"))]
+  "TARGET_MIPS16 && !TARGET_ABICALLS && !TARGET_LONG_CALLS
+   && GET_CODE (operands[3]) == REG && REGNO (operands[3]) == 31"
+  "%*jal\\t%1"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
 (define_insn "call_value_internal1"
   [(set (match_operand 0 "register_operand" "=df")
-        (call (match_operand 1 "memory_operand" "m")
+        (call (mem (match_operand 1 "call_insn_operand" "ri"))
               (match_operand 2 "" "i")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "!TARGET_LONG_CALLS"
+  "!TARGET_ABICALLS && !TARGET_LONG_CALLS"
   "*
 {
-  register rtx target = XEXP (operands[1], 0);
+  register rtx target = operands[1];
 
   if (GET_CODE (target) == SYMBOL_REF)
     return \"%*jal\\t%1\";
-
   else if (GET_CODE (target) == CONST_INT)
-    {
-      operands[1] = target;
-      return \"%*%[li\\t%@,%1\\n\\tjal\\t%3,%@%]\";
-    }
-
+    return \"%[li\\t%@,%1\\n\\t%*jal\\t%3,%@%]\";
   else
-    {
-      operands[1] = target;
-      return \"%*jal\\t%3,%1\";
-    }
+    return \"%*jal\\t%3,%1\";
 }"
   [(set_attr "type"    "call")
    (set_attr "mode"    "none")
@@ -4194,15 +9233,160 @@ move\\t%0,%z4\\n\\
 
 (define_insn "call_value_internal2"
   [(set (match_operand 0 "register_operand" "=df")
+        (call (mem (match_operand 1 "call_insn_operand" "ri"))
+              (match_operand 2 "" "i")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "TARGET_ABICALLS && !TARGET_LONG_CALLS"
+  "*
+{
+  register rtx target = operands[1];
+
+  if (GET_CODE (target) == SYMBOL_REF)
+    {
+      if (GET_MODE (target) == SImode)
+       return \"la\\t%^,%1\\n\\tjal\\t%3,%^\";
+      else
+       return \"dla\\t%^,%1\\n\\tjal\\t%3,%^\";
+    }
+  else if (GET_CODE (target) == CONST_INT)
+    return \"li\\t%^,%1\\n\\tjal\\t%3,%^\";
+  else if (REGNO (target) != PIC_FUNCTION_ADDR_REGNUM)
+    return \"move\\t%^,%1\\n\\tjal\\t%3,%^\";
+  else
+    return \"jal\\t%3,%1\";
+}"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
+(define_insn "call_value_internal3a"
+  [(set (match_operand 0 "register_operand" "=df")
         (call (mem:SI (match_operand:SI 1 "register_operand" "r"))
              (match_operand 2 "" "i")))
    (clobber (match_operand:SI 3 "register_operand" "=d"))]
-  "TARGET_LONG_CALLS"
+  "!TARGET_LONG64 && !TARGET_ABICALLS && TARGET_LONG_CALLS"
+  "%*jal\\t%3,%1"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "1")])
+
+(define_insn "call_value_internal3b"
+  [(set (match_operand 0 "register_operand" "=df")
+        (call (mem:DI (match_operand:DI 1 "se_register_operand" "r"))
+             (match_operand 2 "" "i")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "TARGET_LONG64 && !TARGET_ABICALLS && TARGET_LONG_CALLS"
   "%*jal\\t%3,%1"
   [(set_attr "type"    "call")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
+(define_insn "call_value_internal4a"
+  [(set (match_operand 0 "register_operand" "=df")
+        (call (mem:SI (match_operand:SI 1 "register_operand" "r"))
+             (match_operand 2 "" "i")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "!TARGET_LONG64 && TARGET_ABICALLS && TARGET_LONG_CALLS"
+  "*
+{
+  if (REGNO (operands[1]) != PIC_FUNCTION_ADDR_REGNUM)
+    return \"move\\t%^,%1\\n\\tjal\\t%3,%^\";
+  else
+    return \"jal\\t%3,%1\";
+}"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
+(define_insn "call_value_internal4b"
+  [(set (match_operand 0 "register_operand" "=df")
+        (call (mem:DI (match_operand:DI 1 "se_register_operand" "r"))
+             (match_operand 2 "" "i")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  "TARGET_LONG64 && TARGET_ABICALLS && TARGET_LONG_CALLS"
+  "*
+{
+  if (REGNO (operands[1]) != PIC_FUNCTION_ADDR_REGNUM)
+    return \"move\\t%^,%1\\n\\tjal\\t%3,%^\";
+  else
+    return \"jal\\t%3,%1\";
+}"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
+(define_expand "call_value_multiple_internal0"
+  [(parallel [(set (match_operand 0 "" "")
+                  (call (match_operand 1 "" "")
+                        (match_operand 2 "" "")))
+             (set (match_operand 3 "" "")
+                  (call (match_dup 1)
+                        (match_dup 2)))
+             (clobber (match_operand:SI 4 "" ""))])]
+  ""
+  "")
+
+;; ??? May eventually need all 6 versions of the call patterns with multiple
+;; return values.
+
+(define_insn "call_value_multiple_internal2"
+  [(set (match_operand 0 "register_operand" "=df")
+        (call (mem (match_operand 1 "call_insn_operand" "ri"))
+              (match_operand 2 "" "i")))
+   (set (match_operand 3 "register_operand" "=df")
+        (call (mem (match_dup 1))
+              (match_dup 2)))
+   (clobber (match_operand:SI 4 "register_operand" "=d"))]
+  "TARGET_ABICALLS && !TARGET_LONG_CALLS"
+  "*
+{
+  register rtx target = operands[1];
+
+  if (GET_CODE (target) == SYMBOL_REF)
+    {
+      if (GET_MODE (target) == SImode)
+       return \"la\\t%^,%1\\n\\tjal\\t%4,%^\";
+      else
+       return \"la\\t%^,%1\\n\\tjal\\t%4,%^\";
+    }
+  else if (GET_CODE (target) == CONST_INT)
+    return \"li\\t%^,%1\\n\\tjal\\t%4,%^\";
+  else if (REGNO (target) != PIC_FUNCTION_ADDR_REGNUM)
+    return \"move\\t%^,%1\\n\\tjal\\t%4,%^\";
+  else
+    return \"jal\\t%4,%1\";
+}"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
+
+;; Call subroutine returning any type.
+
+(define_expand "untyped_call"
+  [(parallel [(call (match_operand 0 "" "")
+                   (const_int 0))
+             (match_operand 1 "" "")
+             (match_operand 2 "" "")])]
+  ""
+  "
+{
+  if (operands[0])             /* silence statement not reached warnings */
+    {
+      int i;
+
+      emit_call_insn (gen_call (operands[0], const0_rtx, NULL, const0_rtx));
+
+      for (i = 0; i < XVECLEN (operands[2], 0); i++)
+       {
+         rtx set = XVECEXP (operands[2], 0, i);
+         emit_move_insn (SET_DEST (set), SET_SRC (set));
+       }
+
+      emit_insn (gen_blockage ());
+      DONE;
+    }
+}")
 \f
 ;;
 ;;  ....................
@@ -4220,15 +9404,462 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
-(define_expand "probe"
-  [(set (match_dup 0)
-       (match_dup 1))]
-  ""
+;; The MIPS chip does not seem to require stack probes.
+;;
+;; (define_expand "probe"
+;;   [(set (match_dup 0)
+;;     (match_dup 1))]
+;;   ""
+;;   "
+;; {
+;;   operands[0] = gen_reg_rtx (SImode);
+;;   operands[1] = gen_rtx (MEM, SImode, stack_pointer_rtx);
+;;   MEM_VOLATILE_P (operands[1]) = TRUE;
+;; 
+;;   /* fall through and generate default code */
+;; }")
+;;
+\f
+;;
+;; MIPS4 Conditional move instructions.
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (if_then_else:SI
+        (match_operator 4 "equality_op"
+                        [(match_operand:SI 1 "register_operand" "d,d")
+                         (const_int 0)])
+        (match_operand:SI 2 "reg_or_0_operand" "dJ,0")
+        (match_operand:SI 3 "reg_or_0_operand" "0,dJ")))]
+  "mips_isa >= 4"
+  "@
+    mov%B4\\t%0,%z2,%1
+    mov%b4\\t%0,%z3,%1"
+  [(set_attr "type" "move")
+   (set_attr "mode" "SI")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (if_then_else:SI
+        (match_operator 4 "equality_op"
+                        [(match_operand:DI 1 "se_register_operand" "d,d")
+                         (const_int 0)])
+        (match_operand:SI 2 "reg_or_0_operand" "dJ,0")
+        (match_operand:SI 3 "reg_or_0_operand" "0,dJ")))]
+  "mips_isa >= 4"
+  "@
+    mov%B4\\t%0,%z2,%1
+    mov%b4\\t%0,%z3,%1"
+  [(set_attr "type" "move")
+   (set_attr "mode" "SI")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (if_then_else:SI
+        (match_operator 3 "equality_op" [(match_operand:CC 4
+                                                           "register_operand"
+                                                           "z,z")
+                                         (const_int 0)])
+        (match_operand:SI 1 "reg_or_0_operand" "dJ,0")
+        (match_operand:SI 2 "reg_or_0_operand" "0,dJ")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "@
+    mov%T3\\t%0,%z1,%4
+    mov%t3\\t%0,%z2,%4"
+  [(set_attr "type" "move")
+   (set_attr "mode" "SI")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (if_then_else:DI
+        (match_operator 4 "equality_op"
+                        [(match_operand:SI 1 "register_operand" "d,d")
+                         (const_int 0)])
+        (match_operand:DI 2 "se_reg_or_0_operand" "dJ,0")
+        (match_operand:DI 3 "se_reg_or_0_operand" "0,dJ")))]
+  "mips_isa >= 4"
+  "@
+    mov%B4\\t%0,%z2,%1
+    mov%b4\\t%0,%z3,%1"
+  [(set_attr "type" "move")
+   (set_attr "mode" "DI")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (if_then_else:DI
+        (match_operator 4 "equality_op"
+                        [(match_operand:DI 1 "se_register_operand" "d,d")
+                         (const_int 0)])
+        (match_operand:DI 2 "se_reg_or_0_operand" "dJ,0")
+        (match_operand:DI 3 "se_reg_or_0_operand" "0,dJ")))]
+  "mips_isa >= 4"
+  "@
+    mov%B4\\t%0,%z2,%1
+    mov%b4\\t%0,%z3,%1"
+  [(set_attr "type" "move")
+   (set_attr "mode" "DI")])
+
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d,d")
+       (if_then_else:DI
+        (match_operator 3 "equality_op" [(match_operand:CC 4
+                                                           "register_operand"
+                                                           "z,z")
+                                         (const_int 0)])
+        (match_operand:DI 1 "se_reg_or_0_operand" "dJ,0")
+        (match_operand:DI 2 "se_reg_or_0_operand" "0,dJ")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "@
+    mov%T3\\t%0,%z1,%4
+    mov%t3\\t%0,%z2,%4"
+  [(set_attr "type" "move")
+   (set_attr "mode" "DI")])
+
+(define_insn ""
+  [(set (match_operand:SF 0 "register_operand" "=f,f")
+       (if_then_else:SF
+        (match_operator 4 "equality_op"
+                        [(match_operand:SI 1 "register_operand" "d,d")
+                         (const_int 0)])
+        (match_operand:SF 2 "register_operand" "f,0")
+        (match_operand:SF 3 "register_operand" "0,f")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "@
+    mov%B4.s\\t%0,%2,%1
+    mov%b4.s\\t%0,%3,%1"
+  [(set_attr "type" "move")
+   (set_attr "mode" "SF")])
+
+(define_insn ""
+  [(set (match_operand:SF 0 "register_operand" "=f,f")
+       (if_then_else:SF
+        (match_operator 3 "equality_op" [(match_operand:CC 4
+                                                           "register_operand"
+                                                           "z,z")
+                                         (const_int 0)])
+        (match_operand:SF 1 "register_operand" "f,0")
+        (match_operand:SF 2 "register_operand" "0,f")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "@
+    mov%T3.s\\t%0,%1,%4
+    mov%t3.s\\t%0,%2,%4"
+  [(set_attr "type" "move")
+   (set_attr "mode" "SF")])
+
+(define_insn ""
+  [(set (match_operand:DF 0 "register_operand" "=f,f")
+       (if_then_else:DF
+        (match_operator 4 "equality_op"
+                        [(match_operand:SI 1 "register_operand" "d,d")
+                         (const_int 0)])
+        (match_operand:DF 2 "register_operand" "f,0")
+        (match_operand:DF 3 "register_operand" "0,f")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "@
+    mov%B4.d\\t%0,%2,%1
+    mov%b4.d\\t%0,%3,%1"
+  [(set_attr "type" "move")
+   (set_attr "mode" "DF")])
+
+(define_insn ""
+  [(set (match_operand:DF 0 "register_operand" "=f,f")
+       (if_then_else:DF
+        (match_operator 3 "equality_op" [(match_operand:CC 4
+                                                           "register_operand"
+                                                           "z,z")
+                                         (const_int 0)])
+        (match_operand:DF 1 "register_operand" "f,0")
+        (match_operand:DF 2 "register_operand" "0,f")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "@
+    mov%T3.d\\t%0,%1,%4
+    mov%t3.d\\t%0,%2,%4"
+  [(set_attr "type" "move")
+   (set_attr "mode" "DF")])
+
+;; These are the main define_expand's used to make conditional moves.
+
+(define_expand "movsicc"
+  [(set (match_dup 4) (match_operand 1 "comparison_operator" ""))
+   (set (match_operand:SI 0 "register_operand" "")
+       (if_then_else:SI (match_dup 5)
+                        (match_operand:SI 2 "reg_or_0_operand" "")
+                        (match_operand:SI 3 "reg_or_0_operand" "")))]
+  "mips_isa >= 4"
   "
 {
-  operands[0] = gen_reg_rtx (SImode);
-  operands[1] = gen_rtx (MEM, SImode, stack_pointer_rtx);
-  MEM_VOLATILE_P (operands[1]) = TRUE;
+  gen_conditional_move (operands);
+  DONE;
+}")
 
-  /* fall through and generate default code */
+(define_expand "movdicc"
+  [(set (match_dup 4) (match_operand 1 "comparison_operator" ""))
+   (set (match_operand:DI 0 "register_operand" "")
+       (if_then_else:DI (match_dup 5)
+                        (match_operand:DI 2 "se_reg_or_0_operand" "")
+                        (match_operand:DI 3 "se_reg_or_0_operand" "")))]
+  "mips_isa >= 4"
+  "
+{
+  gen_conditional_move (operands);
+  DONE;
+}")
+
+(define_expand "movsfcc"
+  [(set (match_dup 4) (match_operand 1 "comparison_operator" ""))
+   (set (match_operand:SF 0 "register_operand" "")
+       (if_then_else:SF (match_dup 5)
+                        (match_operand:SF 2 "register_operand" "")
+                        (match_operand:SF 3 "register_operand" "")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT"
+  "
+{
+  gen_conditional_move (operands);
+  DONE;
+}")
+
+(define_expand "movdfcc"
+  [(set (match_dup 4) (match_operand 1 "comparison_operator" ""))
+   (set (match_operand:DF 0 "register_operand" "")
+       (if_then_else:DF (match_dup 5)
+                        (match_operand:DF 2 "register_operand" "")
+                        (match_operand:DF 3 "register_operand" "")))]
+  "mips_isa >= 4 && TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT"
+  "
+{
+  gen_conditional_move (operands);
+  DONE;
 }")
+\f
+;;
+;;  ....................
+;;
+;;     mips16 inline constant tables
+;;
+;;  ....................
+;;
+
+(define_insn "consttable_qi"
+  [(unspec_volatile [(match_operand:QI 0 "consttable_operand" "=g")] 10)]
+  "TARGET_MIPS16"
+  "*
+{
+  assemble_integer (operands[0], 1, 1);
+  return \"\";
+}"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "QI")
+   (set_attr "length"  "2")])
+
+(define_insn "consttable_hi"
+  [(unspec_volatile [(match_operand:HI 0 "consttable_operand" "=g")] 11)]
+  "TARGET_MIPS16"
+  "*
+{
+  assemble_integer (operands[0], 2, 1);
+  return \"\";
+}"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "HI")
+   (set_attr "length"  "2")])
+
+(define_insn "consttable_si"
+  [(unspec_volatile [(match_operand:SI 0 "consttable_operand" "=g")] 12)]
+  "TARGET_MIPS16"
+  "*
+{
+  assemble_integer (operands[0], 4, 1);
+  return \"\";
+}"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "2")])
+
+(define_insn "consttable_di"
+  [(unspec_volatile [(match_operand:DI 0 "consttable_operand" "=g")] 13)]
+  "TARGET_MIPS16"
+  "*
+{
+  assemble_integer (operands[0], 8, 1);
+  return \"\";
+}"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "4")])
+
+(define_insn "consttable_sf"
+  [(unspec_volatile [(match_operand:SF 0 "consttable_operand" "=g")] 14)]
+  "TARGET_MIPS16"
+  "*
+{
+  union real_extract u;
+
+  if (GET_CODE (operands[0]) != CONST_DOUBLE)
+    abort ();
+  bcopy ((char *) &CONST_DOUBLE_LOW (operands[0]), (char *) &u, sizeof u);
+  assemble_real (u.d, SFmode);
+  return \"\";
+}"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "SF")
+   (set_attr "length"  "2")])
+
+(define_insn "consttable_df"
+  [(unspec_volatile [(match_operand:DF 0 "consttable_operand" "=g")] 15)]
+  "TARGET_MIPS16"
+  "*
+{
+  union real_extract u;
+
+  if (GET_CODE (operands[0]) != CONST_DOUBLE)
+    abort ();
+  bcopy ((char *) &CONST_DOUBLE_LOW (operands[0]), (char *) &u, sizeof u);
+  assemble_real (u.d, DFmode);
+  return \"\";
+}"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "DF")
+   (set_attr "length"  "4")])
+
+(define_insn "align_2"
+  [(unspec_volatile [(const_int 0)] 16)]
+  "TARGET_MIPS16"
+  ".align 1"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "HI")
+   (set_attr "length"  "2")])
+
+(define_insn "align_4"
+  [(unspec_volatile [(const_int 0)] 17)]
+  "TARGET_MIPS16"
+  ".align 2"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "2")])
+
+(define_insn "align_8"
+  [(unspec_volatile [(const_int 0)] 18)]
+  "TARGET_MIPS16"
+  ".align 3"
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "DI")
+   (set_attr "length"  "3")])
+\f
+;;
+;;  ....................
+;;
+;;     mips16 peepholes
+;;
+;;  ....................
+;;
+
+;; On the mips16, reload will sometimes decide that a pseudo register
+;; should go into $24, and then later on have to reload that register.
+;; When that happens, we get a load of a general register followed by
+;; a move from the general register to $24 followed by a branch.
+;; These peepholes catch the common case, and fix it to just use the
+;; general register for the branch.
+
+(define_peephole
+  [(set (match_operand:SI 0 "register_operand" "=t")
+       (match_operand:SI 1 "register_operand" "d"))
+   (set (pc)
+       (if_then_else (match_operator:SI 2 "equality_op" [(match_dup 0)
+                                                         (const_int 0)])
+                     (match_operand 3 "pc_or_label_operand" "")
+                     (match_operand 4 "pc_or_label_operand" "")))]
+  "TARGET_MIPS16
+   && GET_CODE (operands[0]) == REG
+   && REGNO (operands[0]) == 24
+   && dead_or_set_p (insn, operands[0])
+   && GET_CODE (operands[1]) == REG
+   && M16_REG_P (REGNO (operands[1]))"
+  "*
+{
+  if (operands[3] != pc_rtx)
+    return \"%*b%C2z\\t%1,%3\";
+  else
+    return \"%*b%N2z\\t%1,%4\";
+}"
+  [(set_attr "type"    "branch")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
+(define_peephole
+  [(set (match_operand:DI 0 "register_operand" "=t")
+       (match_operand:DI 1 "register_operand" "d"))
+   (set (pc)
+       (if_then_else (match_operator:DI 2 "equality_op" [(match_dup 0)
+                                                         (const_int 0)])
+                     (match_operand 3 "pc_or_label_operand" "")
+                     (match_operand 4 "pc_or_label_operand" "")))]
+  "TARGET_MIPS16 && TARGET_64BIT
+   && GET_CODE (operands[0]) == REG
+   && REGNO (operands[0]) == 24
+   && dead_or_set_p (insn, operands[0])
+   && GET_CODE (operands[1]) == REG
+   && M16_REG_P (REGNO (operands[1]))"
+  "*
+{
+  if (operands[3] != pc_rtx)
+    return \"%*b%C2z\\t%1,%3\";
+  else
+    return \"%*b%N2z\\t%1,%4\";
+}"
+  [(set_attr "type"    "branch")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
+;; We can also have the reverse reload: reload will spill $24 into
+;; another register, and then do a branch on that register when it
+;; could have just stuck with $24.
+
+(define_peephole
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (match_operand:SI 1 "register_operand" "t"))
+   (set (pc)
+       (if_then_else (match_operator:SI 2 "equality_op" [(match_dup 0)
+                                                         (const_int 0)])
+                     (match_operand 3 "pc_or_label_operand" "")
+                     (match_operand 4 "pc_or_label_operand" "")))]
+  "TARGET_MIPS16
+   && GET_CODE (operands[1]) == REG
+   && REGNO (operands[1]) == 24
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && dead_or_set_p (insn, operands[0])"
+  "*
+{
+  if (operands[3] != pc_rtx)
+    return \"%*bt%C2z\\t%3\";
+  else
+    return \"%*bt%N2z\\t%4\";
+}"
+  [(set_attr "type"    "branch")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])
+
+(define_peephole
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (match_operand:DI 1 "register_operand" "t"))
+   (set (pc)
+       (if_then_else (match_operator:DI 2 "equality_op" [(match_dup 0)
+                                                         (const_int 0)])
+                     (match_operand 3 "pc_or_label_operand" "")
+                     (match_operand 4 "pc_or_label_operand" "")))]
+  "TARGET_MIPS16 && TARGET_64BIT
+   && GET_CODE (operands[1]) == REG
+   && REGNO (operands[1]) == 24
+   && GET_CODE (operands[0]) == REG
+   && M16_REG_P (REGNO (operands[0]))
+   && dead_or_set_p (insn, operands[0])"
+  "*
+{
+  if (operands[3] != pc_rtx)
+    return \"%*bt%C2z\\t%3\";
+  else
+    return \"%*bt%N2z\\t%4\";
+}"
+  [(set_attr "type"    "branch")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "2")])