* mips.c, mips.h, mips.md: First cut at merging in mips16
authorlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 4 Jan 1998 15:43:52 +0000 (15:43 +0000)
committerlaw <law@138bc75d-0d04-0410-961f-82ee72b054a4>
Sun, 4 Jan 1998 15:43:52 +0000 (15:43 +0000)
        support.  Major modifications throughout all three files.
Note mips16 doesn't work yet (no epilogue support), but I'll be working
on that shortly :-)

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@17292 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/config/mips/mips.c
gcc/config/mips/mips.h
gcc/config/mips/mips.md

index 3be3f32..400f65a 100644 (file)
@@ -1,3 +1,10 @@
+Sun Jan  4 14:25:18 1998  Gavin Koch  <gavin@cygnus.com>
+                          Ian Lance Taylor  <ian@cygnus.com>
+                          Jeff Law  <law@cygnus.com>
+
+       * mips.c, mips.h, mips.md: First cut at merging in mips16
+       support.  Major modifications throughout all three files.
+
 Sun Jan  4 01:01:50 1998  scott snyder  <snyder@d0sgif.fnal.gov>
 
        * configure.in: Make gthr-default.h a forwarding header instead of
index 88569ab..6ea2659 100644 (file)
@@ -52,6 +52,7 @@ Boston, MA 02111-1307, USA.  */
 #include "tree.h"
 #include "expr.h"
 #include "flags.h"
+#include "reload.h"
 
 #ifndef R_OK
 #define R_OK 4
@@ -85,6 +86,9 @@ extern void   warning ();
 
 extern FILE  *asm_out_file;
 
+static void mips16_output_gp_offset ();
+static void build_mips16_function_stub ();
+
 /* Enumeration for all of the relational tests, so that we can build
    arrays indexed by the test type, and not worry about the order
    of EQ, NE, etc. */
@@ -204,6 +208,29 @@ char *mips_cpu_string;             /* for -mcpu=<xxx> */
 char *mips_isa_string;         /* for -mips{1,2,3,4} */
 char *mips_abi_string;         /* for -mabi={o32,32,n32,n64,64,eabi} */
 
+/* Whether we are generating mips16 code.  This is a synonym for
+   TARGET_MIPS16, and exists for use as an attribute.  */
+int mips16;
+
+/* This variable is set by -mno-mips16.  We only care whether
+   -mno-mips16 appears or not, and using a string in this fashion is
+   just a way to avoid using up another bit in target_flags.  */
+char *mips_no_mips16_string;
+
+/* Whether we are generating mips16 hard float code.  In mips16 mode
+   we always set TARGET_SOFT_FLOAT; this variable is nonzero if
+   -msoft-float was not specified by the user, which means that we
+   should arrange to call mips32 hard floating point code.  */
+int mips16_hard_float;
+
+/* This variable is set by -mentry.  We only care whether -mentry
+   appears or not, and using a string in this fashion is just a way to
+   avoid using up another bit in target_flags.  */
+char *mips_entry_string;
+
+/* Whether we should entry and exit pseudo-ops in mips16 mode.  */
+int mips_entry;
+
 /* If TRUE, we split addresses into their high and low parts in the RTL.  */
 int mips_split_addresses;
 
@@ -234,6 +261,27 @@ static char *temp_filename;
    by mips_finalize_pic if it was created.  */
 rtx embedded_pic_fnaddr_rtx;
 
+/* The length of all strings seen when compiling for the mips16.  This
+   is used to tell how many strings are in the constant pool, so that
+   we can see if we may have an overflow.  This is reset each time the
+   constant pool is output.  */
+int mips_string_length;
+
+/* Pseudo-reg holding the value of $28 in a mips16 function which
+   refers to GP relative global variables.  */
+rtx mips16_gp_pseudo_rtx;
+
+/* In mips16 mode, we build a list of all the string constants we see
+   in a particular function.  */
+
+struct string_constant
+{
+  struct string_constant *next;
+  char *label;
+};
+
+static struct string_constant *string_constants;
+
 /* List of all MIPS punctuation characters used by print_operand.  */
 char mips_print_operand_punct[256];
 
@@ -281,13 +329,13 @@ char mips_sw_reg_names[][8] =
 /* Map hard register number to register class */
 enum reg_class mips_regno_to_class[] =
 {
+  GR_REGS,     GR_REGS,        M16_NA_REGS,    M16_NA_REGS,
+  M16_REGS,    M16_REGS,       M16_REGS,       M16_REGS,
   GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
   GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
+  M16_NA_REGS, M16_NA_REGS,    GR_REGS,        GR_REGS,
   GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
-  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
-  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
-  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
-  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
+  T_REG,       GR_REGS,        GR_REGS,        GR_REGS,
   GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
   FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
@@ -397,6 +445,10 @@ arith_operand (op, mode)
   if (GET_CODE (op) == CONST_INT && SMALL_INT (op))
     return TRUE;
 
+  /* On the mips16, a GP relative value is a signed 16 bit offset.  */
+  if (TARGET_MIPS16 && GET_CODE (op) == CONST && mips16_gp_offset_p (op))
+    return 1;
+
   return register_operand (op, mode);
 }
 
@@ -450,7 +502,9 @@ large_int (op, mode)
   return TRUE;
 }
 
-/* Return truth value of whether OP is a register or the constant 0.  */
+/* Return truth value of whether OP is a register or the constant 0.
+   In mips16 mode, we only accept a register, since the mips16 does
+   not have $0.  */
 
 int
 reg_or_0_operand (op, mode)
@@ -463,10 +517,12 @@ reg_or_0_operand (op, mode)
       break;
 
     case CONST_INT:
+      if (TARGET_MIPS16)
+       return FALSE;
       return (INTVAL (op) == 0);
 
     case CONST_DOUBLE:
-      if (op != CONST0_RTX (mode))
+      if (TARGET_MIPS16 || op != CONST0_RTX (mode))
        return FALSE;
 
       return TRUE;
@@ -562,6 +618,38 @@ const_float_1_operand (op, mode)
     return REAL_VALUES_EQUAL (d, onesf);
 }
 
+/* Return true if a memory load or store of REG plus OFFSET in MODE
+   can be represented in a single word on the mips16.  */
+
+static int
+mips16_simple_memory_operand (reg, offset, mode)
+     rtx reg;
+     rtx offset;
+     enum machine_mode mode;
+{
+  int size, off;
+
+  if (mode == BLKmode)
+    {
+      /* We can't tell, because we don't know how the value will
+         eventually be accessed.  Returning FALSE here does no great
+         harm; it just prevents some possible instruction scheduling.  */
+      return FALSE;
+    }
+
+  size = GET_MODE_SIZE (mode);
+
+  if (INTVAL (offset) % size != 0)
+    return FALSE;
+  if (REGNO (reg) == STACK_POINTER_REGNUM && GET_MODE_SIZE (mode) == 4)
+    off = 0x100;
+  else
+    off = 0x20;
+  if (INTVAL (offset) >= 0 && INTVAL (offset) < off * size)
+    return TRUE;
+  return FALSE;
+}
+
 /* Return truth value if a memory operand fits in a single instruction
    (ie, register + small offset).  */
 
@@ -595,6 +683,8 @@ simple_memory_operand (op, mode)
       return TRUE;
 
     case CONST_INT:
+      if (TARGET_MIPS16)
+       return FALSE;
       return SMALL_INT (op);
 
     case PLUS:
@@ -602,12 +692,16 @@ simple_memory_operand (op, mode)
       plus1 = XEXP (addr, 1);
       if (GET_CODE (plus0) == REG
          && GET_CODE (plus1) == CONST_INT
-         && SMALL_INT (plus1))
+         && SMALL_INT (plus1)
+         && (! TARGET_MIPS16
+             || mips16_simple_memory_operand (plus0, plus1, mode)))
        return TRUE;
 
       else if (GET_CODE (plus1) == REG
               && GET_CODE (plus0) == CONST_INT
-              && SMALL_INT (plus0))
+              && SMALL_INT (plus0)
+              && (! TARGET_MIPS16
+                  || mips16_simple_memory_operand (plus1, plus0, mode)))
        return TRUE;
 
       else
@@ -643,11 +737,150 @@ simple_memory_operand (op, mode)
     case SYMBOL_REF:
       return SYMBOL_REF_FLAG (addr);
 #endif
+
+      /* This SYMBOL_REF case is for the mips16.  If the above case is
+         reenabled, this one should be merged in.  */
+    case SYMBOL_REF:
+      /* References to the constant pool on the mips16 use a small
+         offset if the function is small.  The only time we care about
+         getting this right is during delayed branch scheduling, so
+         don't need to check until then.  The machine_dependent_reorg
+         function will set the total length of the instructions used
+         in the function in current_frame_info.  If that is small
+         enough, we know for sure that this is a small offset.  It
+         would be better if we could take into account the location of
+         the instruction within the function, but we can't, because we
+         don't know where we are.  */
+      if (TARGET_MIPS16
+         && CONSTANT_POOL_ADDRESS_P (addr)
+         && current_frame_info.insns_len > 0)
+       {
+         long size;
+
+         size = current_frame_info.insns_len + get_pool_size ();
+         if (GET_MODE_SIZE (mode) == 4)
+           return size < 4 * 0x100;
+         else if (GET_MODE_SIZE (mode) == 8)
+           return size < 8 * 0x20;
+         else
+           return FALSE;
+       }
+
+      return FALSE;
     }
 
   return FALSE;
 }
 
+/* Return true for a memory address that can be used to load or store
+   a doubleword.  */
+
+int
+double_memory_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  rtx addr;
+
+  if (GET_CODE (op) != MEM
+      || ! memory_operand (op, mode))
+    {
+      /* During reload, we accept a pseudo register if it has an
+        appropriate memory address.  If we don't do this, we will
+        wind up reloading into a register, and then reloading that
+        register from memory, when we could just reload directly from
+        memory.  */
+      if (reload_in_progress
+         && GET_CODE (op) == REG
+         && REGNO (op) >= FIRST_PSEUDO_REGISTER
+         && reg_renumber[REGNO (op)] < 0
+         && reg_equiv_mem[REGNO (op)] != 0
+         && double_memory_operand (reg_equiv_mem[REGNO (op)], mode))
+       return TRUE;
+
+      if (reload_in_progress
+         && TARGET_MIPS16
+         && GET_CODE (op) == MEM)
+       {
+         rtx addr;
+
+         addr = XEXP (op, 0);
+
+         /* During reload on the mips16, we accept a large offset
+            from the frame pointer or the stack pointer.  This large
+            address will get reloaded anyhow.  */
+         if (GET_CODE (addr) == PLUS
+             && GET_CODE (XEXP (addr, 0)) == REG
+             && (REGNO (XEXP (addr, 0)) == HARD_FRAME_POINTER_REGNUM
+                 || REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
+             && ((GET_CODE (XEXP (addr, 1)) == CONST_INT
+                  && ! SMALL_INT (XEXP (addr, 1)))
+                 || (GET_CODE (XEXP (addr, 1)) == SYMBOL_REF
+                     && CONSTANT_POOL_ADDRESS_P (XEXP (addr, 1)))))
+           return TRUE;
+
+         /* Similarly, we accept a case where the memory address is
+             itself on the stack, and will be reloaded. */
+         if (GET_CODE (addr) == MEM)
+           {
+             rtx maddr;
+
+             maddr = XEXP (addr, 0);
+             if (GET_CODE (maddr) == PLUS
+                 && GET_CODE (XEXP (maddr, 0)) == REG
+                 && (REGNO (XEXP (maddr, 0)) == HARD_FRAME_POINTER_REGNUM
+                     || REGNO (XEXP (maddr, 0)) == STACK_POINTER_REGNUM)
+                 && ((GET_CODE (XEXP (maddr, 1)) == CONST_INT
+                      && ! SMALL_INT (XEXP (maddr, 1)))
+                     || (GET_CODE (XEXP (maddr, 1)) == SYMBOL_REF
+                         && CONSTANT_POOL_ADDRESS_P (XEXP (maddr, 1)))))
+               return TRUE;
+           }
+
+         /* We also accept the same case when we have a 16 bit signed
+            offset mixed in as well.  The large address will get
+            reloaded, and the 16 bit offset will be OK.  */
+         if (GET_CODE (addr) == PLUS
+             && GET_CODE (XEXP (addr, 0)) == MEM
+             && GET_CODE (XEXP (addr, 1)) == CONST_INT
+             && SMALL_INT (XEXP (addr, 1)))
+           {
+             addr = XEXP (XEXP (addr, 0), 0);
+             if (GET_CODE (addr) == PLUS
+                 && GET_CODE (XEXP (addr, 0)) == REG
+                 && (REGNO (XEXP (addr, 0)) == HARD_FRAME_POINTER_REGNUM
+                     || REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
+                 && ((GET_CODE (XEXP (addr, 1)) == CONST_INT
+                      && ! SMALL_INT (XEXP (addr, 1)))
+                     || (GET_CODE (XEXP (addr, 1)) == SYMBOL_REF
+                         && CONSTANT_POOL_ADDRESS_P (XEXP (addr, 1)))))
+               return TRUE;
+           }
+       }
+
+      return FALSE;
+    }
+
+  if (TARGET_64BIT)
+    {
+      /* In this case we can use an instruction like sd.  */
+      return TRUE;
+    }
+
+  /* Make sure that 4 added to the address is a valid memory address.
+     This essentially just checks for overflow in an added constant.  */
+
+  addr = XEXP (op, 0);
+
+  if (CONSTANT_ADDRESS_P (addr))
+    return TRUE;
+
+  return memory_address_p ((GET_MODE_CLASS (mode) == MODE_INT
+                           ? SImode
+                           : SFmode),
+                          plus_constant_for_output (addr, 4));
+}
+
 /* Return true if the code of this rtx pattern is EQ or NE.  */
 
 int
@@ -722,7 +955,10 @@ move_operand (op, mode)
   return (general_operand (op, mode)
          && (! (mips_split_addresses && mips_check_split (op, mode))
              || reload_in_progress
-             || reload_completed));
+             || reload_completed)
+         && ! (TARGET_MIPS16
+               && GET_CODE (op) == SYMBOL_REF
+               && ! mips16_constant (op, mode, 1, 0)));
                
        
 }
@@ -872,6 +1108,19 @@ se_nonimmediate_operand (op, mode)
   return nonimmediate_operand (op, mode);
 }
 
+/* Accept any operand that can appear in a mips16 constant table
+   instruction.  We can't use any of the standard operand functions
+   because for these instructions we accept values that are not
+   accepted by LEGITIMATE_CONSTANT, such as arbitrary SYMBOL_REFs.  */
+
+int
+consttable_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return CONSTANT_P (op);
+}
+
 /* Return true if we split the address into high and low parts.  */
 
 /* ??? We should also handle reg+array somewhere.  We get four
@@ -908,6 +1157,212 @@ mips_check_split (address, mode)
   return 0;
 }
 \f
+/* We need a lot of little routines to check constant values on the
+   mips16.  These are used to figure out how long the instruction will
+   be.  It would be much better to do this using constraints, but
+   there aren't nearly enough letters available.  */
+
+static int
+m16_check_op (op, low, high, mask)
+     rtx op;
+     int low;
+     int high;
+     int mask;
+{
+  return (GET_CODE (op) == CONST_INT
+         && INTVAL (op) >= low
+         && INTVAL (op) <= high
+         && (INTVAL (op) & mask) == 0);
+}
+
+int
+m16_uimm3_b (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, 0x1, 0x8, 0);
+}
+
+int
+m16_simm4_1 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, - 0x8, 0x7, 0);
+}
+
+int
+m16_nsimm4_1 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, - 0x7, 0x8, 0);
+}
+
+int
+m16_simm5_1 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, - 0x10, 0xf, 0);
+}
+
+int
+m16_nsimm5_1 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, - 0xf, 0x10, 0);
+}
+
+int
+m16_uimm5_4 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, (- 0x10) << 2, 0xf << 2, 3);
+}
+
+int
+m16_nuimm5_4 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, (- 0xf) << 2, 0x10 << 2, 3);
+}
+
+int
+m16_simm8_1 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, - 0x80, 0x7f, 0);
+}
+
+int
+m16_nsimm8_1 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, - 0x7f, 0x80, 0);
+}
+
+int
+m16_uimm8_1 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, 0x0, 0xff, 0);
+}
+
+int
+m16_nuimm8_1 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, - 0xff, 0x0, 0);
+}
+
+int
+m16_uimm8_m1_1 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, - 0x1, 0xfe, 0);
+}
+
+int
+m16_uimm8_4 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, 0x0, 0xff << 2, 3);
+}
+
+int
+m16_nuimm8_4 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, (- 0xff) << 2, 0x0, 3);
+}
+
+int
+m16_simm8_8 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, (- 0x80) << 3, 0x7f << 3, 7);
+}
+
+int
+m16_nsimm8_8 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7);
+}
+
+/* References to the string table on the mips16 only use a small
+   offset if the function is small.  See the comment in the SYMBOL_REF
+   case in simple_memory_operand.  We can't check for LABEL_REF here,
+   because the offset is always large if the label is before the
+   referencing instruction.  */
+
+int
+m16_usym8_4 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) == SYMBOL_REF
+      && SYMBOL_REF_FLAG (op)
+      && current_frame_info.insns_len > 0
+      && XSTR (op, 0)[0] == '*'
+      && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX,
+                 sizeof LOCAL_LABEL_PREFIX - 1) == 0
+      && (current_frame_info.insns_len + get_pool_size () + mips_string_length
+         < 4 * 0x100))
+    {
+      struct string_constant *l;
+
+      /* Make sure this symbol is on thelist of string constants to be
+         output for this function.  It is possible that it has already
+         been output, in which case this requires a large offset.  */
+      for (l = string_constants; l != NULL; l = l->next)
+       if (strcmp (l->label, XSTR (op, 0)) == 0)
+         return 1;
+    }
+
+  return 0;
+}
+
+int
+m16_usym5_4 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) == SYMBOL_REF
+      && SYMBOL_REF_FLAG (op)
+      && current_frame_info.insns_len > 0
+      && XSTR (op, 0)[0] == '*'
+      && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX,
+                 sizeof LOCAL_LABEL_PREFIX - 1) == 0
+      && (current_frame_info.insns_len + get_pool_size () + mips_string_length
+         < 4 * 0x20))
+    {
+      struct string_constant *l;
+
+      /* Make sure this symbol is on thelist of string constants to be
+         output for this function.  It is possible that it has already
+         been output, in which case this requires a large offset.  */
+      for (l = string_constants; l != NULL; l = l->next)
+       if (strcmp (l->label, XSTR (op, 0)) == 0)
+         return 1;
+    }
+
+  return 0;
+}
+\f
 /* Returns an operand string for the given instruction's delay slot,
    after updating filled delay slot statistics.
 
@@ -1242,7 +1697,7 @@ mips_move_1word (operands, insn, unsignedp)
              if (GP_REG_P (regno1))
                {
                  delay = DELAY_HILO;
-                 if (regno0 != HILO_REGNUM)
+                 if (regno0 != HILO_REGNUM && ! TARGET_MIPS16)
                    ret = "mt%0\t%1";
                }
            }
@@ -1316,7 +1771,7 @@ mips_move_1word (operands, insn, unsignedp)
              operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
            }
 
-         if (INTVAL (op1) == 0)
+         if (INTVAL (op1) == 0 && ! TARGET_MIPS16)
            {
              if (GP_REG_P (regno0))
                ret = "move\t%0,%z1";
@@ -1335,9 +1790,19 @@ mips_move_1word (operands, insn, unsignedp)
            }
 
          else if (GP_REG_P (regno0))
-           /* Don't use X format, because that will give out of range
-              numbers for 64 bit host and 32 bit target.  */
-           ret = "li\t%0,%1\t\t\t# %X1";
+           {
+             /* Don't use X format, because that will give out of
+                range numbers for 64 bit host and 32 bit target.  */
+             if (! TARGET_MIPS16)
+               ret = "li\t%0,%1\t\t\t# %X1";
+             else
+               {
+                 if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
+                   ret = "li\t%0,%1";
+                 else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
+                   ret = "li\t%0,%n1\n\tneg\t%0";
+               }
+           }
        }
 
       else if (code1 == CONST_DOUBLE && mode == SFmode)
@@ -1407,6 +1872,27 @@ mips_move_1word (operands, insn, unsignedp)
                    }
                }
            }
+         else if (TARGET_MIPS16
+                  && code1 == CONST
+                  && GET_CODE (XEXP (op1, 0)) == REG
+                  && REGNO (XEXP (op1, 0)) == GP_REG_FIRST + 28)
+           {
+             /* This case arises on the mips16; see
+                 mips16_gp_pseudo_reg.  */
+             ret = "move\t%0,%+";
+           }
+         else if (TARGET_MIPS16
+                  && code1 == SYMBOL_REF
+                  && SYMBOL_REF_FLAG (op1)
+                  && (XSTR (op1, 0)[0] != '*'
+                      || strncmp (XSTR (op1, 0) + 1,
+                                  LOCAL_LABEL_PREFIX,
+                                  sizeof LOCAL_LABEL_PREFIX - 1) != 0))
+           {
+             /* This can occur when reloading the address of a GP
+                 relative symbol on the mips16.  */
+             ret = "move\t%0,%+\n\taddu\t%0,%%gprel(%a1)";
+           }
          else
            {
              if (TARGET_STATS)
@@ -1614,7 +2100,7 @@ mips_move_2words (operands, insn)
                ret = "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1";
            }
 
-         else if (MD_REG_P (regno0) && GP_REG_P (regno1))
+         else if (MD_REG_P (regno0) && GP_REG_P (regno1) && !TARGET_MIPS16)
            {
              delay = DELAY_HILO;
              if (TARGET_64BIT)
@@ -1677,7 +2163,10 @@ mips_move_2words (operands, insn)
                }
 
              else if (TARGET_64BIT)
-               ret = "dli\t%0,%1";
+               {
+                 if (! TARGET_MIPS16)
+                   ret = "dli\t%0,%1";
+               }
 
              else
                {
@@ -1707,7 +2196,7 @@ mips_move_2words (operands, insn)
            }
        }
 
-      else if (code1 == CONST_INT && INTVAL (op1) == 0)
+      else if (code1 == CONST_INT && INTVAL (op1) == 0 && ! TARGET_MIPS16)
        {
          if (GP_REG_P (regno0))
            ret = (TARGET_64BIT)
@@ -1737,7 +2226,14 @@ mips_move_2words (operands, insn)
        {
          if (TARGET_64BIT)
            {
-             if (GET_CODE (operands[1]) == SIGN_EXTEND)
+             if (TARGET_MIPS16)
+               {
+                 if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
+                   ret = "li\t%0,%1";
+                 else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
+                   ret = "li\t%0,%n1\n\tneg\t%0";
+               }
+             else if (GET_CODE (operands[1]) == SIGN_EXTEND)
                ret = "li\t%0,%1\t\t# %X1";
              else if (HOST_BITS_PER_WIDE_INT < 64)
                /* We can't use 'X' for negative numbers, because then we won't
@@ -1752,7 +2248,18 @@ mips_move_2words (operands, insn)
          else if (HOST_BITS_PER_WIDE_INT < 64)
            {
              operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1);
-             ret = "li\t%M0,%2\n\tli\t%L0,%1";
+             if (TARGET_MIPS16)
+               {
+                 if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
+                   ret = "li\t%M0,%2\n\tli\t%L0,%1";
+                 else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
+                   {
+                     operands[2] = GEN_INT (1);
+                     ret = "li\t%M0,%2\n\tneg\t%M0\n\tli\t%L0,%n1\n\tneg\t%L0";
+                   }
+               }
+             else
+               ret = "li\t%M0,%2\n\tli\t%L0,%1";
            }
          else
            {
@@ -1779,7 +2286,7 @@ mips_move_2words (operands, insn)
 #ifdef TARGET_FP_CALL_32
              if (FP_CALL_GP_REG_P (regno0))
                {
-                  if (offsettable_address_p (FALSE, SImode, op1))
+                  if (double_memory_operand (op1, GET_MODE (op1)))
                     ret = "lwu\t%0,%1\n\tlwu\t%D0,4+%1";
                   else
                     ret = "ld\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32";
@@ -1789,7 +2296,7 @@ mips_move_2words (operands, insn)
                ret = "ld\t%0,%1";
            }
 
-         else if (offsettable_address_p (1, DFmode, XEXP (op1, 0)))
+         else if (double_memory_operand (op1, GET_MODE (op1)))
            {
              operands[2] = adj_offsettable_operand (op1, 4);
              if (reg_mentioned_p (op0, op1))
@@ -1846,7 +2353,7 @@ mips_move_2words (operands, insn)
                ret = "sd\t%1,%0";
            }
 
-         else if (offsettable_address_p (1, DFmode, XEXP (op0, 0)))
+         else if (double_memory_operand (op0, GET_MODE (op0)))
            {
              operands[2] = adj_offsettable_operand (op0, 4);
              ret = "sw\t%1,%0\n\tsw\t%D1,%2";
@@ -1857,7 +2364,7 @@ mips_move_2words (operands, insn)
                || (code1 == CONST_DOUBLE
                    && op1 == CONST0_RTX (GET_MODE (op1))))
               && (TARGET_64BIT
-                  || offsettable_address_p (1, DFmode, XEXP (op0, 0))))
+                  || double_memory_operand (op0, GET_MODE (op0))))
        {
          if (TARGET_64BIT)
            ret = "sd\t%.,%0";
@@ -2092,11 +2599,12 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
       if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
        {
          /* Comparisons against zero are simple branches */
-         if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
+         if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0
+             && (! TARGET_MIPS16 || eqne_p))
            return (rtx)0;
 
          /* Test for beq/bne.  */
-         if (eqne_p)
+         if (eqne_p && ! TARGET_MIPS16)
            return (rtx)0;
        }
 
@@ -2178,8 +2686,17 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
 
   if (test == ITEST_NE)
     {
-      convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0);
-      invert = FALSE;
+      if (! TARGET_MIPS16)
+       {
+         convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0);
+         invert = FALSE;
+       }
+      else
+       {
+         reg2 = invert ? gen_reg_rtx (mode) : result;
+         convert_move (reg2, gen_rtx (LTU, mode, reg, const1_rtx), 0);
+         reg = reg2;
+       }
     }
 
   else if (test == ITEST_EQ)
@@ -2190,7 +2707,23 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
     }
 
   if (invert)
-    convert_move (result, gen_rtx (XOR, mode, reg, const1_rtx), 0);
+    {
+      rtx one;
+
+      if (! TARGET_MIPS16)
+       one = const1_rtx;
+      else
+       {
+         /* The value is in $24.  Copy it to another register, so
+             that reload doesn't think it needs to store the $24 and
+             the input to the XOR in the same location.  */
+         reg2 = gen_reg_rtx (mode);
+         emit_move_insn (reg2, reg);
+         reg = reg2;
+         one = force_reg (mode, const1_rtx);
+       }
+      convert_move (result, gen_rtx (XOR, mode, reg, one), 0);
+    }
 
   return result;
 }
@@ -2774,7 +3307,8 @@ output_block_move (insn, operands, num_regs, move_type)
          bytes -= 8;
        }
 
-      else if (TARGET_64BIT && bytes >= 8)
+      /* ??? Fails because of a MIPS assembler bug?  */
+      else if (TARGET_64BIT && bytes >= 8 && ! TARGET_MIPS16)
        {
          if (BYTES_BIG_ENDIAN)
            {
@@ -2810,7 +3344,7 @@ output_block_move (insn, operands, num_regs, move_type)
          bytes -= 4;
        }
 
-      else if (bytes >= 4)
+      else if (bytes >= 4 && ! TARGET_MIPS16)
        {
          if (BYTES_BIG_ENDIAN)
            {
@@ -3046,6 +3580,8 @@ function_arg_advance (cum, mode, type, named)
        cum->fp_arg_words++;
       else
        cum->arg_words++;
+      if (! cum->gp_reg_found && cum->arg_number <= 2)
+       cum->fp_code += 1 << ((cum->arg_number - 1) * 2);
       break;
 
     case DFmode:
@@ -3053,6 +3589,8 @@ function_arg_advance (cum, mode, type, named)
        cum->fp_arg_words += (TARGET_64BIT ? 1 : 2);
       else
        cum->arg_words += (TARGET_64BIT ? 1 : 2);
+      if (! cum->gp_reg_found && ! TARGET_SINGLE_FLOAT && cum->arg_number <= 2)
+       cum->fp_code += 2 << ((cum->arg_number - 1) * 2);
       break;
 
     case DImode:
@@ -3300,8 +3838,20 @@ function_arg (cum, mode, type, named)
        }
     }
 
-  if (mode == VOIDmode && cum->num_adjusts > 0)
-    ret = gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (cum->num_adjusts, cum->adjust));
+  /* We will be called with a mode of VOIDmode after the last argument
+     has been seen.  Whatever we return will be passed to the call
+     insn.  If we need any shifts for small structures, return them in
+     a PARALLEL; in that case, stuff the mips16 fp_code in as the
+     mode.  Otherwise, if we have need a mips16 fp_code, return a REG
+     with the code stored as the mode.  */
+  if (mode == VOIDmode)
+    {
+      if (cum->num_adjusts > 0)
+       ret = gen_rtx (PARALLEL, (enum machine_mode) cum->fp_code,
+                      gen_rtvec_v (cum->num_adjusts, cum->adjust));
+      else if (TARGET_MIPS16 && cum->fp_code != 0)
+       ret = gen_rtx (REG, (enum machine_mode) cum->fp_code, 0);
+    }
 
   return ret;
 }
@@ -3404,20 +3954,43 @@ override_options ()
   else if (optimize)
     target_flags |= MASK_GPOPT;
 
+#ifndef MIPS_ISA_DEFAULT
+#define MIPS_ISA_DEFAULT 1
+#endif
+
+  /* If both single-float and soft-float are set, then clear the one that
+     was set by TARGET_DEFAULT, leaving the one that was set by the
+     user.  We assume here that the specs prevent both being set by the 
+     user. */
+#ifdef TARGET_DEFAULT
+  if (TARGET_SINGLE_FLOAT && TARGET_SOFT_FLOAT)
+    target_flags &= ~(TARGET_DEFAULT&(MASK_SOFT_FLOAT|MASK_SINGLE_FLOAT));
+#endif
+
   /* Get the architectural level.  */
   if (mips_isa_string == (char *)0)
-    {
-#ifdef MIPS_ISA_DEFAULT
-      mips_isa = MIPS_ISA_DEFAULT;
-#else
-      mips_isa = 1;
-#endif
-    }
+    mips_isa = MIPS_ISA_DEFAULT;
 
   else if (isdigit (*mips_isa_string))
     {
       mips_isa = atoi (mips_isa_string);
-      if (mips_isa < 1 || mips_isa > 4)
+      if (mips_isa == 16)
+       {
+         /* -mno-mips16 overrides -mips16.  */
+         if (mips_no_mips16_string == NULL)
+           {
+             target_flags |= MASK_MIPS16;
+             if (TARGET_64BIT)
+               mips_isa = 3;
+             else
+               mips_isa = MIPS_ISA_DEFAULT;
+           }
+         else
+           {
+             mips_isa = MIPS_ISA_DEFAULT;
+           }
+       }
+      else if (mips_isa < 1 || mips_isa > 4)
        {
          error ("-mips%d not supported", mips_isa);
          mips_isa = 1;
@@ -3547,7 +4120,14 @@ override_options ()
       mips_cpu = PROCESSOR_DEFAULT;
       switch (*p)
        {
-       case '2':
+       /* start-sanitize-tx19 */
+       case '1':
+         if (!strcmp (p, "1900"))
+           mips_cpu = PROCESSOR_R3900;
+         break;
+       /* end-sanitize-tx19 */
+
+       case '2':
          if (!strcmp (p, "2000") || !strcmp (p, "2k") || !strcmp (p, "2K"))
            mips_cpu = PROCESSOR_R3000;
          break;
@@ -3686,13 +4266,13 @@ override_options ()
     }
 
   /* This optimization requires a linker that can support a R_MIPS_LO16
-     relocation which is not immediately preceded by a R_MIPS_HI16 relocation.
+     relocation which is not immediately preceeded by a R_MIPS_HI16 relocation.
      GNU ld has this support, but not all other MIPS linkers do, so we enable
      this optimization only if the user requests it, or if GNU ld is the
      standard linker for this configuration.  */
   /* ??? This does not work when target addresses are DImode.
      This is because we are missing DImode high/lo_sum patterns.  */
-  if (TARGET_GAS && TARGET_SPLIT_ADDRESSES && optimize && ! flag_pic
+  if (TARGET_GAS && ! TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES && optimize && ! flag_pic
       && Pmode == SImode)
     mips_split_addresses = 1;
   else
@@ -3706,6 +4286,41 @@ override_options ()
   if (TARGET_NAME_REGS)
     bcopy ((char *) mips_sw_reg_names, (char *) mips_reg_names, sizeof (mips_reg_names));
 
+  /* When compiling for the mips16, we can not use floating point.  We
+     record the original hard float value in mips16_hard_float.  */
+  if (TARGET_MIPS16)
+    {
+      if (TARGET_SOFT_FLOAT)
+       mips16_hard_float = 0;
+      else
+       mips16_hard_float = 1;
+      target_flags |= MASK_SOFT_FLOAT;
+
+      /* Don't run the scheduler before reload, since it tends to
+         increase register pressure.  */
+      flag_schedule_insns = 0;
+    }
+
+  /* We put -mentry in TARGET_OPTIONS rather than TARGET_SWITCHES only
+     to avoid using up another bit in target_flags.  */
+  if (mips_entry_string != NULL)
+    {
+      if (*mips_entry_string != '\0')
+       error ("Invalid option `entry%s'", mips_entry_string);
+
+      if (! TARGET_MIPS16)
+       warning ("-mentry is only meaningful with -mips-16");
+      else
+       mips_entry = 1;
+    }
+
+  /* We copy TARGET_MIPS16 into the mips16 global variable, so that
+     attributes can access it.  */
+  if (TARGET_MIPS16)
+    mips16 = 1;
+  else
+    mips16 = 0;
+
   /* If this is OSF/1, set up a SIGINFO handler so we can see what function
      is currently being compiled.  */
 #ifdef SIGINFO
@@ -3751,8 +4366,12 @@ override_options ()
   mips_print_operand_punct['{'] = TRUE;
   mips_print_operand_punct['}'] = TRUE;
   mips_print_operand_punct['^'] = TRUE;
+  mips_print_operand_punct['$'] = TRUE;
+  mips_print_operand_punct['+'] = TRUE;
 
-  mips_char_to_class['d'] = GR_REGS;
+  mips_char_to_class['d'] = TARGET_MIPS16 ? M16_REGS : GR_REGS;
+  mips_char_to_class['e'] = M16_NA_REGS;
+  mips_char_to_class['t'] = T_REG;
   mips_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS);
   mips_char_to_class['h'] = HI_REG;
   mips_char_to_class['l'] = LO_REG;
@@ -3827,6 +4446,28 @@ override_options ()
     }
 }
 
+/* On the mips16, we want to allocate $24 (T_REG) before other
+   registers for instructions for which it is possible.  This helps
+   avoid shuffling registers around in order to set up for an xor,
+   encouraging the compiler to use a cmp instead.  */
+
+void
+mips_order_regs_for_local_alloc ()
+{
+  register int i;
+
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    reg_alloc_order[i] = i;
+
+  if (TARGET_MIPS16)
+    {
+      /* It really doesn't matter where we put register 0, since it is
+         a fixed register anyhow.  */
+      reg_alloc_order[0] = 24;
+      reg_alloc_order[24] = 0;
+    }
+}
+
 \f
 /*
  * The MIPS debug format wants all automatic variables and arguments
@@ -3850,7 +4491,8 @@ mips_debugger_offset (addr, offset)
   if (!offset)
     offset = INTVAL (offset2);
 
-  if (reg == stack_pointer_rtx || reg == frame_pointer_rtx)
+  if (reg == stack_pointer_rtx || reg == frame_pointer_rtx
+      || reg == hard_frame_pointer_rtx)
     {
       int frame_size = (!current_frame_info.initialized)
                                ? compute_frame_size (get_frame_size ())
@@ -3920,7 +4562,9 @@ mips_debugger_offset (addr, offset)
    '?' Print 'l' if we are to use a branch likely instead of normal branch.
    '@' Print the name of the assembler temporary register (at or $1).
    '.' Print the name of the register with a hard-wired zero (zero or $0).
-   '^' Print the name of the pic call-through register (t9 or $25).  */
+   '^' Print the name of the pic call-through register (t9 or $25).
+   '$' Print the name of the stack pointer register (sp or $29).
+   '+' Print the name of the gp register (gp or $28).  */
 
 void
 print_operand (file, op, letter)
@@ -3955,6 +4599,14 @@ print_operand (file, op, letter)
          fputs (reg_names [GP_REG_FIRST + 0], file);
          break;
 
+       case '$':
+         fputs (reg_names[STACK_POINTER_REGNUM], file);
+         break;
+
+       case '+':
+         fputs (reg_names[GP_REG_FIRST + 28], file);
+         break;
+
        case '&':
          if (final_sequence != 0 && set_noreorder++ == 0)
            fputs (".set\tnoreorder\n\t", file);
@@ -4177,6 +4829,19 @@ print_operand (file, op, letter)
   else if (letter == 't')
     fputs (code == EQ ? "t" : "f", file);
 
+  else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG)
+    {
+      /* This case arises on the mips16; see mips16_gp_pseudo_reg.  */
+      print_operand (file, XEXP (op, 0), letter);
+    }
+
+  else if (TARGET_MIPS16 && code == CONST && mips16_gp_offset_p (op))
+    {
+      fputs ("%gprel(", file);
+      mips16_output_gp_offset (file, op);
+      fputs (")", file);
+    }
+
   else
     output_addr_const (file, op);
 }
@@ -4207,7 +4872,7 @@ print_operand_address (file, addr)
        break;
 
       case REG:
-       if (REGNO (addr) == ARG_POINTER_REGNUM)
+       if (! TARGET_MIPS16 && REGNO (addr) == ARG_POINTER_REGNUM)
          abort_with_insn (addr, "Arg pointer not eliminated.");
 
        fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
@@ -4263,7 +4928,16 @@ print_operand_address (file, addr)
          if (REGNO (reg) == ARG_POINTER_REGNUM)
            abort_with_insn (addr, "Arg pointer not eliminated.");
 
-         output_addr_const (file, offset);
+         if (TARGET_MIPS16
+             && GET_CODE (offset) == CONST
+             && mips16_gp_offset_p (offset))
+           {
+             fputs ("%gprel(", file);
+             mips16_output_gp_offset (file, offset);
+             fputs (")", file);
+           }
+         else
+           output_addr_const (file, offset);
          fprintf (file, "(%s)", reg_names [REGNO (reg)]);
        }
        break;
@@ -4578,6 +5252,9 @@ mips_asm_file_start (stream)
     /* ??? but do not want this (or want pic0) if -non-shared? */
     fprintf (stream, "\t%s\n", ABICALLS_ASM_OP);
 
+  if (TARGET_MIPS16)
+    fprintf (stream, "\t.set\tmips16\n");
+
   /* Start a section, so that the first .popsection directive is guaranteed
      to have a previously defined section to pop back to.  */
   if (mips_abi != ABI_32 && mips_abi != ABI_EABI)
@@ -4586,7 +5263,7 @@ mips_asm_file_start (stream)
   /* This code exists so that we can put all externs before all symbol
      references.  This is necessary for the MIPS assembler's global pointer
      optimizations to work.  */
-  if (TARGET_FILE_SWITCHING)
+  if (TARGET_FILE_SWITCHING && ! TARGET_MIPS16)
     {
       asm_out_data_file = stream;
       asm_out_text_file = make_temp_file ();
@@ -4645,7 +5322,7 @@ mips_asm_file_end (file)
        }
     }
       
-  if (TARGET_FILE_SWITCHING)
+  if (TARGET_FILE_SWITCHING && ! TARGET_MIPS16)
     {
       fprintf (file, "\n\t.text\n");
       rewind (asm_out_text_file);
@@ -4823,10 +5500,39 @@ compute_frame_size (size)
   /* Calculate space needed for gp registers.  */
   for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
     {
-      if (MUST_SAVE_REGISTER (regno))
+      /* $18 is a special case on the mips16.  It may be used to call
+         a function which returns a floating point value, but it is
+         marked in call_used_regs.  $31 is also a special case.  When
+         not using -mentry, it will be used to copy a return value
+         into the floating point registers if the return value is
+         floating point.  */
+      if (MUST_SAVE_REGISTER (regno)
+         || (TARGET_MIPS16
+             && regno == GP_REG_FIRST + 18
+             && regs_ever_live[regno])
+         || (TARGET_MIPS16
+             && regno == GP_REG_FIRST + 31
+             && mips16_hard_float
+             && ! mips_entry
+             && ! aggregate_value_p (DECL_RESULT (current_function_decl))
+             && (GET_MODE_CLASS (DECL_MODE (DECL_RESULT (current_function_decl)))
+                 == MODE_FLOAT)
+             && (! TARGET_SINGLE_FLOAT
+                 || (GET_MODE_SIZE (DECL_MODE (DECL_RESULT (current_function_decl)))
+                     <= 4))))
        {
          gp_reg_size += UNITS_PER_WORD;
          mask |= 1L << (regno - GP_REG_FIRST);
+
+         /* The entry and exit pseudo instructions can not save $17
+            without also saving $16.  */
+         if (mips_entry
+             && regno == GP_REG_FIRST + 17
+             && ! MUST_SAVE_REGISTER (GP_REG_FIRST + 16))
+           {
+             gp_reg_size += UNITS_PER_WORD;
+             mask |= 1L << 16;
+           }
        }
     }
 
@@ -4877,6 +5583,10 @@ compute_frame_size (size)
   if (mips_abi != ABI_32)
     total_size += MIPS_STACK_ALIGN (current_function_pretend_args_size);
 
+  /* The entry pseudo instruction will allocate 32 bytes on the stack.  */
+  if (mips_entry && total_size > 0 && total_size < 32)
+    total_size = 32;
+
   /* Save other computed information.  */
   current_frame_info.total_size  = total_size;
   current_frame_info.var_size    = var_size;
@@ -4892,8 +5602,15 @@ compute_frame_size (size)
 
   if (mask)
     {
-      unsigned long offset = (args_size + extra_size + var_size
-                             + gp_reg_size - UNITS_PER_WORD);
+      unsigned long offset;
+
+      /* When using mips_entry, the registers are always saved at the
+         top of the stack.  */
+      if (! mips_entry)
+       offset = (args_size + extra_size + var_size
+                 + gp_reg_size - UNITS_PER_WORD);
+      else
+       offset = total_size - UNITS_PER_WORD;
       current_frame_info.gp_sp_offset = offset;
       current_frame_info.gp_save_offset = offset - total_size;
     }
@@ -4948,7 +5665,7 @@ save_restore_insns (store_p, large_reg, large_offset, file)
   long end_offset;
   rtx insn;
 
-  if (frame_pointer_needed && !BITSET_P (mask, FRAME_POINTER_REGNUM - GP_REG_FIRST))
+  if (frame_pointer_needed && !BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
     abort ();
 
   if (mask == 0 && fmask == 0)
@@ -4975,6 +5692,14 @@ save_restore_insns (store_p, large_reg, large_offset, file)
        fatal ("gp_offset (%ld) or end_offset (%ld) is less than zero.",
               gp_offset, end_offset);
 
+      /* If we see a large frame in mips16 mode, we save the registers
+         before adjusting the stack pointer, and load them afterward.  */
+      else if (TARGET_MIPS16 && large_offset > 32767)
+       {
+         base_reg_rtx = stack_pointer_rtx;
+         base_offset = large_offset;
+       }
+
       else if (gp_offset < 32768)
        {
          base_reg_rtx = stack_pointer_rtx;
@@ -5054,17 +5779,51 @@ save_restore_insns (store_p, large_reg, large_offset, file)
                     reg_names[STACK_POINTER_REGNUM]);
        }
 
-      for  (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
+      /* When we restore the registers in MIPS16 mode, then if we are
+         using a frame pointer, and this is not a large frame, the
+         current stack pointer will be offset by
+         current_function_outgoing_args_size.  Doing it this way lets
+         us avoid offsetting the frame pointer before copying it into
+         the stack pointer; there is no instruction to set the stack
+         pointer to the sum of a register and a constant.  */
+      if (TARGET_MIPS16
+         && ! store_p
+         && frame_pointer_needed
+         && large_offset <= 32767)
+       base_offset += current_function_outgoing_args_size;
+
+      for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
        {
          if (BITSET_P (mask, regno - GP_REG_FIRST))
            {
              if (file == (FILE *)0)
                {
-                 rtx reg_rtx = gen_rtx (REG, word_mode, regno);
+                 rtx reg_rtx;
                  rtx mem_rtx = gen_rtx (MEM, word_mode,
                                         gen_rtx (PLUS, Pmode, base_reg_rtx,
                                                  GEN_INT (gp_offset - base_offset)));
 
+                 /* The mips16 does not have an instruction to load
+                     $31, so we load $7 instead, and work things out
+                     in the caller.  */
+                 if (TARGET_MIPS16 && ! store_p && regno == GP_REG_FIRST + 31)
+                   reg_rtx = gen_rtx (REG, word_mode, GP_REG_FIRST + 7);
+                 /* The mips16 sometimes needs to save $18.  */
+                 else if (TARGET_MIPS16
+                          && regno != GP_REG_FIRST + 31
+                          && ! M16_REG_P (regno))
+                   {
+                     if (! store_p)
+                       reg_rtx = gen_rtx (REG, word_mode, 6);
+                     else
+                       {
+                         reg_rtx = gen_rtx (REG, word_mode, 3);
+                         emit_move_insn (reg_rtx,
+                                         gen_rtx (REG, word_mode, regno));
+                       }
+                   }
+                 else
+                   reg_rtx = gen_rtx (REG, word_mode, regno);
                  if (store_p)
                    {
                      insn = emit_move_insn (mem_rtx, reg_rtx);
@@ -5072,19 +5831,55 @@ save_restore_insns (store_p, large_reg, large_offset, file)
                    }
                  else if (!TARGET_ABICALLS || mips_abi != ABI_32
                           || regno != (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
-                   emit_move_insn (reg_rtx, mem_rtx);
+                   {
+                     emit_move_insn (reg_rtx, mem_rtx);
+                     if (TARGET_MIPS16
+                         && regno != GP_REG_FIRST + 31
+                         && ! M16_REG_P (regno))
+                       emit_move_insn (gen_rtx (REG, word_mode, regno),
+                                       reg_rtx);
+                   }
                }
              else
                {
                  if (store_p || !TARGET_ABICALLS || mips_abi != ABI_32
                      || regno != (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
-                   fprintf (file, "\t%s\t%s,%ld(%s)\n",
-                            (TARGET_64BIT
-                             ? (store_p) ? "sd" : "ld"
-                             : (store_p) ? "sw" : "lw"),
-                            reg_names[regno],
-                            gp_offset - base_offset,
-                            reg_names[REGNO(base_reg_rtx)]);
+                   {
+                     int r = regno;
+
+                     /* The mips16 does not have an instruction to
+                         load $31, so we load $7 instead, and work
+                         things out in the caller.  */
+                     if (TARGET_MIPS16 && ! store_p && r == GP_REG_FIRST + 31)
+                       r = GP_REG_FIRST + 7;
+                     /* The mips16 sometimes needs to save $18.  */
+                     if (TARGET_MIPS16
+                         && regno != GP_REG_FIRST + 31
+                         && ! M16_REG_P (regno))
+                       {
+                         if (! store_p)
+                           r = GP_REG_FIRST + 6;
+                         else
+                           {
+                             r = GP_REG_FIRST + 3;
+                             fprintf (file, "\tmove\t%s,%s\n",
+                                      reg_names[r], reg_names[regno]);
+                           }
+                       }
+                     fprintf (file, "\t%s\t%s,%ld(%s)\n",
+                              (TARGET_64BIT
+                               ? (store_p) ? "sd" : "ld"
+                               : (store_p) ? "sw" : "lw"),
+                              reg_names[r],
+                              gp_offset - base_offset,
+                              reg_names[REGNO(base_reg_rtx)]);
+                     if (! store_p
+                         && TARGET_MIPS16
+                         && regno != GP_REG_FIRST + 31
+                         && ! M16_REG_P (regno))
+                       fprintf (file, "\tmove\t%s,%s\n",
+                                reg_names[regno], reg_names[r]);
+                   }
 
                }
              gp_offset -= UNITS_PER_WORD;
@@ -5254,6 +6049,14 @@ function_prologue (file, size)
     ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
 #endif
 
+  /* In mips16 mode, we may need to generate a 32 bit to handle
+     floating point arguments.  The linker will arrange for any 32 bit
+     functions to call this stub, which will then jump to the 16 bit
+     function proper.  */
+  if (TARGET_MIPS16 && !TARGET_SOFT_FLOAT
+      && current_function_args_info.fp_code != 0)
+    build_mips16_function_stub (file);
+
   inside_function = 1;
 
 #ifndef FUNCTION_NAME_ALREADY_DECLARED
@@ -5292,6 +6095,121 @@ function_prologue (file, size)
               current_frame_info.fp_save_offset);
     }
 
+  if (mips_entry && ! mips_can_use_return_insn ())
+    {
+      int save16 = BITSET_P (current_frame_info.mask, 16);
+      int save17 = BITSET_P (current_frame_info.mask, 17);
+      int save31 = BITSET_P (current_frame_info.mask, 31);
+      int savearg = 0;
+      rtx insn;
+
+      /* Look through the initial insns to see if any of them store
+        the function parameters into the incoming parameter storage
+        area.  If they do, we delete the insn, and save the register
+        using the entry pseudo-instruction instead.  We don't try to
+        look past a label, jump, or call.  */
+      for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
+       {
+         rtx note, set, src, dest, base, offset;
+         int hireg;
+
+         if (GET_CODE (insn) == CODE_LABEL
+             || GET_CODE (insn) == JUMP_INSN
+             || GET_CODE (insn) == CALL_INSN)
+           break;
+         if (GET_CODE (insn) != INSN)
+           continue;
+         set = PATTERN (insn);
+         if (GET_CODE (set) != SET)
+           continue;
+
+         /* An insn storing a function parameter will still have a
+             REG_EQUIV note on it mentioning the argument pointer.  */
+         note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
+         if (note == NULL_RTX)
+           continue;
+         if (! reg_mentioned_p (arg_pointer_rtx, XEXP (note, 0)))
+           continue;
+
+         src = SET_SRC (set);
+         if (GET_CODE (src) != REG
+             || REGNO (src) < GP_REG_FIRST + 4
+             || REGNO (src) > GP_REG_FIRST + 7)
+           continue;
+
+         dest = SET_DEST (set);
+         if (GET_CODE (dest) != MEM)
+           continue;
+         if (GET_MODE_SIZE (GET_MODE (dest)) == UNITS_PER_WORD)
+           ;
+         else if (GET_MODE_SIZE (GET_MODE (dest)) == 2 * UNITS_PER_WORD
+                  && REGNO (src) < GP_REG_FIRST + 7)
+           ;
+         else
+           continue;
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (dest, 0), &offset);
+         if (GET_CODE (base) != REG
+             || GET_CODE (offset) != CONST_INT)
+           continue;
+         if (REGNO (base) == STACK_POINTER_REGNUM
+             && INTVAL (offset) == tsize + (REGNO (src) - 4) * UNITS_PER_WORD)
+           ;
+         else if (REGNO (base) == HARD_FRAME_POINTER_REGNUM
+                  && (INTVAL (offset)
+                      == (tsize
+                          + (REGNO (src) - 4) * UNITS_PER_WORD
+                          - current_function_outgoing_args_size)))
+           ;
+         else
+           continue;
+
+         /* This insn stores a parameter onto the stack, in the same
+             location where the entry pseudo-instruction will put it.
+             Delete the insn, and arrange to tell the entry
+             instruction to save the register.  */
+         PUT_CODE (insn, NOTE);
+         NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+         NOTE_SOURCE_FILE (insn) = 0;
+
+         hireg = (REGNO (src)
+                  + HARD_REGNO_NREGS (REGNO (src), GET_MODE (dest))
+                  - 1);
+         if (hireg > savearg)
+           savearg = hireg;
+       }
+
+      /* If this is a varargs function, we need to save all the
+         registers onto the stack anyhow.  */
+      if (current_function_stdarg || current_function_varargs)
+       savearg = GP_REG_FIRST + 7;
+
+      fprintf (file, "\tentry\t");
+      if (savearg > 0)
+       {
+         if (savearg == GP_REG_FIRST + 4)
+           fprintf (file, "%s", reg_names[savearg]);
+         else
+           fprintf (file, "%s-%s", reg_names[GP_REG_FIRST + 4],
+                    reg_names[savearg]);
+       }
+      if (save16 || save17)
+       {
+         if (savearg > 0)
+           fprintf (file, ",");
+         fprintf (file, "%s", reg_names[GP_REG_FIRST + 16]);
+         if (save17)
+           fprintf (file, "-%s", reg_names[GP_REG_FIRST + 17]);
+       }
+      if (save31)
+       {
+         if (savearg > 0 || save16 || save17)
+           fprintf (file, ",");
+         fprintf (file, "%s", reg_names[GP_REG_FIRST + 31]);
+       }
+      fprintf (file, "\n");
+    }
+
   if (TARGET_ABICALLS && mips_abi == ABI_32)
     {
       char *sp_str = reg_names[STACK_POINTER_REGNUM];
@@ -5329,6 +6247,7 @@ mips_expand_prologue ()
   tree next_arg;
   tree cur_arg;
   CUMULATIVE_ARGS args_so_far;
+  rtx reg_18_save = NULL_RTX;
 
   /* If struct value address is treated as the first argument, make it so.  */
   if (aggregate_value_p (DECL_RESULT (fndecl))
@@ -5421,6 +6340,7 @@ mips_expand_prologue ()
   /* If this function is a varargs function, store any registers that
      would normally hold arguments ($4 - $7) on the stack.  */
   if (mips_abi == ABI_32
+      && (! mips_entry || mips_can_use_return_insn ())
       && ((TYPE_ARG_TYPES (fntype) != 0
           && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
          || (arg_name != (char *)0
@@ -5444,12 +6364,90 @@ mips_expand_prologue ()
        }
     }
 
+  /* If we are using the entry pseudo instruction, it will
+     automatically subtract 32 from the stack pointer, so we don't
+     need to.  The entry pseudo instruction is emitted by
+     function_prologue.  */
+  if (mips_entry && ! mips_can_use_return_insn ())
+    {
+      if (tsize > 0 && tsize <= 32 && frame_pointer_needed)
+       {
+          rtx insn;
+
+         /* If we are using a frame pointer with a small stack frame,
+             we need to initialize it here since it won't be done
+             below.  */
+         if (TARGET_MIPS16 && current_function_outgoing_args_size != 0)
+           {
+             rtx incr = GEN_INT (current_function_outgoing_args_size);
+             if (TARGET_64BIT)
+               insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
+                                              stack_pointer_rtx,
+                                              incr));
+             else
+               insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
+                                              stack_pointer_rtx,
+                                              incr));
+           }
+         else if (TARGET_64BIT)
+           insn = emit_insn (gen_movdi (hard_frame_pointer_rtx, stack_pointer_rtx));
+         else
+           insn = emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx));
+
+          RTX_FRAME_RELATED_P (insn) = 1;
+       }
+
+      /* We may need to save $18, if it is used to call a function
+        which may return a floating point value.  Set up a sequence
+        of instructions to do so.  Later on we emit them at the right
+        moment.  */
+      if (TARGET_MIPS16 && BITSET_P (current_frame_info.mask, 18))
+       {
+         rtx reg_rtx = gen_rtx (REG, word_mode, GP_REG_FIRST + 3);
+         long gp_offset, base_offset;
+
+         gp_offset = current_frame_info.gp_sp_offset;
+         if (BITSET_P (current_frame_info.mask, 16))
+           gp_offset -= UNITS_PER_WORD;
+         if (BITSET_P (current_frame_info.mask, 17))
+           gp_offset -= UNITS_PER_WORD;
+         if (BITSET_P (current_frame_info.mask, 31))
+           gp_offset -= UNITS_PER_WORD;
+         if (tsize > 32767)
+           base_offset = tsize;
+         else
+           base_offset = 0;
+         start_sequence ();
+         emit_move_insn (reg_rtx,
+                         gen_rtx (REG, word_mode, GP_REG_FIRST + 18));
+         emit_move_insn (gen_rtx (MEM, word_mode,
+                                  gen_rtx (PLUS, Pmode, stack_pointer_rtx,
+                                           GEN_INT (gp_offset
+                                                    - base_offset))),
+                         reg_rtx);
+         reg_18_save = gen_sequence ();
+         end_sequence ();
+       }
+
+      if (tsize > 32)
+       tsize -= 32;
+      else
+       {
+         tsize = 0;
+         if (reg_18_save != NULL_RTX)
+           emit_insn (reg_18_save);
+       }
+    }
+
   if (tsize > 0)
     {
       rtx tsize_rtx = GEN_INT (tsize);
 
-      /* If we are doing svr4-abi, sp move is done by function_prologue.  */
-      if (!TARGET_ABICALLS || mips_abi != ABI_32)
+      /* If we are doing svr4-abi, sp move is done by
+         function_prologue.  In mips16 mode with a large frame, we
+         save the registers before adjusting the stack.  */
+      if ((!TARGET_ABICALLS || mips_abi != ABI_32)
+         && (!TARGET_MIPS16 || tsize <= 32767))
        {
          rtx insn;
 
@@ -5490,16 +6488,78 @@ mips_expand_prologue ()
          RTX_FRAME_RELATED_P (insn) = 1;
        }
 
-      save_restore_insns (TRUE, tmp_rtx, tsize, (FILE *)0);
+      if (! mips_entry)
+       save_restore_insns (TRUE, tmp_rtx, tsize, (FILE *)0);
+      else if (reg_18_save != NULL_RTX)
+       emit_insn (reg_18_save);
 
-      if (frame_pointer_needed)
+      if ((!TARGET_ABICALLS || mips_abi != ABI_32)
+         && TARGET_MIPS16
+         && tsize > 32767)
        {
-         rtx insn;
+         rtx reg_rtx;
 
-         if (TARGET_64BIT)
-           insn= emit_insn (gen_movdi (frame_pointer_rtx, stack_pointer_rtx));
+         if (!frame_pointer_needed)
+           abort ();
+
+         reg_rtx = gen_rtx (REG, word_mode, 3);
+         emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
+         emit_move_insn (reg_rtx, tsize_rtx);
+         if (TARGET_64BIT)
+           emit_insn (gen_subdi3 (hard_frame_pointer_rtx,
+                                  hard_frame_pointer_rtx,
+                                  reg_rtx));
+         else
+           emit_insn (gen_subsi3 (hard_frame_pointer_rtx,
+                                  hard_frame_pointer_rtx,
+                                  reg_rtx));
+         emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
+       }
+
+      if (frame_pointer_needed)
+       {
+          rtx insn = 0;
+
+         /* On the mips16, we encourage the use of unextended
+             instructions when using the frame pointer by pointing the
+             frame pointer ahead of the argument space allocated on
+             the stack.  */
+         if ((! TARGET_ABICALLS || mips_abi != ABI_32)
+             && TARGET_MIPS16
+             && tsize > 32767)
+           {
+             /* In this case, we have already copied the stack
+                 pointer into the frame pointer, above.  We need only
+                 adjust for the outgoing argument size.  */
+             if (current_function_outgoing_args_size != 0)
+               {
+                 rtx incr = GEN_INT (current_function_outgoing_args_size);
+                 if (TARGET_64BIT)
+                   insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
+                                                  hard_frame_pointer_rtx,
+                                                  incr));
+                 else
+                   insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
+                                                  hard_frame_pointer_rtx,
+                                                  incr));
+               }
+           }
+         else if (TARGET_MIPS16 && current_function_outgoing_args_size != 0)
+           {
+             rtx incr = GEN_INT (current_function_outgoing_args_size);
+             if (TARGET_64BIT)
+               insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
+                                              stack_pointer_rtx,
+                                              incr));
+             else
+               insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
+                                              stack_pointer_rtx,
+                                              incr));
+           }
+         else if (TARGET_64BIT)
+           insn = emit_insn (gen_movdi (hard_frame_pointer_rtx, stack_pointer_rtx));
          else
-           insn= emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
+           insn= emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx));
 
          RTX_FRAME_RELATED_P (insn) = 1;
        }
@@ -5597,11 +6657,20 @@ function_epilogue (file, size)
   mips_load_reg2     = (rtx)0;
   current_frame_info = zero_frame_info;
 
+  while (string_constants != NULL)
+    {
+      struct string_constant *next;
+
+      next = string_constants->next;
+      free (string_constants);
+      string_constants = next;
+    }
+
   /* Restore the output file if optimizing the GP (optimizing the GP causes
      the text to be diverted to a tempfile, so that data decls come before
      references to the data).  */
 
-  if (TARGET_GP_OPT)
+  if (TARGET_GP_OPT && ! TARGET_MIPS16 && ! TARGET_GAS)
     asm_out_file = asm_out_data_file;
 }
 
@@ -5621,7 +6690,10 @@ mips_expand_epilogue ()
       return;
     }
 
-  if (tsize > 32767)
+  if (mips_entry && ! mips_can_use_return_insn ())
+    tsize -= 32;
+
+  if (tsize > 32767 && ! TARGET_MIPS16)
     {
       tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
       emit_move_insn (tmp_rtx, tsize_rtx);
@@ -5630,13 +6702,44 @@ mips_expand_epilogue ()
 
   if (tsize > 0)
     {
+      long orig_tsize = tsize;
+
       if (frame_pointer_needed)
        {
          emit_insn (gen_blockage ());
+
+         /* On the mips16, the frame pointer is offset from the stack
+             pointer by current_function_outgoing_args_size.  We
+             account for that by changing tsize.  Note that this can
+             actually make tsize negative.  */
+         if (TARGET_MIPS16)
+           {
+             tsize -= current_function_outgoing_args_size;
+
+             /* If we have a large frame, it's easier to add to $17
+                 than to $sp, since the mips16 has no instruction to
+                 add a register to $sp.  */
+             if (orig_tsize > 32767)
+               {
+                 rtx g6_rtx = gen_rtx (REG, word_mode, GP_REG_FIRST + 6);
+
+                 emit_move_insn (g6_rtx, GEN_INT (tsize));
+                 if (TARGET_LONG64)
+                   emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
+                                          hard_frame_pointer_rtx,
+                                          g6_rtx));
+                 else
+                   emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
+                                          hard_frame_pointer_rtx,
+                                          g6_rtx));
+                 tsize = 0;
+               }
+           }
+
          if (TARGET_LONG64)
-           emit_insn (gen_movdi (stack_pointer_rtx, frame_pointer_rtx));
+           emit_insn (gen_movdi (stack_pointer_rtx, hard_frame_pointer_rtx));
          else
-           emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
+           emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
        }
       /* The GP/PIC register is implicitly used by all SYMBOL_REFs, so if we
         are going to restore it, then we must emit a blockage insn to
@@ -5648,6 +6751,13 @@ mips_expand_epilogue ()
 
       save_restore_insns (FALSE, tmp_rtx, tsize, (FILE *)0);
 
+      /* In mips16 mode with a large frame, we adjust the stack
+         pointer before restoring the registers.  In this case, we
+         should always be using a frame pointer, so everything should
+         have been handled above.  */
+      if (tsize > 32767 && TARGET_MIPS16)
+       abort ();
+
       emit_insn (gen_blockage ());
       if (TARGET_LONG64)
        emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx,
@@ -5657,7 +6767,13 @@ mips_expand_epilogue ()
                               tsize_rtx));
     }
 
-  emit_jump_insn (gen_return_internal ());
+  /* The mips16 loads the return address into $7, not $31.  */
+  if (TARGET_MIPS16 && (current_frame_info.mask & RA_MASK) != 0)
+    emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode,
+                                                 GP_REG_FIRST + 7)));
+  else
+    emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode,
+                                                 GP_REG_FIRST + 31)));
 }
 
 \f
@@ -5674,6 +6790,19 @@ mips_can_use_return_insn ()
   if (regs_ever_live[31] || profile_flag)
     return 0;
 
+  /* In mips16 mode, a function which returns a floating point value
+     needs to arrange to copy the return value into the floating point
+     registers.  */
+  if (TARGET_MIPS16
+      && mips16_hard_float
+      && ! aggregate_value_p (DECL_RESULT (current_function_decl))
+      && (GET_MODE_CLASS (DECL_MODE (DECL_RESULT (current_function_decl)))
+         == MODE_FLOAT)
+      && (! TARGET_SINGLE_FLOAT
+         || (GET_MODE_SIZE (DECL_MODE (DECL_RESULT (current_function_decl)))
+             <= 4)))
+    return 0;
+
   if (current_frame_info.initialized)
     return current_frame_info.total_size == 0;
 
@@ -5687,7 +6816,14 @@ mips_select_rtx_section (mode, x)
      enum machine_mode mode;
      rtx x;
 {
-  if (TARGET_EMBEDDED_DATA)
+  if (TARGET_MIPS16)
+    {
+      /* In mips16 mode, the constant table always goes in the .text
+         section, so that constants can be loaded using PC relative
+         addressing.  */
+      text_section ();
+    }
+  else if (TARGET_EMBEDDED_DATA)
     {
       /* For embedded applications, always put constants in read-only data,
         in order to reduce RAM usage.  */
@@ -5715,13 +6851,15 @@ mips_select_section (decl, reloc)
 {
   int size = int_size_in_bytes (TREE_TYPE (decl));
 
-  if (TARGET_EMBEDDED_PIC
+  if ((TARGET_EMBEDDED_PIC || TARGET_MIPS16)
       && TREE_CODE (decl) == STRING_CST
       && !flag_writable_strings)
     {
       /* For embedded position independent code, put constant strings
         in the text section, because the data section is limited to
-        64K in size.  */
+        64K in size.  For mips16 code, put strings in the text
+        section so that a PC relative load instruction can be used to
+        get their address.  */
 
       text_section ();
     }
@@ -5783,6 +6921,11 @@ mips_function_value (valtype, func)
   int reg = GP_RETURN;
   enum machine_mode mode = TYPE_MODE (valtype);
   enum mode_class mclass = GET_MODE_CLASS (mode);
+  int unsignedp = TREE_UNSIGNED (valtype);
+
+  /* Since we define PROMOTE_FUNCTION_RETURN, we must promote the mode
+     just as PROMOTE_MODE does.  */
+  mode = promote_mode (valtype, mode, &unsignedp, 1);
 
   /* ??? How should we return complex float?  */
   if (mclass == MODE_FLOAT || mclass == MODE_COMPLEX_FLOAT)
@@ -5894,7 +7037,9 @@ mips_secondary_reload_class (class, mode, x, in_p)
      rtx x;
      int in_p;
 {
+  enum reg_class gr_regs = TARGET_MIPS16 ? M16_REGS : GR_REGS;
   int regno = -1;
+  int gp_reg_p;
 
   if (GET_CODE (x) == SIGN_EXTEND)
     {
@@ -5920,39 +7065,51 @@ mips_secondary_reload_class (class, mode, x, in_p)
   else if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
     regno = true_regnum (x);
 
+  gp_reg_p = TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
+
   /* We always require a general register when copying anything to
      HILO_REGNUM, except when copying an SImode value from HILO_REGNUM
      to a general register, or when copying from register 0.  */
   if (class == HILO_REG && regno != GP_REG_FIRST + 0)
     {
       if (! in_p
-         && GP_REG_P (regno)
+         && gp_reg_p
          && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode))
        return NO_REGS;
-      return GR_REGS;
+      return gr_regs;
     }
   if (regno == HILO_REGNUM)
     {
       if (in_p
-         && class == GR_REGS
+         && class == gr_regs
          && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode))
        return NO_REGS;
-      return GR_REGS;
+      return gr_regs;
     }
 
   /* Copying from HI or LO to anywhere other than a general register
      requires a general register.  */
   if (class == HI_REG || class == LO_REG || class == MD_REGS)
     {
-      if (GP_REG_P (regno))
+      if (TARGET_MIPS16 && in_p)
+       {
+         /* We can't really copy to HI or LO at all in mips16 mode.  */
+         return M16_REGS;
+       }
+      if (gp_reg_p)
        return NO_REGS;
-      return GR_REGS;
+      return gr_regs;
     }
   if (MD_REG_P (regno))
     {
-      if (class == GR_REGS)
+      if (TARGET_MIPS16 && ! in_p)
+       {
+         /* We can't really copy to HI or LO at all in mips16 mode.  */
+         return M16_REGS;
+       }
+      if (class == gr_regs)
        return NO_REGS;
-      return GR_REGS;
+      return gr_regs;
     }
 
   /* We can only copy a value to a condition code register from a
@@ -5976,5 +7133,1278 @@ mips_secondary_reload_class (class, mode, x, in_p)
       return GR_REGS;
     }
 
+  /* In mips16 mode, going between memory and anything but M16_REGS
+     requires an M16_REG.  */
+  if (TARGET_MIPS16)
+    {
+      if (class != M16_REGS && class != M16_NA_REGS)
+       {
+         if (gp_reg_p)
+           return NO_REGS;
+         return M16_REGS;
+       }
+      if (! gp_reg_p)
+       {
+         if (class == M16_REGS || class == M16_NA_REGS)
+           return NO_REGS;
+         return M16_REGS;
+       }
+    }
+
   return NO_REGS;
 }
+\f
+/* For each mips16 function which refers to GP relative symbols, we
+   use a pseudo register, initialized at the start of the function, to
+   hold the $gp value.  */
+
+rtx
+mips16_gp_pseudo_reg ()
+{
+  if (mips16_gp_pseudo_rtx == NULL_RTX)
+    {
+      rtx const_gp;
+      rtx insn, scan;
+
+      mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
+      RTX_UNCHANGING_P (mips16_gp_pseudo_rtx) = 1;
+
+      /* We want to initialize this to a value which gcc will believe
+         is constant.  */
+      const_gp = gen_rtx (CONST, Pmode,
+                         gen_rtx (REG, Pmode, GP_REG_FIRST + 28));
+
+      start_sequence ();
+      emit_move_insn (mips16_gp_pseudo_rtx, const_gp);
+      insn = gen_sequence ();
+      end_sequence ();
+
+      push_topmost_sequence ();
+      /* We need to emit the initialization after the FUNCTION_BEG
+         note, so that it will be integrated.  */
+      for (scan = get_insns (); scan != NULL_RTX; scan = NEXT_INSN (scan))
+       if (GET_CODE (scan) == NOTE
+           && NOTE_LINE_NUMBER (scan) == NOTE_INSN_FUNCTION_BEG)
+         break;
+      if (scan == NULL_RTX)
+       scan = get_insns ();
+      insn = emit_insn_after (insn, scan);
+      pop_topmost_sequence ();
+    }
+
+  return mips16_gp_pseudo_rtx;
+}
+
+/* Return an RTX which represents the signed 16 bit offset from the
+   $gp register for the given symbol.  This is only used on the
+   mips16.  */
+
+rtx
+mips16_gp_offset (sym)
+     rtx sym;
+{
+  tree gp;
+
+  if (GET_CODE (sym) != SYMBOL_REF
+      || ! SYMBOL_REF_FLAG (sym))
+    abort ();
+
+  /* We use a special identifier to represent the value of the gp
+     register.  */
+  gp = get_identifier ("__mips16_gp_value");
+
+  return gen_rtx (CONST, Pmode,
+                 gen_rtx (MINUS, Pmode, sym,
+                          gen_rtx (SYMBOL_REF, Pmode,
+                                   IDENTIFIER_POINTER (gp))));
+}
+
+/* Return nonzero if the given RTX represents a signed 16 bit offset
+   from the $gp register.  */
+
+int
+mips16_gp_offset_p (x)
+     rtx x;
+{
+  if (GET_CODE (x) == CONST)
+    x = XEXP (x, 0);
+
+  /* It's OK to add a small integer value to a gp offset.  */
+  if (GET_CODE (x) == PLUS)
+    {
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && SMALL_INT (XEXP (x, 1)))
+       return mips16_gp_offset_p (XEXP (x, 0));
+      if (GET_CODE (XEXP (x, 0)) == CONST_INT
+         && SMALL_INT (XEXP (x, 0)))
+       return mips16_gp_offset_p (XEXP (x, 1));
+      return 0;
+    }
+
+  /* Make sure it is in the form SYM - __mips16_gp_value.  */
+  return (GET_CODE (x) == MINUS
+         && GET_CODE (XEXP (x, 0)) == SYMBOL_REF
+         && SYMBOL_REF_FLAG (XEXP (x, 0))
+         && GET_CODE (XEXP (x, 1)) == SYMBOL_REF
+         && strcmp (XSTR (XEXP (x, 1), 0), "__mips16_gp_value") == 0);
+}
+
+/* Output a GP offset.  We don't want to print the subtraction of
+   __mips16_gp_value; it is implicitly represented by the %gprel which
+   should have been printed by the caller.  */
+
+static void
+mips16_output_gp_offset (file, x)
+     FILE *file;
+     rtx x;
+{
+  if (GET_CODE (x) == CONST)
+    x = XEXP (x, 0);
+
+  if (GET_CODE (x) == PLUS)
+    {
+      mips16_output_gp_offset (file, XEXP (x, 0));
+      fputs ("+", file);
+      mips16_output_gp_offset (file, XEXP (x, 1));
+      return;
+    }
+
+  if (GET_CODE (x) == MINUS
+      && GET_CODE (XEXP (x, 1)) == SYMBOL_REF
+      && strcmp (XSTR (XEXP (x, 1), 0), "__mips16_gp_value") == 0)
+    {
+      mips16_output_gp_offset (file, XEXP (x, 0));
+      return;
+    }
+
+  output_addr_const (file, x);
+}
+
+/* Return nonzero if a constant should not be output until after the
+   function.  This is true of most string constants, so that we can
+   use a more efficient PC relative reference.  However, a static
+   inline function may never call assemble_function_end to write out
+   the constant pool, so don't try to postpone the constant in that
+   case.
+
+   ??? It's really a bug that a static inline function can put stuff
+   in the constant pool even if the function itself is not output.
+
+   We record which string constants we've seen, so that we know which
+   ones might use the more efficient reference.  */
+
+int
+mips16_constant_after_function_p (x)
+     tree x;
+{
+  if (TREE_CODE (x) == STRING_CST
+      && ! flag_writable_strings
+      && current_function_decl != 0
+      && ! DECL_DEFER_OUTPUT (current_function_decl)
+      && ! (DECL_INLINE (current_function_decl)
+           && ((! TREE_PUBLIC (current_function_decl)
+                && ! TREE_ADDRESSABLE (current_function_decl)
+                && ! flag_keep_inline_functions)
+               || DECL_EXTERNAL (current_function_decl))))
+    {
+      struct string_constant *n;
+
+      n = (struct string_constant *) xmalloc (sizeof *n);
+      n->label = XSTR (XEXP (TREE_CST_RTL (x), 0), 0);
+      n->next = string_constants;
+      string_constants = n;
+
+      return 1;
+    }
+
+  return 0;
+}
+
+/* Validate a constant for the mips16.  This rejects general symbolic
+   addresses, which must be loaded from memory.  If ADDR is nonzero,
+   this should reject anything which is not a legal address.  If
+   ADDEND is nonzero, this is being added to something else.  */
+
+int
+mips16_constant (x, mode, addr, addend)
+     rtx x;
+     enum machine_mode mode;
+     int addr;
+     int addend;
+{
+  while (GET_CODE (x) == CONST)
+    x = XEXP (x, 0);
+
+  switch (GET_CODE (x))
+    {
+    default:
+      return 0;
+
+    case PLUS:
+      return (mips16_constant (XEXP (x, 0), mode, addr, 1)
+             && mips16_constant (XEXP (x, 1), mode, addr, 1));
+
+    case SYMBOL_REF:
+      if (addr && GET_MODE_SIZE (mode) != 4 && GET_MODE_SIZE (mode) != 8)
+       return 0;
+      if (CONSTANT_POOL_ADDRESS_P (x))
+       return 1;
+
+      /* If we aren't looking for a memory address, we can accept a GP
+         relative symbol, which will have SYMBOL_REF_FLAG set; movsi
+         knows how to handle this.  We can always accept a string
+         constant, which is the other case in which SYMBOL_REF_FLAG
+         will be set.  */
+      if (! addr && ! addend && SYMBOL_REF_FLAG (x) && mode == Pmode)
+       return 1;
+
+      /* We can accept a string constant, which will have
+         SYMBOL_REF_FLAG set but must be recognized by name to
+         distinguish from a GP accessible symbol.  The name of a
+         string constant will have been generated by
+         ASM_GENERATE_INTERNAL_LABEL as called by output_constant_def.  */
+      if (SYMBOL_REF_FLAG (x))
+       {
+         char *name = XSTR (x, 0);
+
+         return (name[0] == '*'
+                 && strncmp (name + 1, LOCAL_LABEL_PREFIX,
+                             sizeof LOCAL_LABEL_PREFIX - 1) == 0);
+       }
+
+      return 0;
+
+    case LABEL_REF:
+      if (addr && GET_MODE_SIZE (mode) != 4 && GET_MODE_SIZE (mode) != 8)
+       return 0;
+      return 1;
+
+    case CONST_INT:
+      if (addr && ! addend)
+       return 0;
+      return INTVAL (x) > - 0x10000 && INTVAL (x) <= 0xffff;
+
+    case REG:
+      /* We need to treat $gp as a legitimate constant, because
+         mips16_gp_pseudo_reg assumes that.  */
+      return REGNO (x) == GP_REG_FIRST + 28;
+    }
+}
+
+/* Write out code to move floating point arguments in or out of
+   general registers.  Output the instructions to FILE.  FP_CODE is
+   the code describing which arguments are present (see the comment at
+   the definition of CUMULATIVE_ARGS in mips.h).  FROM_FP_P is non-zero if
+   we are copying from the floating point registers.  */
+
+static void
+mips16_fp_args (file, fp_code, from_fp_p)
+     FILE *file;
+     int fp_code;
+     int from_fp_p;
+{
+  char *s;
+  int gparg, fparg;
+  unsigned int f;
+
+  /* This code only works for the original 32 bit ABI.  */
+  if (mips_abi != ABI_32)
+    abort ();
+
+  if (from_fp_p)
+    s = "mfc1";
+  else
+    s = "mtc1";
+  gparg = GP_ARG_FIRST;
+  fparg = FP_ARG_FIRST;
+  for (f = (unsigned int) fp_code; f != 0; f >>= 2)
+    {
+      if ((f & 3) == 1)
+       {
+         if ((fparg & 1) != 0)
+           ++fparg;
+         fprintf (file, "\t%s\t%s,%s\n", s,
+                  reg_names[gparg], reg_names[fparg]);
+       }
+      else if ((f & 3) == 2)
+       {
+         if (TARGET_64BIT)
+           fprintf (file, "\td%s\t%s,%s\n", s,
+                    reg_names[gparg], reg_names[fparg]);
+         else
+           {
+             if ((fparg & 1) != 0)
+               ++fparg;
+             fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
+                      reg_names[gparg], reg_names[fparg + 1], s,
+                      reg_names[gparg + 1], reg_names[fparg]);
+             ++gparg;
+             ++fparg;
+           }
+       }
+      else
+       abort ();
+
+      ++gparg;
+      ++fparg;
+    }
+}
+
+/* Build a mips16 function stub.  This is used for functions which
+   take aruments in the floating point registers.  It is 32 bit code
+   that moves the floating point args into the general registers, and
+   then jumps to the 16 bit code.  */
+
+static void
+build_mips16_function_stub (file)
+     FILE *file;
+{
+  char *fnname;
+  char *secname, *stubname;
+  tree stubid, stubdecl;
+  int need_comma;
+  unsigned int f;
+
+  fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+  secname = (char *) alloca (strlen (fnname) + 20);
+  sprintf (secname, ".mips16.fn.%s", fnname);
+  stubname = (char *) alloca (strlen (fnname) + 20);
+  sprintf (stubname, "__fn_stub_%s", fnname);
+  stubid = get_identifier (stubname);
+  stubdecl = build_decl (FUNCTION_DECL, stubid,
+                        build_function_type (void_type_node, NULL_TREE));
+  DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+
+  fprintf (file, "\t# Stub function for %s (", current_function_name);
+  need_comma = 0;
+  for (f = (unsigned int) current_function_args_info.fp_code; f != 0; f >>= 2)
+    {
+      fprintf (file, "%s%s",
+              need_comma ? ", " : "",
+              (f & 3) == 1 ? "float" : "double");
+      need_comma = 1;
+    }
+  fprintf (file, ")\n");
+
+  fprintf (file, "\t.set\tnomips16\n");
+  function_section (stubdecl);
+  ASM_OUTPUT_ALIGN (file, floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT));
+
+  /* ??? If FUNCTION_NAME_ALREADY_DECLARED is defined, then we are
+     within a .ent, and we can not emit another .ent.  */
+#ifndef FUNCTION_NAME_ALREADY_DECLARED
+  fputs ("\t.ent\t", file);
+  assemble_name (file, stubname);
+  fputs ("\n", file);
+#endif
+
+  assemble_name (file, stubname);
+  fputs (":\n", file);
+
+  /* We don't want the assembler to insert any nops here.  */
+  fprintf (file, "\t.set\tnoreorder\n");
+
+  mips16_fp_args (file, current_function_args_info.fp_code, 1);
+
+  fprintf (asm_out_file, "\t.set\tnoat\n");
+  fprintf (asm_out_file, "\tla\t%s,", reg_names[GP_REG_FIRST + 1]);
+  assemble_name (file, fnname);
+  fprintf (file, "\n");
+  fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
+  fprintf (asm_out_file, "\t.set\tat\n");
+
+  /* Unfortunately, we can't fill the jump delay slot.  We can't fill
+     with one of the mfc1 instructions, because the result is not
+     available for one instruction, so if the very first instruction
+     in the function refers to the register, it will see the wrong
+     value.  */
+  fprintf (file, "\tnop\n");
+
+  fprintf (file, "\t.set\treorder\n");
+
+#ifndef FUNCTION_NAME_ALREADY_DECLARED
+  fputs ("\t.end\t", file);
+  assemble_name (file, stubname);
+  fputs ("\n", file);
+#endif
+
+  fprintf (file, "\t.set\tmips16\n");
+
+  function_section (current_function_decl);
+}
+
+/* We keep a list of functions for which we have already built stubs
+   in build_mips16_call_stub.  */
+
+struct mips16_stub
+{
+  struct mips16_stub *next;
+  char *name;
+  int fpret;
+};
+
+static struct mips16_stub *mips16_stubs;
+
+/* Build a call stub for a mips16 call.  A stub is needed if we are
+   passing any floating point values which should go into the floating
+   point registers.  If we are, and the call turns out to be to a 32
+   bit function, the stub will be used to move the values into the
+   floating point registers before calling the 32 bit function.  The
+   linker will magically adjust the function call to either the 16 bit
+   function or the 32 bit stub, depending upon where the function call
+   is actually defined.
+
+   Similarly, we need a stub if the return value might come back in a
+   floating point register.
+
+   RETVAL, FNMEM, and ARG_SIZE are the values passed to the call insn
+   (RETVAL is NULL if this is call rather than call_value).  FP_CODE
+   is the code built by function_arg.  This function returns a nonzero
+   value if it builds the call instruction itself.  */
+
+int
+build_mips16_call_stub (retval, fnmem, arg_size, fp_code)
+     rtx retval;
+     rtx fnmem;
+     rtx arg_size;
+     int fp_code;
+{
+  int fpret;
+  rtx fn;
+  char *fnname, *secname, *stubname;
+  struct mips16_stub *l;
+  tree stubid, stubdecl;
+  int need_comma;
+  unsigned int f;
+
+  /* We don't need to do anything if we aren't in mips16 mode, or if
+     we were invoked with the -msoft-float option.  */
+  if (! TARGET_MIPS16 || ! mips16_hard_float)
+    return 0;
+
+  /* Figure out whether the value might come back in a floating point
+     register.  */
+  fpret = (retval != 0
+          && GET_MODE_CLASS (GET_MODE (retval)) == MODE_FLOAT
+          && (! TARGET_SINGLE_FLOAT
+              || GET_MODE_SIZE (GET_MODE (retval)) <= 4));
+
+  /* We don't need to do anything if there were no floating point
+     arguments and the value will not be returned in a floating point
+     register.  */
+  if (fp_code == 0 && ! fpret)
+    return 0;
+
+  if (GET_CODE (fnmem) != MEM)
+    abort ();
+  fn = XEXP (fnmem, 0);
+
+  /* We don't need to do anything if this is a call to a special
+     mips16 support function.  */
+  if (GET_CODE (fn) == SYMBOL_REF
+      && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0)
+    return 0;
+
+  /* This code will only work for the standard ABI.  The other ABI's
+     require more sophisticated support.  */
+  if (mips_abi != ABI_32)
+    abort ();
+
+  /* We can only handle SFmode and DFmode floating point return
+     values.  */
+  if (fpret && GET_MODE (retval) != SFmode && GET_MODE (retval) != DFmode)
+    abort ();
+
+  /* If we're calling via a function pointer, then we must always call
+     via a stub.  There are magic stubs provided in libgcc.a for each
+     of the required cases.  Each of them expects the function address
+     to arrive in register $2.  */
+
+  if (GET_CODE (fn) != SYMBOL_REF)
+    {
+      char buf[30];
+      tree id;
+      rtx stub_fn, stub_mem, insn;
+
+      /* ??? If this code is modified to support other ABI's, we need
+         to handle PARALLEL return values here.  */
+
+      sprintf (buf, "__mips16_call_stub_%s%d",
+              (fpret
+               ? (GET_MODE (retval) == SFmode ? "sf_" : "df_")
+               : ""),
+              fp_code);
+      id = get_identifier (buf);
+      stub_fn = gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (id));
+      stub_mem = gen_rtx (MEM, Pmode, stub_fn);
+
+      emit_move_insn (gen_rtx (REG, Pmode, 2), fn);
+
+      if (retval == NULL_RTX)
+       insn = gen_call_internal0 (stub_mem, arg_size,
+                                  gen_rtx (REG, SImode,
+                                           GP_REG_FIRST + 31));
+      else
+       insn = gen_call_value_internal0 (retval, stub_mem, arg_size,
+                                        gen_rtx (REG, SImode,
+                                                 GP_REG_FIRST + 31));
+      insn = emit_call_insn (insn);
+
+      /* Put the register usage information on the CALL.  */
+      if (GET_CODE (insn) != CALL_INSN)
+       abort ();
+      CALL_INSN_FUNCTION_USAGE (insn) =
+       gen_rtx (EXPR_LIST, VOIDmode,
+                gen_rtx (USE, VOIDmode, gen_rtx (REG, Pmode, 2)),
+                CALL_INSN_FUNCTION_USAGE (insn));
+
+      /* If we are handling a floating point return value, we need to
+         save $18 in the function prologue.  Putting a note on the
+         call will mean that regs_ever_live[$18] will be true if the
+         call is not eliminated, and we can check that in the prologue
+         code.  */
+      if (fpret)
+       CALL_INSN_FUNCTION_USAGE (insn) =
+         gen_rtx (EXPR_LIST, VOIDmode,
+                  gen_rtx (USE, VOIDmode, gen_rtx (REG, word_mode, 18)),
+                  CALL_INSN_FUNCTION_USAGE (insn));
+
+      /* Return 1 to tell the caller that we've generated the call
+         insn.  */
+      return 1;
+    }
+
+  /* We know the function we are going to call.  If we have already
+     built a stub, we don't need to do anything further.  */
+
+  fnname = XSTR (fn, 0);
+  for (l = mips16_stubs; l != NULL; l = l->next)
+    if (strcmp (l->name, fnname) == 0)
+      break;
+
+  if (l == NULL)
+    {
+      /* Build a special purpose stub.  When the linker sees a
+        function call in mips16 code, it will check where the target
+        is defined.  If the target is a 32 bit call, the linker will
+        search for the section defined here.  It can tell which
+        symbol this section is associated with by looking at the
+        relocation information (the name is unreliable, since this
+        might be a static function).  If such a section is found, the
+        linker will redirect the call to the start of the magic
+        section.
+
+        If the function does not return a floating point value, the
+        special stub section is named
+            .mips16.call.FNNAME
+
+        If the function does return a floating point value, the stub
+        section is named
+            .mips16.call.fp.FNNAME
+        */
+
+      secname = (char *) alloca (strlen (fnname) + 40);
+      sprintf (secname, ".mips16.call.%s%s",
+              fpret ? "fp." : "",
+              fnname);
+      stubname = (char *) alloca (strlen (fnname) + 20);
+      sprintf (stubname, "__call_stub_%s%s",
+              fpret ? "fp_" : "",
+              fnname);
+      stubid = get_identifier (stubname);
+      stubdecl = build_decl (FUNCTION_DECL, stubid,
+                            build_function_type (void_type_node, NULL_TREE));
+      DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+
+      fprintf (asm_out_file, "\t# Stub function to call %s%s (",
+              (fpret
+               ? (GET_MODE (retval) == SFmode ? "float " : "double ")
+               : ""),
+              fnname);
+      need_comma = 0;
+      for (f = (unsigned int) fp_code; f != 0; f >>= 2)
+       {
+         fprintf (asm_out_file, "%s%s",
+                  need_comma ? ", " : "",
+                  (f & 3) == 1 ? "float" : "double");
+         need_comma = 1;
+       }
+      fprintf (asm_out_file, ")\n");
+
+      fprintf (asm_out_file, "\t.set\tnomips16\n");
+      assemble_start_function (stubdecl, stubname);
+
+#ifndef FUNCTION_NAME_ALREADY_DECLARED
+      fputs ("\t.ent\t", asm_out_file);
+      assemble_name (asm_out_file, stubname);
+      fputs ("\n", asm_out_file);
+
+      assemble_name (asm_out_file, stubname);
+      fputs (":\n", asm_out_file);
+#endif
+
+      /* We build the stub code by hand.  That's the only way we can
+        do it, since we can't generate 32 bit code during a 16 bit
+        compilation. */
+
+      /* We don't want the assembler to insert any nops here.  */
+      fprintf (asm_out_file, "\t.set\tnoreorder\n");
+
+      mips16_fp_args (asm_out_file, fp_code, 0);
+
+      if (! fpret)
+       {
+         fprintf (asm_out_file, "\t.set\tnoat\n");
+         fprintf (asm_out_file, "\tla\t%s,%s\n", reg_names[GP_REG_FIRST + 1],
+                  fnname);
+         fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
+         fprintf (asm_out_file, "\t.set\tat\n");
+         /* Unfortunately, we can't fill the jump delay slot.  We
+            can't fill with one of the mtc1 instructions, because the
+            result is not available for one instruction, so if the
+            very first instruction in the function refers to the
+            register, it will see the wrong value.  */
+         fprintf (asm_out_file, "\tnop\n");
+       }
+      else
+       {
+         fprintf (asm_out_file, "\tmove\t%s,%s\n",
+                  reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]);
+         fprintf (asm_out_file, "\tjal\t%s\n", fnname);
+         /* As above, we can't fill the delay slot.  */
+         fprintf (asm_out_file, "\tnop\n");
+         if (GET_MODE (retval) == SFmode)
+           fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                    reg_names[GP_REG_FIRST + 2], reg_names[FP_REG_FIRST + 0]);
+         else
+           {
+             fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                      reg_names[GP_REG_FIRST + 2],
+                      reg_names[FP_REG_FIRST + 1]);
+             fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+                      reg_names[GP_REG_FIRST + 3],
+                      reg_names[FP_REG_FIRST + 0]);
+           }
+         fprintf (asm_out_file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 18]);
+         /* As above, we can't fill the delay slot.  */
+         fprintf (asm_out_file, "\tnop\n");
+       }
+
+      fprintf (asm_out_file, "\t.set\treorder\n");
+
+#ifdef ASM_DECLARE_FUNCTION_SIZE
+      ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
+#endif
+
+#ifndef FUNCTION_NAME_ALREADY_DECLARED
+      fputs ("\t.end\t", asm_out_file);
+      assemble_name (asm_out_file, stubname);
+      fputs ("\n", asm_out_file);
+#endif
+
+      fprintf (asm_out_file, "\t.set\tmips16\n");
+
+      /* Record this stub.  */
+      l = (struct mips16_stub *) xmalloc (sizeof *l);
+      l->name = (char *) xmalloc (strlen (fnname) + 1);
+      strcpy (l->name, fnname);
+      l->fpret = fpret;
+      l->next = mips16_stubs;
+      mips16_stubs = l;
+    }
+
+  /* If we expect a floating point return value, but we've built a
+     stub which does not expect one, then we're in trouble.  We can't
+     use the existing stub, because it won't handle the floating point
+     value.  We can't build a new stub, because the linker won't know
+     which stub to use for the various calls in this object file.
+     Fortunately, this case is illegal, since it means that a function
+     was declared in two different ways in a single compilation.  */
+  if (fpret && ! l->fpret)
+    error ("can not handle inconsistent calls to `%s'", fnname);
+
+  /* If we are calling a stub which handles a floating point return
+     value, we need to arrange to save $18 in the prologue.  We do
+     this by marking the function call as using the register.  The
+     prologue will later see that it is used, and emit code to save
+     it.  */
+
+  if (l->fpret)
+    {
+      rtx insn;
+
+      if (retval == NULL_RTX)
+       insn = gen_call_internal0 (fnmem, arg_size,
+                                  gen_rtx (REG, SImode,
+                                           GP_REG_FIRST + 31));
+      else
+       insn = gen_call_value_internal0 (retval, fnmem, arg_size,
+                                        gen_rtx (REG, SImode,
+                                                 GP_REG_FIRST + 31));
+      insn = emit_call_insn (insn);
+
+      if (GET_CODE (insn) != CALL_INSN)
+       abort ();
+
+      CALL_INSN_FUNCTION_USAGE (insn) =
+       gen_rtx (EXPR_LIST, VOIDmode,
+                gen_rtx (USE, VOIDmode, gen_rtx (REG, word_mode, 18)),
+                CALL_INSN_FUNCTION_USAGE (insn));
+
+      /* Return 1 to tell the caller that we've generated the call
+         insn.  */
+      return 1;
+    }
+
+  /* Return 0 to let the caller generate the call insn.  */
+  return 0;
+}
+
+/* This function looks through the code for a function, and tries to
+   optimize the usage of the $gp register.  We arrange to copy $gp
+   into a pseudo-register, and then let gcc's normal reload handling
+   deal with the pseudo-register.  Unfortunately, if reload choose to
+   put the pseudo-register into a call-clobbered register, it will
+   emit saves and restores for that register around any function
+   calls.  We don't need the saves, and it's faster to copy $gp than
+   to do an actual restore.  ??? This still means that we waste a
+   stack slot.
+
+   This is an optimization, and the code which gcc has actually
+   generated is correct, so we do not need to catch all cases.  */
+
+static void
+mips16_optimize_gp (first)
+     rtx first;
+{
+  rtx gpcopy, slot, insn;
+
+  /* Look through the instructions.  Set GPCOPY to the register which
+     holds a copy of $gp.  Set SLOT to the stack slot where it is
+     saved.  If we find an instruction which sets GPCOPY to anything
+     other than $gp or SLOT, then we can't use it.  If we find an
+     instruction which sets SLOT to anything other than GPCOPY, we
+     can't use it.  */
+
+  gpcopy = NULL_RTX;
+  slot = NULL_RTX;
+  for (insn = first; insn != NULL_RTX; insn = next_active_insn (insn))
+    {
+      rtx set;
+
+      if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+       continue;
+
+      set = PATTERN (insn);
+
+      /* We know that all references to memory will be inside a SET,
+         because there is no other way to access memory on the mips16.
+         We don't have to worry about a PARALLEL here, because the
+         mips.md file will never generate them for memory references.  */
+      if (GET_CODE (set) != SET)
+       continue;
+
+      if (gpcopy == NULL_RTX
+         && GET_CODE (SET_SRC (set)) == CONST
+         && GET_CODE (XEXP (SET_SRC (set), 0)) == REG
+         && REGNO (XEXP (SET_SRC (set), 0)) == GP_REG_FIRST + 28
+         && GET_CODE (SET_DEST (set)) == REG
+         && GET_MODE (SET_DEST (set)) == Pmode)
+       gpcopy = SET_DEST (set);
+      else if (slot == NULL_RTX
+              && gpcopy != NULL_RTX
+              && GET_CODE (SET_DEST (set)) == MEM
+              && GET_CODE (SET_SRC (set)) == REG
+              && REGNO (SET_SRC (set)) == REGNO (gpcopy)
+              && GET_MODE (SET_DEST (set)) == Pmode)
+       {
+         rtx base, offset;
+
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
+         if (GET_CODE (base) == REG
+             && (REGNO (base) == STACK_POINTER_REGNUM
+                 || REGNO (base) == FRAME_POINTER_REGNUM))
+           slot = SET_DEST (set);
+       }
+      else if (gpcopy != NULL_RTX
+              && (GET_CODE (SET_DEST (set)) == REG
+                  || GET_CODE (SET_DEST (set)) == SUBREG)
+              && reg_overlap_mentioned_p (SET_DEST (set), gpcopy)
+              && (GET_CODE (SET_DEST (set)) != REG
+                  || REGNO (SET_DEST (set)) != REGNO (gpcopy)
+                  || GET_MODE (SET_DEST (set)) != Pmode
+                  || ((GET_CODE (SET_SRC (set)) != CONST
+                       || GET_CODE (XEXP (SET_SRC (set), 0)) != REG
+                       || (REGNO (XEXP (SET_SRC (set), 0))
+                           != GP_REG_FIRST + 28))
+                      && ! rtx_equal_p (SET_SRC (set), slot))))
+       break;
+      else if (slot != NULL_RTX
+              && GET_CODE (SET_DEST (set)) == MEM
+              && rtx_equal_p (SET_DEST (set), slot)
+              && (GET_CODE (SET_SRC (set)) != REG
+                  || REGNO (SET_SRC (set)) != REGNO (gpcopy)))
+       break;
+    }
+
+  /* If we couldn't find a unique value for GPCOPY or SLOT, then try a
+     different optimization.  Any time we find a copy of $28 into a
+     register, followed by an add of a symbol_ref to that register, we
+     convert it to load the value from the constant table instead.
+     The copy and add will take six bytes, just as the load and
+     constant table entry will take six bytes.  However, it is
+     possible that the constant table entry will be shared.
+
+     This could be a peephole optimization, but I don't know if the
+     peephole code can call force_const_mem.
+
+     Using the same register for the copy of $28 and the add of the
+     symbol_ref is actually pretty likely, since the add instruction
+     requires the destination and the first addend to be the same
+     register.  */
+
+  if (insn != NULL_RTX || gpcopy == NULL_RTX || slot == NULL_RTX)
+    {
+      rtx next;
+
+      /* This optimization is only reasonable if the constant table
+         entries are only 4 bytes.  */
+      if (Pmode != SImode)
+       return;
+
+      for (insn = first; insn != NULL_RTX; insn = next)
+       {
+         rtx set1, set2;
+
+         next = insn;
+         do
+           {
+             next = NEXT_INSN (next);
+           }
+         while (next != NULL_RTX
+                && (GET_CODE (next) == NOTE
+                    || (GET_CODE (next) == INSN
+                        && (GET_CODE (PATTERN (next)) == USE
+                            || GET_CODE (PATTERN (next)) == CLOBBER))));
+
+         if (next == NULL_RTX)
+           break;
+
+         if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+           continue;
+
+         if (GET_RTX_CLASS (GET_CODE (next)) != 'i')
+           continue;
+
+         set1 = PATTERN (insn);
+         if (GET_CODE (set1) != SET)
+           continue;
+         set2 = PATTERN (next);
+         if (GET_CODE (set2) != SET)
+           continue;
+
+         if (GET_CODE (SET_DEST (set1)) == REG
+             && GET_CODE (SET_SRC (set1)) == CONST
+             && GET_CODE (XEXP (SET_SRC (set1), 0)) == REG
+             && REGNO (XEXP (SET_SRC (set1), 0)) == GP_REG_FIRST + 28
+             && rtx_equal_p (SET_DEST (set1), SET_DEST (set2))
+             && GET_CODE (SET_SRC (set2)) == PLUS
+             && rtx_equal_p (SET_DEST (set1), XEXP (SET_SRC (set2), 0))
+             && mips16_gp_offset_p (XEXP (SET_SRC (set2), 1))
+             && GET_CODE (XEXP (XEXP (SET_SRC (set2), 1), 0)) == MINUS)
+           {
+             rtx sym;
+
+             /* We've found a case we can change to load from the
+                 constant table.  */
+
+             sym = XEXP (XEXP (XEXP (SET_SRC (set2), 1), 0), 0);
+             if (GET_CODE (sym) != SYMBOL_REF)
+               abort ();
+             emit_insn_after (gen_rtx (SET, VOIDmode, SET_DEST (set1),
+                                       force_const_mem (Pmode, sym)),
+                              next);
+             
+             PUT_CODE (insn, NOTE);
+             NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+             NOTE_SOURCE_FILE (insn) = 0;
+
+             PUT_CODE (next, NOTE);
+             NOTE_LINE_NUMBER (next) = NOTE_INSN_DELETED;
+             NOTE_SOURCE_FILE (next) = 0;
+           }
+       }
+
+      return;
+    }
+
+  /* We can safely remove all assignments to SLOT from GPCOPY, and
+     replace all assignments from SLOT to GPCOPY with assignments from
+     $28.  */
+
+  for (insn = first; insn != NULL_RTX; insn = next_active_insn (insn))
+    {
+      rtx set;
+
+      if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
+       continue;
+
+      set = PATTERN (insn);
+      if (GET_CODE (set) != SET
+         || GET_MODE (SET_DEST (set)) != Pmode)
+       continue;
+
+      if (GET_CODE (SET_DEST (set)) == MEM
+         && rtx_equal_p (SET_DEST (set), slot)
+         && GET_CODE (SET_SRC (set)) == REG
+         && REGNO (SET_SRC (set)) == REGNO (gpcopy))
+       {
+         PUT_CODE (insn, NOTE);
+         NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+         NOTE_SOURCE_FILE (insn) = 0;
+       }
+      else if (GET_CODE (SET_DEST (set)) == REG
+              && REGNO (SET_DEST (set)) == REGNO (gpcopy)
+              && GET_CODE (SET_SRC (set)) == MEM
+              && rtx_equal_p (SET_SRC (set), slot))
+       {
+         emit_insn_after (gen_rtx (SET, Pmode, SET_DEST (set),
+                                   gen_rtx (CONST, Pmode,
+                                            gen_rtx (REG, Pmode,
+                                                     GP_REG_FIRST + 28))),
+                          insn);
+         PUT_CODE (insn, NOTE);
+         NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
+         NOTE_SOURCE_FILE (insn) = 0;
+       }
+    }
+}
+
+/* We keep a list of constants we which we have to add to internal
+   constant tables in the middle of large functions.  */
+
+struct constant
+{
+  struct constant *next;
+  rtx value;
+  rtx label;
+  enum machine_mode mode;
+};
+
+/* Add a constant to the list in *PCONSTANTS.  */
+
+static rtx
+add_constant (pconstants, val, mode)
+     struct constant **pconstants;
+     rtx val;
+     enum machine_mode mode;
+{
+  struct constant *c;
+
+  for (c = *pconstants; c != NULL; c = c->next)
+    if (mode == c->mode && rtx_equal_p (val, c->value))
+      return c->label;
+
+  c = (struct constant *) xmalloc (sizeof *c);
+  c->value = val;
+  c->mode = mode;
+  c->label = gen_label_rtx ();
+  c->next = *pconstants;
+  *pconstants = c;
+  return c->label;
+}
+
+/* Dump out the constants in CONSTANTS after INSN.  */
+
+static void
+dump_constants (constants, insn)
+     struct constant *constants;
+     rtx insn;
+{
+  struct constant *c;
+  int align;
+
+  c = constants;
+  align = 0;
+  while (c != NULL)
+    {
+      rtx r;
+      struct constant *next;
+
+      switch (GET_MODE_SIZE (c->mode))
+       {
+       case 1:
+         align = 0;
+         break;
+       case 2:
+         if (align < 1)
+           insn = emit_insn_after (gen_align_2 (), insn);
+         align = 1;
+         break;
+       case 4:
+         if (align < 2)
+           insn = emit_insn_after (gen_align_4 (), insn);
+         align = 2;
+         break;
+       default:
+         if (align < 3)
+           insn = emit_insn_after (gen_align_8 (), insn);
+         align = 3;
+         break;
+       }
+
+      insn = emit_label_after (c->label, insn);
+
+      switch (c->mode)
+       {
+       case QImode:
+         r = gen_consttable_qi (c->value);
+         break;
+       case HImode:
+         r = gen_consttable_hi (c->value);
+         break;
+       case SImode:
+         r = gen_consttable_si (c->value);
+         break;
+       case SFmode:
+         r = gen_consttable_sf (c->value);
+         break;
+       case DImode:
+         r = gen_consttable_di (c->value);
+         break;
+       case DFmode:
+         r = gen_consttable_df (c->value);
+         break;
+       default:
+         abort ();
+       }
+
+      insn = emit_insn_after (r, insn);
+
+      next = c->next;
+      free (c);
+      c = next;
+    }
+
+  emit_barrier_after (insn);
+}
+
+/* Find the symbol in an address expression.  */
+
+static rtx
+mips_find_symbol (addr)
+     rtx addr;
+{
+  if (GET_CODE (addr) == MEM)
+    addr = XEXP (addr, 0);
+  while (GET_CODE (addr) == CONST)
+    addr = XEXP (addr, 0);
+  if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
+    return addr;
+  if (GET_CODE (addr) == PLUS)
+    {
+      rtx l1, l2;
+
+      l1 = mips_find_symbol (XEXP (addr, 0));
+      l2 = mips_find_symbol (XEXP (addr, 1));
+      if (l1 != NULL_RTX && l2 == NULL_RTX)
+       return l1;
+      else if (l1 == NULL_RTX && l2 != NULL_RTX)
+       return l2;
+    }
+  return NULL_RTX;
+}
+
+/* Exported to toplev.c.
+
+   Do a final pass over the function, just before delayed branch
+   scheduling.  */
+
+void
+machine_dependent_reorg (first)
+     rtx first;
+{
+  int insns_len, max_internal_pool_size, pool_size, addr;
+  rtx insn;
+  struct constant *constants;
+
+  if (! TARGET_MIPS16)
+    return;
+
+  /* If $gp is used, try to remove stores, and replace loads with
+     copies from $gp.  */
+  if (optimize)
+    mips16_optimize_gp (first);
+
+  /* Scan the function looking for PC relative loads which may be out
+     of range.  All such loads will either be from the constant table,
+     or be getting the address of a constant string.  If the size of
+     the function plus the size of the constant table is less than
+     0x8000, then all loads are in range.  */
+
+  insns_len = 0;
+  for (insn = first; insn; insn = NEXT_INSN (insn))
+    {
+      insns_len += get_attr_length (insn) * 2;
+
+      /* ??? We put switch tables in .text, but we don't define
+         JUMP_TABLES_IN_TEXT_SECTION, so get_attr_length will not
+         compute their lengths correctly.  */
+      if (GET_CODE (insn) == JUMP_INSN)
+       {
+         rtx body;
+
+         body = PATTERN (insn);
+         if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
+           insns_len += (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC)
+                         * GET_MODE_SIZE (GET_MODE (body)));
+         insns_len += GET_MODE_SIZE (GET_MODE (body)) - 1;
+       }
+    }
+
+  /* Store the original value of insns_len in current_frame_info, so
+     that simple_memory_operand can look at it.  */
+  current_frame_info.insns_len = insns_len;
+
+  pool_size = get_pool_size ();
+  if (insns_len + pool_size + mips_string_length < 0x8000)
+    return;
+
+  /* Loop over the insns and figure out what the maximum internal pool
+     size could be.  */
+  max_internal_pool_size = 0;
+  for (insn = first; insn; insn = NEXT_INSN (insn))
+    {
+      if (GET_CODE (insn) == INSN
+         && GET_CODE (PATTERN (insn)) == SET)
+       {
+         rtx src;
+
+         src = mips_find_symbol (SET_SRC (PATTERN (insn)));
+         if (src == NULL_RTX)
+           continue;
+         if (CONSTANT_POOL_ADDRESS_P (src))
+           max_internal_pool_size += GET_MODE_SIZE (get_pool_mode (src));
+         else if (SYMBOL_REF_FLAG (src))
+           max_internal_pool_size += GET_MODE_SIZE (Pmode);
+       }
+    }
+
+  constants = NULL;
+  addr = 0;
+
+  for (insn = first; insn; insn = NEXT_INSN (insn))
+    {
+      if (GET_CODE (insn) == INSN
+         && GET_CODE (PATTERN (insn)) == SET)
+       {
+         rtx val, src;
+         enum machine_mode mode;
+
+         val = NULL_RTX;
+         src = mips_find_symbol (SET_SRC (PATTERN (insn)));
+         if (src != NULL_RTX && CONSTANT_POOL_ADDRESS_P (src))
+           {
+             /* ??? This is very conservative, which means that we
+                 will generate too many copies of the constant table.
+                 The only solution would seem to be some form of
+                 relaxing.  */
+             if (((insns_len - addr)
+                  + max_internal_pool_size
+                  + get_pool_offset (src))
+                 >= 0x8000)
+               {
+                 val = get_pool_constant (src);
+                 mode = get_pool_mode (src);
+               }
+             max_internal_pool_size -= GET_MODE_SIZE (get_pool_mode (src));
+           }
+         else if (src != NULL_RTX && SYMBOL_REF_FLAG (src))
+           {
+             /* Including all of mips_string_length is conservative,
+                 and so is including all of max_internal_pool_size.  */
+             if (((insns_len - addr)
+                  + max_internal_pool_size
+                  + pool_size
+                  + mips_string_length)
+                 >= 0x8000)
+             val = src;
+             mode = Pmode;
+             max_internal_pool_size -= Pmode;
+           }
+
+         if (val != NULL_RTX)
+           {
+             rtx lab, newsrc;
+
+             /* This PC relative load is out of range.  ??? In the
+                case of a string constant, we are only guessing that
+                it is range, since we don't know the offset of a
+                particular string constant.  */
+
+             lab = add_constant (&constants, val, mode);
+             newsrc = gen_rtx (MEM, mode,
+                               gen_rtx (LABEL_REF, VOIDmode, lab));
+             RTX_UNCHANGING_P (newsrc) = 1;
+             PATTERN (insn) = gen_rtx (SET, VOIDmode,
+                                       SET_DEST (PATTERN (insn)),
+                                       newsrc);
+             INSN_CODE (insn) = -1;
+           }
+       }
+
+      addr += get_attr_length (insn) * 2;
+
+      /* ??? We put switch tables in .text, but we don't define
+         JUMP_TABLES_IN_TEXT_SECTION, so get_attr_length will not
+         compute their lengths correctly.  */
+      if (GET_CODE (insn) == JUMP_INSN)
+       {
+         rtx body;
+
+         body = PATTERN (insn);
+         if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
+           addr += (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC)
+                         * GET_MODE_SIZE (GET_MODE (body)));
+         addr += GET_MODE_SIZE (GET_MODE (body)) - 1;
+       }
+
+      if (GET_CODE (insn) == BARRIER)
+       {
+         /* Output any constants we have accumulated.  Note that we
+             don't need to change ADDR, since its only use is
+             subtraction from INSNS_LEN, and both would be changed by
+             the same amount.
+            ??? If the instructions up to the next barrier reuse a
+            constant, it would often be better to continue
+            accumulating.  */
+         if (constants != NULL)
+           dump_constants (constants, insn);
+         constants = NULL;
+       }
+
+      /* ??? If we don't find a barrier within 0x8000 bytes of
+         instructions and constants in CONSTANTS, we need to invent
+         one.  This seems sufficiently unlikely that I am not going to
+         worry about it.  */
+    }
+
+  if (constants != NULL)
+    {
+      rtx label, jump, barrier;
+
+      label = gen_label_rtx ();
+      jump = emit_jump_insn_after (gen_jump (label), get_last_insn ());
+      JUMP_LABEL (jump) = label;
+      LABEL_NUSES (label) = 1;
+      barrier = emit_barrier_after (jump);
+      emit_label_after (label, barrier);
+      dump_constants (constants, barrier);
+      constants = NULL;
+    }
+
+  /* ??? If we output all references to a constant in internal
+     constants table, we don't need to output the constant in the real
+     constant table, but we have no way to prevent that.  */
+}
index 919092d..e0b9cc3 100644 (file)
@@ -134,9 +134,14 @@ extern enum cmp_type branch_type;  /* what type of branch to use */
 extern enum processor_type mips_cpu;   /* which cpu are we scheduling for */
 extern enum mips_abicalls_type mips_abicalls;/* for svr4 abi pic calls */
 extern int mips_isa;                   /* architectural level */
+extern int mips16;                     /* whether generating mips16 code */
+extern int mips16_hard_float;          /* mips16 without -msoft-float */
+extern int mips_entry;                 /* generate entry/exit for mips16 */
 extern char *mips_cpu_string;          /* for -mcpu=<xxx> */
 extern char *mips_isa_string;          /* for -mips{1,2,3,4} */
 extern char *mips_abi_string;          /* for -misa={32,n32,64} */
+extern char *mips_entry_string;                /* for -mentry */
+extern char *mips_no_mips16_string;    /* for -mno-mips16 */
 extern int mips_split_addresses;       /* perform high/lo_sum support */
 extern int dslots_load_total;          /* total # load related delay slots */
 extern int dslots_load_filled;         /* # filled load delay slots */
@@ -149,6 +154,8 @@ extern struct rtx_def *mips_load_reg2;      /* 2nd reg to check for load delay */
 extern struct rtx_def *mips_load_reg3; /* 3rd reg to check for load delay */
 extern struct rtx_def *mips_load_reg4; /* 4th reg to check for load delay */
 extern struct rtx_def *embedded_pic_fnaddr_rtx;        /* function address */
+extern int mips_string_length;         /* length of strings for mips16 */
+extern struct rtx_def *mips16_gp_pseudo_rtx; /* psuedo reg holding $gp */
 
 /* Functions within mips.c that we reference.  */
 
@@ -157,7 +164,6 @@ extern int          arith32_operand ();
 extern int             arith_operand ();
 extern int             cmp_op ();
 extern long            compute_frame_size ();
-extern int             epilogue_reg_mentioned_p ();
 extern void            expand_block_move ();
 extern int             equality_op ();
 extern void            final_prescan_insn ();
@@ -200,10 +206,17 @@ extern void               print_options ();
 extern int             reg_or_0_operand ();
 extern int             simple_epilogue_p ();
 extern int             simple_memory_operand ();
+extern int             double_memory_operand ();
 extern int             small_int ();
 extern void            trace();
 extern int             uns_arith_operand ();
 extern struct rtx_def *        embedded_pic_offset ();
+extern void            mips_order_regs_for_local_alloc ();
+extern struct rtx_def *        mips16_gp_pseudo_reg ();
+extern struct rtx_def * mips16_gp_offset ();
+extern int             mips16_gp_offset_p ();
+extern int             mips16_constant_after_function_p ();
+extern int             build_mips16_call_stub ();
 
 /* Recognition functions that return if a condition is true.  */
 extern int             address_operand ();
@@ -225,6 +238,24 @@ extern int         se_uns_arith_operand ();
 extern int             se_arith_operand ();
 extern int             se_nonmemory_operand ();
 extern int             se_nonimmediate_operand ();
+extern int             m16_uimm3_b ();
+extern int             m16_simm4_1 ();
+extern int             m16_nsimm4_1 ();
+extern int             m16_simm5_1 ();
+extern int             m16_nsimm5_1 ();
+extern int             m16_uimm5_4 ();
+extern int             m16_nuimm5_4 ();
+extern int             m16_simm8_1 ();
+extern int             m16_nsimm8_1 ();
+extern int             m16_uimm8_1 ();
+extern int             m16_nuimm8_1 ();
+extern int             m16_uimm8_m1_1 ();
+extern int             m16_uimm8_4 ();
+extern int             m16_nuimm8_4 ();
+extern int             m16_simm8_8 ();
+extern int             m16_nsimm8_8 ();
+extern int             m16_usym8_4 ();
+extern int             m16_usym5_4 ();
 
 /* Functions to change what output section we are using.  */
 extern void            data_section ();
@@ -274,6 +305,7 @@ extern void         text_section ();
 #define MASK_MAD       0x00040000      /* Generate mad/madu as on 4650.  */
 #define MASK_4300_MUL_FIX 0x00080000    /* Work-around early Vr4300 CPU bug */
 #define MASK_MIPS3900  0x00100000      /* like -mips1 only 3900 */
+#define MASK_MIPS16    0x01000000      /* Generate mips16 code */
 
                                        /* Dummy switches used only in spec's*/
 #define MASK_MIPS_TFILE        0x00000000      /* flag for mips-tfile usage */
@@ -285,8 +317,8 @@ extern void         text_section ();
 #define MASK_DEBUG_C   0x08000000      /* don't expand seq, etc. */
 #define MASK_DEBUG_D   0x04000000      /* don't do define_split's */
 #define MASK_DEBUG_E   0x02000000      /* function_arg debug */
-#define MASK_DEBUG_F   0x01000000      /* don't try to suppress load nop's */
-#define MASK_DEBUG_G   0x00800000      /* don't support 64 bit arithmetic */
+#define MASK_DEBUG_F   0
+#define MASK_DEBUG_G   0               /* don't support 64 bit arithmetic */
 #define MASK_DEBUG_H   0               /* allow ints in FP registers */
 #define MASK_DEBUG_I   0x00200000      /* unused */
 
@@ -373,6 +405,9 @@ extern void         text_section ();
    to debug the resulting code.  */
 #define NO_DBX_FUNCTION_END TARGET_FILE_SWITCHING
 
+                                       /* Generate mips16 code */
+#define TARGET_MIPS16          (target_flags & MASK_MIPS16)
+
 /* Macro to define tables used to set the flags.
    This is a list in braces of pairs in braces,
    each pair being { "NAME", VALUE }
@@ -500,15 +535,19 @@ extern void               text_section ();
 {                                                                      \
   SUBTARGET_TARGET_OPTIONS                                             \
   { "cpu=",    &mips_cpu_string        },                              \
-  { "ips",     &mips_isa_string        }                               \
+  { "ips",     &mips_isa_string        },                              \
+  { "entry",   &mips_entry_string      },                              \
+  { "no-mips16", &mips_no_mips16_string        }
 }
 
 /* This is meant to be redefined in the host dependent files.  */
 #define SUBTARGET_TARGET_OPTIONS
 
-#define GENERATE_BRANCHLIKELY  (TARGET_MIPS3900 || (mips_isa >= 2))
-#define GENERATE_MULT3         (TARGET_MIPS3900)
-#define GENERATE_MADD          (TARGET_MIPS3900)
+#define GENERATE_BRANCHLIKELY  (!TARGET_MIPS16 && (TARGET_MIPS3900 || (mips_isa >= 2)))
+#define GENERATE_MULT3         (TARGET_MIPS3900                                \
+                               && !TARGET_MIPS16
+#define GENERATE_MADD          (TARGET_MIPS3900                                \
+                               && !TARGET_MIPS16)
 
 
 
@@ -596,6 +635,22 @@ do                                                                 \
        for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)       \
          fixed_regs[regno] = call_used_regs[regno] = 1;                \
       }                                                                        \
+    /* In mips16 mode, we permit the $t temporary registers to be used \
+       for reload.  We prohibit the unused $s registers, since they    \
+       are caller saved, and saving them via a mips16 register would   \
+       probably waste more time than just reloading the value.  */     \
+    if (TARGET_MIPS16)                                                 \
+      {                                                                        \
+       fixed_regs[18] = call_used_regs[18] = 1;                        \
+       fixed_regs[19] = call_used_regs[19] = 1;                        \
+       fixed_regs[20] = call_used_regs[20] = 1;                        \
+       fixed_regs[21] = call_used_regs[21] = 1;                        \
+       fixed_regs[22] = call_used_regs[22] = 1;                        \
+       fixed_regs[23] = call_used_regs[23] = 1;                        \
+       fixed_regs[26] = call_used_regs[26] = 1;                        \
+       fixed_regs[27] = call_used_regs[27] = 1;                        \
+       fixed_regs[30] = call_used_regs[30] = 1;                        \
+      }                                                                        \
     SUBTARGET_CONDITIONAL_REGISTER_USAGE                               \
   }                                                                    \
 while (0)
@@ -720,6 +775,7 @@ while (0)
 
 #define ASM_SPEC "\
 %{G*} %{EB} %{EL} %{mips1} %{mips2} %{mips3} %{mips4} \
+%{mips16:%{!mno-mips16:-mips16}} %{mno-mips16:-no-mips16} \
 %(subtarget_asm_optimizing_spec) \
 %(subtarget_asm_debugging_spec) \
 %{membedded-pic} \
@@ -852,6 +908,7 @@ while (0)
 %{m4650:%{!msoft-float:-D__mips_single_float}} \
 %{msoft-float:-D__mips_soft_float} \
 %{mabi=eabi:-D__mips_eabi} \
+%{mips16:%{!mno-mips16:-D__mips16}} \
 %{EB:-UMIPSEL -U_MIPSEL -U__MIPSEL -U__MIPSEL__ -D_MIPSEB -D__MIPSEB -D__MIPSEB__ %{!ansi:-DMIPSEB}} \
 %{EL:-UMIPSEB -U_MIPSEB -U__MIPSEB -U__MIPSEB__ -D_MIPSEL -D__MIPSEL -D__MIPSEL__ %{!ansi:-DMIPSEL}} \
 %(long_max_spec) \
@@ -1348,6 +1405,31 @@ do {                                                     \
    be the code that says which one of the two operations is implicitly
    done, NIL if none.  */
 #define LOAD_EXTEND_OP(MODE) ZERO_EXTEND
+
+/* Define this macro if it is advisable to hold scalars in registers
+   in a wider mode than that declared by the program.  In such cases, 
+   the value is constrained to be within the bounds of the declared
+   type, but kept valid in the wider mode.  The signedness of the
+   extension may differ from that of the type.
+
+   We promote any value smaller than SImode up to SImode.  We don't
+   want to promote to DImode when in 64 bit mode, because that would
+   prevent us from using the faster SImode multiply and divide
+   instructions.  */
+
+#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE)    \
+  if (GET_MODE_CLASS (MODE) == MODE_INT                \
+      && GET_MODE_SIZE (MODE) < 4)             \
+    (MODE) = SImode;
+
+/* Define this if function arguments should also be promoted using the above
+   procedure.  */
+
+#define PROMOTE_FUNCTION_ARGS
+
+/* Likewise, if the function return value is promoted.  */
+
+#define PROMOTE_FUNCTION_RETURN
 \f
 /* Standard register usage.  */
 
@@ -1436,6 +1518,8 @@ do {                                                      \
 #define FPSW_REGNUM    ST_REG_FIRST
 
 #define GP_REG_P(REGNO) ((unsigned) ((REGNO) - GP_REG_FIRST) < GP_REG_NUM)
+#define M16_REG_P(REGNO) \
+  (((REGNO) >= 2 && (REGNO) <= 7) || (REGNO) == 16 || (REGNO) == 17)
 #define FP_REG_P(REGNO) ((unsigned) ((REGNO) - FP_REG_FIRST) < FP_REG_NUM)
 #define MD_REG_P(REGNO) ((unsigned) ((REGNO) - MD_REG_FIRST) < MD_REG_NUM)
 #define ST_REG_P(REGNO) ((unsigned) ((REGNO) - ST_REG_FIRST) < ST_REG_NUM)
@@ -1490,8 +1574,16 @@ extern char mips_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER];
    the default value zero.  */
 /* #define STACK_POINTER_OFFSET 0 */
 
-/* Base register for access to local variables of the function.  */
-#define FRAME_POINTER_REGNUM (GP_REG_FIRST + 30)
+/* Base register for access to local variables of the function.  We
+   pretend that the frame pointer is $1, and then eliminate it to
+   HARD_FRAME_POINTER_REGNUM.  We can get away with this because $1 is
+   a fixed register, and will not be used for anything else.  */
+#define FRAME_POINTER_REGNUM (GP_REG_FIRST + 1)
+
+/* $30 is not available on the mips16, so we use $17 as the frame
+   pointer.  */
+#define HARD_FRAME_POINTER_REGNUM \
+  (TARGET_MIPS16 ? GP_REG_FIRST + 17 : GP_REG_FIRST + 30)
 
 /* Value should be nonzero if functions must have frame pointers.
    Zero means the frame pointer need not be set up (and parms
@@ -1552,7 +1644,11 @@ extern char mips_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER];
 /* Initialize embedded_pic_fnaddr_rtx before RTL generation for
    each function.  We used to do this in FINALIZE_PIC, but FINALIZE_PIC
    isn't always called for static inline functions.  */
-#define INIT_EXPANDERS embedded_pic_fnaddr_rtx = NULL;
+#define INIT_EXPANDERS                 \
+do {                                   \
+  embedded_pic_fnaddr_rtx = NULL;      \
+  mips16_gp_pseudo_rtx = NULL;         \
+} while (0)
 \f
 /* Define the classes of registers for register constraints in the
    machine description.  Also define ranges of constants.
@@ -1577,6 +1673,10 @@ extern char mips_hard_regno_mode_ok[][FIRST_PSEUDO_REGISTER];
 enum reg_class
 {
   NO_REGS,                     /* no registers in set */
+  M16_NA_REGS,                 /* mips16 regs not used to pass args */
+  M16_REGS,                    /* mips16 directly accessible registers */
+  T_REG,                       /* mips16 T register ($24) */
+  M16_T_REGS,                  /* mips16 registers plus T register */
   GR_REGS,                     /* integer registers */
   FP_REGS,                     /* floating point registers */
   HI_REG,                      /* hi register */
@@ -1599,6 +1699,10 @@ enum reg_class
 #define REG_CLASS_NAMES                                                        \
 {                                                                      \
   "NO_REGS",                                                           \
+  "M16_NA_REGS",                                                       \
+  "M16_REGS",                                                          \
+  "T_REG",                                                             \
+  "M16_T_REGS",                                                                \
   "GR_REGS",                                                           \
   "FP_REGS",                                                           \
   "HI_REG",                                                            \
@@ -1623,6 +1727,10 @@ enum reg_class
 #define REG_CLASS_CONTENTS                                             \
 {                                                                      \
   { 0x00000000, 0x00000000, 0x00000000 },      /* no registers */      \
+  { 0x0003000c, 0x00000000, 0x00000000 },      /* mips16 nonarg regs */\
+  { 0x000300fc, 0x00000000, 0x00000000 },      /* mips16 registers */  \
+  { 0x01000000, 0x00000000, 0x00000000 },      /* mips16 T register */ \
+  { 0x010300fc, 0x00000000, 0x00000000 },      /* mips16 and T regs */ \
   { 0xffffffff, 0x00000000, 0x00000000 },      /* integer registers */ \
   { 0x00000000, 0xffffffff, 0x00000000 },      /* floating registers*/ \
   { 0x00000000, 0x00000000, 0x00000001 },      /* hi register */       \
@@ -1647,7 +1755,7 @@ extern enum reg_class mips_regno_to_class[];
    valid base register must belong.  A base register is one used in
    an address which is the register value plus a displacement.  */
 
-#define BASE_REG_CLASS  GR_REGS
+#define BASE_REG_CLASS  (TARGET_MIPS16 ? M16_REGS : GR_REGS)
 
 /* A macro whose definition is the name of the class to which a
    valid index register must belong.  An index register is one used
@@ -1657,6 +1765,37 @@ extern enum reg_class mips_regno_to_class[];
 
 #define INDEX_REG_CLASS NO_REGS
 
+/* When SMALL_REGISTER_CLASSES is nonzero, the compiler allows
+   registers explicitly used in the rtl to be used as spill registers
+   but prevents the compiler from extending the lifetime of these
+   registers. */
+
+#define SMALL_REGISTER_CLASSES (TARGET_MIPS16)
+
+/* This macro is used later on in the file.  */
+#define GR_REG_CLASS_P(CLASS)                                          \
+  ((CLASS) == GR_REGS || (CLASS) == M16_REGS || (CLASS) == T_REG       \
+   || (CLASS) == M16_T_REGS || (CLASS) == M16_NA_REGS)
+
+/* REG_ALLOC_ORDER is to order in which to allocate registers.  This
+   is the default value (allocate the registers in numeric order).  We
+   define it just so that we can override it for the mips16 target in
+   ORDER_REGS_FOR_LOCAL_ALLOC.  */
+
+#define REG_ALLOC_ORDER                                                        \
+{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,      \
+  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,      \
+  32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,      \
+  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,      \
+  64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75                       \
+}
+
+/* ORDER_REGS_FOR_LOCAL_ALLOC is a macro which permits reg_alloc_order
+   to be rearranged based on a particular function.  On the mips16, we
+   want to allocate $24 (T_REG) before other registers for
+   instructions for which it is possible.  */
+
+#define ORDER_REGS_FOR_LOCAL_ALLOC mips_order_regs_for_local_alloc ()
 
 /* REGISTER AND CONSTANT CLASSES */
 
@@ -1666,6 +1805,10 @@ extern enum reg_class mips_regno_to_class[];
    DEFINED REGISTER CLASSES:
 
    'd'  General (aka integer) registers
+        Normally this is GR_REGS, but in mips16 mode this is M16_REGS
+   'y'  General registers (in both mips16 and non mips16 mode)
+   'e' mips16 non argument registers (M16_NA_REGS)
+   't'  mips16 temporary register ($24)
    'f' Floating point registers
    'h' Hi register
    'l' Lo register
@@ -1700,9 +1843,9 @@ extern enum reg_class mips_char_to_class[];
    `M' is used for the range of constants that take two words to load
        (ie, not matched by `I', `K', and `L').
 
-   `N' is used for negative 16 bit constants.
+   `N' is used for negative 16 bit constants other than -65536.
 
-   `O' is an exact power of 2 (not yet used in the md file).
+   `O' is a 15 bit signed integer.
 
    `P' is used for positive 16 bit constants.  */
 
@@ -1721,8 +1864,8 @@ extern enum reg_class mips_char_to_class[];
                   && (((VALUE) & 0x0000ffff) != 0                      \
                       || (((VALUE) & ~2147483647) != 0                 \
                           && ((VALUE) & ~2147483647) != ~2147483647))) \
-   : (C) == 'N' ? (((VALUE) & ~0x0000ffff) == ~0x0000ffff)             \
-   : (C) == 'O' ? (exact_log2 (VALUE) >= 0)                            \
+   : (C) == 'N' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0xffff) < 0xffff) \
+   : (C) == 'O' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0x4000) < 0x8000) \
    : (C) == 'P' ? ((VALUE) != 0 && (((VALUE) & ~0x0000ffff) == 0))     \
    : 0)
 
@@ -1743,13 +1886,16 @@ extern enum reg_class mips_char_to_class[];
    operand as its first argument and the constraint letter as its
    second operand.
 
-   `Q' is for memory references which take more than 1 instruction.
+   `Q' is for mips16 GP relative constants
    `R' is for memory references which take 1 word for the instruction.
-   `S' is for references to extern items which are PIC for OSF/rose.  */
+   `S' is for references to extern items which are PIC for OSF/rose.
+   `T' is for memory addresses that can be used to load two words.  */
 
 #define EXTRA_CONSTRAINT(OP,CODE)                                      \
-  ((GET_CODE (OP) != MEM) ? FALSE                                      \
-   : ((CODE) == 'Q')     ? !simple_memory_operand (OP, GET_MODE (OP))  \
+  (((CODE) == 'T')       ? double_memory_operand (OP, GET_MODE (OP))   \
+   : ((CODE) == 'Q')     ? (GET_CODE (OP) == CONST                     \
+                            && mips16_gp_offset_p (OP))                \
+   : (GET_CODE (OP) != MEM) ? FALSE                                    \
    : ((CODE) == 'R')     ? simple_memory_operand (OP, GET_MODE (OP))   \
    : ((CODE) == 'S')     ? (HALF_PIC_P () && CONSTANT_P (OP)           \
                             && HALF_PIC_ADDRESS_P (OP))                \
@@ -1762,13 +1908,19 @@ extern enum reg_class mips_char_to_class[];
 
 #define PREFERRED_RELOAD_CLASS(X,CLASS)                                        \
   ((CLASS) != ALL_REGS                                                 \
-   ? (CLASS)                                                           \
+   ? (! TARGET_MIPS16                                                  \
+      ? (CLASS)                                                                \
+      : ((CLASS) != GR_REGS                                            \
+        ? (CLASS)                                                      \
+        : M16_REGS))                                                   \
    : ((GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT                     \
        || GET_MODE_CLASS (GET_MODE (X)) == MODE_COMPLEX_FLOAT)         \
-      ? (TARGET_SOFT_FLOAT ? GR_REGS : FP_REGS)                                \
+      ? (TARGET_SOFT_FLOAT                                             \
+        ? (TARGET_MIPS16 ? M16_REGS : GR_REGS)                         \
+        : FP_REGS)                                                     \
       : ((GET_MODE_CLASS (GET_MODE (X)) == MODE_INT                    \
          || GET_MODE (X) == VOIDmode)                                  \
-        ? GR_REGS                                                      \
+        ? (TARGET_MIPS16 ? M16_REGS : GR_REGS)                         \
         : (CLASS))))
 
 /* Certain machines have the property that some registers cannot be
@@ -1783,11 +1935,11 @@ extern enum reg_class mips_char_to_class[];
 #define SECONDARY_MEMORY_NEEDED(CLASS1, CLASS2, MODE)                  \
   ((!TARGET_DEBUG_H_MODE                                               \
     && GET_MODE_CLASS (MODE) == MODE_INT                               \
-    && ((CLASS1 == FP_REGS && CLASS2 == GR_REGS)                       \
-       || (CLASS1 == GR_REGS && CLASS2 == FP_REGS)))                   \
+    && ((CLASS1 == FP_REGS && GR_REG_CLASS_P (CLASS2))                 \
+       || (GR_REG_CLASS_P (CLASS1) && CLASS2 == FP_REGS)))             \
    || (TARGET_FLOAT64 && !TARGET_64BIT && (MODE) == DFmode             \
-       && ((CLASS1 == GR_REGS && CLASS2 == FP_REGS)                    \
-          || (CLASS2 == GR_REGS && CLASS1 == FP_REGS))))
+       && ((GR_REG_CLASS_P (CLASS1) && CLASS2 == FP_REGS)              \
+          || (GR_REG_CLASS_P (CLASS2) && CLASS1 == FP_REGS))))
 
 /* The HI and LO registers can only be reloaded via the general
    registers.  Condition code registers can only be loaded to the
@@ -1911,6 +2063,7 @@ struct mips_frame_info
   int  initialized;            /* != 0 if frame size already calculated */
   int  num_gp;                 /* number of gp registers saved */
   int  num_fp;                 /* number of fp registers saved */
+  long insns_len;              /* length of insns; mips16 only */
 };
 
 extern struct mips_frame_info current_frame_info;
@@ -1939,27 +2092,41 @@ extern struct mips_frame_info current_frame_info;
          {FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}
 
    Note that the elimination of the argument pointer with the stack
-   pointer is specified first since that is the preferred elimination.  */
+   pointer is specified first since that is the preferred elimination.
+
+   The eliminations to $17 are only used on the mips16.  See the
+   definition of HARD_FRAME_POINTER_REGNUM.  */
 
 #define ELIMINABLE_REGS                                                        \
 {{ ARG_POINTER_REGNUM,   STACK_POINTER_REGNUM},                                \
- { ARG_POINTER_REGNUM,   FRAME_POINTER_REGNUM},                                \
+ { ARG_POINTER_REGNUM,   GP_REG_FIRST + 30},                           \
+ { ARG_POINTER_REGNUM,   GP_REG_FIRST + 17},                           \
  { RETURN_ADDRESS_POINTER_REGNUM, STACK_POINTER_REGNUM},               \
- { RETURN_ADDRESS_POINTER_REGNUM, FRAME_POINTER_REGNUM},               \
- { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}}
+ { RETURN_ADDRESS_POINTER_REGNUM, GP_REG_FIRST + 30},                  \
+ { RETURN_ADDRESS_POINTER_REGNUM, GP_REG_FIRST + 17},                  \
+ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM},                                \
+ { FRAME_POINTER_REGNUM, GP_REG_FIRST + 30},                           \
+ { FRAME_POINTER_REGNUM, GP_REG_FIRST + 17}}
 
 /* A C expression that returns non-zero if the compiler is allowed to
    try to replace register number FROM-REG with register number
    TO-REG.  This macro need only be defined if `ELIMINABLE_REGS' is
    defined, and will usually be the constant 1, since most of the
    cases preventing register elimination are things that the compiler
-   already knows about.  */
+   already knows about.
+
+   We can always eliminate to the frame pointer.  We can eliminate to
+   the stack pointer unless a frame pointer is needed.  In mips16
+   mode, we need a frame pointer for a large frame; otherwise, reload
+   may be unable to compute the address of a local variable, since
+   there is no way to add a large constant to the stack pointer
+   without using a temporary register.  */
 
 #define CAN_ELIMINATE(FROM, TO)                                                \
-  (!frame_pointer_needed                                               \
-   || ((FROM) == ARG_POINTER_REGNUM && (TO) == FRAME_POINTER_REGNUM)   \
-   || ((FROM) == RETURN_ADDRESS_POINTER_REGNUM                         \
-       && (TO) == FRAME_POINTER_REGNUM))
+  ((TO) == HARD_FRAME_POINTER_REGNUM                                   \
+   || ((TO) == STACK_POINTER_REGNUM && ! frame_pointer_needed          \
+       && (! TARGET_MIPS16                                             \
+          || compute_frame_size (get_frame_size ()) < 32768)))
 
 /* This macro is similar to `INITIAL_FRAME_POINTER_OFFSET'.  It
    specifies the initial difference between the specified pair of
@@ -1968,23 +2135,25 @@ extern struct mips_frame_info current_frame_info;
 
 #define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)                    \
 {  compute_frame_size (get_frame_size ());                              \
-  if ((FROM) == FRAME_POINTER_REGNUM && (TO) == STACK_POINTER_REGNUM)   \
+  if (TARGET_MIPS16 && (FROM) == FRAME_POINTER_REGNUM                   \
+      && (TO) == HARD_FRAME_POINTER_REGNUM)                             \
+    (OFFSET) = - current_function_outgoing_args_size;                   \
+  else if ((FROM) == FRAME_POINTER_REGNUM)                              \
     (OFFSET) = 0;                                                       \
-  else if ((FROM) == ARG_POINTER_REGNUM                                         \
-           && ((TO) == FRAME_POINTER_REGNUM                             \
-               || (TO) == STACK_POINTER_REGNUM))                        \
+  else if (TARGET_MIPS16 && (FROM) == ARG_POINTER_REGNUM                \
+          && (TO) == HARD_FRAME_POINTER_REGNUM)                         \
+    (OFFSET) = (current_frame_info.total_size                           \
+               - current_function_outgoing_args_size                    \
+               - ((mips_abi != ABI_32 && mips_abi != ABI_EABI)          \
+                  ? current_function_pretend_args_size                  \
+                  : 0));                                                \
+  else if ((FROM) == ARG_POINTER_REGNUM)                                \
     (OFFSET) = (current_frame_info.total_size                           \
                - ((mips_abi != ABI_32 && mips_abi != ABI_EABI)          \
                   ? current_function_pretend_args_size                  \
                   : 0));                                                \
-  else if ((FROM) == RETURN_ADDRESS_POINTER_REGNUM                      \
-          && ((TO) == FRAME_POINTER_REGNUM                              \
-              || (TO) == STACK_POINTER_REGNUM))                         \
-    (OFFSET) = current_frame_info.gp_sp_offset                          \
-              + ((UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT))      \
-                 * (BYTES_BIG_ENDIAN != 0));                            \
-  else                                                                  \
-    abort ();                                                           \
+  else if ((FROM) == RETURN_ADDRESS_POINTER_REGNUM)                     \
+    (OFFSET) = current_frame_info.gp_sp_offset;                                 \
 }
 
 /* If we generate an insn to push BYTES bytes,
@@ -2105,10 +2274,16 @@ extern struct mips_frame_info current_frame_info;
 #define MAX_ARGS_IN_REGISTERS  4
 
 /* Define how to find the value returned by a library function
-   assuming the value has mode MODE.  */
+   assuming the value has mode MODE.  Because we define
+   PROMOTE_FUNCTION_RETURN, we must promote the mode just as
+   PROMOTE_MODE does.  */
 
 #define LIBCALL_VALUE(MODE)                                            \
-  gen_rtx (REG, MODE,                                                  \
+  gen_rtx (REG,                                                                \
+          ((GET_MODE_CLASS (MODE) != MODE_INT                          \
+            || GET_MODE_SIZE (MODE) >= 4)                              \
+           ? (MODE)                                                    \
+           : SImode),                                                  \
           ((GET_MODE_CLASS (MODE) == MODE_FLOAT                        \
             && (! TARGET_SINGLE_FLOAT                                  \
                 || GET_MODE_SIZE (MODE) <= 4))                         \
@@ -2137,7 +2312,8 @@ extern struct mips_frame_info current_frame_info;
   (((N) >= GP_ARG_FIRST && (N) <= GP_ARG_LAST)                 \
    || (! TARGET_SOFT_FLOAT                                     \
        && ((N) >= FP_ARG_FIRST && (N) <= FP_ARG_LAST)          \
-       && (TARGET_FLOAT64 || (0 == (N) % 2))))
+       && (TARGET_FLOAT64 || (0 == (N) % 2)))                  \
+   && ! fixed_regs[N])
 
 /* A C expression which can inhibit the returning of certain function
    values in registers, based on the type of value.  A nonzero value says
@@ -2172,7 +2348,18 @@ extern struct mips_frame_info current_frame_info;
    hold all necessary information about the function itself
    and about the args processed so far, enough to enable macros
    such as FUNCTION_ARG to determine where the next arg should go.
-*/
+
+   On the mips16, we need to keep track of which floating point
+   arguments were passed in general registers, but would have been
+   passed in the FP regs if this were a 32 bit function, so that we
+   can move them to the FP regs if we wind up calling a 32 bit
+   function.  We record this information in fp_code, encoded in base
+   four.  A zero digit means no floating point argument, a one digit
+   means an SFmode argument, and a two digit means a DFmode argument,
+   and a three digit is not used.  The low order digit is the first
+   argument.  Thus 6 == 1 * 4 + 2 means a DFmode argument followed by
+   an SFmode argument.  ??? A more sophisticated approach will be
+   needed if MIPS_ABI != ABI_32.  */
 
 typedef struct mips_args {
   int gp_reg_found;            /* whether a gp register was found yet */
@@ -2180,6 +2367,7 @@ typedef struct mips_args {
   int arg_words;               /* # total words the arguments take */
   int fp_arg_words;            /* # words for FP args (MIPS_EABI only) */
   int last_arg_fp;             /* nonzero if last arg was FP (EABI only) */
+  int fp_code;                 /* Mode of FP arguments (mips16) */
   int num_adjusts;             /* number of adjustments made */
                                /* Adjustments made to args pass in regs.  */
                                /* ??? The size is doubled to work around a 
@@ -2261,8 +2449,8 @@ typedef struct mips_args {
 /* Tell prologue and epilogue if register REGNO should be saved / restored.  */
 
 #define MUST_SAVE_REGISTER(regno) \
- ((regs_ever_live[regno] && !call_used_regs[regno])            \
-  || (regno == FRAME_POINTER_REGNUM && frame_pointer_needed)   \
+ ((regs_ever_live[regno] && !call_used_regs[regno])                    \
+  || (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)      \
   || (regno == (GP_REG_FIRST + 31) && regs_ever_live[GP_REG_FIRST + 31]))
 
 /* ALIGN FRAMES on double word boundaries */
@@ -2275,6 +2463,8 @@ typedef struct mips_args {
 
 #define FUNCTION_PROFILER(FILE, LABELNO)                               \
 {                                                                      \
+  if (TARGET_MIPS16)                                                   \
+    sorry ("mips16 function profiling");                               \
   fprintf (FILE, "\t.set\tnoreorder\n");                               \
   fprintf (FILE, "\t.set\tnoat\n");                                    \
   fprintf (FILE, "\tmove\t%s,%s\t\t# save current return address\n",   \
@@ -2390,14 +2580,26 @@ typedef struct mips_args {
    or a pseudo reg currently allocated to a suitable hard reg.
    These definitions are NOT overridden anywhere.  */
 
-#define GP_REG_OR_PSEUDO_STRICT_P(regno) \
-  GP_REG_P((regno < FIRST_PSEUDO_REGISTER) ? regno : reg_renumber[regno])
+#define BASE_REG_P(regno, mode)                                        \
+  (TARGET_MIPS16                                               \
+   ? (M16_REG_P (regno)                                                \
+      || (regno) == FRAME_POINTER_REGNUM                       \
+      || (regno) == ARG_POINTER_REGNUM                         \
+      || ((regno) == STACK_POINTER_REGNUM                      \
+         && (GET_MODE_SIZE (mode) == 4                         \
+             || GET_MODE_SIZE (mode) == 8)))                   \
+   : GP_REG_P (regno))
 
-#define GP_REG_OR_PSEUDO_NONSTRICT_P(regno) \
-  (((regno) >= FIRST_PSEUDO_REGISTER) || (GP_REG_P (regno)))
+#define GP_REG_OR_PSEUDO_STRICT_P(regno, mode)                             \
+  BASE_REG_P((regno < FIRST_PSEUDO_REGISTER) ? regno : reg_renumber[regno], \
+            (mode))
+
+#define GP_REG_OR_PSEUDO_NONSTRICT_P(regno, mode) \
+  (((regno) >= FIRST_PSEUDO_REGISTER) || (BASE_REG_P ((regno), (mode))))
 
 #define REGNO_OK_FOR_INDEX_P(regno)    0
-#define REGNO_OK_FOR_BASE_P(regno)     GP_REG_OR_PSEUDO_STRICT_P (regno)
+#define REGNO_MODE_OK_FOR_BASE_P(regno, mode) \
+  GP_REG_OR_PSEUDO_STRICT_P ((regno), (mode))
 
 /* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
    and check its validity for a certain class.
@@ -2414,13 +2616,15 @@ typedef struct mips_args {
 
 #define REG_OK_STRICT_P 0
 #define REG_OK_FOR_INDEX_P(X) 0
-#define REG_OK_FOR_BASE_P(X)  GP_REG_OR_PSEUDO_NONSTRICT_P (REGNO (X))
+#define REG_MODE_OK_FOR_BASE_P(X, MODE) \
+  GP_REG_OR_PSEUDO_NONSTRICT_P (REGNO (X), (MODE))
 
 #else
 
 #define REG_OK_STRICT_P 1
 #define REG_OK_FOR_INDEX_P(X) 0
-#define REG_OK_FOR_BASE_P(X)  REGNO_OK_FOR_BASE_P  (REGNO (X))
+#define REG_MODE_OK_FOR_BASE_P(X, MODE) \
+  REGNO_MODE_OK_FOR_BASE_P (REGNO (X), (MODE))
 
 #endif
 
@@ -2501,11 +2705,14 @@ typedef struct mips_args {
       GO_DEBUG_RTX (xinsn);                                            \
     }                                                                  \
                                                                        \
-  if (GET_CODE (xinsn) == REG && REG_OK_FOR_BASE_P (xinsn))            \
+  /* The mips16 can only use the stack pointer as a base register when \
+     loading SImode or DImode values.  */                              \
+  if (GET_CODE (xinsn) == REG && REG_MODE_OK_FOR_BASE_P (xinsn, MODE)) \
     goto ADDR;                                                         \
                                                                        \
   if (CONSTANT_ADDRESS_P (xinsn)                                       \
-      && ! (mips_split_addresses && mips_check_split (xinsn, MODE)))   \
+      && ! (mips_split_addresses && mips_check_split (xinsn, MODE))    \
+      && (! TARGET_MIPS16 || mips16_constant (xinsn, MODE, 1, 0)))     \
     goto ADDR;                                                         \
                                                                        \
   if (GET_CODE (xinsn) == LO_SUM && mips_split_addresses)              \
@@ -2513,7 +2720,8 @@ typedef struct mips_args {
       register rtx xlow0 = XEXP (xinsn, 0);                            \
       register rtx xlow1 = XEXP (xinsn, 1);                            \
                                                                        \
-      if (GET_CODE (xlow0) == REG && REG_OK_FOR_BASE_P (xlow0)         \
+      if (GET_CODE (xlow0) == REG                                      \
+         && REG_MODE_OK_FOR_BASE_P (xlow0, MODE)                       \
          && mips_check_split (xlow1, MODE))                            \
        goto ADDR;                                                      \
     }                                                                  \
@@ -2525,13 +2733,22 @@ typedef struct mips_args {
       register enum rtx_code code0 = GET_CODE (xplus0);                        \
       register enum rtx_code code1 = GET_CODE (xplus1);                        \
                                                                        \
-      if (code0 == REG && REG_OK_FOR_BASE_P (xplus0))                  \
+      /* The mips16 can only use the stack pointer as a base register  \
+         when loading SImode or DImode values.  */                     \
+      if (code0 == REG && REG_MODE_OK_FOR_BASE_P (xplus0, MODE))       \
        {                                                               \
          if (code1 == CONST_INT                                        \
              && INTVAL (xplus1) >= -32768                              \
              && INTVAL (xplus1) + GET_MODE_SIZE (MODE) - 1 <= 32767)   \
            goto ADDR;                                                  \
                                                                        \
+         /* On the mips16, we represent GP relative offsets in RTL.    \
+             These are 16 bit signed values, and can serve as register \
+             offsets.  */                                              \
+         if (TARGET_MIPS16                                             \
+             && mips16_gp_offset_p (xplus1))                           \
+           goto ADDR;                                                  \
+                                                                       \
          /* For some code sequences, you actually get better code by   \
             pretending that the MIPS supports an address mode of a     \
             constant address + a register, even though the real        \
@@ -2556,7 +2773,8 @@ typedef struct mips_args {
              && ! mips_split_addresses                                 \
              && (!TARGET_EMBEDDED_PIC                                  \
                  || code1 != CONST                                     \
-                 || GET_CODE (XEXP (xplus1, 0)) != MINUS))             \
+                 || GET_CODE (XEXP (xplus1, 0)) != MINUS)              \
+             && !TARGET_MIPS16)                                        \
            goto ADDR;                                                  \
        }                                                               \
     }                                                                  \
@@ -2601,7 +2819,8 @@ typedef struct mips_args {
   ((GET_CODE (X) != CONST_DOUBLE                                       \
     || mips_const_double_ok (X, GET_MODE (X)))                         \
    && ! (GET_CODE (X) == CONST                                         \
-        && mips_abi != ABI_32 && mips_abi != ABI_EABI))
+        && mips_abi != ABI_32 && mips_abi != ABI_EABI)                 \
+   && (! TARGET_MIPS16 || mips16_constant (X, GET_MODE (X), 0, 0)))
 
 /* A C compound statement that attempts to replace X with a valid
    memory address for an operand of mode MODE.  WIN will be a C
@@ -2693,7 +2912,7 @@ typedef struct mips_args {
          code1 = GET_CODE (xplus1);                                    \
        }                                                               \
                                                                        \
-      if (code0 == REG && REG_OK_FOR_BASE_P (xplus0)                   \
+      if (code0 == REG && REG_MODE_OK_FOR_BASE_P (xplus0, MODE)                \
          && code1 == CONST_INT && !SMALL_INT (xplus1))                 \
        {                                                               \
          rtx int_reg = gen_reg_rtx (Pmode);                            \
@@ -2755,11 +2974,32 @@ typedef struct mips_args {
 
    You can also check the information stored in the `symbol_ref' in
    the definition of `GO_IF_LEGITIMATE_ADDRESS' or
-   `PRINT_OPERAND_ADDRESS'. */
+   `PRINT_OPERAND_ADDRESS'.
+
+   When optimizing for the $gp pointer, SYMBOL_REF_FLAG is set for all
+   small objects.
+
+   When generating embedded PIC code, SYMBOL_REF_FLAG is set for
+   symbols which are not in the .text section.
+
+   When generating mips16 code, SYMBOL_REF_FLAG is set for string
+   constants which are put in the .text section.  We also record the
+   total length of all such strings; this total is used to decide
+   whether we need to split the constant table, and need not be
+   precisely correct.  */
 
 #define ENCODE_SECTION_INFO(DECL)                                      \
 do                                                                     \
   {                                                                    \
+    if (TARGET_MIPS16)                                                 \
+      {                                                                        \
+       if (TREE_CODE (DECL) == STRING_CST                              \
+           && ! flag_writable_strings)                                 \
+         {                                                             \
+           SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (DECL), 0)) = 1;        \
+           mips_string_length += TREE_STRING_LENGTH (DECL);            \
+         }                                                             \
+      }                                                                        \
     if (TARGET_EMBEDDED_PIC)                                           \
       {                                                                        \
         if (TREE_CODE (DECL) == VAR_DECL)                              \
@@ -2786,15 +3026,33 @@ do                                                                      \
   }                                                                    \
 while (0)
 
+/* The mips16 wants the constant pool to be after the function,
+   because the PC relative load instructions use unsigned offsets.  */
+
+#define CONSTANT_POOL_BEFORE_FUNCTION (! TARGET_MIPS16)
+
+#define ASM_OUTPUT_POOL_EPILOGUE(FILE, FNNAME, FNDECL, SIZE)   \
+  mips_string_length = 0;
+
+#if 0
+/* In mips16 mode, put most string constants after the function.  */
+#define CONSTANT_AFTER_FUNCTION_P(tree)                                \
+  (TARGET_MIPS16 && mips16_constant_after_function_p (tree))
+#endif
 \f
 /* Specify the machine mode that this machine uses
-   for the index in the tablejump instruction.  */
-#define CASE_VECTOR_MODE (TARGET_LONG64 ? DImode : SImode)
-
-/* Define this if the tablejump instruction expects the table
-   to contain offsets from the address of the table.
-   Do not define this if the table should contain absolute addresses.  */
-/* #define CASE_VECTOR_PC_RELATIVE 1 */
+   for the index in the tablejump instruction.
+   ??? Using HImode in mips16 mode can cause overflow.  However, the
+   overflow is no more likely than the overflow in a branch
+   instruction.  Large functions can currently break in both ways.  */
+#define CASE_VECTOR_MODE \
+  (TARGET_MIPS16 ? HImode : TARGET_LONG64 ? DImode : SImode)
+
+/* Define as C expression which evaluates to nonzero if the tablejump
+   instruction expects the table to contain offsets from the address of the
+   table.
+   Do not define this if the table should contain absolute addresses. */
+#define CASE_VECTOR_PC_RELATIVE (TARGET_MIPS16)
 
 /* Specify the tree operation to be used to convert reals to integers.  */
 #define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR
@@ -2879,10 +3137,65 @@ while (0)
 
 #define CONST_COSTS(X,CODE,OUTER_CODE)                                 \
   case CONST_INT:                                                      \
-    /* Always return 0, since we don't have different sized            \
-       instructions, hence different costs according to Richard                \
-       Kenner */                                                       \
-    return 0;                                                          \
+    if (! TARGET_MIPS16)                                               \
+      {                                                                        \
+       /* Always return 0, since we don't have different sized         \
+          instructions, hence different costs according to Richard     \
+          Kenner */                                                    \
+       return 0;                                                       \
+      }                                                                        \
+    if ((OUTER_CODE) == SET)                                           \
+      {                                                                        \
+       if (INTVAL (X) >= 0 && INTVAL (X) < 0x100)                      \
+         return 0;                                                     \
+       else if ((INTVAL (X) >= 0 && INTVAL (X) < 0x10000)              \
+                || (INTVAL (X) < 0 && INTVAL (X) > -0x100))            \
+         return COSTS_N_INSNS (1);                                     \
+       else                                                            \
+         return COSTS_N_INSNS (2);                                     \
+      }                                                                        \
+    /* A PLUS could be an address.  We don't want to force an address  \
+       to use a register, so accept any signed 16 bit value without    \
+       complaint.  */                                                  \
+    if ((OUTER_CODE) == PLUS                                           \
+       && INTVAL (X) >= -0x8000 && INTVAL (X) < 0x8000)                \
+      return 0;                                                                \
+    /* A number between 1 and 8 inclusive is efficient for a shift.    \
+       Otherwise, we will need an extended instruction.  */            \
+    if ((OUTER_CODE) == ASHIFT || (OUTER_CODE) == ASHIFTRT             \
+       || (OUTER_CODE) == LSHIFTRT)                                    \
+      {                                                                        \
+       if (INTVAL (X) >= 1 && INTVAL (X) <= 8)                         \
+         return 0;                                                     \
+       return COSTS_N_INSNS (1);                                       \
+      }                                                                        \
+    /* We can use cmpi for an xor with an unsigned 16 bit value.  */   \
+    if ((OUTER_CODE) == XOR                                            \
+       && INTVAL (X) >= 0 && INTVAL (X) < 0x10000)                     \
+      return 0;                                                                \
+    /* We may be able to use slt or sltu for a comparison with a       \
+       signed 16 bit value.  (The boundary conditions aren't quite     \
+       right, but this is just a heuristic anyhow.)  */                        \
+    if (((OUTER_CODE) == LT || (OUTER_CODE) == LE                      \
+        || (OUTER_CODE) == GE || (OUTER_CODE) == GT                    \
+        || (OUTER_CODE) == LTU || (OUTER_CODE) == LEU                  \
+        || (OUTER_CODE) == GEU || (OUTER_CODE) == GTU)                 \
+       && INTVAL (X) >= -0x8000 && INTVAL (X) < 0x8000)                \
+      return 0;                                                                \
+    /* Equality comparisons with 0 are cheap.  */                      \
+    if (((OUTER_CODE) == EQ || (OUTER_CODE) == NE)                     \
+       && INTVAL (X) == 0)                                             \
+      return 0;                                                                \
+                                                                       \
+    /* Otherwise, work out the cost to load the value into a           \
+       register.  */                                                   \
+    if (INTVAL (X) >= 0 && INTVAL (X) < 0x100)                         \
+      return COSTS_N_INSNS (1);                                                \
+    else if ((INTVAL (X) >= 0 && INTVAL (X) < 0x10000)                 \
+            || (INTVAL (X) < 0 && INTVAL (X) > -0x100))                \
+      return COSTS_N_INSNS (2);                                                \
+    else                                                               \
+      return COSTS_N_INSNS (3);                                                \
                                                                        \
   case LABEL_REF:                                                      \
     return COSTS_N_INSNS (2);                                          \
@@ -2892,6 +3205,17 @@ while (0)
       rtx offset = const0_rtx;                                         \
       rtx symref = eliminate_constant_term (XEXP (X, 0), &offset);     \
                                                                        \
+      if (TARGET_MIPS16 && mips16_gp_offset_p (X))                     \
+       {                                                               \
+         /* Treat this like a signed 16 bit CONST_INT.  */             \
+         if ((OUTER_CODE) == PLUS)                                     \
+           return 0;                                                   \
+         else if ((OUTER_CODE) == SET)                                 \
+           return COSTS_N_INSNS (1);                                   \
+         else                                                          \
+           return COSTS_N_INSNS (2);                                   \
+       }                                                               \
+                                                                       \
       if (GET_CODE (symref) == LABEL_REF)                              \
        return COSTS_N_INSNS (2);                                       \
                                                                        \
@@ -2911,6 +3235,8 @@ while (0)
   case CONST_DOUBLE:                                                   \
     {                                                                  \
       rtx high, low;                                                   \
+      if (TARGET_MIPS16)                                               \
+       return COSTS_N_INSNS (4);                                       \
       split_double (X, &high, &low);                                   \
       return COSTS_N_INSNS ((high == CONST0_RTX (GET_MODE (high))      \
                             || low == CONST0_RTX (GET_MODE (low)))     \
@@ -2953,7 +3279,7 @@ while (0)
     if (GET_MODE (X) == DImode && !TARGET_64BIT)                       \
       return COSTS_N_INSNS (2);                                                \
                                                                        \
-    return COSTS_N_INSNS (1);                                          \
+    break;                                                             \
                                                                        \
   case ASHIFT:                                                         \
   case ASHIFTRT:                                                       \
@@ -2961,7 +3287,7 @@ while (0)
     if (GET_MODE (X) == DImode && !TARGET_64BIT)                       \
       return COSTS_N_INSNS ((GET_CODE (XEXP (X, 1)) == CONST_INT) ? 4 : 12); \
                                                                        \
-    return COSTS_N_INSNS (1);                                          \
+    break;                                                             \
                                                                        \
   case ABS:                                                            \
     {                                                                  \
@@ -2990,11 +3316,14 @@ while (0)
       if (xmode == DImode && !TARGET_64BIT)                            \
        return COSTS_N_INSNS (4);                                       \
                                                                        \
-      return COSTS_N_INSNS (1);                                                \
+      break;                                                           \
     }                                                                  \
                                                                        \
   case NEG:                                                            \
-    return COSTS_N_INSNS ((GET_MODE (X) == DImode && !TARGET_64BIT) ? 4 : 1); \
+    if (GET_MODE (X) == DImode && !TARGET_64BIT)                       \
+      return 4;                                                                \
+                                                                       \
+    break;                                                             \
                                                                        \
   case MULT:                                                           \
     {                                                                  \
@@ -3154,17 +3483,24 @@ while (0)
    not allow such copying.  */
 
 #define REGISTER_MOVE_COST(FROM, TO)   \
-  ((FROM) == GR_REGS && (TO) == GR_REGS ? 2                            \
+  ((FROM) == M16_REGS && GR_REG_CLASS_P (TO) ? 2                       \
+   : (FROM) == M16_NA_REGS && GR_REG_CLASS_P (TO) ? 2                  \
+   : GR_REG_CLASS_P (FROM) && (TO) == M16_REGS ? 2                     \
+   : GR_REG_CLASS_P (FROM) && (TO) == M16_NA_REGS ? 2                  \
+   : GR_REG_CLASS_P (FROM) && GR_REG_CLASS_P (TO) ? (TARGET_MIPS16 ? 4 : 2) \
    : (FROM) == FP_REGS && (TO) == FP_REGS ? 2                          \
-   : (FROM) == GR_REGS && (TO) == FP_REGS ? 4                          \
-   : (FROM) == FP_REGS && (TO) == GR_REGS ? 4                          \
+   : GR_REG_CLASS_P (FROM) && (TO) == FP_REGS ? 4                      \
+   : (FROM) == FP_REGS && GR_REG_CLASS_P (TO) ? 4                      \
+   : (((FROM) == HI_REG || (FROM) == LO_REG                            \
+       || (FROM) == MD_REGS || (FROM) == HILO_REG)                     \
+      && ((TO) == M16_REGS || (TO) == M16_NA_REGS)) ? 6                        \
    : (((FROM) == HI_REG || (FROM) == LO_REG                            \
        || (FROM) == MD_REGS || (FROM) == HILO_REG)                     \
-      && (TO) == GR_REGS) ? 6                                          \
+      && GR_REG_CLASS_P (TO)) ? (TARGET_MIPS16 ? 8 : 6)                        \
    : (((TO) == HI_REG || (TO) == LO_REG                                        \
-       || (TO) == MD_REGS || (FROM) == HILO_REG)                       \
-      && (FROM) == GR_REGS) ? 6                                                \
-   : (FROM) == ST_REGS && (TO) == GR_REGS ? 4                          \
+       || (TO) == MD_REGS || (TO) == HILO_REG)                         \
+      && GR_REG_CLASS_P (FROM)) ? (TARGET_MIPS16 ? 12 : 6)             \
+   : (FROM) == ST_REGS && GR_REG_CLASS_P (TO) ? 4                      \
    : (FROM) == FP_REGS && (TO) == ST_REGS ? 8                          \
    : 12)
 
@@ -3176,8 +3512,10 @@ while (0)
    1 is the default; other values are interpreted relative to that.  */
 
 /* ??? Fix this to be right for the R8000.  */
-#define BRANCH_COST \
-  ((mips_cpu == PROCESSOR_R4000 || mips_cpu == PROCESSOR_R6000) ? 2 : 1)
+#define BRANCH_COST                                                    \
+  ((! TARGET_MIPS16                                                    \
+    && (mips_cpu == PROCESSOR_R4000 || mips_cpu == PROCESSOR_R6000))   \
+   ? 2 : 1)
 
 /* A C statement (sans semicolon) to update the integer variable COST
    based on the relationship between INSN that is dependent on
@@ -3241,7 +3579,9 @@ while (0)
   {"se_nonmemory_operand",     { CONST_INT, CONST_DOUBLE, CONST,       \
                                  SYMBOL_REF, LABEL_REF, SUBREG,        \
                                  REG, SIGN_EXTEND }},                  \
-  {"se_nonimmediate_operand",   { SUBREG, REG, MEM, SIGN_EXTEND }},
+  {"se_nonimmediate_operand",   { SUBREG, REG, MEM, SIGN_EXTEND }},    \
+  {"consttable_operand",       { LABEL_REF, SYMBOL_REF, CONST_INT,     \
+                                 CONST_DOUBLE, CONST }},
 
 \f
 /* If defined, a C statement to be executed just prior to the
@@ -3677,7 +4017,7 @@ while (0)
 #define ASM_DECLARE_FUNCTION_NAME(STREAM,NAME,DECL)                    \
 {                                                                      \
   extern FILE *asm_out_text_file;                                      \
-  if (TARGET_GP_OPT)                                                   \
+  if (TARGET_GP_OPT && ! TARGET_MIPS16)                                        \
     {                                                                  \
       STREAM = asm_out_text_file;                                      \
       /* ??? text_section gets called too soon.  If the previous       \
@@ -3787,7 +4127,10 @@ do {                                                                     \
 
 #define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, VALUE, REL)                   \
 do {                                                                   \
-  if (TARGET_EMBEDDED_PIC)                                             \
+  if (TARGET_MIPS16)                                                   \
+    fprintf (STREAM, "\t.half\t%sL%d-%sL%d\n",                         \
+            LOCAL_LABEL_PREFIX, VALUE, LOCAL_LABEL_PREFIX, REL);       \
+  else if (TARGET_EMBEDDED_PIC)                                                \
     fprintf (STREAM, "\t%s\t%sL%d-%sLS%d\n",                           \
             TARGET_LONG64 ? ".dword" : ".word",                        \
             LOCAL_LABEL_PREFIX, VALUE, LOCAL_LABEL_PREFIX, REL);       \
@@ -3801,16 +4144,16 @@ do {                                                                    \
             LOCAL_LABEL_PREFIX, VALUE);                                \
 } while (0)
 
-/* When generating embedded PIC code we want to put the jump table in
-   the .text section.  In all other cases, we want to put the jump
-   table in the .rdata section.  Unfortunately, we can't use
+/* When generating embedded PIC or mips16 code we want to put the jump
+   table in the .text section.  In all other cases, we want to put the
+   jump table in the .rdata section.  Unfortunately, we can't use
    JUMP_TABLES_IN_TEXT_SECTION, because it is not conditional.
    Instead, we use ASM_OUTPUT_CASE_LABEL to switch back to the .text
    section if appropriate.  */
 #define ASM_OUTPUT_CASE_LABEL(FILE, PREFIX, NUM, INSN)                 \
 do {                                                                   \
-  if (TARGET_EMBEDDED_PIC)                                             \
-    text_section ();                                                   \
+  if (TARGET_EMBEDDED_PIC || TARGET_MIPS16)                            \
+    function_section (current_function_decl);                          \
   ASM_OUTPUT_INTERNAL_LABEL (FILE, PREFIX, NUM);                       \
 } while (0)
 
@@ -4052,6 +4395,13 @@ while (0)
    true.  */
 
 #define DONT_ACCESS_GBLS_AFTER_EPILOGUE (TARGET_ABICALLS && mips_abi != ABI_32)
+\f
+/* In mips16 mode, we need to look through the function to check for
+   PC relative loads that are out of range.  */
+#define MACHINE_DEPENDENT_REORG(X) machine_dependent_reorg (X)
+
+/* We need to use a special set of functions to handle hard floating
+   point code in mips16 mode.  */
 
 #ifndef INIT_SUBTARGET_OPTABS
 #define INIT_SUBTARGET_OPTABS
@@ -4060,6 +4410,65 @@ while (0)
 #define INIT_TARGET_OPTABS                                             \
 do                                                                     \
   {                                                                    \
-    INIT_SUBTARGET_OPTABS;                                             \
+    if (! TARGET_MIPS16 || ! mips16_hard_float)                                \
+      INIT_SUBTARGET_OPTABS;                                           \
+    else                                                               \
+      {                                                                        \
+       add_optab->handlers[(int) SFmode].libfunc =                     \
+         gen_rtx (SYMBOL_REF, Pmode, "__mips16_addsf3");               \
+       sub_optab->handlers[(int) SFmode].libfunc =                     \
+         gen_rtx (SYMBOL_REF, Pmode, "__mips16_subsf3");               \
+       smul_optab->handlers[(int) SFmode].libfunc =                    \
+         gen_rtx (SYMBOL_REF, Pmode, "__mips16_mulsf3");               \
+       flodiv_optab->handlers[(int) SFmode].libfunc =                  \
+         gen_rtx (SYMBOL_REF, Pmode, "__mips16_divsf3");               \
+                                                                       \
+       eqsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_eqsf2");  \
+       nesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_nesf2");  \
+       gtsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_gtsf2");  \
+       gesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_gesf2");  \
+       ltsf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_ltsf2");  \
+       lesf2_libfunc = gen_rtx (SYMBOL_REF, Pmode, "__mips16_lesf2");  \
+                                                                       \
+       floatsisf_libfunc =                                             \
+         gen_rtx (SYMBOL_REF, Pmode, "__mips16_floatsisf");            \
+       fixsfsi_libfunc =                                               \
+         gen_rtx (SYMBOL_REF, Pmode, "__mips16_fixsfsi");              \
+                                                                       \
+       if (TARGET_DOUBLE_FLOAT)                                        \
+         {                                                             \
+           add_optab->handlers[(int) DFmode].libfunc =                 \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_adddf3");           \
+           sub_optab->handlers[(int) DFmode].libfunc =                 \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_subdf3");           \
+           smul_optab->handlers[(int) DFmode].libfunc =                \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_muldf3");           \
+           flodiv_optab->handlers[(int) DFmode].libfunc =              \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_divdf3");           \
+                                                                       \
+           extendsfdf2_libfunc =                                       \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_extendsfdf2");      \
+           truncdfsf2_libfunc =                                        \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_truncdfsf2");       \
+                                                                       \
+           eqdf2_libfunc =                                             \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_eqdf2");            \
+           nedf2_libfunc =                                             \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_nedf2");            \
+           gtdf2_libfunc =                                             \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_gtdf2");            \
+           gedf2_libfunc =                                             \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_gedf2");            \
+           ltdf2_libfunc =                                             \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_ltdf2");            \
+           ledf2_libfunc =                                             \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_ledf2");            \
+                                                                       \
+           floatsidf_libfunc =                                         \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_floatsidf");        \
+           fixdfsi_libfunc =                                           \
+             gen_rtx (SYMBOL_REF, Pmode, "__mips16_fixdfsi");          \
+         }                                                             \
+      }                                                                        \
   }                                                                    \
 while (0)
index 5dfbdef..8fd4dfd 100644 (file)
 
 ;; 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")))
 
 ;;
 ;; .........................
 
-(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")))])
        (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)
   ""
   "
 {
-  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)"
+  "*
+{
+  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)])])
+
+
+;; 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 "se_register_operand" "")
                            (match_operand:DI 2 "se_arith_operand" "")))
              (clobber (match_dup 3))])]
-  "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+  "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
   "
 {
-  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 (DImode, operands[2]);
 
   if (TARGET_64BIT)
        (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_64BIT && !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_64BIT && !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_64BIT && !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_64BIT && !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_64BIT && !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_64BIT && !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"
   [(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 && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+  "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)
    (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 && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+  "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)
    (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
 ;;
 ;;  ....................
   ""
   "
 {
-  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))"
+  "*
+{
+  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_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 "se_register_operand" "d")
                             (match_operand:DI 2 "se_register_operand" "d")))
              (clobber (match_dup 3))])]
-  "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+  "TARGET_64BIT || (!TARGET_DEBUG_G_MODE && !TARGET_MIPS16)"
   "
 {
   if (TARGET_64BIT)
        (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_64BIT && !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_64BIT && !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_64BIT && !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_64BIT && !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_64BIT && !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_64BIT && !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"
   [(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 && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+  "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)
    (set_attr "mode"    "DI")
    (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 ""
+  [(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))"
+  "*
+{
+  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.
+
+(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]);
+
+  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" "")
+       (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]);
+
+  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 "subsi3_internal_2"
   [(set (match_operand:DI 0 "register_operand" "=d")
        (sign_extend:DI (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
                                  (match_operand:SI 2 "arith_operand" "dI"))))]
-  "TARGET_64BIT && (GET_CODE (operands[2]) != CONST_INT || INTVAL (operands[2]) != -32768)"
+  "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)
    (set_attr "mode"    "DI")
    (set_attr "length"  "1")])
 
+(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))"
+  "*
+{
+  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_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
 ;;
 ;;  ....................
     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)
+  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]));
                 (match_operand:SI 2 "register_operand" "d")))
    (clobber (match_scratch:SI 3 "=h"))
    (clobber (match_scratch:SI 4 "=a"))]
-  "mips_cpu != PROCESSOR_R4000"
+  "mips_cpu != PROCESSOR_R4000 || TARGET_MIPS16"
   "mult\\t%1,%2"
   [(set_attr "type"    "imul")
    (set_attr "mode"    "SI")
    (clobber (match_scratch:SI 3 "=h"))
    (clobber (match_scratch:SI 4 "=l"))
    (clobber (match_scratch:SI 5 "=a"))]
-  "mips_cpu == PROCESSOR_R4000"
+  "mips_cpu == PROCESSOR_R4000 && !TARGET_MIPS16"
   "*
 {
   rtx xoperands[10];
   "TARGET_64BIT"
   "
 {
-  if (GENERATE_MULT3 || mips_cpu == PROCESSOR_R4000)
+  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]));
                 (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_64BIT && mips_cpu != PROCESSOR_R4000 && !TARGET_MIPS16"
   "dmult\\t%1,%2"
   [(set_attr "type"    "imul")
    (set_attr "mode"    "DI")
    (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_64BIT && !TARGET_MIPS16 && (GENERATE_MULT3 || mips_cpu == PROCESSOR_R4000)"
   "*
 {
   if (GENERATE_MULT3)
 (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++;
 (define_insn "absdi2"
   [(set (match_operand:DI 0 "register_operand" "=d")
        (abs:DI (match_operand:DI 1 "se_register_operand" "d")))]
-  "TARGET_64BIT"
+  "TARGET_64BIT && !TARGET_MIPS16"
   "*
 {
   dslots_jump_total++;
        (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;
@@ -1916,7 +2525,7 @@ move\\t%0,%z4\\n\\
        (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_64BIT && !TARGET_MIPS16"
   "*
 {
   dslots_jump_total += 2;
@@ -1961,6 +2570,8 @@ move\\t%0,%z4\\n\\
   ""
   "*
 {
+  if (TARGET_MIPS16)
+    return \"neg\\t%0,%1\";
   operands[2] = const0_rtx;
   return \"subu\\t%0,%z2,%1\";
 }"
@@ -1972,7 +2583,7 @@ move\\t%0,%z4\\n\\
   [(parallel [(set (match_operand:DI 0 "register_operand" "=d")
                   (neg:DI (match_operand:DI 1 "se_register_operand" "d")))
              (clobber (match_dup 2))])]
-  "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+  "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16"
   "
 {
   if (TARGET_64BIT)
@@ -1988,7 +2599,7 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && !TARGET_DEBUG_G_MODE"
+  "! TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
   "*
 {
   operands[3] = const0_rtx;
@@ -2001,7 +2612,7 @@ move\\t%0,%z4\\n\\
 (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_64BIT && !TARGET_MIPS16"
   "*
 {
   operands[2] = const0_rtx;
@@ -2035,6 +2646,8 @@ move\\t%0,%z4\\n\\
   ""
   "*
 {
+  if (TARGET_MIPS16)
+    return \"not\\t%0,%1\";
   operands[2] = const0_rtx;
   return \"nor\\t%0,%z2,%1\";
 }"
@@ -2048,6 +2661,12 @@ move\\t%0,%z4\\n\\
   ""
   "*
 {
+  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\";
@@ -2080,11 +2699,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"
@@ -2092,11 +2725,32 @@ 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 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16"
   "*
 {
   if (TARGET_64BIT)
@@ -2110,6 +2764,24 @@ move\\t%0,%z4\\n\\
                       (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")
+       (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" "")
@@ -2127,7 +2799,7 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && !TARGET_MIPS16"
   "@
    and\\t%0,%1,%2
    andi\\t%0,%1,%x2"
@@ -2135,11 +2807,22 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "DI")
    (set_attr "length"  "1")])
 
-(define_insn "iorsi3"
+(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"
@@ -2147,14 +2830,31 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
+(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_insn "iordi3"
+(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 "se_register_operand" "d")
+               (match_operand:DI 2 "se_register_operand" "d")))]
+  "(TARGET_64BIT || !TARGET_DEBUG_G_MODE) && !TARGET_MIPS16"
   "*
 {
   if (TARGET_64BIT)
@@ -2168,6 +2868,24 @@ move\\t%0,%z4\\n\\
                       (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" "")
@@ -2181,11 +2899,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"
@@ -2193,13 +2918,38 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1")])
 
+(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_insn "xordi3"
+(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)
@@ -2213,6 +2963,34 @@ move\\t%0,%z4\\n\\
                       (const_int 1)
                       (const_int 2)))])
 
+(define_insn ""
+  [(set (match_operand:DI 0 "register_operand" "=d")
+       (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" "")
@@ -2227,10 +3005,10 @@ move\\t%0,%z4\\n\\
   "")
 
 (define_insn "xordi3_immed"
-  [(set (match_operand:DI 0 "register_operand" "d")
+  [(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_64BIT && !TARGET_MIPS16"
   "xori\\t%0,%1,%x2"
   [(set_attr "type"    "arith")
    (set_attr "mode"    "DI")
@@ -2240,7 +3018,7 @@ move\\t%0,%z4\\n\\
   [(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")
@@ -2250,7 +3028,7 @@ move\\t%0,%z4\\n\\
   [(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)
@@ -2268,7 +3046,7 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && !TARGET_DEBUG_D_MODE && !TARGET_DEBUG_G_MODE
+  "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]))"
@@ -2297,15 +3075,22 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "=d")
        (truncate:SI (match_operand:DI 1 "se_register_operand" "d")))]
   "TARGET_64BIT"
-  "dsll\\t%0,%1,32\;dsra\\t%0,%0,32"
+  "*
+{
+  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"  "2")])
+   (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_64BIT && !TARGET_MIPS16"
   "andi\\t%0,%1,0xffff"
   [(set_attr "type"    "darith")
    (set_attr "mode"    "HI")
@@ -2314,7 +3099,7 @@ move\\t%0,%z4\\n\\
 (define_insn "truncdiqi2"
   [(set (match_operand:QI 0 "register_operand" "=d")
        (truncate:QI (match_operand:DI 1 "se_register_operand" "d")))]
-  "TARGET_64BIT"
+  "TARGET_64BIT && !TARGET_MIPS16"
   "andi\\t%0,%1,0x00ff"
   [(set_attr "type"    "darith")
    (set_attr "mode"    "QI")
@@ -2325,7 +3110,7 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && !TARGET_MIPS16"
   "*
 {
   int shift_amt = INTVAL (operands[2]) & 0x3f;
@@ -2349,7 +3134,7 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && !TARGET_MIPS16"
   "*
 {
   int shift_amt = INTVAL (operands[2]) & 0x3f;
@@ -2383,6 +3168,8 @@ move\\t%0,%z4\\n\\
   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
@@ -2398,7 +3185,7 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "=d")
        (zero_extend:SI (truncate:HI
                         (match_operand:DI 1 "se_register_operand" "d"))))]
-  "TARGET_64BIT"
+  "TARGET_64BIT && !TARGET_MIPS16"
   "andi\\t%0,%1,0xffff"
   [(set_attr "type"    "darith")
    (set_attr "mode"    "SI")
@@ -2408,7 +3195,7 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:SI 0 "register_operand" "=d")
        (zero_extend:SI (truncate:QI
                         (match_operand:DI 1 "se_register_operand" "d"))))]
-  "TARGET_64BIT"
+  "TARGET_64BIT && !TARGET_MIPS16"
   "andi\\t%0,%1,0xff"
   [(set_attr "type"    "darith")
    (set_attr "mode"    "SI")
@@ -2418,7 +3205,7 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:HI 0 "register_operand" "=d")
        (zero_extend:HI (truncate:QI
                         (match_operand:DI 1 "se_register_operand" "d"))))]
-  "TARGET_64BIT"
+  "TARGET_64BIT && !TARGET_MIPS16"
   "andi\\t%0,%1,0xff"
   [(set_attr "type"    "darith")
    (set_attr "mode"    "HI")
@@ -2464,10 +3251,26 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "DI")
    (set_attr "length"  "1,2")])
 
-(define_insn "zero_extendhisi2"
+(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)
@@ -2479,10 +3282,35 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1,1,2")])
 
-(define_insn "zero_extendhidi2"
+(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_64BIT && !TARGET_MIPS16"
   "*
 {
   if (which_alternative == 0)
@@ -2494,10 +3322,36 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "DI")
    (set_attr "length"  "1,1,2")])
 
-(define_insn "zero_extendqihi2"
+(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)
@@ -2509,10 +3363,35 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "HI")
    (set_attr "length"  "1,1,2")])
 
-(define_insn "zero_extendqisi2"
+(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 ""
   [(set (match_operand:SI 0 "register_operand" "=d,d,d")
        (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
-  ""
+  "!TARGET_MIPS16"
   "*
 {
   if (which_alternative == 0)
@@ -2524,10 +3403,35 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1,1,2")])
 
-(define_insn "zero_extendqidi2"
+(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_64BIT && !TARGET_MIPS16"
   "*
 {
   if (which_alternative == 0)
@@ -2557,15 +3461,11 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "DI")
    (set_attr "length"  "1,2")])
 
-(define_insn "*paradoxical_extendqidi2"
+(define_insn ""
   [(set (match_operand:DI 0 "register_operand" "=d,d")
-       (sign_extend:DI
-        (subreg:SI (match_operand:QI 1 "memory_operand" "R,m") 0)))]
-  "TARGET_64BIT"
-  "*
-{
-  return mips_move_1word (operands, insn, TRUE);
-}"
+       (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")])
@@ -2780,7 +3680,7 @@ move\\t%0,%z4\\n\\
 ;; 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"))]
@@ -2805,7 +3705,7 @@ 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"))]
@@ -2839,7 +3739,7 @@ move\\t%0,%z4\\n\\
 ;;; 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,o")
+  [(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"
@@ -2866,7 +3766,7 @@ move\\t%0,%z4\\n\\
 ;;; 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,o")
+  [(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"
@@ -3152,7 +4052,7 @@ move\\t%0,%z4\\n\\
        (sign_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
@@ -3179,7 +4079,7 @@ move\\t%0,%z4\\n\\
        (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
@@ -3206,7 +4106,7 @@ move\\t%0,%z4\\n\\
                         (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
@@ -3233,7 +4133,7 @@ move\\t%0,%z4\\n\\
 (define_insn "movsi_ulw"
   [(set (match_operand:SI 0 "register_operand" "=&d,&d")
        (unspec:SI [(match_operand:BLK 1 "general_operand" "R,o")] 0))]
-  ""
+  "!TARGET_MIPS16"
   "*
 {
   rtx offset = const0_rtx;
@@ -3262,7 +4162,7 @@ move\\t%0,%z4\\n\\
 (define_insn "movsi_usw"
   [(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;
@@ -3294,7 +4194,7 @@ move\\t%0,%z4\\n\\
 (define_insn "high"
   [(set (match_operand:SI 0 "register_operand" "=r")
        (high:SI (match_operand:SI 1 "immediate_operand" "")))]
-  "mips_split_addresses"
+  "mips_split_addresses && !TARGET_MIPS16"
   "lui\\t%0,%%hi(%1) # high"
   [(set_attr "type"    "move")
    (set_attr "length"  "1")])
@@ -3303,7 +4203,7 @@ move\\t%0,%z4\\n\\
   [(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"
+  "mips_split_addresses && !TARGET_MIPS16"
   "addiu\\t%0,%1,%%lo(%2) # low"
   [(set_attr "type"    "arith")
    (set_attr "mode"    "SI")
@@ -3364,11 +4264,53 @@ move\\t%0,%z4\\n\\
       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);
@@ -3376,10 +4318,27 @@ 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,*x,*d,*x")
        (match_operand:DI 1 "general_operand" "d,iF,R,o,d,d,J,*x,*d"))]
-  "!TARGET_64BIT
+  "!TARGET_64BIT && !TARGET_MIPS16
    && (register_operand (operands[0], DImode)
        || register_operand (operands[1], DImode)
        || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
@@ -3389,6 +4348,17 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "DI")
    (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" ""))]
@@ -3403,7 +4373,7 @@ move\\t%0,%z4\\n\\
 (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_64BIT && !TARGET_MIPS16
    && (register_operand (operands[0], DImode)
        || se_register_operand (operands[1], DImode)
        || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0)
@@ -3413,6 +4383,77 @@ move\\t%0,%z4\\n\\
    (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
@@ -3550,7 +4591,7 @@ move\\t%0,%z4\\n\\
 (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)
@@ -3615,10 +4656,53 @@ move\\t%0,%z4\\n\\
       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)
-      && (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 (SImode, operands[1]);
       emit_move_insn (operands[0], temp);
@@ -3626,13 +4710,30 @@ 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: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_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))"
@@ -3644,7 +4745,7 @@ move\\t%0,%z4\\n\\
 (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_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))"
@@ -3653,29 +4754,221 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "SI")
    (set_attr "length"  "1,2,1,2,1,2,1,2,1,1,1,1,1,1")])
 
-;; Reload HILO_REGNUM in SI mode.  This needs a scratch register in
-;; order to set the sign bit correctly in the HI register.
+;; 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_expand "reload_outsi"
-  [(set (match_operand:SI 0 "general_operand" "=b")
-       (match_operand:SI 1 "register_operand" "d"))
-   (clobber (match_operand:SI 2 "register_operand" "=&d"))]
-  "TARGET_64BIT"
+(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))))]
   "
 {
-  if (GET_CODE (operands[0]) == REG && REGNO (operands[0]) == HILO_REGNUM)
+  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
@@ -3868,7 +5161,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);
@@ -3882,7 +5177,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))"
@@ -3894,7 +5189,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))"
@@ -3903,6 +5198,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
 
@@ -3920,7 +5283,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);
@@ -3934,7 +5299,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))"
@@ -3946,7 +5311,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))"
@@ -3955,6 +5320,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
 
@@ -3967,8 +5388,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);
@@ -3993,7 +5415,7 @@ 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,Fm,d,d"))]
-  "TARGET_SOFT_FLOAT
+  "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)
@@ -4003,6 +5425,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
 
@@ -4015,8 +5448,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);
@@ -4025,8 +5459,8 @@ 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,F,*d,*f,*d*G,*R,*o*F,*d,*d"))]
+  [(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)
@@ -4039,8 +5473,8 @@ move\\t%0,%z4\\n\\
    (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,o,o,f,*d,*d,*d,*o,*R")
-       (match_operand:DF 1 "general_operand"      " f,o,f,G,f,G,F,*F,*o,*R,*d,*d"))]
+  [(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)
@@ -4055,9 +5489,9 @@ move\\t%0,%z4\\n\\
    (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,oF,d,d"))]
-  "(TARGET_SOFT_FLOAT || TARGET_SINGLE_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)
@@ -4067,6 +5501,17 @@ 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" ""))]
@@ -4102,7 +5547,7 @@ move\\t%0,%z4\\n\\
                   (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 */
@@ -4130,6 +5575,58 @@ move\\t%0,%z4\\n\\
    (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")])
+
 ;; Split a block move into 2 parts, the first part is everything
 ;; except for the last move, and the second part is just the last
 ;; store, which is exactly 1 instruction (ie, not a usw), so it can
@@ -4189,6 +5686,22 @@ move\\t%0,%z4\\n\\
    (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")])
+
 (define_insn "movstrsi_internal3"
   [(set (match_operand:BLK 0 "memory_operand" "=Ro")   ;; destination
        (match_operand:BLK 1 "memory_operand" "Ro"))    ;; source
@@ -4205,6 +5718,22 @@ move\\t%0,%z4\\n\\
    (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
 ;;
 ;;  ....................
@@ -4213,11 +5742,45 @@ 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)
@@ -4229,17 +5792,80 @@ 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 "se_register_operand" "")
                              (match_operand:SI 2 "arith_operand" "")))
              (clobber (match_dup  3))])]
-  "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+  "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;
@@ -4254,7 +5880,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_64BIT && !TARGET_DEBUG_G_MODE"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
   "* 
 {
   operands[4] = const0_rtx;
@@ -4288,7 +5914,8 @@ 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_64BIT && !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, INTVAL (operands[2]) & 0x1f);
@@ -4305,7 +5932,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_64BIT && !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"
@@ -4321,7 +5949,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_64BIT && !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"
@@ -4337,7 +5966,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_64BIT && !TARGET_DEBUG_G_MODE
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && (INTVAL (operands[2]) & 63) < 32
    && (INTVAL (operands[2]) & 63) != 0"
   "*
@@ -4360,7 +5989,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_64BIT && !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
@@ -4394,7 +6024,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_64BIT && !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
@@ -4427,7 +6058,7 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && !TARGET_MIPS16"
   "*
 {
   if (GET_CODE (operands[2]) == CONST_INT)
@@ -4439,16 +6070,84 @@ move\\t%0,%z4\\n\\
    (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))])])
+
 
-(define_insn "ashrsi3"
+;; 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_rtx (CONST_INT, VOIDmode, INTVAL (operands[2]) & 0x1f);
+    operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
 
   return \"sra\\t%0,%1,%2\";
 }"
@@ -4456,17 +6155,77 @@ move\\t%0,%z4\\n\\
    (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, INTVAL (operands[2]) & 0x1f);
+
+  return \"sra\\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" "")
+       (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 "se_register_operand" "")
                                (match_operand:SI 2 "arith_operand" "")))
              (clobber (match_dup  3))])]
-  "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+  "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;
@@ -4481,7 +6240,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_64BIT && !TARGET_DEBUG_G_MODE"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
   "* 
 {
   operands[4] = const0_rtx;
@@ -4563,7 +6322,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_64BIT && !TARGET_DEBUG_G_MODE
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && (INTVAL (operands[2]) & 63) < 32
    && (INTVAL (operands[2]) & 63) != 0"
   "*
@@ -4585,7 +6344,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_64BIT && !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
@@ -4619,7 +6379,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_64BIT && !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
@@ -4652,7 +6413,7 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && !TARGET_MIPS16"
   "*
 {
   if (GET_CODE (operands[2]) == CONST_INT)
@@ -4664,12 +6425,76 @@ move\\t%0,%z4\\n\\
    (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_insn "lshrsi3"
+(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)
@@ -4681,17 +6506,109 @@ 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 "se_register_operand" "")
                                (match_operand:SI 2 "arith_operand" "")))
              (clobber (match_dup  3))])]
-  "TARGET_64BIT || !TARGET_DEBUG_G_MODE"
+  "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;
@@ -4706,7 +6623,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_64BIT && !TARGET_DEBUG_G_MODE"
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16"
   "* 
 {
   operands[4] = const0_rtx;
@@ -4740,7 +6657,8 @@ 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_64BIT && !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, INTVAL (operands[2]) & 0x1f);
@@ -4757,7 +6675,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_64BIT && !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"
@@ -4773,7 +6692,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_64BIT && !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"
@@ -4789,7 +6709,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_64BIT && !TARGET_DEBUG_G_MODE
+  "!TARGET_64BIT && !TARGET_DEBUG_G_MODE && !TARGET_MIPS16
    && (INTVAL (operands[2]) & 63) < 32
    && (INTVAL (operands[2]) & 63) != 0"
   "*
@@ -4811,7 +6731,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_64BIT && !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
@@ -4845,7 +6766,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_64BIT && !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
@@ -4878,7 +6800,7 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && !TARGET_MIPS16"
   "*
 {
   if (GET_CODE (operands[2]) == CONST_INT)
@@ -4890,6 +6812,44 @@ move\\t%0,%z4\\n\\
    (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
 ;;
 ;;  ....................
@@ -5055,7 +7015,7 @@ move\\t%0,%z4\\n\\
                                          (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));
@@ -5093,6 +7053,35 @@ move\\t%0,%z4\\n\\
    (set_attr "length"  "1")])
 
 
+(define_insn ""
+  [(set (pc)
+       (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"
+  "*
+{
+  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_zero_di"
   [(set (pc)
        (if_then_else (match_operator:DI 0 "cmp_op"
@@ -5100,7 +7089,7 @@ move\\t%0,%z4\\n\\
                                          (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));
@@ -5137,6 +7126,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)
@@ -5145,7 +7163,7 @@ 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));
@@ -5165,7 +7183,7 @@ move\\t%0,%z4\\n\\
                                          (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));
@@ -5361,7 +7379,7 @@ move\\t%0,%z4\\n\\
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (TARGET_64BIT || !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;
@@ -5378,27 +7396,47 @@ move\\t%0,%z4\\n\\
   [(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)))]
+  "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_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"
@@ -5410,7 +7448,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)
@@ -5424,7 +7462,7 @@ move\\t%0,%z4\\n\\
   [(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_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"
@@ -5437,6 +7475,7 @@ move\\t%0,%z4\\n\\
        (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)
@@ -5446,11 +7485,13 @@ move\\t%0,%z4\\n\\
                (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 && (!TARGET_64BIT || branch_type != CMP_DI))
@@ -5476,7 +7517,7 @@ 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 "mode"    "SI")
@@ -5486,7 +7527,7 @@ move\\t%0,%z4\\n\\
   [(set (match_operand:DI 0 "register_operand" "=d")
        (ne:DI (match_operand:DI 1 "se_register_operand" "d")
               (const_int 0)))]
-  "TARGET_64BIT"
+  "TARGET_64BIT && !TARGET_MIPS16"
   "sltu\\t%0,%.,%1"
   [(set_attr "type"    "arith")
    (set_attr "mode"    "DI")
@@ -5496,7 +7537,7 @@ move\\t%0,%z4\\n\\
   [(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"
@@ -5508,7 +7549,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)
@@ -5522,7 +7563,7 @@ move\\t%0,%z4\\n\\
   [(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_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"
@@ -5535,6 +7576,7 @@ move\\t%0,%z4\\n\\
        (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)
@@ -5558,7 +7600,7 @@ move\\t%0,%z4\\n\\
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (TARGET_64BIT || !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;
@@ -5574,22 +7616,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 "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_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)
@@ -5604,7 +7666,7 @@ move\\t%0,%z4\\n\\
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (TARGET_64BIT || !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;
@@ -5617,7 +7679,7 @@ 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 "mode"    "SI")
@@ -5627,7 +7689,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)))
@@ -5640,7 +7702,7 @@ move\\t%0,%z4\\n\\
   [(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_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")
@@ -5650,7 +7712,8 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+   && !TARGET_MIPS16"
   [(set (match_dup 0)
        (lt:DI (match_dup 1)
               (match_dup 2)))
@@ -5673,7 +7736,7 @@ move\\t%0,%z4\\n\\
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (TARGET_64BIT || !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;
@@ -5686,22 +7749,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 "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_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)
@@ -5716,7 +7807,7 @@ move\\t%0,%z4\\n\\
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (TARGET_64BIT || !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;
@@ -5732,7 +7823,7 @@ 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);
@@ -5742,11 +7833,27 @@ move\\t%0,%z4\\n\\
    (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 && INTVAL (operands[2]) < 32767"
+  "TARGET_64BIT && !TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
   "*
 {
   operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2])+1);
@@ -5756,11 +7863,27 @@ move\\t%0,%z4\\n\\
    (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 "mode"    "SI")
@@ -5770,7 +7893,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)))
@@ -5783,7 +7906,7 @@ move\\t%0,%z4\\n\\
   [(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_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")
@@ -5793,7 +7916,8 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+   && !TARGET_MIPS16"
   [(set (match_dup 0)
        (lt:DI (match_dup 2)
               (match_dup 1)))
@@ -5816,7 +7940,7 @@ move\\t%0,%z4\\n\\
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (TARGET_64BIT || !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;
@@ -5838,6 +7962,16 @@ move\\t%0,%z4\\n\\
    (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")
@@ -5848,6 +7982,16 @@ move\\t%0,%z4\\n\\
    (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)
@@ -5862,7 +8006,7 @@ move\\t%0,%z4\\n\\
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (TARGET_64BIT || !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;
@@ -5875,7 +8019,7 @@ 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 "mode"    "SI")
@@ -5885,7 +8029,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)))
@@ -5898,7 +8042,7 @@ move\\t%0,%z4\\n\\
   [(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_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")
@@ -5908,7 +8052,8 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+   && !TARGET_MIPS16"
   [(set (match_dup 0)
        (ltu:DI (match_dup 1)
                (match_dup 2)))
@@ -5931,7 +8076,7 @@ move\\t%0,%z4\\n\\
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (TARGET_64BIT || !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;
@@ -5944,22 +8089,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 "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_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)
@@ -5974,7 +8147,7 @@ move\\t%0,%z4\\n\\
   operands[1] = branch_cmp[0];
   operands[2] = branch_cmp[1];
 
-  if (TARGET_64BIT || !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;
@@ -5990,7 +8163,7 @@ 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);
@@ -6000,11 +8173,27 @@ move\\t%0,%z4\\n\\
    (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 && INTVAL (operands[2]) < 32767"
+  "TARGET_64BIT && !TARGET_MIPS16 && INTVAL (operands[2]) < 32767"
   "*
 {
   operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[2])+1);
@@ -6014,11 +8203,27 @@ move\\t%0,%z4\\n\\
    (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 "mode"    "SI")
@@ -6028,7 +8233,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)))
@@ -6041,7 +8246,7 @@ move\\t%0,%z4\\n\\
   [(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_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")
@@ -6051,7 +8256,8 @@ move\\t%0,%z4\\n\\
   [(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_64BIT && TARGET_DEBUG_C_MODE && !TARGET_DEBUG_D_MODE
+   && !TARGET_MIPS16"
   [(set (match_dup 0)
        (ltu:DI (match_dup 2)
                (match_dup 1)))
@@ -6211,7 +8417,7 @@ move\\t%0,%z4\\n\\
 (define_insn "jump"
   [(set (pc)
        (label_ref (match_operand 0 "" "")))]
-  ""
+  "!TARGET_MIPS16"
   "*
 {
   if (GET_CODE (operands[0]) == REG)
@@ -6228,6 +8434,18 @@ move\\t%0,%z4\\n\\
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
+;; 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"))]
   ""
@@ -6277,6 +8495,17 @@ move\\t%0,%z4\\n\\
 
   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 ();
 
@@ -6326,6 +8555,50 @@ move\\t%0,%z4\\n\\
   ""
   "")
 
+(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.
 
@@ -6517,17 +8790,25 @@ move\\t%0,%z4\\n\\
 (define_insn "return"
   [(return)]
   "mips_can_use_return_insn ()"
-  "%*j\\t$31"
+  "*
+{
+  if (TARGET_MIPS16)
+    return \"%*j\\t$7\";
+  return \"%*j\\t$31\";
+}"
   [(set_attr "type"    "jump")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
 
 ;; Normal return.
 (define_insn "return_internal"
-  [(use (reg:SI 31))
+  [(use (match_operand:SI 0 "register_operand" ""))
    (return)]
   ""
-  "%*j\\t$31"
+  "*
+{
+  return \"%*j\\t%0\";
+}"
   [(set_attr "type"    "jump")
    (set_attr "mode"    "none")
    (set_attr "length"  "1")])
@@ -6591,8 +8872,19 @@ move\\t%0,%z4\\n\\
            emit_insn (RTVEC_ELT (adjust, i));
        }
 
+      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;
     }
 }")
@@ -6604,6 +8896,20 @@ move\\t%0,%z4\\n\\
   ""
   "")
 
+;; 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 (mem (match_operand 0 "call_insn_operand" "ri"))
         (match_operand 1 "" "i"))
@@ -6740,6 +9046,18 @@ move\\t%0,%z4\\n\\
            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)
@@ -6772,6 +9090,21 @@ move\\t%0,%z4\\n\\
   ""
   "")
 
+;; 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 (mem (match_operand 1 "call_insn_operand" "ri"))
@@ -7192,3 +9525,236 @@ move\\t%0,%z4\\n\\
   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")])