Thu Mar 20 13:42:01 1997 H.J. Lu <hjl@lucon.org>
[binutils-gdb.git] / gas / config / tc-mips.c
index 1f7e256faf041f81a8fe5c7066b95ea8fbd47f8c..5a92c2490adb266e233a5fb7b0e45ac24994465d 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-mips.c -- assemble code for a MIPS chip.
-   Copyright (C) 1993, 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
    Contributed by the OSF and Ralph Campbell.
    Written by Keith Knowles and Ralph Campbell, working independently.
    Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
@@ -25,7 +25,6 @@
 #include "as.h"
 #include "config.h"
 #include "subsegs.h"
-#include "libiberty.h"
 
 #include <ctype.h>
 
 #endif
 
 #include "opcode/mips.h"
+#include "itbl-ops.h"
+
+#ifdef DEBUG
+#define DBG(x) printf x
+#else
+#define DBG(x)
+#endif
 
 #ifdef OBJ_MAYBE_ELF
 /* Clean up namespace so we can include obj-elf.h too.  */
+static int mips_output_flavor PARAMS ((void));
 static int mips_output_flavor () { return OUTPUT_FLAVOR; }
 #undef OBJ_PROCESS_STAB
 #undef OUTPUT_FLAVOR
@@ -49,6 +56,7 @@ static int mips_output_flavor () { return OUTPUT_FLAVOR; }
 #undef S_SET_SIZE
 #undef TARGET_SYMBOL_FIELDS
 #undef obj_frob_file
+#undef obj_frob_file_after_relocs
 #undef obj_frob_symbol
 #undef obj_pop_insert
 #undef obj_sec_sym_ok_for_reloc
@@ -75,6 +83,7 @@ static char *mips_regmask_frag;
 #endif
 
 #define AT  1
+#define TREG 24
 #define PIC_CALL_REG 25
 #define KT0 26
 #define KT1 27
@@ -83,6 +92,14 @@ static char *mips_regmask_frag;
 #define FP  30
 #define RA  31
 
+#define ILLEGAL_REG (32)
+
+/* Allow override of standard little-endian ECOFF format.  */
+
+#ifndef ECOFF_LITTLE_FORMAT
+#define ECOFF_LITTLE_FORMAT "ecoff-littlemips"
+#endif
+
 extern int target_big_endian;
 
 /* 1 is we should use the 64 bit MIPS ELF ABI, 0 if we should use the
@@ -98,7 +115,7 @@ mips_target_format ()
     case bfd_target_aout_flavour:
       return target_big_endian ? "a.out-mips-big" : "a.out-mips-little";
     case bfd_target_ecoff_flavour:
-      return target_big_endian ? "ecoff-bigmips" : "ecoff-littlemips";
+      return target_big_endian ? "ecoff-bigmips" : ECOFF_LITTLE_FORMAT;
     case bfd_target_elf_flavour:
       return (target_big_endian
              ? (mips_64 ? "elf64-bigmips" : "elf32-bigmips")
@@ -130,6 +147,9 @@ static int mips_isa = -1;
 /* MIPS ISA we are using for this output file.  */
 static int file_mips_isa;
 
+/* Whether we are assembling for the mips16 processor.  */
+static int mips16 = -1;
+
 /* The CPU type as a number: 2000, 3000, 4000, 4400, etc.  */
 static int mips_cpu = -1;
 
@@ -142,12 +162,18 @@ static int mips_4010 = -1;
 /* Whether the 4100 MADD16 and DMADD16 are permitted. */
 static int mips_4100 = -1;
 
+/* start-sanitize-r5900 */
+/* Whether Toshiba r5900 instructions are permitted. */
+static int mips_5900 = -1;
+/* end-sanitize-r5900 */
+
 /* Whether the processor uses hardware interlocks, and thus does not
    require nops to be inserted.  */
 static int interlocks = -1;
 
 /* As with "interlocks" this is used by hardware that has FP
    (co-processor) interlocks.  */
+/* Itbl support may require additional care here. */
 static int cop_interlocks = -1;
 
 /* MIPS PIC level.  */
@@ -181,6 +207,9 @@ static int mips_big_got;
    instructions.  */
 static int mips_trap;
 
+/* 1 if we should autoextend mips16 instructions.  */
+static int mips16_autoextend = 1;
+
 static int mips_warn_about_macros;
 static int mips_noreorder;
 static int mips_any_noreorder;
@@ -212,6 +241,9 @@ static int nopic_need_relax PARAMS ((symbolS *));
 /* handle of the OPCODE hash table */
 static struct hash_control *op_hash = NULL;
 
+/* The opcode hash table we use for the mips16.  */
+static struct hash_control *mips16_op_hash = NULL;
+
 /* This array holds the chars that always start a comment.  If the
     pre-processor is disabled, these aren't very useful */
 const char comment_chars[] = "#";
@@ -243,13 +275,8 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 static char *insn_error;
 
-static int byte_order;
-
 static int auto_align = 1;
 
-/* Symbol labelling the current insn.  */
-static symbolS *insn_label;
-
 /* When outputting SVR4 PIC code, the assembler needs to know the
    offset in the stack frame from which to restore the $gp register.
    This is set by the .cprestore pseudo-op, and saved in this
@@ -292,6 +319,9 @@ static struct frag *prev_insn_frag;
 /* The offset into prev_insn_frag for the previous instruction.  */
 static long prev_insn_where;
 
+/* The reloc type for the previous instruction, if any.  */
+static bfd_reloc_code_real_type prev_insn_reloc_type;
+
 /* The reloc for the previous instruction, if any.  */
 static fixS *prev_insn_fixp;
 
@@ -301,10 +331,30 @@ static int prev_insn_is_delay_slot;
 /* Non-zero if the previous instruction was in a .set noreorder.  */
 static int prev_insn_unreordered;
 
+/* Non-zero if the previous instruction uses an extend opcode (if
+   mips16).  */
+static int prev_insn_extended;
+
 /* Non-zero if the previous previous instruction was in a .set
    noreorder.  */
 static int prev_prev_insn_unreordered;
 
+/* If this is set, it points to a frag holding nop instructions which
+   were inserted before the start of a noreorder section.  If those
+   nops turn out to be unnecessary, the size of the frag can be
+   decreased.  */
+static fragS *prev_nop_frag;
+
+/* The number of nop instructions we created in prev_nop_frag.  */
+static int prev_nop_frag_holds;
+
+/* The number of nop instructions that we know we need in
+   prev_nop_frag. */
+static int prev_nop_frag_required;
+
+/* The number of instructions we've seen since prev_nop_frag.  */
+static int prev_nop_frag_since;
+
 /* For ECOFF and ELF, relocations against symbols are done in two
    parts, with a HI relocation and a LO relocation.  Each relocation
    has only 16 bits of space to store an addend.  This means that in
@@ -329,6 +379,25 @@ struct mips_hi_fixup
 /* The list of unmatched HI relocs.  */
 
 static struct mips_hi_fixup *mips_hi_fixup_list;
+
+/* Map normal MIPS register numbers to mips16 register numbers.  */
+
+#define X ILLEGAL_REG
+static const int mips32_to_16_reg_map[] =
+{
+  X, X, 2, 3, 4, 5, 6, 7,
+  X, X, X, X, X, X, X, X,
+  0, 1, X, X, X, X, X, X,
+  X, X, X, X, X, X, X, X
+};
+#undef X
+
+/* Map mips16 register numbers to normal MIPS register numbers.  */
+
+static const int mips16_to_32_reg_map[] =
+{
+  16, 17, 2, 3, 4, 5, 6, 7
+};
 \f
 /* Since the MIPS does not have multiple forms of PC relative
    instructions, we do not have to do relaxing as is done on other
@@ -393,18 +462,58 @@ static struct mips_hi_fixup *mips_hi_fixup_list;
    one if needed.  */
 #define RELAX_ENCODE(old, new, reloc1, reloc2, reloc3, warn) \
   ((relax_substateT) \
-   (((old) << 24) \
+   (((old) << 23) \
     | ((new) << 16) \
     | (((reloc1) + 64) << 9) \
     | (((reloc2) + 64) << 2) \
     | ((reloc3) ? (1 << 1) : 0) \
     | ((warn) ? 1 : 0)))
-#define RELAX_OLD(i) (((i) >> 24) & 0xff)
-#define RELAX_NEW(i) (((i) >> 16) & 0xff)
+#define RELAX_OLD(i) (((i) >> 23) & 0x7f)
+#define RELAX_NEW(i) (((i) >> 16) & 0x7f)
 #define RELAX_RELOC1(i) ((bfd_vma)(((i) >> 9) & 0x7f) - 64)
 #define RELAX_RELOC2(i) ((bfd_vma)(((i) >> 2) & 0x7f) - 64)
 #define RELAX_RELOC3(i) (((i) >> 1) & 1)
 #define RELAX_WARN(i) ((i) & 1)
+
+/* For mips16 code, we use an entirely different form of relaxation.
+   mips16 supports two versions of most instructions which take
+   immediate values: a small one which takes some small value, and a
+   larger one which takes a 16 bit value.  Since branches also follow
+   this pattern, relaxing these values is required.
+
+   We can assemble both mips16 and normal MIPS code in a single
+   object.  Therefore, we need to support this type of relaxation at
+   the same time that we support the relaxation described above.  We
+   use the high bit of the subtype field to distinguish these cases.
+
+   The information we store for this type of relaxation is the
+   argument code found in the opcode file for this relocation, whether
+   the user explicitly requested a small or extended form, and whether
+   the relocation is in a jump or jal delay slot.  That tells us the
+   size of the value, and how it should be stored.  We also store
+   whether the fragment is considered to be extended or not.  We also
+   store whether this is known to be a branch to a different section,
+   whether we have tried to relax this frag yet, and whether we have
+   ever extended a PC relative fragment because of a shift count.  */
+#define RELAX_MIPS16_ENCODE(type, small, ext, dslot, jal_dslot)        \
+  (0x80000000                                                  \
+   | ((type) & 0xff)                                           \
+   | ((small) ? 0x100 : 0)                                     \
+   | ((ext) ? 0x200 : 0)                                       \
+   | ((dslot) ? 0x400 : 0)                                     \
+   | ((jal_dslot) ? 0x800 : 0))
+#define RELAX_MIPS16_P(i) (((i) & 0x80000000) != 0)
+#define RELAX_MIPS16_TYPE(i) ((i) & 0xff)
+#define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0)
+#define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0)
+#define RELAX_MIPS16_DSLOT(i) (((i) & 0x400) != 0)
+#define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x800) != 0)
+#define RELAX_MIPS16_EXTENDED(i) (((i) & 0x1000) != 0)
+#define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x1000)
+#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x1000)
+#define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0)
+#define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000)
+#define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000)
 \f
 /* Prototypes for static functions.  */
 
@@ -415,16 +524,18 @@ static struct mips_hi_fixup *mips_hi_fixup_list;
 #define internalError() as_fatal ("MIPS internal Error");
 #endif
 
+enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };
+
 static int insn_uses_reg PARAMS ((struct mips_cl_insn *ip,
-                                 unsigned int reg, int fpr));
+                                 unsigned int reg, enum mips_regclass class));
 static int reg_needs_delay PARAMS ((int));
 static void append_insn PARAMS ((char *place,
                                 struct mips_cl_insn * ip,
                                 expressionS * p,
                                 bfd_reloc_code_real_type r,
                                 boolean));
-static void mips_no_prev_insn PARAMS ((void));
-static void mips_emit_delays PARAMS ((void));
+static void mips_no_prev_insn PARAMS ((int));
+static void mips_emit_delays PARAMS ((boolean));
 #ifdef USE_STDARG
 static void macro_build PARAMS ((char *place, int *counter, expressionS * ep,
                                 const char *name, const char *fmt,
@@ -432,6 +543,9 @@ static void macro_build PARAMS ((char *place, int *counter, expressionS * ep,
 #else
 static void macro_build ();
 #endif
+static void mips16_macro_build PARAMS ((char *, int *, expressionS *,
+                                       const char *, const char *,
+                                       va_list));
 static void macro_build_lui PARAMS ((char *place, int *counter,
                                     expressionS * ep, int regnum));
 static void set_at PARAMS ((int *counter, int reg, int unsignedp));
@@ -440,10 +554,15 @@ static void check_absolute_expr PARAMS ((struct mips_cl_insn * ip,
 static void load_register PARAMS ((int *, int, expressionS *, int));
 static void load_address PARAMS ((int *counter, int reg, expressionS *ep));
 static void macro PARAMS ((struct mips_cl_insn * ip));
+static void mips16_macro PARAMS ((struct mips_cl_insn * ip));
 #ifdef LOSING_COMPILER
 static void macro2 PARAMS ((struct mips_cl_insn * ip));
 #endif
 static void mips_ip PARAMS ((char *str, struct mips_cl_insn * ip));
+static void mips16_ip PARAMS ((char *str, struct mips_cl_insn * ip));
+static void mips16_immed PARAMS ((char *, unsigned int, int, offsetT, boolean,
+                                 boolean, boolean, unsigned long *,
+                                 boolean *, unsigned short *));
 static int my_getSmallExpression PARAMS ((expressionS * ep, char *str));
 static void my_getExpression PARAMS ((expressionS * ep, char *str));
 static symbolS *get_symbol PARAMS ((void));
@@ -460,12 +579,14 @@ static void s_cpload PARAMS ((int));
 static void s_cprestore PARAMS ((int));
 static void s_gpword PARAMS ((int));
 static void s_cpadd PARAMS ((int));
+static void s_insn PARAMS ((int));
 static void md_obj_begin PARAMS ((void));
 static void md_obj_end PARAMS ((void));
 static long get_number PARAMS ((void));
 static void s_ent PARAMS ((int));
 static void s_mipsend PARAMS ((int));
 static void s_file PARAMS ((int));
+static int mips16_extended_frag PARAMS ((fragS *, asection *, long));
 \f
 /* Pseudo-op table.
 
@@ -498,6 +619,7 @@ static const pseudo_typeS mips_pseudo_table[] =
   {"cprestore", s_cprestore, 0},
   {"gpword", s_gpword, 0},
   {"cpadd", s_cpadd, 0},
+  {"insn", s_insn, 0},
 
  /* Relatively generic pseudo-ops that happen to be used on MIPS
      chips.  */
@@ -555,6 +677,30 @@ mips_pop_insert ()
     pop_insert (mips_nonecoff_pseudo_table);
 }
 \f
+/* Symbols labelling the current insn.  */
+
+struct insn_label_list
+{
+  struct insn_label_list *next;
+  symbolS *label;
+};
+
+static struct insn_label_list *insn_labels;
+static struct insn_label_list *free_insn_labels;
+
+static void mips_clear_insn_labels PARAMS ((void));
+
+static inline void
+mips_clear_insn_labels ()
+{
+  register struct insn_label_list **pl;
+
+  for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
+    ;
+  *pl = insn_labels;
+  insn_labels = NULL;
+}
+\f
 static char *expr_end;
 
 /* Expressions which appear in instructions.  These are set by
@@ -572,6 +718,10 @@ static bfd_reloc_code_real_type offset_reloc;
 
 static boolean imm_unmatched_hi;
 
+/* These are set by mips16_ip if an explicit extension is used.  */
+
+static boolean mips16_small, mips16_ext;
+
 /*
  * This function is called once, at assembler startup time.  It should
  * set up all the tables, etc. that the MD part of the assembler will need.
@@ -661,6 +811,25 @@ md_begin ()
          if (mips_4010 == -1)
            mips_4010 = 1;
        }
+      else if (strcmp (cpu, "r5000") == 0
+              || strcmp (cpu, "mips64vr5000") == 0)
+       {
+         mips_isa = 4;
+         if (mips_cpu == -1)
+           mips_cpu = 5000;
+       }
+      /* start-sanitize-r5900 */
+      else if (strcmp (cpu, "r5900") == 0
+              || strcmp (cpu, "mips64vr5900") == 0
+               || strcmp (cpu, "mips64vr5900el") == 0)
+       {
+         mips_isa = 3;
+         if (mips_cpu == -1)
+           mips_cpu = 5900;
+          if (mips_5900 == -1)
+            mips_5900 = 1;
+       }
+      /* end-sanitize-r5900 */
       else if (strcmp (cpu, "r8000") == 0
               || strcmp (cpu, "mips4") == 0)
        {
@@ -674,6 +843,12 @@ md_begin ()
          if (mips_cpu == -1)
            mips_cpu = 10000;
        }
+      else if (strcmp (cpu, "mips16") == 0)
+       {
+         mips_isa = 3;
+         if (mips_cpu == -1)
+           mips_cpu = 0; /* FIXME */
+       }
       else
        {
          mips_isa = 1;
@@ -685,6 +860,14 @@ md_begin ()
        free (a);
     }
 
+  if (mips16 < 0)
+    {
+      if (strncmp (TARGET_CPU, "mips16", sizeof "mips16" - 1) == 0)
+       mips16 = 1;
+      else
+       mips16 = 0;
+    }
+
   if (mips_4650 < 0)
     mips_4650 = 0;
 
@@ -694,11 +877,17 @@ md_begin ()
   if (mips_4100 < 0)
     mips_4100 = 0;
 
-  if (mips_4650 || mips_4010 || mips_4100 || mips_cpu == 4300)
+  /* start-sanitize-r5900 */
+  if (mips_5900 < 0)
+    mips_5900 = 0;
+  /* end-sanitize-r5900 */
+
+  if (mips_4010 || mips_4100 || mips_cpu == 4300)
     interlocks = 1;
   else
     interlocks = 0;
 
+  /* Itbl support may require additional care here. */
   if (mips_cpu == 4300)
     cop_interlocks = 1;
   else
@@ -755,7 +944,56 @@ md_begin ()
       while ((i < NUMOPCODES) && !strcmp (mips_opcodes[i].name, name));
     }
 
-  mips_no_prev_insn ();
+  mips16_op_hash = hash_new ();
+
+  i = 0;
+  while (i < bfd_mips16_num_opcodes)
+    {
+      const char *name = mips16_opcodes[i].name;
+
+      retval = hash_insert (mips16_op_hash, name, (PTR) &mips16_opcodes[i]);
+      if (retval != NULL)
+       as_fatal ("internal error: can't hash `%s': %s\n",
+                 mips16_opcodes[i].name, retval);
+      do
+       {
+         if (mips16_opcodes[i].pinfo != INSN_MACRO
+             && ((mips16_opcodes[i].match & mips16_opcodes[i].mask)
+                 != mips16_opcodes[i].match))
+           as_fatal ("internal error: bad opcode: `%s' \"%s\"\n",
+                     mips16_opcodes[i].name, mips16_opcodes[i].args);
+         ++i;
+       }
+      while (i < bfd_mips16_num_opcodes
+            && strcmp (mips16_opcodes[i].name, name) == 0);
+    }
+
+  /* We add all the general register names to the symbol table.  This
+     helps us detect invalid uses of them.  */
+  for (i = 0; i < 32; i++)
+    {
+      char buf[5];
+
+      sprintf (buf, "$%d", i);
+      symbol_table_insert (symbol_new (buf, reg_section, i,
+                                      &zero_address_frag));
+    }
+  symbol_table_insert (symbol_new ("$fp", reg_section, FP,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$sp", reg_section, SP,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$gp", reg_section, GP,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$at", reg_section, AT,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$kt0", reg_section, KT0,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$kt1", reg_section, KT1,
+                                  &zero_address_frag));
+  symbol_table_insert (symbol_new ("$pc", reg_section, -1,
+                                  &zero_address_frag));
+
+  mips_no_prev_insn (false);
 
   mips_gprmask = 0;
   mips_cprmask[0] = 0;
@@ -771,30 +1009,40 @@ md_begin ()
 
   if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
     {
-      /* Sections must be aligned to 16 byte boundaries.  */
-      (void) bfd_set_section_alignment (stdoutput, text_section, 4);
-      (void) bfd_set_section_alignment (stdoutput, data_section, 4);
-      (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
+      /* On a native system, sections must be aligned to 16 byte
+        boundaries.  When configured for an embedded ELF target, we
+        don't bother.  */
+      if (strcmp (TARGET_OS, "elf") != 0)
+       {
+         (void) bfd_set_section_alignment (stdoutput, text_section, 4);
+         (void) bfd_set_section_alignment (stdoutput, data_section, 4);
+         (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
+       }
 
       /* Create a .reginfo section for register masks and a .mdebug
         section for debugging information.  */
       {
        segT seg;
        subsegT subseg;
+       flagword flags;
        segT sec;
 
        seg = now_seg;
        subseg = now_subseg;
 
+       /* The ABI says this section should be loaded so that the
+          running program can access it.  However, we don't load it
+          if we are configured for an embedded target */
+       flags = SEC_READONLY | SEC_DATA;
+       if (strcmp (TARGET_OS, "elf") != 0)
+         flags |= SEC_ALLOC | SEC_LOAD;
+
        if (! mips_64)
          {
            sec = subseg_new (".reginfo", (subsegT) 0);
 
-           /* The ABI says this section should be loaded so that the
-              running program can access it.  */
-           (void) bfd_set_section_flags (stdoutput, sec,
-                                         (SEC_ALLOC | SEC_LOAD
-                                          | SEC_READONLY | SEC_DATA));
+
+           (void) bfd_set_section_flags (stdoutput, sec, flags);
            (void) bfd_set_section_alignment (stdoutput, sec, 2);
        
 #ifdef OBJ_ELF
@@ -806,9 +1054,7 @@ md_begin ()
            /* The 64-bit ABI uses a .MIPS.options section rather than
                .reginfo section.  */
            sec = subseg_new (".MIPS.options", (subsegT) 0);
-           (void) bfd_set_section_flags (stdoutput, sec,
-                                         (SEC_ALLOC | SEC_LOAD
-                                          | SEC_READONLY | SEC_DATA));
+           (void) bfd_set_section_flags (stdoutput, sec, flags);
            (void) bfd_set_section_alignment (stdoutput, sec, 3);
 
 #ifdef OBJ_ELF
@@ -866,15 +1112,27 @@ md_assemble (str)
   offset_expr.X_op = O_absent;
   offset_reloc = BFD_RELOC_UNUSED;
 
-  mips_ip (str, &insn);
+  if (mips16)
+    mips16_ip (str, &insn);
+  else
+    {
+      mips_ip (str, &insn);
+      DBG(("returned from mips_ip(%s) insn_opcode = 0x%x\n", 
+               str, insn.insn_opcode));
+    }
+
   if (insn_error)
     {
       as_bad ("%s `%s'", insn_error, str);
       return;
     }
+
   if (insn.insn_mo->pinfo == INSN_MACRO)
     {
-      macro (&insn);
+      if (mips16)
+       mips16_macro (&insn);
+      else
+       macro (&insn);
     }
   else
     {
@@ -888,21 +1146,29 @@ md_assemble (str)
     }
 }
 
-/* See whether instruction IP reads register REG.  If FPR is non-zero,
-   REG is a floating point register.  */
+/* See whether instruction IP reads register REG.  CLASS is the type
+   of register.  */
 
 static int
-insn_uses_reg (ip, reg, fpr)
+insn_uses_reg (ip, reg, class)
      struct mips_cl_insn *ip;
      unsigned int reg;
-     int fpr;
+     enum mips_regclass class;
 {
+  if (class == MIPS16_REG)
+    {
+      assert (mips16);
+      reg = mips16_to_32_reg_map[reg];
+      class = MIPS_GR_REG;
+    }
+
   /* Don't report on general register 0, since it never changes.  */
-  if (! fpr && reg == 0)
+  if (class == MIPS_GR_REG && reg == 0)
     return 0;
 
-  if (fpr)
+  if (class == MIPS_FP_REG)
     {
+      assert (! mips16);
       /* If we are called with either $f0 or $f1, we must check $f0.
         This is not optimal, because it will introduce an unnecessary
         NOP between "lwc1 $f0" and "swc1 $f1".  To fix this we would
@@ -919,7 +1185,7 @@ insn_uses_reg (ip, reg, fpr)
              == (reg &~ (unsigned) 1)))
        return 1;
     }
-  else
+  else if (! mips16)
     {
       if ((ip->insn_mo->pinfo & INSN_READ_GPR_S)
          && ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == reg)
@@ -928,6 +1194,29 @@ insn_uses_reg (ip, reg, fpr)
          && ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT) == reg)
        return 1;
     }
+  else
+    {
+      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X)
+         && ((ip->insn_opcode >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX) == reg)
+       return 1;
+      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Y)
+         && ((ip->insn_opcode >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY) == reg)
+       return 1;
+      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Z)
+         && ((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z)
+             & MIPS16OP_MASK_MOVE32Z) == reg)
+       return 1;
+      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_T) && reg == TREG)
+       return 1;
+      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_SP) && reg == SP)
+       return 1;
+      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_31) && reg == RA)
+       return 1;
+      if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_GPR_X)
+         && ((ip->insn_opcode >> MIPS16OP_SH_REGR32)
+             & MIPS16OP_MASK_REGR32) == reg)
+       return 1;
+    }
 
   return 0;
 }
@@ -952,6 +1241,7 @@ reg_needs_delay (reg)
         delays delay the use of general register rt for one
         instruction on the r3000.  The r6000 and r4000 use
         interlocks.  */
+      /* Itbl support may require additional care here. */
       know (prev_pinfo & INSN_WRITE_GPR_T);
       if (reg == ((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT))
        return 1;
@@ -978,11 +1268,34 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
   fixS *fixp;
   int nops = 0;
 
+  /* Mark instruction labels in mips16 mode.  This permits the linker
+     to handle them specially, such as generating jalx instructions
+     when needed.  We also make them odd for the duration of the
+     assembly, in order to generate the right sort of code.  We will
+     make them even in the adjust_symtab routine, while leaving them
+     marked.  This is convenient for the debugger and the
+     disassembler.  The linker knows to make them odd again.  */
+  if (mips16)
+    {
+      struct insn_label_list *l;
+
+      for (l = insn_labels; l != NULL; l = l->next)
+       {
+#ifdef S_SET_OTHER
+         if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+           S_SET_OTHER (l->label, STO_MIPS16);
+#endif
+         ++l->label->sy_value.X_add_number;
+       }
+    }
+
   prev_pinfo = prev_insn.insn_mo->pinfo;
   pinfo = ip->insn_mo->pinfo;
 
-  if (place == NULL && ! mips_noreorder)
+  if (place == NULL && (! mips_noreorder || prev_nop_frag != NULL))
     {
+      int prev_prev_nop;
+
       /* If the previous insn required any delay slots, see if we need
         to insert a NOP or two.  There are eight kinds of possible
         hazards, of which an instruction can have at most one type.
@@ -1004,11 +1317,15 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
         it.  */
 
       /* This is how a NOP is emitted.  */
-#define emit_nop() md_number_to_chars (frag_more (4), 0, 4)
+#define emit_nop()                                     \
+  (mips16                                              \
+   ? md_number_to_chars (frag_more (2), 0x6500, 2)     \
+   : md_number_to_chars (frag_more (4), 0, 4))
 
       /* The previous insn might require a delay slot, depending upon
         the contents of the current insn.  */
-      if (mips_isa < 4
+      if (! mips16
+         && mips_isa < 4
          && (((prev_pinfo & INSN_LOAD_COPROC_DELAY)
                && ! cop_interlocks)
              || (mips_isa < 2
@@ -1018,15 +1335,17 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             delays delay the use of general register rt for one
             instruction on the r3000.  The r6000 and r4000 use
             interlocks.  */
+          /* Itbl support may require additional care here. */
          know (prev_pinfo & INSN_WRITE_GPR_T);
          if (mips_optimize == 0
              || insn_uses_reg (ip,
                                ((prev_insn.insn_opcode >> OP_SH_RT)
                                 & OP_MASK_RT),
-                               0))
+                               MIPS_GR_REG))
            ++nops;
        }
-      else if (mips_isa < 4
+      else if (! mips16
+              && mips_isa < 4
               && (((prev_pinfo & INSN_COPROC_MOVE_DELAY)
                     && ! cop_interlocks)
                   || (mips_isa < 2
@@ -1046,13 +1365,16 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             knowledge of CP0 handling, and the coprocessors other
             than the floating point unit are not distinguished at
             all.  */
+          /* Itbl support may require additional care here. FIXME!
+             Need to modify this to include knowledge about 
+             user specified delays!  */
          if (prev_pinfo & INSN_WRITE_FPR_T)
            {
              if (mips_optimize == 0
                  || insn_uses_reg (ip,
                                    ((prev_insn.insn_opcode >> OP_SH_FT)
                                     & OP_MASK_FT),
-                                   1))
+                                   MIPS_FP_REG))
                ++nops;
            }
          else if (prev_pinfo & INSN_WRITE_FPR_S)
@@ -1061,7 +1383,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  || insn_uses_reg (ip,
                                    ((prev_insn.insn_opcode >> OP_SH_FS)
                                     & OP_MASK_FS),
-                                   1))
+                                   MIPS_FP_REG))
                ++nops;
            }
          else
@@ -1072,6 +1394,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                 instruction may set the condition codes, and the
                 current instruction uses them, we must insert two
                 NOPS.  */
+              /* Itbl support may require additional care here. */
              if (mips_optimize == 0
                  || ((prev_pinfo & INSN_WRITE_COND_CODE)
                      && (pinfo & INSN_READ_COND_CODE)))
@@ -1080,7 +1403,8 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                ++nops;
            }
        }
-      else if (mips_isa < 4
+      else if (! mips16
+              && mips_isa < 4
               && (prev_pinfo & INSN_WRITE_COND_CODE)
                && ! cop_interlocks)
        {
@@ -1089,6 +1413,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             (this means it is a floating point comparison
             instruction).  If this instruction uses the condition
             codes, we need to insert a single NOP.  */
+          /* Itbl support may require additional care here. */
          if (mips_optimize == 0
              || (pinfo & INSN_READ_COND_CODE))
            ++nops;
@@ -1097,8 +1422,7 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        {
          /* The previous instruction reads the LO register; if the
             current instruction writes to the LO register, we must
-            insert two NOPS.  The R4650, VR4100 and VR4300 have
-            interlocks.  */
+            insert two NOPS.  Some newer processors have interlocks.  */
          if (! interlocks
              && (mips_optimize == 0
                  || (pinfo & INSN_WRITE_LO)))
@@ -1108,50 +1432,65 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        {
          /* The previous instruction reads the HI register; if the
             current instruction writes to the HI register, we must
-            insert a NOP.  The R4650, VR4100 and VR4300 have
-            interlocks.  */
+            insert a NOP.  Some newer processors have interlocks.  */
          if (! interlocks
              && (mips_optimize == 0
                  || (pinfo & INSN_WRITE_HI)))
            nops += 2;
        }
 
+      /* If the previous instruction was in a noreorder section, then
+         we don't want to insert the nop after all.  */
+      /* Itbl support may require additional care here. */
+      if (prev_insn_unreordered)
+       nops = 0;
+
       /* There are two cases which require two intervening
         instructions: 1) setting the condition codes using a move to
         coprocessor instruction which requires a general coprocessor
         delay and then reading the condition codes 2) reading the HI
-        or LO register and then writing to it (except on the R4650,
-        VR4100, and VR4300 which have interlocks).  If we are not
-        already emitting a NOP instruction, we must check for these
-        cases compared to the instruction previous to the previous
-        instruction.  */
-      if (nops == 0
-         && ((mips_isa < 4
-              && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
-              && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
-              && (pinfo & INSN_READ_COND_CODE)
-               && ! cop_interlocks)
-             || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
-                 && (pinfo & INSN_WRITE_LO)
-                 && ! interlocks)
-             || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
-                 && (pinfo & INSN_WRITE_HI)
-                 && ! interlocks)))
+        or LO register and then writing to it (except on processors
+        which have interlocks).  If we are not already emitting a NOP
+        instruction, we must check for these cases compared to the
+        instruction previous to the previous instruction.  */
+      if ((! mips16
+          && mips_isa < 4
+          && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
+          && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+          && (pinfo & INSN_READ_COND_CODE)
+          && ! cop_interlocks)
+         || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
+             && (pinfo & INSN_WRITE_LO)
+             && ! interlocks)
+         || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
+             && (pinfo & INSN_WRITE_HI)
+             && ! interlocks))
+       prev_prev_nop = 1;
+      else
+       prev_prev_nop = 0;
+
+      if (prev_prev_insn_unreordered)
+       prev_prev_nop = 0;
+
+      if (prev_prev_nop && nops == 0)
        ++nops;
 
       /* If we are being given a nop instruction, don't bother with
         one of the nops we would otherwise output.  This will only
         happen when a nop instruction is used with mips_optimize set
         to 0.  */
-      if (nops > 0 && ip->insn_opcode == 0)
+      if (nops > 0
+         && ! mips_noreorder
+         && ip->insn_opcode == (mips16 ? 0x6500 : 0))
        --nops;
 
       /* Now emit the right number of NOP instructions.  */
-      if (nops > 0)
+      if (nops > 0 && ! mips_noreorder)
        {
          fragS *old_frag;
          unsigned long old_frag_offset;
          int i;
+         struct insn_label_list *l;
 
          old_frag = frag_now;
          old_frag_offset = frag_now_fix ();
@@ -1173,11 +1512,14 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              frag_grow (40);
            }
 
-         if (insn_label != NULL)
+         for (l = insn_labels; l != NULL; l = l->next)
            {
-             assert (S_GET_SEGMENT (insn_label) == now_seg);
-             insn_label->sy_frag = frag_now;
-             S_SET_VALUE (insn_label, (valueT) frag_now_fix ());
+             assert (S_GET_SEGMENT (l->label) == now_seg);
+             l->label->sy_frag = frag_now;
+             S_SET_VALUE (l->label, (valueT) frag_now_fix ());
+             /* mips16 text labels are stored as odd.  */
+             if (mips16)
+               ++l->label->sy_value.X_add_number;
            }
 
 #ifndef NO_ECOFF_DEBUGGING
@@ -1185,14 +1527,80 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
            ecoff_fix_loc (old_frag, old_frag_offset);
 #endif
        }
+      else if (prev_nop_frag != NULL)
+       {
+         /* We have a frag holding nops we may be able to remove.  If
+             we don't need any nops, we can decrease the size of
+             prev_nop_frag by the size of one instruction.  If we do
+             need some nops, we count them in prev_nops_required. */
+         if (prev_nop_frag_since == 0)
+           {
+             if (nops == 0)
+               {
+                 prev_nop_frag->fr_fix -= mips16 ? 2 : 4;
+                 --prev_nop_frag_holds;
+               }
+             else
+               prev_nop_frag_required += nops;
+           }
+         else
+           {
+             if (prev_prev_nop == 0)
+               {
+                 prev_nop_frag->fr_fix -= mips16 ? 2 : 4;
+                 --prev_nop_frag_holds;
+               }
+             else
+               ++prev_nop_frag_required;
+           }
+
+         if (prev_nop_frag_holds <= prev_nop_frag_required)
+           prev_nop_frag = NULL;
+
+         ++prev_nop_frag_since;
+
+         /* Sanity check: by the time we reach the second instruction
+             after prev_nop_frag, we should have used up all the nops
+             one way or another.  */
+         assert (prev_nop_frag_since <= 1 || prev_nop_frag == NULL);
+       }
     }
-  
-  if (place == NULL)
-    f = frag_more (4);
-  else
+
+  if (reloc_type > BFD_RELOC_UNUSED)
+    {
+      /* We need to set up a variant frag.  */
+      assert (mips16 && address_expr != NULL);
+      f = frag_var (rs_machine_dependent, 4, 0,
+                   RELAX_MIPS16_ENCODE (reloc_type - BFD_RELOC_UNUSED,
+                                        mips16_small, mips16_ext,
+                                        (prev_pinfo
+                                         & INSN_UNCOND_BRANCH_DELAY),
+                                        (prev_insn_reloc_type
+                                         == BFD_RELOC_MIPS16_JMP)),
+                   make_expr_symbol (address_expr), (offsetT) 0,
+                   (char *) NULL);
+    }
+  else if (place != NULL)
     f = place;
+  else if (mips16 && ! ip->use_extend && reloc_type != BFD_RELOC_MIPS16_JMP)
+    {
+      /* Make sure there is enough room to swap this instruction with
+         a following jump instruction.  */
+      frag_grow (6);
+      f = frag_more (2);
+    }
+  else
+    {
+      if (mips16
+         && mips_noreorder
+         && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+       as_warn ("extended instruction in delay slot");
+
+      f = frag_more (4);
+    }
+
   fixp = NULL;
-  if (address_expr != NULL)
+  if (address_expr != NULL && reloc_type < BFD_RELOC_UNUSED)
     {
       if (address_expr->X_op == O_constant)
        {
@@ -1207,6 +1615,22 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              break;
 
            case BFD_RELOC_MIPS_JMP:
+             if ((address_expr->X_add_number & 3) != 0)
+               as_bad ("jump to misaligned address (0x%lx)",
+                       (unsigned long) address_expr->X_add_number);
+             ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff;
+             break;
+
+           case BFD_RELOC_MIPS16_JMP:
+             if ((address_expr->X_add_number & 3) != 0)
+               as_bad ("jump to misaligned address (0x%lx)",
+                       (unsigned long) address_expr->X_add_number);
+             ip->insn_opcode |=
+               (((address_expr->X_add_number & 0x7c0000) << 3)
+                | ((address_expr->X_add_number & 0xf800000) >> 7)
+                | ((address_expr->X_add_number & 0x3fffc) >> 2));
+             break;
+
            case BFD_RELOC_16_PCREL_S2:
              goto need_reloc;
 
@@ -1216,7 +1640,6 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        }
       else
        {
-         assert (reloc_type != BFD_RELOC_UNUSED);
        need_reloc:
          /* Don't generate a reloc if we are writing into a variant
             frag.  */
@@ -1242,31 +1665,78 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
        }
     }
 
-  md_number_to_chars (f, ip->insn_opcode, 4);
+  if (! mips16)
+    md_number_to_chars (f, ip->insn_opcode, 4);
+  else if (reloc_type == BFD_RELOC_MIPS16_JMP)
+    {
+      md_number_to_chars (f, ip->insn_opcode >> 16, 2);
+      md_number_to_chars (f + 2, ip->insn_opcode & 0xffff, 2);
+    }
+  else
+    {
+      if (ip->use_extend)
+       {
+         md_number_to_chars (f, 0xf000 | ip->extend, 2);
+         f += 2;
+       }
+      md_number_to_chars (f, ip->insn_opcode, 2);
+    }
 
   /* Update the register mask information.  */
-  if (pinfo & INSN_WRITE_GPR_D)
-    mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD);
-  if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0)
-    mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT);
-  if (pinfo & INSN_READ_GPR_S)
-    mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS);
-  if (pinfo & INSN_WRITE_GPR_31)
-    mips_gprmask |= 1 << 31;
-  if (pinfo & INSN_WRITE_FPR_D)
-    mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FD) & OP_MASK_FD);
-  if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0)
-    mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS);
-  if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0)
-    mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT);
-  if ((pinfo & INSN_READ_FPR_R) != 0)
-    mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR);
-  if (pinfo & INSN_COP)
-    {
-      /* We don't keep enough information to sort these cases out.  */
-    }
-  /* Never set the bit for $0, which is always zero.  */
-  mips_gprmask &=~ 1 << 0;
+  if (! mips16)
+    {
+      if (pinfo & INSN_WRITE_GPR_D)
+       mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD);
+      if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0)
+       mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RT) & OP_MASK_RT);
+      if (pinfo & INSN_READ_GPR_S)
+       mips_gprmask |= 1 << ((ip->insn_opcode >> OP_SH_RS) & OP_MASK_RS);
+      if (pinfo & INSN_WRITE_GPR_31)
+       mips_gprmask |= 1 << 31;
+      if (pinfo & INSN_WRITE_FPR_D)
+       mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FD) & OP_MASK_FD);
+      if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0)
+       mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FS) & OP_MASK_FS);
+      if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0)
+       mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FT) & OP_MASK_FT);
+      if ((pinfo & INSN_READ_FPR_R) != 0)
+       mips_cprmask[1] |= 1 << ((ip->insn_opcode >> OP_SH_FR) & OP_MASK_FR);
+      if (pinfo & INSN_COP)
+       {
+         /* We don't keep enough information to sort these cases out. 
+            The itbl support does keep this information however, although 
+            we currently don't support itbl fprmats as part of the cop 
+            instruction.  May want to add this support in the future. */
+       }
+      /* Never set the bit for $0, which is always zero.  */
+      mips_gprmask &=~ 1 << 0;
+    }
+  else
+    {
+      if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X))
+       mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RX)
+                             & MIPS16OP_MASK_RX);
+      if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y))
+       mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RY)
+                             & MIPS16OP_MASK_RY);
+      if (pinfo & MIPS16_INSN_WRITE_Z)
+       mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_RZ)
+                             & MIPS16OP_MASK_RZ);
+      if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T))
+       mips_gprmask |= 1 << TREG;
+      if (pinfo & (MIPS16_INSN_WRITE_SP | MIPS16_INSN_READ_SP))
+       mips_gprmask |= 1 << SP;
+      if (pinfo & (MIPS16_INSN_WRITE_31 | MIPS16_INSN_READ_31))
+       mips_gprmask |= 1 << RA;
+      if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
+       mips_gprmask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
+      if (pinfo & MIPS16_INSN_READ_Z)
+       mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_MOVE32Z)
+                             & MIPS16OP_MASK_MOVE32Z);
+      if (pinfo & MIPS16_INSN_READ_GPR_X)
+       mips_gprmask |= 1 << ((ip->insn_opcode >> MIPS16OP_SH_REGR32)
+                             & MIPS16OP_MASK_REGR32);
+    }
 
   if (place == NULL && ! mips_noreorder)
     {
@@ -1313,10 +1783,13 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                 whether there is a label on this instruction.  If
                 there are any branches to anything other than a
                 label, users must use .set noreorder.  */
-             || insn_label != NULL
+             || insn_labels != NULL
              /* If the previous instruction is in a variant frag, we
-                can not do the swap.  */
-             || prev_insn_frag->fr_type == rs_machine_dependent
+                can not do the swap.  This does not apply to the
+                mips16, which uses variant frags for different
+                purposes.  */
+             || (! mips16
+                 && prev_insn_frag->fr_type == rs_machine_dependent)
              /* If the branch reads the condition codes, we don't
                 even try to swap, because in the sequence
                   ctc1 $X,$31
@@ -1325,13 +1798,16 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                   bc1t LABEL
                 we can not swap, and I don't feel like handling that
                 case.  */
-             || (mips_isa < 4
+             || (! mips16
+                 && mips_isa < 4
                  && (pinfo & INSN_READ_COND_CODE))
              /* We can not swap with an instruction that requires a
                 delay slot, becase the target of the branch might
                 interfere with that instruction.  */
-             || (mips_isa < 4
+             || (! mips16
+                 && mips_isa < 4
                  && (prev_pinfo
+              /* Itbl support may require additional care here. */
                      & (INSN_LOAD_COPROC_DELAY
                         | INSN_COPROC_MOVE_DELAY
                         | INSN_WRITE_COND_CODE)))
@@ -1339,9 +1815,11 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  && (prev_pinfo
                      & (INSN_READ_LO
                         | INSN_READ_HI)))
-             || (mips_isa < 2
+             || (! mips16
+                 && mips_isa < 2
                  && (prev_pinfo
                      & (INSN_LOAD_MEMORY_DELAY
+              /* Itbl support may require additional care here. */
                         | INSN_COPROC_MEMORY_DELAY)))
              /* We can not swap with a branch instruction.  */
              || (prev_pinfo
@@ -1354,20 +1832,51 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              || (prev_pinfo & INSN_TRAP)
              /* If the branch reads a register that the previous
                 instruction sets, we can not swap.  */
-             || ((prev_pinfo & INSN_WRITE_GPR_T)
+             || (! mips16
+                 && (prev_pinfo & INSN_WRITE_GPR_T)
                  && insn_uses_reg (ip,
                                    ((prev_insn.insn_opcode >> OP_SH_RT)
                                     & OP_MASK_RT),
-                                   0))
-             || ((prev_pinfo & INSN_WRITE_GPR_D)
+                                   MIPS_GR_REG))
+             || (! mips16
+                 && (prev_pinfo & INSN_WRITE_GPR_D)
                  && insn_uses_reg (ip,
                                    ((prev_insn.insn_opcode >> OP_SH_RD)
                                     & OP_MASK_RD),
-                                   0))
+                                   MIPS_GR_REG))
+             || (mips16
+                 && (((prev_pinfo & MIPS16_INSN_WRITE_X)
+                      && insn_uses_reg (ip,
+                                        ((prev_insn.insn_opcode
+                                          >> MIPS16OP_SH_RX)
+                                         & MIPS16OP_MASK_RX),
+                                        MIPS16_REG))
+                     || ((prev_pinfo & MIPS16_INSN_WRITE_Y)
+                         && insn_uses_reg (ip,
+                                           ((prev_insn.insn_opcode
+                                             >> MIPS16OP_SH_RY)
+                                            & MIPS16OP_MASK_RY),
+                                           MIPS16_REG))
+                     || ((prev_pinfo & MIPS16_INSN_WRITE_Z)
+                         && insn_uses_reg (ip,
+                                           ((prev_insn.insn_opcode
+                                             >> MIPS16OP_SH_RZ)
+                                            & MIPS16OP_MASK_RZ),
+                                           MIPS16_REG))
+                     || ((prev_pinfo & MIPS16_INSN_WRITE_T)
+                         && insn_uses_reg (ip, TREG, MIPS_GR_REG))
+                     || ((prev_pinfo & MIPS16_INSN_WRITE_31)
+                         && insn_uses_reg (ip, RA, MIPS_GR_REG))
+                     || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
+                         && insn_uses_reg (ip,
+                                           MIPS16OP_EXTRACT_REG32R (prev_insn.
+                                                                    insn_opcode),
+                                           MIPS_GR_REG))))
              /* If the branch writes a register that the previous
                 instruction sets, we can not swap (we know that
                 branches write only to RD or to $31).  */
-             || ((prev_pinfo & INSN_WRITE_GPR_T)
+             || (! mips16
+                 && (prev_pinfo & INSN_WRITE_GPR_T)
                  && (((pinfo & INSN_WRITE_GPR_D)
                       && (((prev_insn.insn_opcode >> OP_SH_RT) & OP_MASK_RT)
                           == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD)))
@@ -1375,7 +1884,8 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                          && (((prev_insn.insn_opcode >> OP_SH_RT)
                               & OP_MASK_RT)
                              == 31))))
-             || ((prev_pinfo & INSN_WRITE_GPR_D)
+             || (! mips16
+                 && (prev_pinfo & INSN_WRITE_GPR_D)
                  && (((pinfo & INSN_WRITE_GPR_D)
                       && (((prev_insn.insn_opcode >> OP_SH_RD) & OP_MASK_RD)
                           == ((ip->insn_opcode >> OP_SH_RD) & OP_MASK_RD)))
@@ -1383,25 +1893,38 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                          && (((prev_insn.insn_opcode >> OP_SH_RD)
                               & OP_MASK_RD)
                              == 31))))
+             || (mips16
+                 && (pinfo & MIPS16_INSN_WRITE_31)
+                 && ((prev_pinfo & MIPS16_INSN_WRITE_31)
+                     || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
+                         && (MIPS16OP_EXTRACT_REG32R (prev_insn.insn_opcode)
+                             == RA))))
              /* If the branch writes a register that the previous
                 instruction reads, we can not swap (we know that
                 branches only write to RD or to $31).  */
-             || ((pinfo & INSN_WRITE_GPR_D)
+             || (! mips16
+                 && (pinfo & INSN_WRITE_GPR_D)
                  && insn_uses_reg (&prev_insn,
                                    ((ip->insn_opcode >> OP_SH_RD)
                                     & OP_MASK_RD),
-                                   0))
-             || ((pinfo & INSN_WRITE_GPR_31)
-                 && insn_uses_reg (&prev_insn, 31, 0))
+                                   MIPS_GR_REG))
+             || (! mips16
+                 && (pinfo & INSN_WRITE_GPR_31)
+                 && insn_uses_reg (&prev_insn, 31, MIPS_GR_REG))
+             || (mips16
+                 && (pinfo & MIPS16_INSN_WRITE_31)
+                 && insn_uses_reg (&prev_insn, RA, MIPS_GR_REG))
              /* If we are generating embedded PIC code, the branch
                 might be expanded into a sequence which uses $at, so
                 we can't swap with an instruction which reads it.  */
              || (mips_pic == EMBEDDED_PIC
-                 && insn_uses_reg (&prev_insn, AT, 0))
+                 && insn_uses_reg (&prev_insn, AT, MIPS_GR_REG))
              /* If the previous previous instruction has a load
                 delay, and sets a register that the branch reads, we
                 can not swap.  */
-             || (mips_isa < 4
+             || (! mips16
+                 && mips_isa < 4
+              /* Itbl support may require additional care here. */
                  && ((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
                      || (mips_isa < 2
                          && (prev_prev_insn.insn_mo->pinfo
@@ -1409,7 +1932,24 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
                  && insn_uses_reg (ip,
                                    ((prev_prev_insn.insn_opcode >> OP_SH_RT)
                                     & OP_MASK_RT),
-                                   0)))
+                                   MIPS_GR_REG))
+             /* If one instruction sets a condition code and the
+                 other one uses a condition code, we can not swap.  */
+             || ((pinfo & INSN_READ_COND_CODE)
+                 && (prev_pinfo & INSN_WRITE_COND_CODE))
+             || ((pinfo & INSN_WRITE_COND_CODE)
+                 && (prev_pinfo & INSN_READ_COND_CODE))
+             /* If the previous instruction uses the PC, we can not
+                 swap.  */
+             || (mips16
+                 && (prev_pinfo & MIPS16_INSN_READ_PC))
+             /* If the previous instruction was extended, we can not
+                 swap.  */
+             || (mips16 && prev_insn_extended)
+             /* If the previous instruction had a fixup in mips16
+                 mode, we can not swap.  This normally means that the
+                 previous instruction was a 4 byte branch anyhow.  */
+             || (mips16 && prev_insn_fixp))
            {
              /* We could do even better for unconditional branches to
                 portions of this object file; we could pick up the
@@ -1422,24 +1962,53 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
            }
          else
            {
-             char *prev_f;
-             char temp[4];
-
              /* It looks like we can actually do the swap.  */
-             prev_f = prev_insn_frag->fr_literal + prev_insn_where;
-             memcpy (temp, prev_f, 4);
-             memcpy (prev_f, f, 4);
-             memcpy (f, temp, 4);
-             if (prev_insn_fixp)
+             if (! mips16)
                {
-                 prev_insn_fixp->fx_frag = frag_now;
-                 prev_insn_fixp->fx_where = f - frag_now->fr_literal;
+                 char *prev_f;
+                 char temp[4];
+
+                 prev_f = prev_insn_frag->fr_literal + prev_insn_where;
+                 memcpy (temp, prev_f, 4);
+                 memcpy (prev_f, f, 4);
+                 memcpy (f, temp, 4);
+                 if (prev_insn_fixp)
+                   {
+                     prev_insn_fixp->fx_frag = frag_now;
+                     prev_insn_fixp->fx_where = f - frag_now->fr_literal;
+                   }
+                 if (fixp)
+                   {
+                     fixp->fx_frag = prev_insn_frag;
+                     fixp->fx_where = prev_insn_where;
+                   }
                }
-             if (fixp)
+             else
                {
-                 fixp->fx_frag = prev_insn_frag;
-                 fixp->fx_where = prev_insn_where;
+                 char *prev_f;
+                 char temp[2];
+
+                 assert (prev_insn_fixp == NULL);
+                 prev_f = prev_insn_frag->fr_literal + prev_insn_where;
+                 memcpy (temp, prev_f, 2);
+                 memcpy (prev_f, f, 2);
+                 if (reloc_type != BFD_RELOC_MIPS16_JMP)
+                   {
+                     assert (reloc_type == BFD_RELOC_UNUSED);
+                     memcpy (f, temp, 2);
+                   }
+                 else
+                   {
+                     memcpy (f, f + 2, 2);
+                     memcpy (f + 2, temp, 2);
+                   }
+                 if (fixp)
+                   {
+                     fixp->fx_frag = prev_insn_frag;
+                     fixp->fx_where = prev_insn_where;
+                   }
                }
+
              /* Update the previous insn information; leave prev_insn
                 unchanged.  */
              prev_prev_insn = *ip;
@@ -1453,6 +2022,10 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
              prev_prev_insn.insn_mo = &dummy_opcode;
              prev_insn.insn_mo = &dummy_opcode;
            }
+
+         prev_insn_fixp = NULL;
+         prev_insn_reloc_type = BFD_RELOC_UNUSED;
+         prev_insn_extended = 0;
        }
       else if (pinfo & INSN_COND_BRANCH_LIKELY)
        {
@@ -1464,6 +2037,9 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
          /* Update the previous insn information.  */
          prev_prev_insn = *ip;
          prev_insn.insn_mo = &dummy_opcode;
+         prev_insn_fixp = NULL;
+         prev_insn_reloc_type = BFD_RELOC_UNUSED;
+         prev_insn_extended = 0;
        }
       else
        {
@@ -1478,49 +2054,79 @@ append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
             immediately; since this insn is not a branch, we know it
             is not in a delay slot.  */
          prev_insn_is_delay_slot = 0;
+
+         prev_insn_fixp = fixp;
+         prev_insn_reloc_type = reloc_type;
+         if (mips16)
+           prev_insn_extended = (ip->use_extend
+                                 || reloc_type > BFD_RELOC_UNUSED);
        }
 
       prev_prev_insn_unreordered = prev_insn_unreordered;
       prev_insn_unreordered = 0;
       prev_insn_frag = frag_now;
       prev_insn_where = f - frag_now->fr_literal;
-      prev_insn_fixp = fixp;
       prev_insn_valid = 1;
     }
+  else if (place == NULL)
+    {
+      /* We need to record a bit of information even when we are not
+         reordering, in order to determine the base address for mips16
+         PC relative relocs.  */
+      prev_prev_insn = prev_insn;
+      prev_insn = *ip;
+      prev_insn_reloc_type = reloc_type;
+      prev_prev_insn_unreordered = prev_insn_unreordered;
+      prev_insn_unreordered = 1;
+    }
 
   /* We just output an insn, so the next one doesn't have a label.  */
-  insn_label = NULL;
+  mips_clear_insn_labels ();
 }
 
 /* This function forgets that there was any previous instruction or
-   label.  */
+   label.  If PRESERVE is non-zero, it remembers enough information to
+   know whether nops are needed before a noreorder section. */
 
 static void
-mips_no_prev_insn ()
+mips_no_prev_insn (preserve)
+     int preserve;
 {
-  prev_insn.insn_mo = &dummy_opcode;
-  prev_prev_insn.insn_mo = &dummy_opcode;
+  if (! preserve)
+    {
+      prev_insn.insn_mo = &dummy_opcode;
+      prev_prev_insn.insn_mo = &dummy_opcode;
+      prev_nop_frag = NULL;
+      prev_nop_frag_holds = 0;
+      prev_nop_frag_required = 0;
+      prev_nop_frag_since = 0;
+    }
   prev_insn_valid = 0;
   prev_insn_is_delay_slot = 0;
   prev_insn_unreordered = 0;
+  prev_insn_extended = 0;
+  prev_insn_reloc_type = BFD_RELOC_UNUSED;
   prev_prev_insn_unreordered = 0;
-  insn_label = NULL;
+  mips_clear_insn_labels ();
 }
 
 /* This function must be called whenever we turn on noreorder or emit
    something other than instructions.  It inserts any NOPS which might
    be needed by the previous instruction, and clears the information
-   kept for the previous instructions.  */
+   kept for the previous instructions.  The INSNS parameter is true if
+   instructions are to follow. */
 
 static void
-mips_emit_delays ()
+mips_emit_delays (insns)
+     boolean insns;
 {
   if (! mips_noreorder)
     {
-      int nop;
+      int nops;
 
-      nop = 0;
-      if ((mips_isa < 4
+      nops = 0;
+      if ((! mips16
+          && mips_isa < 4
           && (! cop_interlocks
                && (prev_insn.insn_mo->pinfo
                    & (INSN_LOAD_COPROC_DELAY
@@ -1530,40 +2136,100 @@ mips_emit_delays ()
              && (prev_insn.insn_mo->pinfo
                  & (INSN_READ_LO
                     | INSN_READ_HI)))
-         || (mips_isa < 2
+         || (! mips16
+             && mips_isa < 2
              && (prev_insn.insn_mo->pinfo
                  & (INSN_LOAD_MEMORY_DELAY
                     | INSN_COPROC_MEMORY_DELAY))))
        {
-         nop = 1;
-         if ((mips_isa < 4
+          /* Itbl support may require additional care here. */
+         ++nops;
+         if ((! mips16
+              && mips_isa < 4
               && (! cop_interlocks
                    && prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
              || (! interlocks
                  && ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
                      || (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
-           emit_nop ();
+           ++nops;
+
+         if (prev_insn_unreordered)
+           nops = 0;
        }
-      else if ((mips_isa < 4
+      else if ((! mips16
+               && mips_isa < 4
                && (! cop_interlocks
                     && prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE))
               || (! interlocks
                   && ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
                       || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
-       nop = 1;
-      if (nop)
        {
-         emit_nop ();
-         if (insn_label != NULL)
+          /* Itbl support may require additional care here. */
+         if (! prev_prev_insn_unreordered)
+           ++nops;
+       }
+
+      if (nops > 0)
+       {
+         struct insn_label_list *l;
+
+         if (insns)
+           {
+             /* Record the frag which holds the nop instructions, so
+                 that we can remove them if we don't need them.  */
+             frag_grow (mips16 ? nops * 2 : nops * 4);
+             prev_nop_frag = frag_now;
+             prev_nop_frag_holds = nops;
+             prev_nop_frag_required = 0;
+             prev_nop_frag_since = 0;
+           }
+
+         for (; nops > 0; --nops)
+           emit_nop ();
+
+         if (insns)
+           {
+             /* Move on to a new frag, so that it is safe to simply
+                 decrease the size of prev_nop_frag. */
+             frag_wane (frag_now);
+             frag_new (0);
+           }
+
+         for (l = insn_labels; l != NULL; l = l->next)
            {
-             assert (S_GET_SEGMENT (insn_label) == now_seg);
-             insn_label->sy_frag = frag_now;
-             S_SET_VALUE (insn_label, (valueT) frag_now_fix ());
+             assert (S_GET_SEGMENT (l->label) == now_seg);
+             l->label->sy_frag = frag_now;
+             S_SET_VALUE (l->label, (valueT) frag_now_fix ());
+             /* mips16 text labels are stored as odd.  */
+             if (mips16)
+               ++l->label->sy_value.X_add_number;
            }
        }
     }
 
-  mips_no_prev_insn ();
+  /* Mark instruction labels in mips16 mode.  This permits the linker
+     to handle them specially, such as generating jalx instructions
+     when needed.  We also make them odd for the duration of the
+     assembly, in order to generate the right sort of code.  We will
+     make them even in the adjust_symtab routine, while leaving them
+     marked.  This is convenient for the debugger and the
+     disassembler.  The linker knows to make them odd again.  */
+  if (mips16 && insns)
+    {
+      struct insn_label_list *l;
+
+      for (l = insn_labels; l != NULL; l = l->next)
+       {
+#ifdef S_SET_OTHER
+         if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+           S_SET_OTHER (l->label, STO_MIPS16);
+#endif
+         if ((l->label->sy_value.X_add_number & 1) == 0)
+           ++l->label->sy_value.X_add_number;
+       }
+    }
+
+  mips_no_prev_insn (insns);
 }
 
 /* Build an instruction created by a macro expansion.  This is passed
@@ -1611,6 +2277,13 @@ macro_build (place, counter, ep, name, fmt, va_alist)
   if (place == NULL)
     *counter += 1;             /* bump instruction counter */
 
+  if (mips16)
+    {
+      mips16_macro_build (place, counter, ep, name, fmt, args);
+      va_end (args);
+      return;
+    }
+
   r = BFD_RELOC_UNUSED;
   insn.insn_mo = (struct mips_opcode *) hash_find (op_hash, name);
   assert (insn.insn_mo);
@@ -1629,7 +2302,12 @@ macro_build (place, counter, ep, name, fmt, va_alist)
         || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4010
             && ! mips_4010)
         || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4100
-            && ! mips_4100))
+            && ! mips_4100)
+        /* start-sanitize-r5900 */
+         || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_5900
+            && ! mips_5900)
+         /* end-sanitize-r5900 */
+         )
     {
       ++insn.insn_mo;
       assert (insn.insn_mo->name);
@@ -1762,49 +2440,177 @@ macro_build (place, counter, ep, name, fmt, va_alist)
   append_insn (place, &insn, ep, r, false);
 }
 
-/*
- * Generate a "lui" instruction.
- */
 static void
-macro_build_lui (place, counter, ep, regnum)
+mips16_macro_build (place, counter, ep, name, fmt, args)
      char *place;
      int *counter;
      expressionS *ep;
-     int regnum;
+     const char *name;
+     const char *fmt;
+     va_list args;
 {
-  expressionS high_expr;
   struct mips_cl_insn insn;
   bfd_reloc_code_real_type r;
-  CONST char *name = "lui";
-  CONST char *fmt = "t,u";
 
-  if (place == NULL)
-    high_expr = *ep;
-  else
-    {
-      high_expr.X_op = O_constant;
-      high_expr.X_add_number = ep->X_add_number;
-    }
+  r = BFD_RELOC_UNUSED;
+  insn.insn_mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
+  assert (insn.insn_mo);
+  assert (strcmp (name, insn.insn_mo->name) == 0);
 
-  if (high_expr.X_op == O_constant)
-    {
-      /* we can compute the instruction now without a relocation entry */
-      if (high_expr.X_add_number & 0x8000)
-       high_expr.X_add_number += 0x10000;
-      high_expr.X_add_number =
-       ((unsigned long) high_expr.X_add_number >> 16) & 0xffff;
-      r = BFD_RELOC_UNUSED;
-    }
-  else
+  while (strcmp (fmt, insn.insn_mo->args) != 0
+        || insn.insn_mo->pinfo == INSN_MACRO)
     {
-      assert (ep->X_op == O_symbol);
-      /* _gp_disp is a special case, used from s_cpload.  */
-      assert (mips_pic == NO_PIC
-             || strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0);
-      r = BFD_RELOC_HI16_S;
+      ++insn.insn_mo;
+      assert (insn.insn_mo->name);
+      assert (strcmp (name, insn.insn_mo->name) == 0);
     }
 
-  /*
+  insn.insn_opcode = insn.insn_mo->match;
+  insn.use_extend = false;
+
+  for (;;)
+    {
+      int c;
+
+      c = *fmt++;
+      switch (c)
+       {
+       case '\0':
+         break;
+
+       case ',':
+       case '(':
+       case ')':
+         continue;
+
+       case 'y':
+       case 'w':
+         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RY;
+         continue;
+
+       case 'x':
+       case 'v':
+         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RX;
+         continue;
+
+       case 'z':
+         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_RZ;
+         continue;
+
+       case 'Z':
+         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_MOVE32Z;
+         continue;
+
+       case '0':
+       case 'S':
+       case 'P':
+       case 'R':
+         continue;
+
+       case 'X':
+         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_REGR32;
+         continue;
+
+       case 'Y':
+         {
+           int regno;
+
+           regno = va_arg (args, int);
+           regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
+           insn.insn_opcode |= regno << MIPS16OP_SH_REG32R;
+         }
+         continue;
+
+       case '<':
+       case '>':
+       case '4':
+       case '5':
+       case 'H':
+       case 'W':
+       case 'D':
+       case 'j':
+       case '8':
+       case 'V':
+       case 'C':
+       case 'U':
+       case 'k':
+       case 'K':
+       case 'p':
+       case 'q':
+         {
+           assert (ep != NULL);
+
+           if (ep->X_op != O_constant)
+             r = BFD_RELOC_UNUSED + c;
+           else
+             {
+               mips16_immed ((char *) NULL, 0, c, ep->X_add_number, false,
+                             false, false, &insn.insn_opcode,
+                             &insn.use_extend, &insn.extend);
+               ep = NULL;
+               r = BFD_RELOC_UNUSED;
+             }
+         }
+         continue;
+
+       case '6':
+         insn.insn_opcode |= va_arg (args, int) << MIPS16OP_SH_IMM6;
+         continue;
+       }
+
+      break;
+    }
+
+  assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+
+  append_insn (place, &insn, ep, r, false);
+}
+
+/*
+ * Generate a "lui" instruction.
+ */
+static void
+macro_build_lui (place, counter, ep, regnum)
+     char *place;
+     int *counter;
+     expressionS *ep;
+     int regnum;
+{
+  expressionS high_expr;
+  struct mips_cl_insn insn;
+  bfd_reloc_code_real_type r;
+  CONST char *name = "lui";
+  CONST char *fmt = "t,u";
+
+  assert (! mips16);
+
+  if (place == NULL)
+    high_expr = *ep;
+  else
+    {
+      high_expr.X_op = O_constant;
+      high_expr.X_add_number = ep->X_add_number;
+    }
+
+  if (high_expr.X_op == O_constant)
+    {
+      /* we can compute the instruction now without a relocation entry */
+      if (high_expr.X_add_number & 0x8000)
+       high_expr.X_add_number += 0x10000;
+      high_expr.X_add_number =
+       ((unsigned long) high_expr.X_add_number >> 16) & 0xffff;
+      r = BFD_RELOC_UNUSED;
+    }
+  else
+    {
+      assert (ep->X_op == O_symbol);
+      /* _gp_disp is a special case, used from s_cpload.  */
+      assert (mips_pic == NO_PIC
+             || strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0);
+      r = BFD_RELOC_HI16_S;
+    }
+
+  /*
    * If the macro is about to expand into a second instruction,
    * print a warning if needed. We need to pass ip as a parameter
    * to generate a better warning message here...
@@ -1864,6 +2670,82 @@ check_absolute_expr (ip, ex)
     as_warn ("Instruction %s requires absolute expression", ip->insn_mo->name);
 }
 
+/* Count the leading zeroes by performing a binary chop. This is a
+   bulky bit of source, but performance is a LOT better for the
+   majority of values than a simple loop to count the bits:
+       for (lcnt = 0; (lcnt < 32); lcnt++)
+         if ((v) & (1 << (31 - lcnt)))
+           break;
+  However it is not code size friendly, and the gain will drop a bit
+  on certain cached systems.
+*/
+#define COUNT_TOP_ZEROES(v)             \
+  (((v) & ~0xffff) == 0                 \
+   ? ((v) & ~0xff) == 0                 \
+     ? ((v) & ~0xf) == 0                \
+       ? ((v) & ~0x3) == 0              \
+         ? ((v) & ~0x1) == 0            \
+           ? !(v)                       \
+             ? 32                       \
+             : 31                       \
+           : 30                         \
+         : ((v) & ~0x7) == 0            \
+           ? 29                         \
+           : 28                         \
+       : ((v) & ~0x3f) == 0             \
+         ? ((v) & ~0x1f) == 0           \
+           ? 27                         \
+           : 26                         \
+         : ((v) & ~0x7f) == 0           \
+           ? 25                         \
+           : 24                         \
+     : ((v) & ~0xfff) == 0              \
+       ? ((v) & ~0x3ff) == 0            \
+         ? ((v) & ~0x1ff) == 0          \
+           ? 23                         \
+           : 22                         \
+         : ((v) & ~0x7ff) == 0          \
+           ? 21                         \
+           : 20                         \
+       : ((v) & ~0x3fff) == 0           \
+         ? ((v) & ~0x1fff) == 0         \
+           ? 19                         \
+           : 18                         \
+         : ((v) & ~0x7fff) == 0         \
+           ? 17                         \
+           : 16                         \
+   : ((v) & ~0xffffff) == 0             \
+     ? ((v) & ~0xfffff) == 0            \
+       ? ((v) & ~0x3ffff) == 0          \
+         ? ((v) & ~0x1ffff) == 0        \
+           ? 15                         \
+           : 14                         \
+         : ((v) & ~0x7ffff) == 0        \
+           ? 13                         \
+           : 12                         \
+       : ((v) & ~0x3fffff) == 0         \
+         ? ((v) & ~0x1fffff) == 0       \
+           ? 11                         \
+           : 10                         \
+         : ((v) & ~0x7fffff) == 0       \
+           ? 9                          \
+           : 8                          \
+     : ((v) & ~0xfffffff) == 0          \
+       ? ((v) & ~0x3ffffff) == 0        \
+         ? ((v) & ~0x1ffffff) == 0      \
+           ? 7                          \
+           : 6                          \
+         : ((v) & ~0x7ffffff) == 0      \
+           ? 5                          \
+           : 4                          \
+       : ((v) & ~0x3fffffff) == 0       \
+         ? ((v) & ~0x1fffffff) == 0     \
+           ? 3                          \
+           : 2                          \
+         : ((v) & ~0x7fffffff) == 0     \
+           ? 1                          \
+           : 0)
+
 /*                     load_register()
  *  This routine generates the least number of instructions neccessary to load
  *  an absolute expression value into a register.
@@ -1875,8 +2757,8 @@ load_register (counter, reg, ep, dbl)
      expressionS *ep;
      int dbl;
 {
-  int shift, freg;
-  expressionS hi32, lo32, tmp;
+  int freg;
+  expressionS hi32, lo32;
 
   if (ep->X_op != O_big)
     {
@@ -1910,8 +2792,12 @@ load_register (counter, reg, ep, dbl)
                    || ! ep->X_unsigned
                    || sizeof (ep->X_add_number) > 4
                    || (ep->X_add_number & 0x80000000) == 0))
+              || ((mips_isa < 3 || ! dbl)
+                  && (ep->X_add_number &~ (offsetT) 0xffffffff) == 0)
               || (mips_isa < 3
-                  && (ep->X_add_number &~ 0xffffffff) == 0))
+                  && ! dbl
+                  && ((ep->X_add_number &~ (offsetT) 0xffffffff)
+                      == ~ (offsetT) 0xffffffff)))
        {
          /* 32 bit values require an lui.  */
          macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
@@ -1921,19 +2807,6 @@ load_register (counter, reg, ep, dbl)
                         (int) BFD_RELOC_LO16);
          return;
        }
-      else
-       {
-         /* 32 bit value with high bit set being loaded into a 64 bit
-             register.  We can't use lui, because that would
-             incorrectly set the 32 high bits.  */
-         generic_bignum[3] = 0;
-         generic_bignum[2] = 0;
-         generic_bignum[1] = (ep->X_add_number >> 16) & 0xffff;
-         generic_bignum[0] = ep->X_add_number & 0xffff;
-         tmp.X_op = O_big;
-         tmp.X_add_number = 4;
-         ep = &tmp;
-       }
     }
 
   /* The value is larger than 32 bits.  */
@@ -1949,11 +2822,9 @@ load_register (counter, reg, ep, dbl)
   if (ep->X_op != O_big)
     {
       hi32 = *ep;
-      shift = 32;
-      hi32.X_add_number >>= shift;
+      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+      hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
       hi32.X_add_number &= 0xffffffff;
-      if ((hi32.X_add_number & 0x80000000) != 0)
-       hi32.X_add_number |= ~ (offsetT) 0xffffffff;
       lo32 = *ep;
       lo32.X_add_number &= 0xffffffff;
     }
@@ -1974,23 +2845,130 @@ load_register (counter, reg, ep, dbl)
     freg = 0;
   else
     {
+      int shift, bit;
+      unsigned long hi, lo;
+
       if (hi32.X_add_number == 0xffffffff)
         {
           if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
             {
-              macro_build ((char *) NULL, counter, &lo32, "addiu", "t,r,j", reg, 0,
-                           (int) BFD_RELOC_LO16);
+              macro_build ((char *) NULL, counter, &lo32, "addiu", "t,r,j",
+                          reg, 0, (int) BFD_RELOC_LO16);
               return;
             }
           if (lo32.X_add_number & 0x80000000)
             {
               macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
                            (int) BFD_RELOC_HI16);
-              macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, reg,
-                           (int) BFD_RELOC_LO16);
+             if (lo32.X_add_number & 0xffff)
+               macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i",
+                            reg, reg, (int) BFD_RELOC_LO16);
+              return;
+            }
+        }
+
+      /* Check for 16bit shifted constant.  We know that hi32 is
+         non-zero, so start the mask on the first bit of the hi32
+         value.  */
+      shift = 17;
+      do
+       {
+        unsigned long himask, lomask;
+
+        if (shift < 32)
+          {
+            himask = 0xffff >> (32 - shift);
+            lomask = (0xffff << shift) & 0xffffffff;
+          }
+        else
+          {
+            himask = 0xffff << (shift - 32);
+            lomask = 0;
+          }
+        if ((hi32.X_add_number & ~ (offsetT) himask) == 0
+            && (lo32.X_add_number & ~ (offsetT) lomask) == 0)
+          {
+            expressionS tmp;
+
+            tmp.X_op = O_constant;
+            if (shift < 32)
+              tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
+                                  | (lo32.X_add_number >> shift));
+            else
+              tmp.X_add_number = hi32.X_add_number >> (shift - 32);
+            macro_build ((char *) NULL, counter, &tmp, "ori", "t,r,i", reg, 0,
+                         (int) BFD_RELOC_LO16);
+            macro_build ((char *) NULL, counter, NULL,
+                         (shift >= 32) ? "dsll32" : "dsll",
+                         "d,w,<", reg, reg,
+                         (shift >= 32) ? shift - 32 : shift);
+            return;
+          }
+         shift++;
+       } while (shift <= (64 - 16));
+
+      /* Find the bit number of the lowest one bit, and store the
+         shifted value in hi/lo.  */
+      hi = (unsigned long) (hi32.X_add_number & 0xffffffff);
+      lo = (unsigned long) (lo32.X_add_number & 0xffffffff);
+      if (lo != 0)
+       {
+         bit = 0;
+         while ((lo & 1) == 0)
+           {
+             lo >>= 1;
+             ++bit;
+           }
+         lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+         hi >>= bit;
+       }
+      else
+       {
+         bit = 32;
+         while ((hi & 1) == 0)
+           {
+             hi >>= 1;
+             ++bit;
+           }
+         lo = hi;
+         hi = 0;
+       }
+
+      /* Optimize if the shifted value is a (power of 2) - 1.  */
+      if ((hi == 0 && ((lo + 1) & lo) == 0)
+         || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
+        {
+          shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
+         if (shift != 0)
+            {
+             expressionS tmp;
+
+             /* This instruction will set the register to be all
+                 ones.  */
+              tmp.X_op = O_constant;
+              tmp.X_add_number = (offsetT) -1;
+              macro_build ((char *) NULL, counter, &tmp, "addiu", "t,r,j",
+                          reg, 0, (int) BFD_RELOC_LO16);
+              if (bit != 0)
+                {
+                  bit += shift;
+                  macro_build ((char *) NULL, counter, NULL,
+                               (bit >= 32) ? "dsll32" : "dsll",
+                               "d,w,<", reg, reg,
+                               (bit >= 32) ? bit - 32 : bit);
+                }
+              macro_build ((char *) NULL, counter, NULL,
+                          (shift >= 32) ? "dsrl32" : "dsrl",
+                           "d,w,<", reg, reg,
+                          (shift >= 32) ? shift - 32 : shift);
               return;
             }
         }
+
+      /* Sign extend hi32 before calling load_register, because we can
+         generally get better code when we load a sign extended value.  */
+      if ((hi32.X_add_number & 0x80000000) != 0)
+       hi32.X_add_number |= ~ (offsetT) 0xffffffff;
       load_register (counter, reg, &hi32, 0);
       freg = reg;
     }
@@ -2012,7 +2990,7 @@ load_register (counter, reg, ep, dbl)
          macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
                       (int) BFD_RELOC_HI16);
           macro_build ((char *) NULL, counter, NULL, "dsrl32", "d,w,<", reg,
-                       reg, 32);
+                       reg, 0);
           return;
         }
 
@@ -2077,7 +3055,7 @@ load_address (counter, reg, ep)
                       "t,r,j", reg, GP, (int) BFD_RELOC_MIPS_GPREL);
          p = frag_var (rs_machine_dependent, 8, 0,
                        RELAX_ENCODE (4, 8, 0, 4, 0, mips_warn_about_macros),
-                       ep->X_add_symbol, (long) 0, (char *) NULL);
+                       ep->X_add_symbol, (offsetT) 0, (char *) NULL);
        }
       macro_build_lui (p, counter, ep, reg);
       if (p != NULL)
@@ -2106,7 +3084,7 @@ load_address (counter, reg, ep)
       macro_build ((char *) NULL, counter, (expressionS *) NULL, "nop", "");
       p = frag_var (rs_machine_dependent, 4, 0,
                    RELAX_ENCODE (0, 4, -8, 0, 0, mips_warn_about_macros),
-                   ep->X_add_symbol, (long) 0, (char *) NULL);
+                   ep->X_add_symbol, (offsetT) 0, (char *) NULL);
       macro_build (p, counter, ep,
                   mips_isa < 3 ? "addiu" : "daddiu",
                   "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
@@ -2153,7 +3131,7 @@ load_address (counter, reg, ep)
       p = frag_var (rs_machine_dependent, 12 + off, 0,
                    RELAX_ENCODE (12, 12 + off, off, 8 + off, 0,
                                  mips_warn_about_macros),
-                   ep->X_add_symbol, (long) 0, (char *) NULL);
+                   ep->X_add_symbol, (offsetT) 0, (char *) NULL);
       if (off > 0)
        {
          /* We need a nop before loading from $gp.  This special
@@ -2236,6 +3214,8 @@ macro (ip)
   char *p;
   int hold_mips_optimize;
 
+  assert (! mips16);
+
   treg = (ip->insn_opcode >> 16) & 0x1f;
   dreg = (ip->insn_opcode >> 11) & 0x1f;
   sreg = breg = (ip->insn_opcode >> 21) & 0x1f;
@@ -2256,7 +3236,7 @@ macro (ip)
         sub v0,$zero,$a0
         */
 
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
 
@@ -2714,7 +3694,7 @@ macro (ip)
          return;
        }
 
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL,
@@ -2854,7 +3834,7 @@ macro (ip)
       s = "ddivu";
       s2 = "mfhi";
     do_divu3:
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, treg);
@@ -2946,7 +3926,7 @@ macro (ip)
              p = frag_var (rs_machine_dependent, 8, 0,
                            RELAX_ENCODE (4, 8, 0, 4, 0,
                                          mips_warn_about_macros),
-                           offset_expr.X_add_symbol, (long) 0,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
            }
          macro_build_lui (p, &icnt, &offset_expr, tempreg);
@@ -3010,7 +3990,7 @@ macro (ip)
                                          (breg == 0
                                           ? mips_warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (long) 0,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
              if (breg == 0)
                {
@@ -3034,7 +4014,7 @@ macro (ip)
                           "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
              (void) frag_var (rs_machine_dependent, 0, 0,
                               RELAX_ENCODE (0, 0, -12, -4, 0, 0),
-                              offset_expr.X_add_symbol, (long) 0,
+                              offset_expr.X_add_symbol, (offsetT) 0,
                               (char *) NULL);
            }
          else
@@ -3077,7 +4057,7 @@ macro (ip)
                           "d,v,t", tempreg, tempreg, AT);
              (void) frag_var (rs_machine_dependent, 0, 0,
                               RELAX_ENCODE (0, 0, -16 + off1, -8, 0, 0),
-                              offset_expr.X_add_symbol, (long) 0,
+                              offset_expr.X_add_symbol, (offsetT) 0,
                               (char *) NULL);
              used_at = 1;
            }
@@ -3160,7 +4140,7 @@ macro (ip)
                                          (breg == 0
                                           ? mips_warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (long) 0,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
            }
          else if (expr1.X_add_number >= -0x8000
@@ -3177,7 +4157,7 @@ macro (ip)
                                          (breg == 0
                                           ? mips_warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (long) 0,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
            }
          else
@@ -3228,7 +4208,7 @@ macro (ip)
                                          (breg == 0
                                           ? mips_warn_about_macros
                                           : 0)),
-                           offset_expr.X_add_symbol, (long) 0,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
 
              used_at = 1;
@@ -3394,7 +4374,8 @@ macro (ip)
                           "nop", "");
              p = frag_var (rs_machine_dependent, 4, 0,
                            RELAX_ENCODE (0, 4, -8, 0, 0, 0),
-                           offset_expr.X_add_symbol, (long) 0, (char *) NULL);
+                           offset_expr.X_add_symbol, (offsetT) 0,
+                           (char *) NULL);
            }
          else
            {
@@ -3418,7 +4399,8 @@ macro (ip)
              p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
                            RELAX_ENCODE (16, 12 + gpdel, gpdel, 8 + gpdel,
                                          0, 0),
-                           offset_expr.X_add_symbol, (long) 0, (char *) NULL);
+                           offset_expr.X_add_symbol, (offsetT) 0,
+                           (char *) NULL);
              if (gpdel > 0)
                {
                  macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
@@ -3481,18 +4463,22 @@ macro (ip)
       goto ld;
     case M_LWC0_AB:
       s = "lwc0";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LWC1_AB:
       s = "lwc1";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LWC2_AB:
       s = "lwc2";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LWC3_AB:
       s = "lwc3";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LWL_AB:
@@ -3505,14 +4491,17 @@ macro (ip)
       goto ld;
     case M_LDC1_AB:
       s = "ldc1";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LDC2_AB:
       s = "ldc2";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LDC3_AB:
       s = "ldc3";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ld;
     case M_LDL_AB:
@@ -3554,18 +4543,22 @@ macro (ip)
       goto st;
     case M_SWC0_AB:
       s = "swc0";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SWC1_AB:
       s = "swc1";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SWC2_AB:
       s = "swc2";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SWC3_AB:
       s = "swc3";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SWL_AB:
@@ -3583,13 +4576,16 @@ macro (ip)
     case M_SDC1_AB:
       s = "sdc1";
       coproc = 1;
+      /* Itbl support may require additional care here. */
       goto st;
     case M_SDC2_AB:
       s = "sdc2";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SDC3_AB:
       s = "sdc3";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto st;
     case M_SDL_AB:
@@ -3601,6 +4597,7 @@ macro (ip)
       tempreg = AT;
       used_at = 1;
     ld_st:
+      /* Itbl support may require additional care here. */
       if (mask == M_LWC1_AB
          || mask == M_SWC1_AB
          || mask == M_LDC1_AB
@@ -3657,7 +4654,7 @@ macro (ip)
                                RELAX_ENCODE (4, 8, 0, 4, 0,
                                              (mips_warn_about_macros
                                               || (used_at && mips_noat))),
-                               offset_expr.X_add_symbol, (long) 0,
+                               offset_expr.X_add_symbol, (offsetT) 0,
                                (char *) NULL);
                  used_at = 0;
                }
@@ -3682,7 +4679,7 @@ macro (ip)
                               treg, (int) BFD_RELOC_MIPS_GPREL, tempreg);
                  p = frag_var (rs_machine_dependent, 12, 0,
                                RELAX_ENCODE (8, 12, 0, 8, 0, 0),
-                               offset_expr.X_add_symbol, (long) 0,
+                               offset_expr.X_add_symbol, (offsetT) 0,
                                (char *) NULL);
                }
              macro_build_lui (p, &icnt, &offset_expr, tempreg);
@@ -3727,7 +4724,7 @@ macro (ip)
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
          p = frag_var (rs_machine_dependent, 4, 0, 
                        RELAX_ENCODE (0, 4, -8, 0, 0, 0),
-                       offset_expr.X_add_symbol, (long) 0,
+                       offset_expr.X_add_symbol, (offsetT) 0,
                        (char *) NULL);
          macro_build (p, &icnt, &offset_expr,
                       mips_isa < 3 ? "addiu" : "daddiu",
@@ -3781,7 +4778,7 @@ macro (ip)
                       tempreg);
          p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
                        RELAX_ENCODE (12, 12 + gpdel, gpdel, 8 + gpdel, 0, 0),
-                       offset_expr.X_add_symbol, (long) 0, (char *) NULL);
+                       offset_expr.X_add_symbol, (offsetT) 0, (char *) NULL);
          if (gpdel > 0)
            {
              macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
@@ -3969,13 +4966,13 @@ macro (ip)
     dob:
       assert (mips_isa < 2);
       macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
-                  byte_order == LITTLE_ENDIAN ? treg : treg + 1,
+                  target_big_endian ? treg + 1 : treg,
                   (int) r, breg);
       /* FIXME: A possible overflow which I don't know how to deal
         with.  */
       offset_expr.X_add_number += 4;
       macro_build ((char *) NULL, &icnt, &offset_expr, "lwc1", "T,o(b)",
-                  byte_order == LITTLE_ENDIAN ? treg + 1 : treg,
+                  target_big_endian ? treg : treg + 1,
                   (int) r, breg);
 
       /* To avoid confusion in tc_gen_reloc, we must ensure that this
@@ -3999,6 +4996,7 @@ macro (ip)
        * But, the resulting address is the same after relocation so why
        * generate the extra instruction?
        */
+      /* Itbl support may require additional care here. */
       coproc = 1;
       if (mips_isa >= 2)
        {
@@ -4019,6 +5017,7 @@ macro (ip)
 
       s = "swc1";
       fmt = "T,o(b)";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ldd_std;
 
@@ -4054,7 +5053,8 @@ macro (ip)
       /* Even on a big endian machine $fn comes before $fn+1.  We have
         to adjust when loading from memory.  We set coproc if we must
         load $fn+1 first.  */
-      if (byte_order == LITTLE_ENDIAN)
+      /* Itbl support may require additional care here. */
+      if (! target_big_endian)
        coproc = 0;
 
       if (mips_pic == NO_PIC
@@ -4102,6 +5102,7 @@ macro (ip)
                  used_at = 1;
                }
 
+              /* Itbl support may require additional care here. */
              macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                           coproc ? treg + 1 : treg,
                           (int) BFD_RELOC_MIPS_GPREL, tempreg);
@@ -4111,6 +5112,7 @@ macro (ip)
                  undesired nop.  */
              hold_mips_optimize = mips_optimize;
              mips_optimize = 2;
+              /* Itbl support may require additional care here. */
              macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                           coproc ? treg : treg + 1,
                           (int) BFD_RELOC_MIPS_GPREL, tempreg);
@@ -4119,7 +5121,7 @@ macro (ip)
              p = frag_var (rs_machine_dependent, 12 + off, 0,
                            RELAX_ENCODE (8 + off, 12 + off, 0, 4 + off, 1,
                                          used_at && mips_noat),
-                           offset_expr.X_add_symbol, (long) 0,
+                           offset_expr.X_add_symbol, (offsetT) 0,
                            (char *) NULL);
 
              /* We just generated two relocs.  When tc_gen_reloc
@@ -4144,6 +5146,7 @@ macro (ip)
              if (p != NULL)
                p += 4;
            }
+          /* Itbl support may require additional care here. */
          macro_build (p, &icnt, &offset_expr, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -4151,6 +5154,7 @@ macro (ip)
            p += 4;
          /* FIXME: How do we handle overflow here?  */
          offset_expr.X_add_number += 4;
+          /* Itbl support may require additional care here. */
          macro_build (p, &icnt, &offset_expr, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -4191,6 +5195,7 @@ macro (ip)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                         mips_isa < 3 ? "addu" : "daddu",
                         "d,v,t", AT, breg, AT);
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -4200,6 +5205,7 @@ macro (ip)
              nop.  */
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -4207,7 +5213,7 @@ macro (ip)
 
          (void) frag_var (rs_machine_dependent, 0, 0,
                           RELAX_ENCODE (0, 0, -16 - off, -8, 1, 0),
-                          offset_expr.X_add_symbol, (long) 0,
+                          offset_expr.X_add_symbol, (offsetT) 0,
                           (char *) NULL);
        }
       else if (mips_pic == SVR4_PIC)
@@ -4257,6 +5263,7 @@ macro (ip)
            macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                         mips_isa < 3 ? "addu" : "daddu",
                         "d,v,t", AT, breg, AT);
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -4266,6 +5273,7 @@ macro (ip)
              nop.  */
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -4275,7 +5283,7 @@ macro (ip)
          p = frag_var (rs_machine_dependent, 16 + gpdel + off, 0,
                        RELAX_ENCODE (24 + off, 16 + gpdel + off, gpdel,
                                      8 + gpdel + off, 1, 0),
-                       offset_expr.X_add_symbol, (long) 0,
+                       offset_expr.X_add_symbol, (offsetT) 0,
                        (char *) NULL);
          if (gpdel > 0)
            {
@@ -4295,6 +5303,7 @@ macro (ip)
                           "d,v,t", AT, breg, AT);
              p += 4;
            }
+          /* Itbl support may require additional care here. */
          macro_build (p, &icnt, &expr1, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_LO16, AT);
@@ -4305,6 +5314,7 @@ macro (ip)
              nop.  */
          hold_mips_optimize = mips_optimize;
          mips_optimize = 2;
+          /* Itbl support may require additional care here. */
          macro_build (p, &icnt, &expr1, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_LO16, AT);
@@ -4334,10 +5344,12 @@ macro (ip)
              used_at = 1;
            }
 
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                       coproc ? treg + 1 : treg,
                       (int) BFD_RELOC_MIPS_GPREL, tempreg);
          offset_expr.X_add_number += 4;
+          /* Itbl support may require additional care here. */
          macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
                       coproc ? treg : treg + 1,
                       (int) BFD_RELOC_MIPS_GPREL, tempreg);
@@ -4363,8 +5375,59 @@ macro (ip)
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg + 1,
                   (int) BFD_RELOC_LO16, breg);
       return;
+
+   /* New code added to support COPZ instructions.
+      This code builds table entries out of the macros in mip_opcodes.
+      R4000 uses interlocks to handle coproc delays.
+      Other chips (like the R3000) require nops to be inserted for delays.
+
+      FIXME: Currently, we require that the user handle delays.
+      In order to fill delay slots for non-interlocked chips,
+      we must have a way to specify delays based on the coprocessor.
+      Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
+      What are the side-effects of the cop instruction?
+      What cache support might we have and what are its effects?
+      Both coprocessor & memory require delays. how long???
+      What registers are read/set/modified? 
+
+      If an itbl is provided to interpret cop instructions,
+      this knowledge can be encoded in the itbl spec. */
+
+    case M_COP0:
+      s = "cop0";
+      goto copz;
+    case M_COP1:
+      s = "cop1";
+      goto copz;
+    case M_COP2:
+      s = "cop2";
+      goto copz;
+    case M_COP3:
+      s = "cop3";
+    copz:
+      /* For now we just do C (same as Cz). */
+      macro_build ((char *) NULL, &icnt, &offset_expr, s, "C");
+      return;
+
 #ifdef LOSING_COMPILER
     default:
+      /* Try and see if this is a new itbl instruction.
+         This code builds table entries out of the macros in mip_opcodes.
+         FIXME: For now we just assemble the expression and pass it's
+         value along as a 32-bit immediate.
+         We may want to have the assembler assemble this value, 
+         so that we gain the assembler's knowledge of delay slots,
+         symbols, etc.
+         Would it be more efficient to use mask (id) here? */
+      if (itbl_have_entries 
+         && (immed_expr = itbl_assemble (ip->insn_mo->name, "")))
+        {
+         s = ip->insn_mo->name;
+         s2 = "cop3";
+         coproc = ITBL_DECODE_PNUM (immed_expr);;
+         macro_build ((char *) NULL, &icnt, &immed_expr, s, "C");
+         return;
+        }
       macro2 (ip);
       return;
     }
@@ -4433,7 +5496,7 @@ macro2 (ip)
     case M_DMULO:
       dbl = 1;
     case M_MULO:
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL,
@@ -4460,7 +5523,7 @@ macro2 (ip)
     case M_DMULOU:
       dbl = 1;
     case M_MULOU:
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL,
@@ -4517,11 +5580,11 @@ macro2 (ip)
       /* Even on a big endian machine $fn comes before $fn+1.  We have
         to adjust when storing to memory.  */
       macro_build ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)",
-                  byte_order == LITTLE_ENDIAN ? treg : treg + 1,
+                  target_big_endian ? treg + 1 : treg,
                   (int) BFD_RELOC_LO16, breg);
       offset_expr.X_add_number += 4;
       macro_build ((char *) NULL, &icnt, &offset_expr, "swc1", "T,o(b)",
-                  byte_order == LITTLE_ENDIAN ? treg + 1 : treg,
+                  target_big_endian ? treg : treg + 1,
                   (int) BFD_RELOC_LO16, breg);
       return;
 
@@ -4806,7 +5869,7 @@ macro2 (ip)
        * Is the double cfc1 instruction a bug in the mips assembler;
        * or is there a reason for it?
        */
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL, "cfc1", "t,G", treg, 31);
@@ -4836,11 +5899,11 @@ macro2 (ip)
       if (offset_expr.X_add_number >= 0x7fff)
        as_bad ("operand overflow");
       /* avoid load delay */
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number += 1;
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number -= 1;
       else
        offset_expr.X_add_number += 1;
@@ -4862,11 +5925,11 @@ macro2 (ip)
     ulw:
       if (offset_expr.X_add_number >= 0x8000 - off)
        as_bad ("operand overflow");
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number += off;
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number -= off;
       else
        offset_expr.X_add_number += off;
@@ -4889,13 +5952,13 @@ macro2 (ip)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                     mips_isa < 3 ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = off;
       else
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, AT);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = 0;
       else
        expr1.X_add_number = off;
@@ -4910,12 +5973,12 @@ macro2 (ip)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                     mips_isa < 3 ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1,
                   mask == M_ULH_A ? "lb" : "lbu", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, AT);
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        expr1.X_add_number = 1;
       else
        expr1.X_add_number = 0;
@@ -4930,12 +5993,12 @@ macro2 (ip)
     case M_USH:
       if (offset_expr.X_add_number >= 0x7fff)
        as_bad ("operand overflow");
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        offset_expr.X_add_number += 1;
       macro_build ((char *) NULL, &icnt, &offset_expr, "sb", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, treg, 8);
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        offset_expr.X_add_number -= 1;
       else
        offset_expr.X_add_number += 1;
@@ -4955,11 +6018,11 @@ macro2 (ip)
     usw:
       if (offset_expr.X_add_number >= 0x8000 - off)
        as_bad ("operand overflow");
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number += off;
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        offset_expr.X_add_number -= off;
       else
        offset_expr.X_add_number += off;
@@ -4982,13 +6045,13 @@ macro2 (ip)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                     mips_isa < 3 ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = off;
       else
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, AT);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = 0;
       else
        expr1.X_add_number = off;
@@ -5002,19 +6065,19 @@ macro2 (ip)
        macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                     mips_isa < 3 ? "addu" : "daddu",
                     "d,v,t", AT, AT, breg);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, AT);
       macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", treg,
                   treg, 8);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = 1;
       else
        expr1.X_add_number = 0;
       macro_build ((char *) NULL, &icnt, &expr1, "sb", "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, AT);
-      if (byte_order == LITTLE_ENDIAN)
+      if (! target_big_endian)
        expr1.X_add_number = 0;
       else
        expr1.X_add_number = 1;
@@ -5027,6 +6090,8 @@ macro2 (ip)
       break;
 
     default:
+      /* FIXME: Check if this is one of the itbl macros, since they
+        are added dynamically. */
       as_bad ("Macro %s not implemented yet", ip->insn_mo->name);
       break;
     }
@@ -5034,6 +6099,238 @@ macro2 (ip)
     as_warn ("Macro used $at after \".set noat\"");
 }
 
+/* Implement macros in mips16 mode.  */
+
+static void
+mips16_macro (ip)
+     struct mips_cl_insn *ip;
+{
+  int mask;
+  int xreg, yreg, zreg, tmp;
+  int icnt;
+  expressionS expr1;
+  int dbl;
+  const char *s, *s2, *s3;
+
+  mask = ip->insn_mo->mask;
+
+  xreg = (ip->insn_opcode >> MIPS16OP_SH_RX) & MIPS16OP_MASK_RX;
+  yreg = (ip->insn_opcode >> MIPS16OP_SH_RY) & MIPS16OP_MASK_RY;
+  zreg = (ip->insn_opcode >> MIPS16OP_SH_RZ) & MIPS16OP_MASK_RZ;
+
+  icnt = 0;
+
+  expr1.X_op = O_constant;
+  expr1.X_op_symbol = NULL;
+  expr1.X_add_symbol = NULL;
+  expr1.X_add_number = 1;
+
+  dbl = 0;
+
+  switch (mask)
+    {
+    default:
+      internalError ();
+
+    case M_DDIV_3:
+      dbl = 1;
+    case M_DIV_3:
+      s = "mflo";
+      goto do_div3;
+    case M_DREM_3:
+      dbl = 1;
+    case M_REM_3:
+      s = "mfhi";
+    do_div3:
+      mips_emit_delays (true);
+      ++mips_noreorder;
+      mips_any_noreorder = 1;
+      macro_build ((char *) NULL, &icnt, NULL,
+                  dbl ? "ddiv" : "div",
+                  "0,x,y", xreg, yreg);
+      expr1.X_add_number = 2;
+      macro_build ((char *) NULL, &icnt, &expr1, "bnez", "x,p", yreg);
+      macro_build ((char *) NULL, &icnt, NULL, "break", "6", 7);
+      /* FIXME: The normal code checks for of -1 / -0x80000000 here,
+         since that causes an overflow.  We should do that as well,
+         but I don't see how to do the comparisons without a temporary
+         register.  */
+      --mips_noreorder;
+      macro_build ((char *) NULL, &icnt, NULL, s, "x", zreg);
+      break;
+
+    case M_DIVU_3:
+      s = "divu";
+      s2 = "mflo";
+      goto do_divu3;
+    case M_REMU_3:
+      s = "divu";
+      s2 = "mfhi";
+      goto do_divu3;
+    case M_DDIVU_3:
+      s = "ddivu";
+      s2 = "mflo";
+      goto do_divu3;
+    case M_DREMU_3:
+      s = "ddivu";
+      s2 = "mfhi";
+    do_divu3:
+      mips_emit_delays (true);
+      ++mips_noreorder;
+      mips_any_noreorder = 1;
+      macro_build ((char *) NULL, &icnt, NULL, s, "0,x,y", xreg, yreg);
+      expr1.X_add_number = 2;
+      macro_build ((char *) NULL, &icnt, &expr1, "bnez", "x,p", yreg);
+      macro_build ((char *) NULL, &icnt, NULL, "break", "6", 7);
+      --mips_noreorder;
+      macro_build ((char *) NULL, &icnt, NULL, s2, "x", zreg);
+      break;
+
+    case M_DSUBU_I:
+      dbl = 1;
+      goto do_subu;
+    case M_SUBU_I:
+    do_subu:
+      imm_expr.X_add_number = -imm_expr.X_add_number;
+      macro_build ((char *) NULL, &icnt, &imm_expr,
+                  dbl ? "daddiu" : "addiu",
+                  "y,x,4", yreg, xreg);
+      break;
+
+    case M_SUBU_I_2:
+      imm_expr.X_add_number = -imm_expr.X_add_number;
+      macro_build ((char *) NULL, &icnt, &imm_expr, "addiu",
+                  "x,k", xreg);
+      break;
+
+    case M_DSUBU_I_2:
+      imm_expr.X_add_number = -imm_expr.X_add_number;
+      macro_build ((char *) NULL, &icnt, &imm_expr, "daddiu",
+                  "y,j", yreg);
+      break;
+
+    case M_BEQ:
+      s = "cmp";
+      s2 = "bteqz";
+      goto do_branch;
+    case M_BNE:
+      s = "cmp";
+      s2 = "btnez";
+      goto do_branch;
+    case M_BLT:
+      s = "slt";
+      s2 = "btnez";
+      goto do_branch;
+    case M_BLTU:
+      s = "sltu";
+      s2 = "btnez";
+      goto do_branch;
+    case M_BLE:
+      s = "slt";
+      s2 = "bteqz";
+      goto do_reverse_branch;
+    case M_BLEU:
+      s = "sltu";
+      s2 = "bteqz";
+      goto do_reverse_branch;
+    case M_BGE:
+      s = "slt";
+      s2 = "bteqz";
+      goto do_branch;
+    case M_BGEU:
+      s = "sltu";
+      s2 = "bteqz";
+      goto do_branch;
+    case M_BGT:
+      s = "slt";
+      s2 = "btnez";
+      goto do_reverse_branch;
+    case M_BGTU:
+      s = "sltu";
+      s2 = "btnez";
+
+    do_reverse_branch:
+      tmp = xreg;
+      xreg = yreg;
+      yreg = tmp;
+
+    do_branch:
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL, s, "x,y",
+                  xreg, yreg);
+      macro_build ((char *) NULL, &icnt, &offset_expr, s2, "p");
+      break;
+
+    case M_BEQ_I:
+      s = "cmpi";
+      s2 = "bteqz";
+      s3 = "x,U";
+      goto do_branch_i;
+    case M_BNE_I:
+      s = "cmpi";
+      s2 = "btnez";
+      s3 = "x,U";
+      goto do_branch_i;
+    case M_BLT_I:
+      s = "slti";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BLTU_I:
+      s = "sltiu";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BLE_I:
+      s = "slti";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_addone_branch_i;
+    case M_BLEU_I:
+      s = "sltiu";
+      s2 = "btnez";
+      s3 = "x,8";
+      goto do_addone_branch_i;
+    case M_BGE_I:
+      s = "slti";
+      s2 = "bteqz";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BGEU_I:
+      s = "sltiu";
+      s2 = "bteqz";
+      s3 = "x,8";
+      goto do_branch_i;
+    case M_BGT_I:
+      s = "slti";
+      s2 = "bteqz";
+      s3 = "x,8";
+      goto do_addone_branch_i;
+    case M_BGTU_I:
+      s = "sltiu";
+      s2 = "bteqz";
+      s3 = "x,8";
+
+    do_addone_branch_i:
+      ++imm_expr.X_add_number;
+
+    do_branch_i:
+      macro_build ((char *) NULL, &icnt, &imm_expr, s, s3, xreg);
+      macro_build ((char *) NULL, &icnt, &offset_expr, s2, "p");
+      break;
+
+    case M_ABS:
+      expr1.X_add_number = 0;
+      macro_build ((char *) NULL, &icnt, &expr1, "slti", "x,8",  yreg);
+      if (xreg != yreg)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                    "move", "y,X", xreg, yreg);
+      expr1.X_add_number = 2;
+      macro_build ((char *) NULL, &icnt, &expr1, "bteqz", "p");
+      macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                  "neg", "x,w", xreg, xreg);
+    }
+}
+
 /* This routine assembles an instruction into its binary format.  As a
    side effect, it sets one of the global variables imm_reloc or
    offset_reloc to the type of relocation to do if one of the operands
@@ -5055,20 +6352,11 @@ mips_ip (str, ip)
 
   insn_error = NULL;
 
-  for (s = str; islower (*s) || (*s >= '0' && *s <= '3') || *s == '6' || *s == '.'; ++s)
+  for (s = str; *s != '\0' && !isspace(*s); ++s)
     continue;
-  switch (*s)
-    {
-    case '\0':
-      break;
+  if (isspace (*s))
+    *s++ = '\0';
 
-    case ' ':
-      *s++ = '\0';
-      break;
-
-    default:
-      as_fatal ("Unknown opcode: `%s'", str);
-    }
   if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL)
     {
       insn_error = "unrecognized opcode";
@@ -5093,12 +6381,18 @@ mips_ip (str, ip)
        insn_isa = 1;
 
       if (insn_isa > mips_isa
-         || ((insn->pinfo & INSN_ISA) == INSN_4650
-             && ! mips_4650)
-         || ((insn->pinfo & INSN_ISA) == INSN_4010
-             && ! mips_4010)
-         || ((insn->pinfo & INSN_ISA) == INSN_4100
-             && ! mips_4100))
+         || (insn->pinfo != INSN_MACRO
+             && (((insn->pinfo & INSN_ISA) == INSN_4650
+                  && ! mips_4650)
+                 || ((insn->pinfo & INSN_ISA) == INSN_4010
+                     && ! mips_4010)
+                 || ((insn->pinfo & INSN_ISA) == INSN_4100
+                     && ! mips_4100)
+                 /* start-sanitize-r5900 */
+                 || ((insn->pinfo & INSN_ISA) == INSN_5900
+                     && ! mips_5900)
+                 /* end-sanitize-r5900 */
+                 )))
        {
          if (insn + 1 < &mips_opcodes[NUMOPCODES]
              && strcmp (insn->name, insn[1].name) == 0)
@@ -5106,7 +6400,15 @@ mips_ip (str, ip)
              ++insn;
              continue;
            }
-         insn_error = "opcode not supported on this processor";
+         if (insn_isa <= mips_isa)
+           insn_error = "opcode not supported on this processor";
+         else
+           {
+             static char buf[100];
+
+             sprintf (buf, "opcode requires -mips%d or greater", insn_isa);
+             insn_error = buf;
+           }
          return;
        }
 
@@ -5308,6 +6610,31 @@ mips_ip (str, ip)
                          s += 4;
                          regno = KT1;
                        }
+                     else if (itbl_have_entries)
+                       {
+                         char *p, *n;
+                         int r;
+
+                         p = s+1;      /* advance past '$' */
+                         n = itbl_get_field (&p);  /* n is name */
+
+                         /* See if this is a register defined in an 
+                            itbl entry */
+                         r = itbl_get_reg_val (n);
+                         if (r)
+                           {
+                             /* Get_field advances to the start of
+                                the next field, so we need to back
+                                rack to the end of the last field. */
+                             if (p) 
+                               s = p - 1;
+                             else 
+                               s = strchr (s,'\0');
+                             regno = r;
+                           }
+                         else
+                           goto notreg;
+                         }
                      else
                        goto notreg;
                    }
@@ -5331,6 +6658,9 @@ mips_ip (str, ip)
                  /* 'z' only matches $0.  */
                  if (c == 'z' && regno != 0)
                    break;
+
+       /* Now that we have assembled one operand, we use the args string 
+        * to figure out where it goes in the instruction. */
                  switch (c)
                    {
                    case 'r':
@@ -5363,6 +6693,11 @@ mips_ip (str, ip)
                         is $0.  This only matches $0, and is checked
                         outside the switch.  */
                      break;
+                   case 'D':
+                     /* Itbl operand; not yet implemented. FIXME ?? */
+                     break;
+                     /* What about all other operands like 'i', which
+                        can be specified in the opcode table? */
                    }
                  lastregno = regno;
                  continue;
@@ -5404,10 +6739,12 @@ mips_ip (str, ip)
 
                  if ((regno & 1) != 0
                      && mips_isa < 3
-                     && ! (strcmp (str, "mtc1") == 0 ||
-                           strcmp (str, "mfc1") == 0 ||
-                           strcmp (str, "lwc1") == 0 ||
-                           strcmp (str, "swc1") == 0))
+                     && ! (strcmp (str, "mtc1") == 0
+                           || strcmp (str, "mfc1") == 0
+                           || strcmp (str, "lwc1") == 0
+                           || strcmp (str, "swc1") == 0
+                           || strcmp (str, "l.s") == 0
+                           || strcmp (str, "s.s") == 0))
                    as_warn ("Float register should be even, was %d",
                             regno);
 
@@ -5534,7 +6871,7 @@ mips_ip (str, ip)
                        ))
                  {
                    imm_expr.X_op = O_constant;
-                   if (byte_order == LITTLE_ENDIAN)
+                   if (! target_big_endian)
                      imm_expr.X_add_number =
                        (((((((int) temp[3] << 8)
                             | temp[2]) << 8)
@@ -5579,8 +6916,9 @@ mips_ip (str, ip)
                                              | SEC_LOAD
                                              | SEC_READONLY
                                              | SEC_DATA));
-                   frag_align (*args == 'l' ? 2 : 3, 0);
-                   if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+                   frag_align (*args == 'l' ? 2 : 3, 0, 0);
+                   if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+                       && strcmp (TARGET_OS, "elf") != 0)
                      record_alignment (new_seg, 4);
                    else
                      record_alignment (new_seg, *args == 'l' ? 2 : 3);
@@ -5759,20 +7097,23 @@ mips_ip (str, ip)
 
            case 'N':           /* 3 bit branch condition code */
            case 'M':           /* 3 bit compare condition code */
-             my_getExpression (&imm_expr, s);
-             check_absolute_expr (ip, &imm_expr);
-              if ((unsigned long) imm_expr.X_add_number > 7)
+             if (strncmp (s, "$fcc", 4) != 0)
+               break;
+             s += 4;
+             regno = 0;
+             do
                {
-                  as_warn ("Condition code > 7 (%ld)",
-                          (long) imm_expr.X_add_number);
-                  imm_expr.X_add_number &= 7;
+                 regno *= 10;
+                 regno += *s - '0';
+                 ++s;
                }
+             while (isdigit (*s));
+             if (regno > 7)
+               as_bad ("invalid condition code register $fcc%d", regno);
              if (*args == 'N')
-               ip->insn_opcode |= imm_expr.X_add_number << OP_SH_BCC;
+               ip->insn_opcode |= regno << OP_SH_BCC;
              else
-               ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CCC;
-              imm_expr.X_op = O_absent;
-              s = expr_end;
+               ip->insn_opcode |= regno << OP_SH_CCC;
               continue;
 
            default:
@@ -5789,11 +7130,723 @@ mips_ip (str, ip)
          s = argsStart;
          continue;
        }
-      insn_error = "illegal operands";
-      return;
+      insn_error = "illegal operands";
+      return;
+    }
+}
+
+/* This routine assembles an instruction into its binary format when
+   assembling for the mips16.  As a side effect, it sets one of the
+   global variables imm_reloc or offset_reloc to the type of
+   relocation to do if one of the operands is an address expression.
+   It also sets mips16_small and mips16_ext if the user explicitly
+   requested a small or extended instruction.  */
+
+static void
+mips16_ip (str, ip)
+     char *str;
+     struct mips_cl_insn *ip;
+{
+  char *s;
+  const char *args;
+  struct mips_opcode *insn;
+  char *argsstart;
+  unsigned int regno;
+  unsigned int lastregno = 0;
+  char *s_reset;
+
+  insn_error = NULL;
+
+  mips16_small = false;
+  mips16_ext = false;
+
+  for (s = str; islower (*s); ++s)
+    ;
+  switch (*s)
+    {
+    case '\0':
+      break;
+
+    case ' ':
+      *s++ = '\0';
+      break;
+
+    case '.':
+      if (s[1] == 't' && s[2] == ' ')
+       {
+         *s = '\0';
+         mips16_small = true;
+         s += 3;
+         break;
+       }
+      else if (s[1] == 'e' && s[2] == ' ')
+       {
+         *s = '\0';
+         mips16_ext = true;
+         s += 3;
+         break;
+       }
+      /* Fall through.  */
+    default:
+      insn_error = "unknown opcode";
+      return;
+    }
+
+  if (! mips16_autoextend && ! mips16_ext)
+    mips16_small = true;
+
+  if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL)
+    {
+      insn_error = "unrecognized opcode";
+      return;
+    }
+
+  argsstart = s;
+  for (;;)
+    {
+      assert (strcmp (insn->name, str) == 0);
+
+      ip->insn_mo = insn;
+      ip->insn_opcode = insn->match;
+      ip->use_extend = false;
+      imm_expr.X_op = O_absent;
+      imm_reloc = BFD_RELOC_UNUSED;
+      offset_expr.X_op = O_absent;
+      offset_reloc = BFD_RELOC_UNUSED;
+      for (args = insn->args; 1; ++args)
+       {
+         int c;
+
+         if (*s == ' ')
+           ++s;
+
+         /* In this switch statement we call break if we did not find
+             a match, continue if we did find a match, or return if we
+             are done.  */
+
+         c = *args;
+         switch (c)
+           {
+           case '\0':
+             if (*s == '\0')
+               {
+                 /* Stuff the immediate value in now, if we can.  */
+                 if (imm_expr.X_op == O_constant
+                     && imm_reloc > BFD_RELOC_UNUSED
+                     && insn->pinfo != INSN_MACRO)
+                   {
+                     mips16_immed ((char *) NULL, 0,
+                                   imm_reloc - BFD_RELOC_UNUSED,
+                                   imm_expr.X_add_number, true, mips16_small,
+                                   mips16_ext, &ip->insn_opcode,
+                                   &ip->use_extend, &ip->extend);
+                     imm_expr.X_op = O_absent;
+                     imm_reloc = BFD_RELOC_UNUSED;
+                   }
+
+                 return;
+               }
+             break;
+
+           case ',':
+             if (*s++ == c)
+               continue;
+             s--;
+             switch (*++args)
+               {
+               case 'v':
+                 ip->insn_opcode |= lastregno << MIPS16OP_SH_RX;
+                 continue;
+               case 'w':
+                 ip->insn_opcode |= lastregno << MIPS16OP_SH_RY;
+                 continue;
+               }
+             break;
+
+           case '(':
+           case ')':
+             if (*s++ == c)
+               continue;
+             break;
+
+           case 'v':
+           case 'w':
+             if (s[0] != '$')
+               {
+                 if (c == 'v')
+                   ip->insn_opcode |= lastregno << MIPS16OP_SH_RX;
+                 else
+                   ip->insn_opcode |= lastregno << MIPS16OP_SH_RY;
+                 ++args;
+                 continue;
+               }
+             /* Fall through.  */
+           case 'x':
+           case 'y':
+           case 'z':
+           case 'Z':
+           case '0':
+           case 'S':
+           case 'R':
+           case 'X':
+           case 'Y':
+             if (s[0] != '$')
+               break;
+             s_reset = s;
+             if (isdigit (s[1]))
+               {
+                 ++s;
+                 regno = 0;
+                 do
+                   {
+                     regno *= 10;
+                     regno += *s - '0';
+                     ++s;
+                   }
+                 while (isdigit (*s));
+                 if (regno > 31)
+                   {
+                     as_bad ("invalid register number (%d)", regno);
+                     regno = 2;
+                   }
+               }
+             else
+               {
+                 if (s[1] == 'f' && s[2] == 'p')
+                   {
+                     s += 3;
+                     regno = FP;
+                   }
+                 else if (s[1] == 's' && s[2] == 'p')
+                   {
+                     s += 3;
+                     regno = SP;
+                   }
+                 else if (s[1] == 'g' && s[2] == 'p')
+                   {
+                     s += 3;
+                     regno = GP;
+                   }
+                 else if (s[1] == 'a' && s[2] == 't')
+                   {
+                     s += 3;
+                     regno = AT;
+                   }
+                 else if (s[1] == 'k' && s[2] == 't' && s[3] == '0')
+                   {
+                     s += 4;
+                     regno = KT0;
+                   }
+                 else if (s[1] == 'k' && s[2] == 't' && s[3] == '1')
+                   {
+                     s += 4;
+                     regno = KT1;
+                   }
+                 else
+                   break;
+               }
+
+             if (*s == ' ')
+               ++s;
+             if (args[1] != *s)
+               {
+                 if (c == 'v' || c == 'w')
+                   {
+                     regno = mips16_to_32_reg_map[lastregno];
+                     s = s_reset;
+                     args++;
+                   }
+               }
+
+             switch (c)
+               {
+               case 'x':
+               case 'y':
+               case 'z':
+               case 'v':
+               case 'w':
+               case 'Z':
+                 regno = mips32_to_16_reg_map[regno];
+                 break;
+
+               case '0':
+                 if (regno != 0)
+                   regno = ILLEGAL_REG;
+                 break;
+
+               case 'S':
+                 if (regno != SP)
+                   regno = ILLEGAL_REG;
+                 break;
+
+               case 'R':
+                 if (regno != RA)
+                   regno = ILLEGAL_REG;
+                 break;
+
+               case 'X':
+               case 'Y':
+                 if (regno == AT && ! mips_noat)
+                   as_warn ("used $at without \".set noat\"");
+                 break;
+
+               default:
+                 internalError ();
+               }
+
+             if (regno == ILLEGAL_REG)
+               break;
+
+             switch (c)
+               {
+               case 'x':
+               case 'v':
+                 ip->insn_opcode |= regno << MIPS16OP_SH_RX;
+                 break;
+               case 'y':
+               case 'w':
+                 ip->insn_opcode |= regno << MIPS16OP_SH_RY;
+                 break;
+               case 'z':
+                 ip->insn_opcode |= regno << MIPS16OP_SH_RZ;
+                 break;
+               case 'Z':
+                 ip->insn_opcode |= regno << MIPS16OP_SH_MOVE32Z;
+               case '0':
+               case 'S':
+               case 'R':
+                 break;
+               case 'X':
+                 ip->insn_opcode |= regno << MIPS16OP_SH_REGR32;
+                 break;
+               case 'Y':
+                 regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
+                 ip->insn_opcode |= regno << MIPS16OP_SH_REG32R;
+                 break;
+               default:
+                 internalError ();
+               }
+
+             lastregno = regno;
+             continue;
+
+           case 'P':
+             if (strncmp (s, "$pc", 3) == 0)
+               {
+                 s += 3;
+                 continue;
+               }
+             break;
+
+           case '<':
+           case '>':
+           case '[':
+           case ']':
+           case '4':
+           case '5':
+           case 'H':
+           case 'W':
+           case 'D':
+           case 'j':
+           case '8':
+           case 'V':
+           case 'C':
+           case 'U':
+           case 'k':
+           case 'K':
+             if (s[0] == '%'
+                 && strncmp (s + 1, "gprel(", sizeof "gprel(" - 1) == 0)
+               {
+                 /* This is %gprel(SYMBOL).  We need to read SYMBOL,
+                     and generate the appropriate reloc.  If the text
+                     inside %gprel is not a symbol name with an
+                     optional offset, then we generate a normal reloc
+                     and will probably fail later.  */
+                 my_getExpression (&imm_expr, s + sizeof "%gprel" - 1);
+                 if (imm_expr.X_op == O_symbol)
+                   {
+                     mips16_ext = true;
+                     imm_reloc = BFD_RELOC_MIPS16_GPREL;
+                     s = expr_end;
+                     ip->use_extend = true;
+                     ip->extend = 0;
+                     continue;
+                   }
+               }
+             else
+               {
+                 /* Just pick up a normal expression.  */
+                 my_getExpression (&imm_expr, s);
+               }
+
+             if (imm_expr.X_op == O_register)
+               {
+                 /* What we thought was an expression turned out to
+                     be a register.  */
+
+                 if (s[0] == '(' && args[1] == '(')
+                   {
+                     /* It looks like the expression was omitted
+                        before a register indirection, which means
+                        that the expression is implicitly zero.  We
+                        still set up imm_expr, so that we handle
+                        explicit extensions correctly.  */
+                     imm_expr.X_op = O_constant;
+                     imm_expr.X_add_number = 0;
+                     imm_reloc = (int) BFD_RELOC_UNUSED + c;
+                     continue;
+                   }
+
+                 break;
+               }
+
+             /* We need to relax this instruction.  */
+             imm_reloc = (int) BFD_RELOC_UNUSED + c;
+             s = expr_end;
+             continue;
+
+           case 'p':
+           case 'q':
+           case 'A':
+           case 'B':
+           case 'E':
+             /* We use offset_reloc rather than imm_reloc for the PC
+                 relative operands.  This lets macros with both
+                 immediate and address operands work correctly.  */
+             my_getExpression (&offset_expr, s);
+
+             if (offset_expr.X_op == O_register)
+               break;
+
+             /* We need to relax this instruction.  */
+             offset_reloc = (int) BFD_RELOC_UNUSED + c;
+             s = expr_end;
+             continue;
+
+           case '6':           /* break code */
+             my_getExpression (&imm_expr, s);
+             check_absolute_expr (ip, &imm_expr);
+             if ((unsigned long) imm_expr.X_add_number > 63)
+               {
+                 as_warn ("Invalid value for `%s' (%lu)",
+                          ip->insn_mo->name,
+                          (unsigned long) imm_expr.X_add_number);
+                 imm_expr.X_add_number &= 0x3f;
+               }
+             ip->insn_opcode |= imm_expr.X_add_number << MIPS16OP_SH_IMM6;
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
+
+           case 'a':           /* 26 bit address */
+             my_getExpression (&offset_expr, s);
+             s = expr_end;
+             offset_reloc = BFD_RELOC_MIPS16_JMP;
+             ip->insn_opcode <<= 16;
+             continue;
+
+           case 'l':           /* register list for entry macro */
+           case 'L':           /* register list for exit macro */
+             {
+               int mask;
+
+               if (c == 'l')
+                 mask = 0;
+               else
+                 mask = 7 << 3;
+               while (*s != '\0')
+                 {
+                   int freg, reg1, reg2;
+
+                   while (*s == ' ' || *s == ',')
+                     ++s;
+                   if (*s != '$')
+                     {
+                       as_bad ("can't parse register list");
+                       break;
+                     }
+                   ++s;
+                   if (*s != 'f')
+                     freg = 0;
+                   else
+                     {
+                       freg = 1;
+                       ++s;
+                     }
+                   reg1 = 0;
+                   while (isdigit (*s))
+                     {
+                       reg1 *= 10;
+                       reg1 += *s - '0';
+                       ++s;
+                     }
+                   if (*s == ' ')
+                     ++s;
+                   if (*s != '-')
+                     reg2 = reg1;
+                   else
+                     {
+                       ++s;
+                       if (*s != '$')
+                         break;
+                       ++s;
+                       if (freg)
+                         {
+                           if (*s == 'f')
+                             ++s;
+                           else
+                             {
+                               as_bad ("invalid register list");
+                               break;
+                             }
+                         }
+                       reg2 = 0;
+                       while (isdigit (*s))
+                         {
+                           reg2 *= 10;
+                           reg2 += *s - '0';
+                           ++s;
+                         }
+                     }
+                   if (freg && reg1 == 0 && reg2 == 0 && c == 'L')
+                     {
+                       mask &= ~ (7 << 3);
+                       mask |= 5 << 3;
+                     }
+                   else if (freg && reg1 == 0 && reg2 == 1 && c == 'L')
+                     {
+                       mask &= ~ (7 << 3);
+                       mask |= 6 << 3;
+                     }
+                   else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
+                     mask |= (reg2 - 3) << 3;
+                   else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17)
+                     mask |= (reg2 - 15) << 1;
+                   else if (reg1 == 31 && reg2 == 31)
+                     mask |= 1;
+                   else
+                     {
+                       as_bad ("invalid register list");
+                       break;
+                     }
+                 }
+               /* The mask is filled in in the opcode table for the
+                   benefit of the disassembler.  We remove it before
+                   applying the actual mask.  */
+               ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6);
+               ip->insn_opcode |= mask << MIPS16OP_SH_IMM6;
+             }
+           continue;
+
+           case 'e':           /* extend code */
+             my_getExpression (&imm_expr, s);
+             check_absolute_expr (ip, &imm_expr);
+             if ((unsigned long) imm_expr.X_add_number > 0x7ff)
+               {
+                 as_warn ("Invalid value for `%s' (%lu)",
+                          ip->insn_mo->name,
+                          (unsigned long) imm_expr.X_add_number);
+                 imm_expr.X_add_number &= 0x7ff;
+               }
+             ip->insn_opcode |= imm_expr.X_add_number;
+             imm_expr.X_op = O_absent;
+             s = expr_end;
+             continue;
+
+           default:
+             internalError ();
+           }
+         break;
+       }
+
+      /* Args don't match.  */
+      if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] &&
+         strcmp (insn->name, insn[1].name) == 0)
+       {
+         ++insn;
+         s = argsstart;
+         continue;
+       }
+
+      insn_error = "illegal operands";
+
+      return;
+    }
+}
+
+/* This structure holds information we know about a mips16 immediate
+   argument type.  */
+
+struct mips16_immed_operand
+{
+  /* The type code used in the argument string in the opcode table.  */
+  int type;
+  /* The number of bits in the short form of the opcode.  */
+  int nbits;
+  /* The number of bits in the extended form of the opcode.  */
+  int extbits;
+  /* The amount by which the short form is shifted when it is used;
+     for example, the sw instruction has a shift count of 2.  */
+  int shift;
+  /* The amount by which the short form is shifted when it is stored
+     into the instruction code.  */
+  int op_shift;
+  /* Non-zero if the short form is unsigned.  */
+  int unsp;
+  /* Non-zero if the extended form is unsigned.  */
+  int extu;
+  /* Non-zero if the value is PC relative.  */
+  int pcrel;
+};
+
+/* The mips16 immediate operand types.  */
+
+static const struct mips16_immed_operand mips16_immed_operands[] =
+{
+  { '<',  3,  5, 0, MIPS16OP_SH_RZ,   1, 1, 0 },
+  { '>',  3,  5, 0, MIPS16OP_SH_RX,   1, 1, 0 },
+  { '[',  3,  6, 0, MIPS16OP_SH_RZ,   1, 1, 0 },
+  { ']',  3,  6, 0, MIPS16OP_SH_RX,   1, 1, 0 },
+  { '4',  4, 15, 0, MIPS16OP_SH_IMM4, 0, 0, 0 },
+  { '5',  5, 16, 0, MIPS16OP_SH_IMM5, 1, 0, 0 },
+  { 'H',  5, 16, 1, MIPS16OP_SH_IMM5, 1, 0, 0 },
+  { 'W',  5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 0 },
+  { 'D',  5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 0 },
+  { 'j',  5, 16, 0, MIPS16OP_SH_IMM5, 0, 0, 0 },
+  { '8',  8, 16, 0, MIPS16OP_SH_IMM8, 1, 0, 0 },
+  { 'V',  8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 0 },
+  { 'C',  8, 16, 3, MIPS16OP_SH_IMM8, 1, 0, 0 },
+  { 'U',  8, 16, 0, MIPS16OP_SH_IMM8, 1, 1, 0 },
+  { 'k',  8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 0 },
+  { 'K',  8, 16, 3, MIPS16OP_SH_IMM8, 0, 0, 0 },
+  { 'p',  8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 },
+  { 'q', 11, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 },
+  { 'A',  8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 1 },
+  { 'B',  5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 1 },
+  { 'E',  5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 1 }
+};
+
+#define MIPS16_NUM_IMMED \
+  (sizeof mips16_immed_operands / sizeof mips16_immed_operands[0])
+
+/* Handle a mips16 instruction with an immediate value.  This or's the
+   small immediate value into *INSN.  It sets *USE_EXTEND to indicate
+   whether an extended value is needed; if one is needed, it sets
+   *EXTEND to the value.  The argument type is TYPE.  The value is VAL.
+   If SMALL is true, an unextended opcode was explicitly requested.
+   If EXT is true, an extended opcode was explicitly requested.  If
+   WARN is true, warn if EXT does not match reality.  */
+
+static void
+mips16_immed (file, line, type, val, warn, small, ext, insn, use_extend,
+             extend)
+     char *file;
+     unsigned int line;
+     int type;
+     offsetT val;
+     boolean warn;
+     boolean small;
+     boolean ext;
+     unsigned long *insn;
+     boolean *use_extend;
+     unsigned short *extend;
+{
+  register const struct mips16_immed_operand *op;
+  int mintiny, maxtiny;
+  boolean needext;
+
+  op = mips16_immed_operands;
+  while (op->type != type)
+    {
+      ++op;
+      assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
+    }
+
+  if (op->unsp)
+    {
+      if (type == '<' || type == '>' || type == '[' || type == ']')
+       {
+         mintiny = 1;
+         maxtiny = 1 << op->nbits;
+       }
+      else
+       {
+         mintiny = 0;
+         maxtiny = (1 << op->nbits) - 1;
+       }
+    }
+  else
+    {
+      mintiny = - (1 << (op->nbits - 1));
+      maxtiny = (1 << (op->nbits - 1)) - 1;
+    }
+
+  /* Branch offsets have an implicit 0 in the lowest bit.  */
+  if (type == 'p' || type == 'q')
+    val /= 2;
+
+  if ((val & ((1 << op->shift) - 1)) != 0
+      || val < (mintiny << op->shift)
+      || val > (maxtiny << op->shift))
+    needext = true;
+  else
+    needext = false;
+
+  if (warn && ext && ! needext)
+    as_warn_where (file, line, "extended operand requested but not required");
+  if (small && needext)
+    as_bad_where (file, line, "invalid unextended operand value");
+
+  if (small || (! ext && ! needext))
+    {
+      int insnval;
+
+      *use_extend = false;
+      insnval = ((val >> op->shift) & ((1 << op->nbits) - 1));
+      insnval <<= op->op_shift;
+      *insn |= insnval;
+    }
+  else
+    {
+      long minext, maxext;
+      int extval;
+
+      if (op->extu)
+       {
+         minext = 0;
+         maxext = (1 << op->extbits) - 1;
+       }
+      else
+       {
+         minext = - (1 << (op->extbits - 1));
+         maxext = (1 << (op->extbits - 1)) - 1;
+       }
+      if (val < minext || val > maxext)
+       as_bad_where (file, line,
+                     "operand value out of range for instruction");
+
+      *use_extend = true;
+      if (op->extbits == 16)
+       {
+         extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
+         val &= 0x1f;
+       }
+      else if (op->extbits == 15)
+       {
+         extval = ((val >> 11) & 0xf) | (val & 0x7f0);
+         val &= 0xf;
+       }
+      else
+       {
+         extval = ((val & 0x1f) << 6) | (val & 0x20);
+         val = 0;
+       }
+
+      *extend = (unsigned short) extval;
+      *insn |= val;
     }
 }
-
+\f
 #define LP '('
 #define RP ')'
 
@@ -5896,6 +7949,19 @@ my_getExpression (ep, str)
   expression (ep);
   expr_end = input_line_pointer;
   input_line_pointer = save_in;
+
+  /* If we are in mips16 mode, and this is an expression based on `.',
+     then we bump the value of the symbol by 1 since that is how other
+     text symbols are handled.  We don't bother to handle complex
+     expressions, just `.' plus or minus a constant.  */
+  if (mips16
+      && ep->X_op == O_symbol
+      && strcmp (S_GET_NAME (ep->X_add_symbol), FAKE_LABEL_NAME) == 0
+      && S_GET_SEGMENT (ep->X_add_symbol) == now_seg
+      && ep->X_add_symbol->sy_frag == frag_now
+      && ep->X_add_symbol->sy_value.X_op == O_constant
+      && ep->X_add_symbol->sy_value.X_add_number == frag_now_fix ())
+    ++ep->X_add_symbol->sy_value.X_add_number;
 }
 
 /* Turn a string in input_line_pointer into a floating point constant
@@ -5935,7 +8001,7 @@ md_atof (type, litP, sizeP)
 
   *sizeP = prec * 2;
 
-  if (byte_order == LITTLE_ENDIAN)
+  if (! target_big_endian)
     {
       for (i = prec - 1; i >= 0; i--)
        {
@@ -5961,19 +8027,10 @@ md_number_to_chars (buf, val, n)
      valueT val;
      int n;
 {
-  switch (byte_order)
-    {
-    case LITTLE_ENDIAN:
-      number_to_chars_littleendian (buf, val, n);
-      break;
-
-    case BIG_ENDIAN:
-      number_to_chars_bigendian (buf, val, n);
-      break;
-
-    default:
-      internalError ();
-    }
+  if (target_big_endian)
+    number_to_chars_bigendian (buf, val, n);
+  else
+    number_to_chars_littleendian (buf, val, n);
 }
 \f
 CONST char *md_shortopts = "O::g::G:";
@@ -6014,6 +8071,16 @@ struct option md_longopts[] = {
   {"m4100", no_argument, NULL, OPTION_M4100},
 #define OPTION_NO_M4100 (OPTION_MD_BASE + 18)
   {"no-m4100", no_argument, NULL, OPTION_NO_M4100},
+#define OPTION_MIPS16 (OPTION_MD_BASE + 22)
+  {"mips16", no_argument, NULL, OPTION_MIPS16},
+#define OPTION_NO_MIPS16 (OPTION_MD_BASE + 23)
+  {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16},
+  /* start-sanitize-r5900 */
+#define OPTION_M5900 (OPTION_MD_BASE + 24)
+  {"m5900", no_argument, NULL, OPTION_M5900},
+#define OPTION_NO_M5900 (OPTION_MD_BASE + 25)
+  {"no-m5900", no_argument, NULL, OPTION_NO_M5900},
+  /* end-sanitize-r5900 */
 
 #define OPTION_CALL_SHARED (OPTION_MD_BASE + 7)
 #define OPTION_NON_SHARED (OPTION_MD_BASE + 8)
@@ -6072,7 +8139,7 @@ md_parse_option (c, arg)
          optimizations which limit full symbolic debugging.  We take
          that to be equivalent to -O0.  */
       if (mips_debug == 2)
-       mips_optimize = 0;
+       mips_optimize = 1;
       break;
 
     case OPTION_MIPS1:
@@ -6178,6 +8245,17 @@ md_parse_option (c, arg)
                  }
                break;
 
+             case '5':
+               if (strcmp (p, "5000") == 0
+                   || strcmp (p, "5k") == 0
+                   || strcmp (p, "5K") == 0)
+                 mips_cpu = 5000;
+                /* start-sanitize-r5900 */
+                else if (strcmp (p, "5900") == 0)
+                  mips_cpu = 5900;
+                /* end-sanitize-r5900 */
+               break;
+
              case '6':
                if (strcmp (p, "6000") == 0
                    || strcmp (p, "6k") == 0
@@ -6198,7 +8276,7 @@ md_parse_option (c, arg)
                break;
              }
 
-           if (sv && mips_cpu != 4300 && mips_cpu != 4100)
+           if (sv && mips_cpu != 4300 && mips_cpu != 4100 && mips_cpu != 5000)
              {
                as_bad ("ignoring invalid leading 'v' in -mcpu=%s switch", arg);
                return 0;
@@ -6237,6 +8315,26 @@ md_parse_option (c, arg)
       mips_4100 = 0;
       break;
 
+      /* start-sanitize-r5900 */
+    case OPTION_M5900:
+      mips_5900 = 1;
+      break;
+
+    case OPTION_NO_M5900:
+      mips_5900 = 0;
+      break;
+      /* end-sanitize-r5900 */
+
+    case OPTION_MIPS16:
+      mips16 = 1;
+      mips_no_prev_insn (false);
+      break;
+
+    case OPTION_NO_MIPS16:
+      mips16 = 0;
+      mips_no_prev_insn (false);
+      break;
+
     case OPTION_MEMBEDDED_PIC:
       mips_pic = EMBEDDED_PIC;
       if (USE_GLOBAL_POINTER_OPT && g_switch_seen)
@@ -6352,6 +8450,9 @@ MIPS options:\n\
 -m4100                  permit VR4100 instructions\n\
 -no-m4100              do not permit VR4100 instructions\n");
   fprintf(stream, "\
+-mips16                        generate mips16 instructions\n\
+-no-mips16             do not generate mips16 instructions\n");
+  fprintf(stream, "\
 -O0                    remove unneeded NOPs, do not swap branches\n\
 -O                     remove unneeded NOPs and swap branches\n\
 --trap, --no-break     trap exception on div by 0 and mult overflow\n\
@@ -6365,16 +8466,15 @@ MIPS options:\n\
 -64                    create 64 bit object file\n");
 #endif
 }
-
+\f
 void
 mips_init_after_args ()
 {
-  if (target_big_endian)
-    byte_order = BIG_ENDIAN;
-  else
-    byte_order = LITTLE_ENDIAN;
+  /* initialize opcodes */
+  bfd_mips_num_opcodes = bfd_mips_num_builtin_opcodes;
+  mips_opcodes = (struct mips_opcode*) mips_builtin_opcodes;
 }
-\f
+
 long
 md_pcrel_from (fixP)
      fixS *fixP;
@@ -6403,14 +8503,16 @@ cons_fix_new_mips (frag, where, nbytes, exp)
      unsigned int nbytes;
      expressionS *exp;
 {
+#ifndef OBJ_ELF
   /* If we are assembling in 32 bit mode, turn an 8 byte reloc into a
      4 byte reloc.  */
   if (nbytes == 8 && ! mips_64)
     {
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        where += 4;
       nbytes = 4;
     }
+#endif
 
   if (nbytes != 2 && nbytes != 4 && nbytes != 8)
     as_bad ("Unsupported reloc size %d", nbytes);
@@ -6421,6 +8523,24 @@ cons_fix_new_mips (frag, where, nbytes, exp)
                : (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
 }
 
+/* This is called before the symbol table is processed.  In order to
+   work with gcc when using mips-tfile, we must keep all local labels.
+   However, in other cases, we want to discard them.  If we were
+   called with -g, but we didn't see any debugging information, it may
+   mean that gcc is smuggling debugging information through to
+   mips-tfile, in which case we must generate all local labels.  */
+
+void
+mips_frob_file_before_adjust ()
+{
+#ifndef NO_ECOFF_DEBUGGING
+  if (ECOFF_DEBUGGING
+      && mips_debug != 0
+      && ! ecoff_debugging_seen)
+    flag_keep_locals = 1;
+#endif
+}
+
 /* Sort any unmatched HI16_S relocs so that they immediately precede
    the corresponding LO reloc.  This is called before md_apply_fix and
    tc_gen_reloc.  Unmatched HI16_S relocs can only be generated by
@@ -6539,9 +8659,30 @@ md_apply_fix (fixP, valueP)
   unsigned char *buf;
   long insn, value;
 
-  assert (fixP->fx_size == 4 || fixP->fx_r_type == BFD_RELOC_16);
+  assert (fixP->fx_size == 4
+         || fixP->fx_r_type == BFD_RELOC_16
+         || fixP->fx_r_type == BFD_RELOC_64);
 
   value = *valueP;
+
+  /* If we aren't adjusting this fixup to be against the section
+     symbol, we need to adjust the value.  */
+#ifdef S_GET_OTHER
+  if (fixP->fx_addsy != NULL
+      && OUTPUT_FLAVOR == bfd_target_elf_flavour
+      && S_GET_OTHER (fixP->fx_addsy) == STO_MIPS16)
+    {
+      value -= S_GET_VALUE (fixP->fx_addsy);
+      if (value != 0 && ! fixP->fx_pcrel)
+       {
+         /* In this case, the bfd_install_relocation routine will
+             incorrectly add the symbol value back in.  We just want
+             the addend to appear in the object file.  */
+         value -= S_GET_VALUE (fixP->fx_addsy);
+       }
+    }
+#endif
+
   fixP->fx_addnumber = value;  /* Remember value for tc_gen_reloc */
 
   if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel)
@@ -6561,15 +8702,23 @@ md_apply_fix (fixP, valueP)
     case BFD_RELOC_MIPS_GOT_LO16:
     case BFD_RELOC_MIPS_CALL_HI16:
     case BFD_RELOC_MIPS_CALL_LO16:
+    case BFD_RELOC_MIPS16_GPREL:
       if (fixP->fx_pcrel)
        as_bad_where (fixP->fx_file, fixP->fx_line,
                      "Invalid PC relative reloc");
       /* Nothing needed to do. The value comes from the reloc entry */
       break;
 
+    case BFD_RELOC_MIPS16_JMP:
+      /* We currently always generate a reloc against a symbol, which
+         means that we don't want an addend even if the symbol is
+         defined.  */
+      fixP->fx_addnumber = 0;
+      break;
+
     case BFD_RELOC_PCREL_HI16_S:
       /* The addend for this is tricky if it is internal, so we just
-        do everything here rather than in bfd_perform_relocation.  */
+        do everything here rather than in bfd_install_relocation.  */
       if ((fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0)
        {
          /* For an external symbol adjust by the address to make it
@@ -6582,22 +8731,51 @@ md_apply_fix (fixP, valueP)
        value += 0x10000;
       value >>= 16;
       buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        buf += 2;
       md_number_to_chars (buf, value, 2);
       break;
 
     case BFD_RELOC_PCREL_LO16:
       /* The addend for this is tricky if it is internal, so we just
-        do everything here rather than in bfd_perform_relocation.  */
+        do everything here rather than in bfd_install_relocation.  */
       if ((fixP->fx_addsy->bsym->flags & BSF_SECTION_SYM) == 0)
        value += fixP->fx_frag->fr_address + fixP->fx_where;
       buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
-      if (byte_order == BIG_ENDIAN)
+      if (target_big_endian)
        buf += 2;
       md_number_to_chars (buf, value, 2);
       break;
 
+    case BFD_RELOC_64:
+      /* This is handled like BFD_RELOC_32, but we output a sign
+         extended value if we are only 32 bits.  */
+      if (fixP->fx_done
+         || (mips_pic == EMBEDDED_PIC && SWITCH_TABLE (fixP)))
+       {
+         if (8 <= sizeof (valueT))
+           md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+                               value, 8);
+         else
+           {
+             long w1, w2;
+             long hiv;
+
+             w1 = w2 = fixP->fx_where;
+             if (target_big_endian)
+               w1 += 4;
+             else
+               w2 += 4;
+             md_number_to_chars (fixP->fx_frag->fr_literal + w1, value, 4);
+             if ((value & 0x80000000) != 0)
+               hiv = 0xffffffff;
+             else
+               hiv = 0;
+             md_number_to_chars (fixP->fx_frag->fr_literal + w2, hiv, 4);
+           }
+       }
+      break;
+
     case BFD_RELOC_32:
       /* If we are deleting this reloc entry, we must fill in the
         value now.  This can happen if we have a .word which is not
@@ -6628,7 +8806,7 @@ md_apply_fix (fixP, valueP)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          "relocation overflow");
          buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
-         if (byte_order == BIG_ENDIAN)
+         if (target_big_endian)
            buf += 2;
          md_number_to_chars (buf, value, 2);
        }
@@ -6640,27 +8818,17 @@ md_apply_fix (fixP, valueP)
        * might be deleting the relocation entry (i.e., a branch within
        * the current segment).
        */
-      if (value & 0x3)
-       as_warn_where (fixP->fx_file, fixP->fx_line,
-                      "Branch to odd address (%lx)", value);
+      if ((value & 0x3) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     "Branch to odd address (%lx)", value);
       value >>= 2;
 
       /* update old instruction data */
       buf = (unsigned char *) (fixP->fx_where + fixP->fx_frag->fr_literal);
-      switch (byte_order)
-       {
-       case LITTLE_ENDIAN:
-         insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
-         break;
-
-       case BIG_ENDIAN:
-         insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
-         break;
-
-       default:
-         internalError ();
-         return 0;
-       }
+      if (target_big_endian)
+       insn = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+      else
+       insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
 
       if (value >= -0x8000 && value < 0x8000)
        insn |= value & 0xffff;
@@ -6697,7 +8865,7 @@ md_apply_fix (fixP, valueP)
                  handle these cases, but it appears to do it
                  incorrectly.  */
              as_bad_where (fixP->fx_file, fixP->fx_line,
-                           "Relocation overflow");
+                           "Branch out of range");
            }
        }
 
@@ -6822,8 +8990,8 @@ mips_align (to, fill, label)
      int fill;
      symbolS *label;
 {
-  mips_emit_delays ();
-  frag_align (to, fill);
+  mips_emit_delays (false);
+  frag_align (to, fill, 0);
   record_alignment (now_seg, to);
   if (label != NULL)
     {
@@ -6874,7 +9042,8 @@ s_align (x)
   if (temp)
     {
       auto_align = 1;
-      mips_align (temp, (int) temp_fill, insn_label);
+      mips_align (temp, (int) temp_fill,
+                 insn_labels != NULL ? insn_labels->label : NULL);
     }
   else
     {
@@ -6887,8 +9056,8 @@ s_align (x)
 void
 mips_flush_pending_output ()
 {
-  mips_emit_delays ();
-  insn_label = NULL;
+  mips_emit_delays (false);
+  mips_clear_insn_labels ();
 }
 
 static void
@@ -6904,7 +9073,7 @@ s_change_sec (sec)
       && (sec == 'd' || sec == 'r'))
     sec = 's';
 
-  mips_emit_delays ();
+  mips_emit_delays (false);
   switch (sec)
     {
     case 't':
@@ -6931,7 +9100,8 @@ s_change_sec (sec)
                                      | SEC_READONLY
                                      | SEC_RELOC
                                      | SEC_DATA));
-             bfd_set_section_alignment (stdoutput, seg, 4);
+             if (strcmp (TARGET_OS, "elf") != 0)
+               bfd_set_section_alignment (stdoutput, seg, 4);
            }
          demand_empty_rest_of_line ();
        }
@@ -6952,7 +9122,8 @@ s_change_sec (sec)
              bfd_set_section_flags (stdoutput, seg,
                                     SEC_ALLOC | SEC_LOAD | SEC_RELOC
                                     | SEC_DATA);
-             bfd_set_section_alignment (stdoutput, seg, 4);
+             if (strcmp (TARGET_OS, "elf") != 0)
+               bfd_set_section_alignment (stdoutput, seg, 4);
            }
          demand_empty_rest_of_line ();
          break;
@@ -6980,11 +9151,11 @@ s_cons (log_size)
 {
   symbolS *label;
 
-  label = insn_label;
-  mips_emit_delays ();
+  label = insn_labels != NULL ? insn_labels->label : NULL;
+  mips_emit_delays (false);
   if (log_size > 0 && auto_align)
     mips_align (log_size, 0, label);
-  insn_label = NULL;
+  mips_clear_insn_labels ();
   cons (1 << log_size);
 }
 
@@ -6994,9 +9165,9 @@ s_float_cons (type)
 {
   symbolS *label;
 
-  label = insn_label;
+  label = insn_labels != NULL ? insn_labels->label : NULL;
 
-  mips_emit_delays ();
+  mips_emit_delays (false);
 
   if (auto_align)
     if (type == 'd')
@@ -7004,7 +9175,7 @@ s_float_cons (type)
     else
       mips_align (2, 0, label);
 
-  insn_label = NULL;
+  mips_clear_insn_labels ();
 
   float_cons (type);
 }
@@ -7110,16 +9281,19 @@ s_mipsset (x)
 
   if (strcmp (name, "reorder") == 0)
     {
-      if (mips_noreorder)
+      if (mips_noreorder && prev_nop_frag != NULL)
        {
-         prev_insn_unreordered = 1;
-         prev_prev_insn_unreordered = 1;
+         /* If we still have pending nops, we can discard them.  The
+            usual nop handling will insert any that are still
+            needed. */
+         prev_nop_frag->fr_fix -= prev_nop_frag_holds * (mips16 ? 2 : 4);
+         prev_nop_frag = NULL;
        }
       mips_noreorder = 0;
     }
   else if (strcmp (name, "noreorder") == 0)
     {
-      mips_emit_delays ();
+      mips_emit_delays (true);
       mips_noreorder = 1;
       mips_any_noreorder = 1;
     }
@@ -7157,6 +9331,12 @@ s_mipsset (x)
     {
       mips_nobopt = 1;
     }
+  else if (strcmp (name, "mips16") == 0
+          || strcmp (name, "MIPS-16") == 0)
+    mips16 = 1;
+  else if (strcmp (name, "nomips16") == 0
+          || strcmp (name, "noMIPS-16") == 0)
+    mips16 = 0;
   else if (strncmp (name, "mips", 4) == 0)
     {
       int isa;
@@ -7171,6 +9351,10 @@ s_mipsset (x)
       else
        mips_isa = isa;
     }
+  else if (strcmp (name, "autoextend") == 0)
+    mips16_autoextend = 1;
+  else if (strcmp (name, "noautoextend") == 0)
+    mips16_autoextend = 0;
   else
     {
       as_warn ("Tried to set unrecognized symbol: %s\n", name);
@@ -7293,11 +9477,11 @@ s_gpword (ignore)
       return;
     }
 
-  label = insn_label;
-  mips_emit_delays ();
+  label = insn_labels != NULL ? insn_labels->label : NULL;
+  mips_emit_delays (true);
   if (auto_align)
     mips_align (2, 0, label);
-  insn_label = NULL;
+  mips_clear_insn_labels ();
 
   expression (&ex);
 
@@ -7341,6 +9525,38 @@ s_cpadd (ignore)
   demand_empty_rest_of_line ();  
 }
 
+/* Handle the .insn pseudo-op.  This marks instruction labels in
+   mips16 mode.  This permits the linker to handle them specially,
+   such as generating jalx instructions when needed.  We also make
+   them odd for the duration of the assembly, in order to generate the
+   right sort of code.  We will make them even in the adjust_symtab
+   routine, while leaving them marked.  This is convenient for the
+   debugger and the disassembler.  The linker knows to make them odd
+   again.  */
+
+static void
+s_insn (ignore)
+     int ignore;
+{
+  if (mips16)
+    {
+      struct insn_label_list *l;
+
+      for (l = insn_labels; l != NULL; l = l->next)
+       {
+#ifdef S_SET_OTHER
+         if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+           S_SET_OTHER (l->label, STO_MIPS16);
+#endif
+         ++l->label->sy_value.X_add_number;
+       }
+
+      mips_clear_insn_labels ();
+    }
+
+  demand_empty_rest_of_line ();
+}
+
 /* Parse a register string into a number.  Called from the ECOFF code
    to parse .frame.  The argument is non-zero if this is the frame
    register, so that we can record it in mips_frame_reg.  */
@@ -7395,6 +9611,17 @@ md_section_align (seg, addr)
 {
   int align = bfd_get_section_alignment (stdoutput, seg);
 
+#ifdef OBJ_ELF
+  /* We don't need to align ELF sections to the full alignment.
+     However, Irix 5 may prefer that we align them at least to a 16
+     byte boundary.  We don't bother to align the sections if we are
+     targeted for an embedded system.  */
+  if (strcmp (TARGET_OS, "elf") == 0)
+    return addr;
+  if (align > 4)
+    align = 4;
+#endif
+
   return ((addr + (1 << align) - 1) & (-1 << align));
 }
 
@@ -7458,9 +9685,221 @@ nopic_need_relax (sym)
     return 1;
 }
 
-/* Estimate the size of a frag before relaxing.  We are not really
-   relaxing here, and the final size is encoded in the subtype
-   information.  */
+/* Given a mips16 variant frag FRAGP, return non-zero if it needs an
+   extended opcode.  SEC is the section the frag is in.  */
+
+static int
+mips16_extended_frag (fragp, sec, stretch)
+     fragS *fragp;
+     asection *sec;
+     long stretch;
+{
+  int type;
+  register const struct mips16_immed_operand *op;
+  offsetT val;
+  int mintiny, maxtiny;
+  segT symsec;
+
+  if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype))
+    return 0;
+  if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype))
+    return 1;
+
+  type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
+  op = mips16_immed_operands;
+  while (op->type != type)
+    {
+      ++op;
+      assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
+    }
+
+  if (op->unsp)
+    {
+      if (type == '<' || type == '>' || type == '[' || type == ']')
+       {
+         mintiny = 1;
+         maxtiny = 1 << op->nbits;
+       }
+      else
+       {
+         mintiny = 0;
+         maxtiny = (1 << op->nbits) - 1;
+       }
+    }
+  else
+    {
+      mintiny = - (1 << (op->nbits - 1));
+      maxtiny = (1 << (op->nbits - 1)) - 1;
+    }
+
+  /* We can't call S_GET_VALUE here, because we don't want to lock in
+     a particular frag address.  */
+  if (fragp->fr_symbol->sy_value.X_op == O_constant)
+    {
+      val = (fragp->fr_symbol->sy_value.X_add_number
+            + fragp->fr_symbol->sy_frag->fr_address);
+      symsec = S_GET_SEGMENT (fragp->fr_symbol);
+    }
+  else if (fragp->fr_symbol->sy_value.X_op == O_symbol
+          && (fragp->fr_symbol->sy_value.X_add_symbol->sy_value.X_op
+              == O_constant))
+    {
+      val = (fragp->fr_symbol->sy_value.X_add_symbol->sy_value.X_add_number
+            + fragp->fr_symbol->sy_value.X_add_symbol->sy_frag->fr_address
+            + fragp->fr_symbol->sy_value.X_add_number
+            + fragp->fr_symbol->sy_frag->fr_address);
+      symsec = S_GET_SEGMENT (fragp->fr_symbol->sy_value.X_add_symbol);
+    }
+  else
+    return 1;
+
+  if (op->pcrel)
+    {
+      addressT addr;
+
+      /* We won't have the section when we are called from
+         mips_relax_frag.  However, we will always have been called
+         from md_estimate_size_before_relax first.  If this is a
+         branch to a different section, we mark it as such.  If SEC is
+         NULL, and the frag is not marked, then it must be a branch to
+         the same section.  */
+      if (sec == NULL)
+       {
+         if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype))
+           return 1;
+       }
+      else
+       {
+         if (symsec != sec)
+           {
+             fragp->fr_subtype =
+               RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+
+             /* FIXME: We should support this, and let the linker
+                 catch branches and loads that are out of range.  */
+             as_bad_where (fragp->fr_file, fragp->fr_line,
+                           "unsupported PC relative reference to different section");
+
+             return 1;
+           }
+       }
+
+      /* In this case, we know for sure that the symbol fragment is in
+        the same section.  If the fr_address of the symbol fragment
+        is greater then the address of this fragment we want to add
+        in STRETCH in order to get a better estimate of the address.
+        This particularly matters because of the shift bits.  */
+      if (stretch != 0
+         && fragp->fr_symbol->sy_frag->fr_address >= fragp->fr_address)
+       {
+         fragS *f;
+
+         /* Adjust stretch for any alignment frag.  Note that if have
+             been expanding the earlier code, the symbol may be
+             defined in what appears to be an earlier frag.  FIXME:
+             This doesn't handle the fr_subtype field, which specifies
+             a maximum number of bytes to skip when doing an
+             alignment.  */
+         for (f = fragp;
+              f != NULL && f != fragp->fr_symbol->sy_frag;
+              f = f->fr_next)
+           {
+             if (f->fr_type == rs_align || f->fr_type == rs_align_code)
+               {
+                 if (stretch < 0)
+                   stretch = - ((- stretch)
+                                & ~ ((1 << (int) f->fr_offset) - 1));
+                 else
+                   stretch &= ~ ((1 << (int) f->fr_offset) - 1);
+                 if (stretch == 0)
+                   break;
+               }
+           }
+         if (f != NULL)
+           val += stretch;
+       }
+
+      addr = fragp->fr_address + fragp->fr_fix;
+
+      /* The base address rules are complicated.  The base address of
+         a branch is the following instruction.  The base address of a
+         PC relative load or add is the instruction itself, but if it
+         is in a delay slot (in which case it can not be extended) use
+         the address of the instruction whose delay slot it is in.  */
+      if (type == 'p' || type == 'q')
+       {
+         addr += 2;
+
+         /* If we are currently assuming that this frag should be
+            extended, then, the current address is two bytes
+            higher. */
+         if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+           addr += 2;
+
+         /* Ignore the low bit in the target, since it will be set
+             for a text label.  */
+         if ((val & 1) != 0)
+           --val;
+       }
+      else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
+       addr -= 4;
+      else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
+       addr -= 2;
+
+      val -= addr & ~ ((1 << op->shift) - 1);
+
+      /* Branch offsets have an implicit 0 in the lowest bit.  */
+      if (type == 'p' || type == 'q')
+       val /= 2;
+
+      /* If any of the shifted bits are set, we must use an extended
+         opcode.  If the address depends on the size of this
+         instruction, this can lead to a loop, so we arrange to always
+         use an extended opcode.  We only check this when we are in
+         the main relaxation loop, when SEC is NULL.  */
+      if ((val & ((1 << op->shift) - 1)) != 0 && sec == NULL)
+       {
+         fragp->fr_subtype =
+           RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+         return 1;
+       }
+
+      /* If we are about to mark a frag as extended because the value
+         is precisely maxtiny + 1, then there is a chance of an
+         infinite loop as in the following code:
+            la $4,foo
+            .skip      1020
+            .align     2
+          foo:
+        In this case when the la is extended, foo is 0x3fc bytes
+        away, so the la can be shrunk, but then foo is 0x400 away, so
+        the la must be extended.  To avoid this loop, we mark the
+        frag as extended if it was small, and is about to become
+        extended with a value of maxtiny + 1.  */
+      if (val == ((maxtiny + 1) << op->shift)
+         && ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype)
+         && sec == NULL)
+       {
+         fragp->fr_subtype =
+           RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+         return 1;
+       }
+    }
+  else if (symsec != absolute_section && sec != NULL)
+    as_bad_where (fragp->fr_file, fragp->fr_line, "unsupported relocation");
+
+  if ((val & ((1 << op->shift) - 1)) != 0
+      || val < (mintiny << op->shift)
+      || val > (maxtiny << op->shift))
+    return 1;
+  else
+    return 0;
+}
+
+/* Estimate the size of a frag before relaxing.  Unless this is the
+   mips16, we are not really relaxing here, and the final size is
+   encoded in the subtype information.  For the mips16, we have to
+   decide whether we are using an extended opcode or not.  */
 
 /*ARGSUSED*/
 int
@@ -7470,13 +9909,46 @@ md_estimate_size_before_relax (fragp, segtype)
 {
   int change;
 
+  if (RELAX_MIPS16_P (fragp->fr_subtype))
+    {
+      if (mips16_extended_frag (fragp, segtype, 0))
+       {
+         fragp->fr_subtype = RELAX_MIPS16_MARK_EXTENDED (fragp->fr_subtype);
+         return 4;
+       }
+      else
+       {
+         fragp->fr_subtype = RELAX_MIPS16_CLEAR_EXTENDED (fragp->fr_subtype);
+         return 2;
+       }
+    }
+
   if (mips_pic == NO_PIC)
     {
       change = nopic_need_relax (fragp->fr_symbol);
     }
   else if (mips_pic == SVR4_PIC)
     {
-      asection *symsec = fragp->fr_symbol->bsym->section;
+      symbolS *sym;
+      asection *symsec;
+
+      sym = fragp->fr_symbol;
+
+      /* Handle the case of a symbol equated to another symbol.  */
+      while (sym->sy_value.X_op == O_symbol
+            && (! S_IS_DEFINED (sym) || S_IS_COMMON (sym)))
+       {
+         symbolS *n;
+
+         /* It's possible to get a loop here in a badly written
+             program.  */
+         n = sym->sy_value.X_add_symbol;
+         if (n == sym)
+           break;
+         sym = n;
+       }
+
+      symsec = S_GET_SEGMENT (sym);
 
       /* This must duplicate the test in adjust_reloc_syms.  */
       change = (symsec != &bfd_und_section
@@ -7506,6 +9978,30 @@ md_estimate_size_before_relax (fragp, segtype)
     return RELAX_NEW (fragp->fr_subtype) - RELAX_OLD (fragp->fr_subtype);
 }
 
+/* This is called to see whether a reloc against a defined symbol
+   should be converted into a reloc against a section.  Don't adjust
+   MIPS16 jump relocations, so we don't have to worry about the format
+   of the offset in the .o file.  Don't adjust relocations against
+   mips16 symbols, so that the linker can find them if it needs to set
+   up a stub.  */
+
+int
+mips_fix_adjustable (fixp)
+     fixS *fixp;
+{
+  if (fixp->fx_r_type == BFD_RELOC_MIPS16_JMP)
+    return 0;
+  if (fixp->fx_addsy == NULL)
+    return 1;
+#ifdef S_GET_OTHER
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+      && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16
+      && fixp->fx_subsy == NULL)
+    return 0;
+#endif
+  return 1;
+}
+
 /* Translate internal representation of relocation info to BFD target
    format.  */
 
@@ -7584,6 +10080,8 @@ tc_gen_reloc (section, fixp)
     {
       arelent *reloc2;
 
+      assert (! RELAX_MIPS16_P (fixp->fx_frag->fr_subtype));
+
       /* If this is not the last reloc in this frag, then we have two
         GPREL relocs, or a GOT_HI16/GOT_LO16 pair, or a
         CALL_HI16/CALL_LO16, both of which are being replaced.  Let
@@ -7667,9 +10165,13 @@ tc_gen_reloc (section, fixp)
        case BFD_RELOC_32:
          code = BFD_RELOC_32_PCREL;
          break;
+       case BFD_RELOC_64:
+         code = BFD_RELOC_64_PCREL;
+         break;
        case BFD_RELOC_8_PCREL:
        case BFD_RELOC_16_PCREL:
        case BFD_RELOC_32_PCREL:
+       case BFD_RELOC_64_PCREL:
        case BFD_RELOC_16_PCREL_S2:
        case BFD_RELOC_PCREL_HI16_S:
        case BFD_RELOC_PCREL_LO16:
@@ -7702,6 +10204,35 @@ tc_gen_reloc (section, fixp)
   return retval;
 }
 
+/* Relax a machine dependent frag.  This returns the amount by which
+   the current size of the frag should change.  */
+
+int
+mips_relax_frag (fragp, stretch)
+     fragS *fragp;
+     long stretch;
+{
+  if (! RELAX_MIPS16_P (fragp->fr_subtype))
+    return 0;
+
+  if (mips16_extended_frag (fragp, (asection *) NULL, stretch))
+    {
+      if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+       return 0;
+      fragp->fr_subtype = RELAX_MIPS16_MARK_EXTENDED (fragp->fr_subtype);
+      return 2;
+    }
+  else
+    {
+      if (! RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+       return 0;
+      fragp->fr_subtype = RELAX_MIPS16_CLEAR_EXTENDED (fragp->fr_subtype);
+      return -2;
+    }
+
+  return 0;
+}
+
 /* Convert a machine dependent frag.  */
 
 void
@@ -7713,19 +10244,144 @@ md_convert_frag (abfd, asec, fragp)
   int old, new;
   char *fixptr;
 
-  if (fragp->fr_opcode == NULL)
-    return;
+  if (RELAX_MIPS16_P (fragp->fr_subtype))
+    {
+      int type;
+      register const struct mips16_immed_operand *op;
+      boolean small, ext;
+      offsetT val;
+      bfd_byte *buf;
+      unsigned long insn;
+      boolean use_extend;
+      unsigned short extend;
+
+      type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
+      op = mips16_immed_operands;
+      while (op->type != type)
+       ++op;
+
+      if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+       {
+         small = false;
+         ext = true;
+       }
+      else
+       {
+         small = true;
+         ext = false;
+       }
+
+      resolve_symbol_value (fragp->fr_symbol);
+      val = S_GET_VALUE (fragp->fr_symbol);
+      if (op->pcrel)
+       {
+         addressT addr;
+
+         addr = fragp->fr_address + fragp->fr_fix;
+
+         /* The rules for the base address of a PC relative reloc are
+             complicated; see mips16_extended_frag.  */
+         if (type == 'p' || type == 'q')
+           {
+             addr += 2;
+             if (ext)
+               addr += 2;
+             /* Ignore the low bit in the target, since it will be
+                 set for a text label.  */
+             if ((val & 1) != 0)
+               --val;
+           }
+         else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
+           addr -= 4;
+         else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
+           addr -= 2;
+
+         addr &= ~ (addressT) ((1 << op->shift) - 1);
+         val -= addr;
+
+         /* Make sure the section winds up with the alignment we have
+             assumed.  */
+         if (op->shift > 0)
+           record_alignment (asec, op->shift);
+       }
+
+      if (ext
+         && (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)
+             || RELAX_MIPS16_DSLOT (fragp->fr_subtype)))
+       as_warn_where (fragp->fr_file, fragp->fr_line,
+                      "extended instruction in delay slot");
+
+      buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix);
+
+      if (target_big_endian)
+       insn = bfd_getb16 (buf);
+      else
+       insn = bfd_getl16 (buf);
+
+      mips16_immed (fragp->fr_file, fragp->fr_line, type, val,
+                   RELAX_MIPS16_USER_EXT (fragp->fr_subtype),
+                   small, ext, &insn, &use_extend, &extend);
+
+      if (use_extend)
+       {
+         md_number_to_chars (buf, 0xf000 | extend, 2);
+         fragp->fr_fix += 2;
+         buf += 2;
+       }
+
+      md_number_to_chars (buf, insn, 2);
+      fragp->fr_fix += 2;
+      buf += 2;
+    }
+  else
+    {
+      if (fragp->fr_opcode == NULL)
+       return;
+
+      old = RELAX_OLD (fragp->fr_subtype);
+      new = RELAX_NEW (fragp->fr_subtype);
+      fixptr = fragp->fr_literal + fragp->fr_fix;
+
+      if (new > 0)
+       memcpy (fixptr - old, fixptr, new);
+
+      fragp->fr_fix += new - old;
+    }
+}
+
+#ifdef OBJ_ELF
+
+/* This function is called after the relocs have been generated.
+   We've been storing mips16 text labels as odd.  Here we convert them
+   back to even for the convenience of the debugger.  */
 
-  old = RELAX_OLD (fragp->fr_subtype);
-  new = RELAX_NEW (fragp->fr_subtype);
-  fixptr = fragp->fr_literal + fragp->fr_fix;
+void
+mips_frob_file_after_relocs ()
+{
+  asymbol **syms;
+  unsigned int count, i;
 
-  if (new > 0)
-    memcpy (fixptr - old, fixptr, new);
+  if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+    return;
 
-  fragp->fr_fix += new - old;
+  syms = bfd_get_outsymbols (stdoutput);
+  count = bfd_get_symcount (stdoutput);
+  for (i = 0; i < count; i++, syms++)
+    {
+      if (elf_symbol (*syms)->internal_elf_sym.st_other == STO_MIPS16
+         && ((*syms)->value & 1) != 0)
+       {
+         (*syms)->value &= ~1;
+         /* If the symbol has an odd size, it was probably computed
+            incorrectly, so adjust that as well.  */
+         if ((elf_symbol (*syms)->internal_elf_sym.st_size & 1) != 0)
+           ++elf_symbol (*syms)->internal_elf_sym.st_size;
+       }
+    }
 }
 
+#endif
+
 /* This function is called whenever a label is defined.  It is used
    when handling branch delays; if a branch has a label, we assume we
    can not move it.  */
@@ -7734,34 +10390,19 @@ void
 mips_define_label (sym)
      symbolS *sym;
 {
-  insn_label = sym;
-}
+  struct insn_label_list *l;
 
-/* Decide whether a label is local.  This is called by LOCAL_LABEL.
-   In order to work with gcc when using mips-tfile, we must keep all
-   local labels.  However, in other cases, we want to discard them,
-   since they are useless.  */
-
-int
-mips_local_label (name)
-     const char *name;
-{
-#ifndef NO_ECOFF_DEBUGGING
-  if (ECOFF_DEBUGGING
-      && mips_debug != 0
-      && ! ecoff_debugging_seen)
+  if (free_insn_labels == NULL)
+    l = (struct insn_label_list *) xmalloc (sizeof *l);
+  else
     {
-      /* We were called with -g, but we didn't see any debugging
-         information.  That may mean that gcc is smuggling debugging
-         information through to mips-tfile, in which case we must
-         generate all local labels.  */
-      return 0;
+      l = free_insn_labels;
+      free_insn_labels = l->next;
     }
-#endif
-
-  /* Here it's OK to discard local labels.  */
 
-  return name[0] == '$';
+  l->label = sym;
+  l->next = insn_labels;
+  insn_labels = l;
 }
 \f
 #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)