* itbl-parse.y: Fix indentation mistakes from indent program.
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 9a5df7b0b6c6147fc98979941629471d397dd433..f22f8e8a33ed2776e30035a854e50b9f8c038c29 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-mips.c -- assemble code for a MIPS chip.
-   Copyright (C) 1993 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
@@ -18,8 +18,9 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with GAS; see the file COPYING.  If not, write to
-   the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+   along with GAS; see the file COPYING.  If not, write to the Free
+   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
 
 #include "as.h"
 #include "config.h"
 
 #include <ctype.h>
 
-#ifndef __STDC__
-#ifndef NO_STDARG
-#define NO_STDARG
+#ifdef USE_STDARG
+#include <stdarg.h>
 #endif
+#ifdef USE_VARARGS
+#include <varargs.h>
 #endif
 
-#ifndef NO_STDARG
-#include <stdarg.h>
+#include "opcode/mips.h"
+#include "itbl-ops.h"
+
+#ifdef DEBUG
+#define DBG(x) printf x
 #else
-#ifndef NO_VARARGS
-#include <varargs.h>
-#endif /* NO_VARARGS */
-#endif /* NO_STDARG */
+#define DBG(x)
+#endif
 
-#include "opcode/mips.h"
+#ifdef OBJ_MAYBE_ELF
+/* Clean up namespace so we can include obj-elf.h too.  */
+static int mips_output_flavor () { return OUTPUT_FLAVOR; }
+#undef OBJ_PROCESS_STAB
+#undef OUTPUT_FLAVOR
+#undef S_GET_ALIGN
+#undef S_GET_SIZE
+#undef S_SET_ALIGN
+#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
+
+#include "obj-elf.h"
+/* Fix any of them that we actually care about.  */
+#undef OUTPUT_FLAVOR
+#define OUTPUT_FLAVOR mips_output_flavor()
+#endif
 
-#ifdef OBJ_ELF
+#if defined (OBJ_ELF)
 #include "elf/mips.h"
+#endif
+
+#ifndef ECOFF_DEBUGGING
+#define NO_ECOFF_DEBUGGING
+#define ECOFF_DEBUGGING 0
+#endif
 
+#include "ecoff.h"
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 static char *mips_regmask_frag;
 #endif
 
 #define AT  1
+#define TREG 24
 #define PIC_CALL_REG 25
+#define KT0 26
+#define KT1 27
 #define GP  28
 #define SP  29
 #define FP  30
 #define RA  31
 
-/* Decide whether to do GP reference optimizations based on the object
-   file format.  */
-#undef GPOPT
-#ifdef OBJ_ECOFF
-#define GPOPT
-#endif
-#ifdef OBJ_ELF
-#define GPOPT
-#endif
+#define ILLEGAL_REG (32)
 
-/* The default target format to use.  */
-#ifdef OBJ_AOUT
-#ifdef TARGET_BYTES_BIG_ENDIAN
-#define DEFAULT_TARGET_FORMAT "a.out-mips-big"
-#else
-#define DEFAULT_TARGET_FORMAT "a.out-mips-little"
-#endif
-#endif /* OBJ_AOUT */
-#ifdef OBJ_ECOFF
-#ifdef TARGET_BYTES_BIG_ENDIAN
-#define DEFAULT_TARGET_FORMAT "ecoff-bigmips"
-#else
-#define DEFAULT_TARGET_FORMAT "ecoff-littlemips"
-#endif
-#endif /* OBJ_ECOFF */
-#ifdef OBJ_ELF
-#ifdef TARGET_BYTES_BIG_ENDIAN
-#define DEFAULT_TARGET_FORMAT "elf32-bigmips"
-#else
-#define DEFAULT_TARGET_FORMAT "elf32-littlemips"
-#endif
-#endif /* OBJ_ELF */
+extern int target_big_endian;
+
+/* 1 is we should use the 64 bit MIPS ELF ABI, 0 if we should use the
+   32 bit ABI.  This has no meaning for ECOFF.  */
+static int mips_64;
 
-const char *mips_target_format = DEFAULT_TARGET_FORMAT;
+/* The default target format to use.  */
+const char *
+mips_target_format ()
+{
+  switch (OUTPUT_FLAVOR)
+    {
+    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";
+    case bfd_target_elf_flavour:
+      return (target_big_endian
+             ? (mips_64 ? "elf64-bigmips" : "elf32-bigmips")
+             : (mips_64 ? "elf64-littlemips" : "elf32-littlemips"));
+    default:
+      abort ();
+    }
+}
 
 /* The name of the readonly data section.  */
-#ifdef OBJ_AOUT
-#define RDATA_SECTION_NAME ".data"
-#endif
-#ifdef OBJ_ECOFF
-#define RDATA_SECTION_NAME ".rdata"
-#endif
-#ifdef OBJ_ELF
-#define RDATA_SECTION_NAME ".rodata"
-#endif
+#define RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \
+                           ? ".data" \
+                           : OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
+                           ? ".rdata" \
+                           : OUTPUT_FLAVOR == bfd_target_elf_flavour \
+                           ? ".rodata" \
+                           : (abort (), ""))
 
 /* These variables are filled in with the masks of registers used.
    The object format code reads them and puts them in the appropriate
@@ -115,9 +140,35 @@ 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;
 
+/* Whether the 4650 instructions (mad/madu) are permitted.  */
+static int mips_4650 = -1;
+
+/* Whether the 4010 instructions are permitted.  */
+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.  */
 
 enum mips_pic_level
@@ -141,10 +192,17 @@ enum mips_pic_level
 
 static enum mips_pic_level mips_pic;
 
+/* 1 if we should generate 32 bit offsets from the GP register in
+   SVR4_PIC mode.  Currently has no meaning in other modes.  */
+static int mips_big_got;
+
 /* 1 if trap instructions should used for overflow rather than break
    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;
@@ -152,19 +210,33 @@ static int mips_nomove;
 static int mips_noat;
 static int mips_nobopt;
 
-#ifdef GPOPT
 /* The size of the small data section.  */
 static int g_switch_value = 8;
 /* Whether the -G option was used.  */
 static int g_switch_seen = 0;
-#endif
 
 #define N_RMASK 0xc4
 #define N_VFP   0xd4
 
+/* If we can determine in advance that GP optimization won't be
+   possible, we can skip the relaxation stuff that tries to produce
+   GP-relative references.  This makes delay slot optimization work
+   better.
+
+   This function can only provide a guess, but it seems to work for
+   gcc output.  If it guesses wrong, the only loss should be in
+   efficiency; it shouldn't introduce any bugs.
+
+   I don't know if a fix is needed for the SVR4_PIC mode.  I've only
+   fixed it for the non-PIC mode.  KR 95/04/07  */
+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[] = "#";
@@ -196,13 +268,8 @@ const char FLT_CHARS[] = "rRsSfFdDxXpP";
 
 static char *insn_error;
 
-static int byte_order = 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
@@ -222,6 +289,10 @@ static int mips_frame_reg = SP;
    insert NOPs.  */
 static int mips_optimize = 2;
 
+/* Debugging level.  -g sets this to 2.  -gN sets this to N.  -g0 is
+   equivalent to seeing no -g option at all.  */
+static int mips_debug = 0;
+
 /* The previous instruction.  */
 static struct mips_cl_insn prev_insn;
 
@@ -241,6 +312,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;
 
@@ -250,9 +324,73 @@ 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
+   order for the linker to handle carries correctly, it must be able
+   to locate both the HI and the LO relocation.  This means that the
+   relocations must appear in order in the relocation table.
+
+   In order to implement this, we keep track of each unmatched HI
+   relocation.  We then sort them so that they immediately precede the
+   corresponding LO relocation. */
+
+struct mips_hi_fixup
+{
+  /* Next HI fixup.  */
+  struct mips_hi_fixup *next;
+  /* This fixup.  */
+  fixS *fixp;
+  /* The section this fixup is in.  */
+  segT seg;
+};
+
+/* 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
@@ -317,18 +455,58 @@ static int prev_prev_insn_unreordered;
    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_RELOC1(i) ((((i) >> 9) & 0x7f) - 64)
-#define RELAX_RELOC2(i) ((((i) >> 2) & 0x7f) - 64)
+#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.  */
 
@@ -339,65 +517,69 @@ static int prev_prev_insn_unreordered;
 #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));
-static void mips_no_prev_insn PARAMS ((void));
-static void mips_emit_delays PARAMS ((void));
+                                bfd_reloc_code_real_type r,
+                                boolean));
+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,
                                 ...));
+#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));
 static void check_absolute_expr PARAMS ((struct mips_cl_insn * ip,
                                         expressionS *));
-static void load_register PARAMS ((int *counter, int reg, expressionS * ep));
+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));
 static void mips_align PARAMS ((int to, int fill, symbolS *label));
 static void s_align PARAMS ((int));
-static void s_stringer PARAMS ((int));
 static void s_change_sec PARAMS ((int));
 static void s_cons PARAMS ((int));
-static void s_err PARAMS ((int));
-static void s_extern PARAMS ((int));
 static void s_float_cons PARAMS ((int));
 static void s_mips_globl PARAMS ((int));
 static void s_option PARAMS ((int));
 static void s_mipsset PARAMS ((int));
-static void s_mips_space PARAMS ((int));
 static void s_abicalls PARAMS ((int));
 static void s_cpload PARAMS ((int));
 static void s_cprestore PARAMS ((int));
 static void s_gpword PARAMS ((int));
 static void s_cpadd PARAMS ((int));
-#ifndef ECOFF_DEBUGGING
+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));
-#if 0
-static void s_frame PARAMS ((int));
-static void s_loc PARAMS ((int));
-static void s_mask PARAMS ((char));
-#endif
-#endif
-#ifdef OBJ_ELF
-static void s_elf_section PARAMS ((int));
-#endif
+static int mips16_extended_frag PARAMS ((fragS *, asection *, long));
 \f
 /* Pseudo-op table.
 
@@ -417,7 +599,7 @@ static void s_elf_section PARAMS ((int));
    they are not currently supported: .asm0, .endr, .lab, .repeat,
    .struct, .weakext.  */
 
-const pseudo_typeS md_pseudo_table[] =
+static const pseudo_typeS mips_pseudo_table[] =
 {
  /* MIPS specific pseudo-ops.  */
   {"option", s_option, 0},
@@ -425,15 +607,16 @@ const pseudo_typeS md_pseudo_table[] =
   {"rdata", s_change_sec, 'r'},
   {"sdata", s_change_sec, 's'},
   {"livereg", s_ignore, 0},
-  { "abicalls", s_abicalls, 0},
-  { "cpload", s_cpload, 0},
-  { "cprestore", s_cprestore, 0},
-  { "gpword", s_gpword, 0},
-  { "cpadd", s_cpadd, 0},
+  {"abicalls", s_abicalls, 0},
+  {"cpload", s_cpload, 0},
+  {"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.  */
-  {"asciiz", s_stringer, 1},
+  {"asciiz", stringer, 1},
   {"bss", s_change_sec, 'b'},
   {"err", s_err, 0},
   {"half", s_cons, 1},
@@ -442,12 +625,9 @@ const pseudo_typeS md_pseudo_table[] =
  /* These pseudo-ops are defined in read.c, but must be overridden
      here for one reason or another.  */
   {"align", s_align, 0},
-  {"ascii", s_stringer, 0},
-  {"asciz", s_stringer, 1},
   {"byte", s_cons, 0},
   {"data", s_change_sec, 'd'},
   {"double", s_float_cons, 'd'},
-  {"extern", s_extern, 0},
   {"float", s_float_cons, 'f'},
   {"globl", s_mips_globl, 0},
   {"global", s_mips_globl, 0},
@@ -458,11 +638,12 @@ const pseudo_typeS md_pseudo_table[] =
   {"quad", s_cons, 3},
   {"short", s_cons, 1},
   {"single", s_float_cons, 'f'},
-  {"space", s_mips_space, 0},
   {"text", s_change_sec, 't'},
   {"word", s_cons, 2},
+  { 0 },
+};
 
-#ifndef ECOFF_DEBUGGING
+static const pseudo_typeS mips_nonecoff_pseudo_table[] = {
  /* These pseudo-ops should be defined by the object file format.
     However, a.out doesn't support them, so we have versions here.  */
   {"aent", s_ent, 1},
@@ -476,31 +657,63 @@ const pseudo_typeS md_pseudo_table[] =
   {"loc", s_ignore, 0},
   {"mask", s_ignore, 'R'},
   {"verstamp", s_ignore, 0},
-#endif
+  { 0 },
+};
 
-#ifdef OBJ_ELF
-  /* We need to tweak the ELF ".section" pseudo-op a bit.  */
-  {"section", s_elf_section, 0},
-#endif
+extern void pop_insert PARAMS ((const pseudo_typeS *));
 
- /* Sentinel.  */
-  {NULL}
-};
+void
+mips_pop_insert ()
+{
+  pop_insert (mips_pseudo_table);
+  if (! ECOFF_DEBUGGING)
+    pop_insert (mips_nonecoff_pseudo_table);
+}
 \f
-const relax_typeS md_relax_table[] =
+/* Symbols labelling the current insn.  */
+
+struct insn_label_list
 {
-  { 0 }
+  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
+   mips_ip.  */
+
 static expressionS imm_expr;
 static expressionS offset_expr;
+
+/* Relocs associated with imm_expr and offset_expr.  */
+
 static bfd_reloc_code_real_type imm_reloc;
 static bfd_reloc_code_real_type offset_reloc;
 
-/* FIXME: This should be handled in a different way.  */
-extern int target_big_endian;
+/* This is set by mips_ip if imm_reloc is an unmatched HI16_S 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
@@ -561,6 +774,74 @@ md_begin ()
          if (mips_cpu == -1)
            mips_cpu = 4600;
        }
+      else if (strcmp (cpu, "r4650") == 0)
+       {
+         mips_isa = 3;
+         if (mips_cpu == -1)
+           mips_cpu = 4650;
+         if (mips_4650 == -1)
+           mips_4650 = 1;
+       }
+      else if (strcmp (cpu, "mips64vr4300") == 0)
+       {
+         mips_isa = 3;
+         if (mips_cpu == -1)
+           mips_cpu = 4300;
+       }
+      else if (strcmp (cpu, "mips64vr4100") == 0)
+        {
+          mips_isa = 3;
+          if (mips_cpu == -1)
+            mips_cpu = 4100;
+          if (mips_4100 == -1)
+            mips_4100 = 1;
+        }
+      else if (strcmp (cpu, "r4010") == 0)
+       {
+         mips_isa = 2;
+         if (mips_cpu == -1)
+           mips_cpu = 4010;
+         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)
+       {
+         mips_isa = 4;
+         if (mips_cpu == -1)
+           mips_cpu = 8000;
+       }
+      else if (strcmp (cpu, "r10000") == 0)
+       {
+         mips_isa = 4;
+         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;
@@ -572,6 +853,39 @@ 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;
+
+  if (mips_4010 < 0)
+    mips_4010 = 0;
+
+  if (mips_4100 < 0)
+    mips_4100 = 0;
+
+  /* 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
+    cop_interlocks = 0;
+
   if (mips_isa < 2 && mips_trap)
     as_bad ("trap exception not supported at ISA 1");
 
@@ -586,6 +900,9 @@ md_begin ()
     case 3:
       ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 4000);
       break;
+    case 4:
+      ok = bfd_set_arch_mach (stdoutput, bfd_arch_mips, 8000);
+      break;
     }
   if (! ok)
     as_warn ("Could not set architecture and machine");
@@ -620,7 +937,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;
@@ -631,61 +997,100 @@ md_begin ()
   /* set the default alignment for the text section (2**2) */
   record_alignment (text_section, 2);
 
-  /* FIXME: This should be handled in a different way.  */
-  target_big_endian = byte_order == BIG_ENDIAN;
+  if (USE_GLOBAL_POINTER_OPT)
+    bfd_set_gp_size (stdoutput, g_switch_value);
 
-#ifdef GPOPT
-  bfd_set_gp_size (stdoutput, g_switch_value);
-#endif
+  if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+    {
+      /* 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);
 
+
+           (void) bfd_set_section_flags (stdoutput, sec, flags);
+           (void) bfd_set_section_alignment (stdoutput, sec, 2);
+       
 #ifdef OBJ_ELF
-  /* 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);
+           mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
+#endif
+         }
+       else
+         {
+           /* 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, flags);
+           (void) bfd_set_section_alignment (stdoutput, sec, 3);
 
-  /* Create a .reginfo section for register masks and a .mdebug
-     section for debugging information.  */
-  {
-    segT seg;
-    subsegT subseg;
-    segT sec;
-
-    seg = now_seg;
-    subseg = now_subseg;
-    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_alignment (stdoutput, sec, 2);
-
-    mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
-
-#ifdef ECOFF_DEBUGGING
-    sec = subseg_new (".mdebug", (subsegT) 0);
-    (void) bfd_set_section_flags (stdoutput, sec,
-                                 SEC_HAS_CONTENTS | SEC_READONLY);
-    (void) bfd_set_section_alignment (stdoutput, sec, 2);
+#ifdef OBJ_ELF
+           /* Set up the option header.  */
+           {
+             Elf_Internal_Options opthdr;
+             char *f;
+
+             opthdr.kind = ODK_REGINFO;
+             opthdr.size = (sizeof (Elf_External_Options)
+                            + sizeof (Elf64_External_RegInfo));
+             opthdr.section = 0;
+             opthdr.info = 0;
+             f = frag_more (sizeof (Elf_External_Options));
+             bfd_mips_elf_swap_options_out (stdoutput, &opthdr,
+                                            (Elf_External_Options *) f);
+
+             mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo));
+           }
 #endif
+         }
 
-    subseg_set (seg, subseg);
-  }
-#endif /* OBJ_ELF */
+       if (ECOFF_DEBUGGING)
+         {
+           sec = subseg_new (".mdebug", (subsegT) 0);
+           (void) bfd_set_section_flags (stdoutput, sec,
+                                         SEC_HAS_CONTENTS | SEC_READONLY);
+           (void) bfd_set_section_alignment (stdoutput, sec, 2);
+         }
 
-#ifndef ECOFF_DEBUGGING
-  md_obj_begin ();
-#endif
+       subseg_set (seg, subseg);
+      }
+    }
+
+  if (! ECOFF_DEBUGGING)
+    md_obj_begin ();
 }
 
 void
 md_mips_end ()
 {
-#ifndef ECOFF_DEBUGGING
-  md_obj_end ();
-#endif
+  if (! ECOFF_DEBUGGING)
+    md_obj_end ();
 }
 
 void
@@ -695,44 +1100,68 @@ md_assemble (str)
   struct mips_cl_insn insn;
 
   imm_expr.X_op = O_absent;
+  imm_reloc = BFD_RELOC_UNUSED;
+  imm_unmatched_hi = false;
   offset_expr.X_op = O_absent;
+  offset_reloc = BFD_RELOC_UNUSED;
+
+  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));
+    }
 
-  mips_ip (str, &insn);
   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
     {
       if (imm_expr.X_op != O_absent)
-       append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc);
+       append_insn ((char *) NULL, &insn, &imm_expr, imm_reloc,
+                    imm_unmatched_hi);
       else if (offset_expr.X_op != O_absent)
-       append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc);
+       append_insn ((char *) NULL, &insn, &offset_expr, offset_reloc, false);
       else
-       append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED);
+       append_insn ((char *) NULL, &insn, NULL, BFD_RELOC_UNUSED, false);
     }
 }
 
-/* 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
@@ -749,7 +1178,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)
@@ -758,6 +1187,58 @@ 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;
+}
+
+/* This function returns true if modifying a register requires a
+   delay.  */
+
+static int
+reg_needs_delay (reg)
+     int reg;
+{
+  unsigned long prev_pinfo;
+
+  prev_pinfo = prev_insn.insn_mo->pinfo;
+  if (! mips_noreorder
+      && mips_isa < 4
+      && ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
+         || (mips_isa < 2
+             && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))))
+    {
+      /* A load from a coprocessor or from memory.  All load
+        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;
+    }
 
   return 0;
 }
@@ -768,22 +1249,46 @@ insn_uses_reg (ip, reg, fpr)
    used with RELOC_TYPE.  */
 
 static void
-append_insn (place, ip, address_expr, reloc_type)
+append_insn (place, ip, address_expr, reloc_type, unmatched_hi)
      char *place;
      struct mips_cl_insn *ip;
      expressionS *address_expr;
      bfd_reloc_code_real_type reloc_type;
+     boolean unmatched_hi;
 {
   register unsigned long prev_pinfo, pinfo;
   char *f;
   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.
@@ -805,29 +1310,39 @@ append_insn (place, ip, address_expr, reloc_type)
         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 ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
-         || (mips_isa < 2
-             && (prev_pinfo & INSN_LOAD_MEMORY_DELAY)))
+      if (! mips16
+         && mips_isa < 4
+         && (((prev_pinfo & INSN_LOAD_COPROC_DELAY)
+               && ! cop_interlocks)
+             || (mips_isa < 2
+                 && (prev_pinfo & INSN_LOAD_MEMORY_DELAY))))
        {
          /* A load from a coprocessor or from memory.  All load
             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 ((prev_pinfo & INSN_COPROC_MOVE_DELAY)
-              || (mips_isa < 2
-                  && (prev_pinfo & INSN_COPROC_MEMORY_DELAY)))
+      else if (! mips16
+              && mips_isa < 4
+              && (((prev_pinfo & INSN_COPROC_MOVE_DELAY)
+                    && ! cop_interlocks)
+                  || (mips_isa < 2
+                      && (prev_pinfo & INSN_COPROC_MEMORY_DELAY))))
        {
          /* A generic coprocessor delay.  The previous instruction
             modified a coprocessor general or control register.  If
@@ -843,13 +1358,16 @@ append_insn (place, ip, address_expr, reloc_type)
             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)
@@ -858,7 +1376,7 @@ append_insn (place, ip, address_expr, reloc_type)
                  || insn_uses_reg (ip,
                                    ((prev_insn.insn_opcode >> OP_SH_FS)
                                     & OP_MASK_FS),
-                                   1))
+                                   MIPS_FP_REG))
                ++nops;
            }
          else
@@ -869,6 +1387,7 @@ append_insn (place, ip, address_expr, reloc_type)
                 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)))
@@ -877,13 +1396,17 @@ append_insn (place, ip, address_expr, reloc_type)
                ++nops;
            }
        }
-      else if (prev_pinfo & INSN_WRITE_COND_CODE)
+      else if (! mips16
+              && mips_isa < 4
+              && (prev_pinfo & INSN_WRITE_COND_CODE)
+               && ! cop_interlocks)
        {
          /* The previous instruction sets the coprocessor condition
             codes, but does not require a general coprocessor delay
             (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;
@@ -892,70 +1415,185 @@ append_insn (place, ip, address_expr, reloc_type)
        {
          /* The previous instruction reads the LO register; if the
             current instruction writes to the LO register, we must
-            insert two NOPS.  */
-         if (mips_optimize == 0
-             || (pinfo & INSN_WRITE_LO))
+            insert two NOPS.  Some newer processors have interlocks.  */
+         if (! interlocks
+             && (mips_optimize == 0
+                 || (pinfo & INSN_WRITE_LO)))
            nops += 2;
        }
       else if (prev_insn.insn_mo->pinfo & INSN_READ_HI)
        {
          /* The previous instruction reads the HI register; if the
             current instruction writes to the HI register, we must
-            insert a NOP.  */
-         if (mips_optimize == 0
-             || (pinfo & INSN_WRITE_HI))
+            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.  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
-         && (((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))
-             || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
-                 && (pinfo & INSN_WRITE_LO))
-             || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
-                 && (pinfo & INSN_WRITE_HI))))
+        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 ();
 
          for (i = 0; i < nops; i++)
            emit_nop ();
+
          if (listing)
-           listing_prev_line ();
-         if (insn_label != NULL)
            {
-             assert (S_GET_SEGMENT (insn_label) == now_seg);
-             insn_label->sy_frag = frag_now;
-             S_SET_VALUE (insn_label, (valueT) frag_now_fix ());
+             listing_prev_line ();
+             /* We may be at the start of a variant frag.  In case we
+                 are, make sure there is enough space for the frag
+                 after the frags created by listing_prev_line.  The
+                 argument to frag_grow here must be at least as large
+                 as the argument to all other calls to frag_grow in
+                 this file.  We don't have to worry about being in the
+                 middle of a variant frag, because the variants insert
+                 all needed nop instructions themselves.  */
+             frag_grow (40);
+           }
+
+         for (l = insn_labels; l != NULL; l = l->next)
+           {
+             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
+         if (ECOFF_DEBUGGING)
+           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), (long) 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)
        {
@@ -970,6 +1608,22 @@ append_insn (place, ip, address_expr, reloc_type)
              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;
 
@@ -979,41 +1633,103 @@ append_insn (place, ip, address_expr, reloc_type)
        }
       else
        {
-         assert (reloc_type != BFD_RELOC_UNUSED);
        need_reloc:
          /* Don't generate a reloc if we are writing into a variant
             frag.  */
          if (place == NULL)
-           fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
-                               address_expr,
-                               reloc_type == BFD_RELOC_16_PCREL_S2,
-                               reloc_type);
+           {
+             fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+                                 address_expr,
+                                 reloc_type == BFD_RELOC_16_PCREL_S2,
+                                 reloc_type);
+             if (unmatched_hi)
+               {
+                 struct mips_hi_fixup *hi_fixup;
+
+                 assert (reloc_type == BFD_RELOC_HI16_S);
+                 hi_fixup = ((struct mips_hi_fixup *)
+                             xmalloc (sizeof (struct mips_hi_fixup)));
+                 hi_fixup->fixp = fixp;
+                 hi_fixup->seg = now_seg;
+                 hi_fixup->next = mips_hi_fixup_list;
+                 mips_hi_fixup_list = hi_fixup;
+               }
+           }
        }
     }
 
-  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_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)
     {
@@ -1060,10 +1776,13 @@ append_insn (place, ip, address_expr, reloc_type)
                 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
@@ -1072,19 +1791,28 @@ append_insn (place, ip, address_expr, reloc_type)
                   bc1t LABEL
                 we can not swap, and I don't feel like handling that
                 case.  */
-             || (pinfo & INSN_READ_COND_CODE)
+             || (! 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.  */
-             || (prev_pinfo
-                 & (INSN_LOAD_COPROC_DELAY
-                    | INSN_COPROC_MOVE_DELAY
-                    | INSN_WRITE_COND_CODE
-                    | INSN_READ_LO
-                    | INSN_READ_HI))
-             || (mips_isa < 2
+             || (! 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)))
+             || (! interlocks
+                 && (prev_pinfo
+                     & (INSN_READ_LO
+                        | INSN_READ_HI)))
+             || (! 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
@@ -1097,20 +1825,51 @@ append_insn (place, ip, address_expr, reloc_type)
              || (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)))
@@ -1118,7 +1877,8 @@ append_insn (place, ip, address_expr, reloc_type)
                          && (((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)))
@@ -1126,32 +1886,63 @@ append_insn (place, ip, address_expr, reloc_type)
                          && (((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.  */
-             || (((prev_prev_insn.insn_mo->pinfo & INSN_LOAD_COPROC_DELAY)
-                  || (mips_isa < 2
-                      && (prev_prev_insn.insn_mo->pinfo
-                          & INSN_LOAD_MEMORY_DELAY)))
+             || (! 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
+                             & INSN_LOAD_MEMORY_DELAY)))
                  && 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
@@ -1164,24 +1955,53 @@ append_insn (place, ip, address_expr, reloc_type)
            }
          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;
@@ -1195,6 +2015,10 @@ append_insn (place, ip, address_expr, reloc_type)
              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)
        {
@@ -1206,6 +2030,9 @@ append_insn (place, ip, address_expr, reloc_type)
          /* 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
        {
@@ -1220,82 +2047,182 @@ append_insn (place, ip, address_expr, reloc_type)
             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;
-
-      nop = 0;
-      if ((prev_insn.insn_mo->pinfo
-          & (INSN_LOAD_COPROC_DELAY
-             | INSN_COPROC_MOVE_DELAY
-             | INSN_WRITE_COND_CODE
-             | INSN_READ_LO
-             | INSN_READ_HI))
-         || (mips_isa < 2
+      int nops;
+
+      nops = 0;
+      if ((! mips16
+          && mips_isa < 4
+          && (! cop_interlocks
+               && (prev_insn.insn_mo->pinfo
+                   & (INSN_LOAD_COPROC_DELAY
+                      | INSN_COPROC_MOVE_DELAY
+                      | INSN_WRITE_COND_CODE))))
+         || (! interlocks
+             && (prev_insn.insn_mo->pinfo
+                 & (INSN_READ_LO
+                    | INSN_READ_HI)))
+         || (! mips16
+             && mips_isa < 2
              && (prev_insn.insn_mo->pinfo
                  & (INSN_LOAD_MEMORY_DELAY
                     | INSN_COPROC_MEMORY_DELAY))))
        {
-         nop = 1;
-         if ((prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
-             || (prev_insn.insn_mo->pinfo & INSN_READ_HI)
-             || (prev_insn.insn_mo->pinfo & INSN_READ_LO))
-           emit_nop ();
+          /* 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))))
+           ++nops;
+
+         if (prev_insn_unreordered)
+           nops = 0;
        }
-      else if ((prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
-              || (prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
-              || (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))
-       nop = 1;
-      if (nop)
+      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))))
        {
-         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
@@ -1303,7 +2230,7 @@ mips_emit_delays ()
    expression, the name of the instruction to build, an operand format
    string, and corresponding arguments.  */
 
-#ifndef NO_STDARG
+#ifdef USE_STDARG
 static void
 macro_build (char *place,
             int *counter,
@@ -1311,7 +2238,7 @@ macro_build (char *place,
             const char *name,
             const char *fmt,
             ...)
-#else /* ! defined (NO_STDARG) */
+#else
 static void
 macro_build (place, counter, ep, name, fmt, va_alist)
      char *place;
@@ -1320,13 +2247,13 @@ macro_build (place, counter, ep, name, fmt, va_alist)
      const char *name;
      const char *fmt;
      va_dcl
-#endif /* ! defined (NO_STDARG) */
+#endif
 {
   struct mips_cl_insn insn;
   bfd_reloc_code_real_type r;
   va_list args;
 
-#ifndef NO_STDARG
+#ifdef USE_STDARG
   va_start (args, fmt);
 #else
   va_start (args);
@@ -1343,13 +2270,37 @@ 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);
   assert (strcmp (name, insn.insn_mo->name) == 0);
 
   while (strcmp (fmt, insn.insn_mo->args) != 0
-        || insn.insn_mo->pinfo == INSN_MACRO)
+        || insn.insn_mo->pinfo == INSN_MACRO
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_ISA2
+            && mips_isa < 2)
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_ISA3
+            && mips_isa < 3)
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_ISA4
+            && mips_isa < 4)
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4650
+            && ! mips_4650)
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4010
+            && ! mips_4010)
+        || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_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);
@@ -1421,6 +2372,8 @@ macro_build (place, counter, ep, name, fmt, va_alist)
                  || r == BFD_RELOC_LO16
                  || r == BFD_RELOC_MIPS_GOT16
                  || r == BFD_RELOC_MIPS_CALL16
+                 || r == BFD_RELOC_MIPS_GOT_LO16
+                 || r == BFD_RELOC_MIPS_CALL_LO16
                  || (ep->X_op == O_subtract
                      && now_seg == text_section
                      && r == BFD_RELOC_PCREL_LO16));
@@ -1432,7 +2385,9 @@ macro_build (place, counter, ep, name, fmt, va_alist)
                  && (ep->X_op == O_constant
                      || (ep->X_op == O_symbol
                          && (r == BFD_RELOC_HI16_S
-                             || r == BFD_RELOC_HI16))
+                             || r == BFD_RELOC_HI16
+                             || r == BFD_RELOC_MIPS_GOT_HI16
+                             || r == BFD_RELOC_MIPS_CALL_HI16))
                      || (ep->X_op == O_subtract
                          && now_seg == text_section
                          && r == BFD_RELOC_PCREL_HI16_S)));
@@ -1475,7 +2430,133 @@ macro_build (place, counter, ep, name, fmt, va_alist)
   va_end (args);
   assert (r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
 
-  append_insn (place, &insn, ep, r);
+  append_insn (place, &insn, ep, r, false);
+}
+
+static void
+mips16_macro_build (place, counter, ep, name, fmt, args)
+     char *place;
+     int *counter;
+     expressionS *ep;
+     const char *name;
+     const char *fmt;
+     va_list args;
+{
+  struct mips_cl_insn insn;
+  bfd_reloc_code_real_type r;
+
+  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);
+
+  while (strcmp (fmt, insn.insn_mo->args) != 0
+        || insn.insn_mo->pinfo == INSN_MACRO)
+    {
+      ++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);
 }
 
 /*
@@ -1494,12 +2575,14 @@ macro_build_lui (place, counter, ep, regnum)
   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 = 0;
+      high_expr.X_add_number = ep->X_add_number;
     }
 
   if (high_expr.X_op == O_constant)
@@ -1540,10 +2623,10 @@ macro_build_lui (place, counter, ep, regnum)
   if (r == BFD_RELOC_UNUSED)
     {
       insn.insn_opcode |= high_expr.X_add_number;
-      append_insn (place, &insn, NULL, r);
+      append_insn (place, &insn, NULL, r, false);
     }
   else
-    append_insn (place, &insn, &high_expr, r);
+    append_insn (place, &insn, &high_expr, r, false);
 }
 
 /*                     set_at()
@@ -1562,7 +2645,7 @@ set_at (counter, reg, unsignedp)
                 "t,r,j", AT, reg, (int) BFD_RELOC_LO16);
   else
     {
-      load_register (counter, AT, &imm_expr);
+      load_register (counter, AT, &imm_expr, 0);
       macro_build ((char *) NULL, counter, NULL,
                   unsignedp ? "sltu" : "slt",
                   "d,v,t", AT, reg, AT);
@@ -1580,23 +2663,105 @@ 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.
  */
 static void
-load_register (counter, reg, ep)
+load_register (counter, reg, ep, dbl)
      int *counter;
      int reg;
      expressionS *ep;
+     int dbl;
 {
-  int shift;
+  int freg;
   expressionS hi32, lo32;
 
   if (ep->X_op != O_big)
     {
       assert (ep->X_op == O_constant);
-      if (ep->X_add_number >= -0x8000 && ep->X_add_number < 0x8000)
+      if (ep->X_add_number < 0x8000
+         && (ep->X_add_number >= 0
+             || (ep->X_add_number >= -0x8000
+                 && (! dbl
+                     || ! ep->X_unsigned
+                     || sizeof (ep->X_add_number) > 4))))
        {
          /* We can handle 16 bit signed values with an addiu to
             $zero.  No need to ever use daddiu here, since $zero and
@@ -1613,9 +2778,15 @@ load_register (counter, reg, ep)
                       (int) BFD_RELOC_LO16);
          return;
        }
-      else if ((ep->X_add_number &~ (offsetT) 0x7fffffff) == 0
-              || ((ep->X_add_number &~ (offsetT) 0x7fffffff)
-                  == ~ (offsetT) 0x7fffffff))
+      else if ((((ep->X_add_number &~ (offsetT) 0x7fffffff) == 0
+                || ((ep->X_add_number &~ (offsetT) 0x7fffffff)
+                    == ~ (offsetT) 0x7fffffff))
+               && (! 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))
        {
          /* 32 bit values require an lui.  */
          macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
@@ -1640,11 +2811,9 @@ load_register (counter, reg, ep)
   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;
     }
@@ -1661,25 +2830,175 @@ load_register (counter, reg, ep)
       hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16);
     }
 
-  load_register (counter, reg, &hi32);
+  if (hi32.X_add_number == 0)
+    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);
+              return;
+            }
+          if (lo32.X_add_number & 0x80000000)
+            {
+              macro_build ((char *) NULL, counter, &lo32, "lui", "t,u", reg,
+                           (int) BFD_RELOC_HI16);
+             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;
+    }
   if ((lo32.X_add_number & 0xffff0000) == 0)
-    macro_build ((char *) NULL, counter, NULL, "dsll32", "d,w,<", reg,
-                reg, 0);
+    {
+      if (freg != 0)
+       {
+         macro_build ((char *) NULL, counter, NULL, "dsll32", "d,w,<", reg,
+                      freg, 0);
+         freg = reg;
+       }
+    }
   else
     {
       expressionS mid16;
 
-      macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
-                  reg, 16);
+      if ((freg == 0) && (lo32.X_add_number == 0xffffffff))
+        {
+         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, 0);
+          return;
+        }
+
+      if (freg != 0)
+       {
+         macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
+                      freg, 16);
+         freg = reg;
+       }
       mid16 = lo32;
       mid16.X_add_number >>= 16;
       macro_build ((char *) NULL, counter, &mid16, "ori", "t,r,i", reg,
-                  reg, (int) BFD_RELOC_LO16);
+                  freg, (int) BFD_RELOC_LO16);
       macro_build ((char *) NULL, counter, NULL, "dsll", "d,w,<", reg,
                   reg, 16);
+      freg = reg;
     }
   if ((lo32.X_add_number & 0xffff) != 0)
-    macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, reg,
+    macro_build ((char *) NULL, counter, &lo32, "ori", "t,r,i", reg, freg,
                 (int) BFD_RELOC_LO16);
 }
 
@@ -1702,7 +3021,7 @@ load_address (counter, reg, ep)
 
   if (ep->X_op == O_constant)
     {
-      load_register (counter, reg, ep);
+      load_register (counter, reg, ep, 0);
       return;
     }
 
@@ -1714,7 +3033,8 @@ load_address (counter, reg, ep)
           lui          $reg,<sym>              (BFD_RELOC_HI16_S)
           addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
         If we have an addend, we always use the latter form.  */
-      if (ep->X_add_number != 0)
+      if ((valueT) ep->X_add_number >= MAX_GPREL_OFFSET
+          || nopic_need_relax (ep->X_add_symbol))
        p = NULL;
       else
        {
@@ -1733,7 +3053,7 @@ load_address (counter, reg, ep)
                   mips_isa < 3 ? "addiu" : "daddiu",
                   "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
     }
-  else if (mips_pic == SVR4_PIC)
+  else if (mips_pic == SVR4_PIC && ! mips_big_got)
     {
       expressionS ex;
 
@@ -1762,7 +3082,69 @@ load_address (counter, reg, ep)
          if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
            as_bad ("PIC code offset overflow (max 16 signed bits)");
          ex.X_op = O_constant;
-         macro_build (p, counter, &ex,
+         macro_build ((char *) NULL, counter, &ex,
+                      mips_isa < 3 ? "addiu" : "daddiu",
+                      "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+       }
+    }
+  else if (mips_pic == SVR4_PIC)
+    {
+      expressionS ex;
+      int off;
+
+      /* This is the large GOT case.  If this is a reference to an
+        external symbol, we want
+          lui          $reg,<sym>              (BFD_RELOC_MIPS_GOT_HI16)
+          addu         $reg,$reg,$gp
+          lw           $reg,<sym>($reg)        (BFD_RELOC_MIPS_GOT_LO16)
+        Otherwise, for a reference to a local symbol, we want
+          lw           $reg,<sym>($gp)         (BFD_RELOC_MIPS_GOT16)
+          nop
+          addiu        $reg,$reg,<sym>         (BFD_RELOC_LO16)
+        If there is a constant, it must be added in after.  */
+      ex.X_add_number = ep->X_add_number;
+      ep->X_add_number = 0;
+      if (reg_needs_delay (GP))
+       off = 4;
+      else
+       off = 0;
+      frag_grow (32);
+      macro_build ((char *) NULL, counter, ep, "lui", "t,u", reg,
+                  (int) BFD_RELOC_MIPS_GOT_HI16);
+      macro_build ((char *) NULL, counter, (expressionS *) NULL,
+                  mips_isa < 3 ? "addu" : "daddu",
+                  "d,v,t", reg, reg, GP);
+      macro_build ((char *) NULL, counter, ep,
+                  mips_isa < 3 ? "lw" : "ld",
+                  "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT_LO16, reg);
+      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);
+      if (off > 0)
+       {
+         /* We need a nop before loading from $gp.  This special
+             check is required because the lui which starts the main
+             instruction stream does not refer to $gp, and so will not
+             insert the nop which may be required.  */
+         macro_build (p, counter, (expressionS *) NULL, "nop", "");
+         p += 4;
+       }
+      macro_build (p, counter, ep,
+                  mips_isa < 3 ? "lw" : "ld",
+                  "t,o(b)", reg, (int) BFD_RELOC_MIPS_GOT16, GP);
+      p += 4;
+      macro_build (p, counter, (expressionS *) NULL, "nop", "");
+      p += 4;
+      macro_build (p, counter, ep,
+                  mips_isa < 3 ? "addiu" : "daddiu",
+                  "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
+      if (ex.X_add_number != 0)
+       {
+         if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+           as_bad ("PIC code offset overflow (max 16 signed bits)");
+         ex.X_op = O_constant;
+         macro_build ((char *) NULL, counter, &ex,
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", reg, reg, (int) BFD_RELOC_LO16);
        }
@@ -1814,11 +3196,15 @@ macro (ip)
   int likely = 0;
   int dbl = 0;
   int coproc = 0;
+  int lr = 0;
   offsetT maxnum;
+  int off;
   bfd_reloc_code_real_type r;
   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;
@@ -1839,7 +3225,7 @@ macro (ip)
         sub v0,$zero,$a0
         */
 
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
 
@@ -1880,7 +3266,7 @@ macro (ip)
                       (int) BFD_RELOC_LO16);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, dbl);
       macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
       break;
 
@@ -1915,7 +3301,7 @@ macro (ip)
          return;
        }
 
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, NULL, s2, "d,v,t", treg, sreg, AT);
       break;
 
@@ -1939,7 +3325,7 @@ macro (ip)
                       0);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, &offset_expr, s, "s,t,p", sreg, AT);
       break;
 
@@ -2297,7 +3683,7 @@ macro (ip)
          return;
        }
 
-      mips_emit_delays ();
+      mips_emit_delays (true);
       ++mips_noreorder;
       mips_any_noreorder = 1;
       macro_build ((char *) NULL, &icnt, NULL,
@@ -2416,7 +3802,7 @@ macro (ip)
          return;
        }
 
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, dbl);
       macro_build ((char *) NULL, &icnt, NULL, s, "z,s,t", sreg, AT);
       macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg);
       break;
@@ -2437,7 +3823,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);
@@ -2454,6 +3840,8 @@ macro (ip)
       macro_build ((char *) NULL, &icnt, NULL, s2, "d", dreg);
       return;
 
+    case M_DLA_AB:
+      dbl = 1;
     case M_LA_AB:
       /* Load the address of a symbol into a register.  If breg is not
         zero, we then add a base register to it.  */
@@ -2505,7 +3893,7 @@ macro (ip)
        }
 
       if (offset_expr.X_op == O_constant)
-       load_register (&icnt, tempreg, &offset_expr);
+       load_register (&icnt, tempreg, &offset_expr, dbl);
       else if (mips_pic == NO_PIC)
        {
          /* If this is a reference to an GP relative symbol, we want
@@ -2515,7 +3903,8 @@ macro (ip)
               addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
             If we have a constant, we need two instructions anyhow,
             so we may as well always use the latter form.  */
-         if (offset_expr.X_add_number != 0)
+         if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+             || nopic_need_relax (offset_expr.X_add_symbol))
            p = NULL;
          else
            {
@@ -2536,7 +3925,7 @@ macro (ip)
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
        }
-      else if (mips_pic == SVR4_PIC)
+      else if (mips_pic == SVR4_PIC && ! mips_big_got)
        {
          /* If this is a reference to an external symbol, and there
             is no constant, we want
@@ -2568,7 +3957,7 @@ macro (ip)
          offset_expr.X_add_number = 0;
          frag_grow (32);
          macro_build ((char *) NULL, &icnt, &offset_expr,
-                      mips_isa < 3 ? "lw" : "ld",
+                      dbl ? "ld" : "lw",
                       "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
          if (expr1.X_add_number == 0)
            {
@@ -2662,6 +4051,214 @@ macro (ip)
              used_at = 1;
            }
        }
+      else if (mips_pic == SVR4_PIC)
+       {
+         int gpdel;
+
+         /* This is the large GOT case.  If this is a reference to an
+            external symbol, and there is no constant, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+            For a local symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+            If we have a small constant, and this is a reference to
+            an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              nop
+              addiu    $tempreg,$tempreg,<constant>
+            For a local symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<constant> (BFD_RELOC_LO16)
+
+            If we have a large constant, and this is a reference to
+            an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>
+              addu     $tempreg,$tempreg,$at
+            For a local symbol, we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              lui      $at,<hiconstant>
+              addiu    $at,$at,<loconstant>    (BFD_RELOC_LO16)
+              addu     $tempreg,$tempreg,$at
+            */
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         frag_grow (52);
+         if (reg_needs_delay (GP))
+           gpdel = 4;
+         else
+           gpdel = 0;
+         macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                      tempreg, (int) BFD_RELOC_MIPS_GOT_HI16);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                      mips_isa < 3 ? "addu" : "daddu",
+                      "d,v,t", tempreg, tempreg, GP);
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      dbl ? "ld" : "lw",
+                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16,
+                      tempreg);
+         if (expr1.X_add_number == 0)
+           {
+             int off;
+
+             if (breg == 0)
+               off = 0;
+             else
+               {
+                 /* We're going to put in an addu instruction using
+                    tempreg, so we may as well insert the nop right
+                    now.  */
+                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                              "nop", "");
+                 off = 4;
+               }
+
+             p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
+                           RELAX_ENCODE (12 + off, 12 + gpdel, gpdel,
+                                         8 + gpdel, 0,
+                                         (breg == 0
+                                          ? mips_warn_about_macros
+                                          : 0)),
+                           offset_expr.X_add_symbol, (long) 0,
+                           (char *) NULL);
+           }
+         else if (expr1.X_add_number >= -0x8000
+                  && expr1.X_add_number < 0x8000)
+           {
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          "nop", "");
+             macro_build ((char *) NULL, &icnt, &expr1,
+                          mips_isa < 3 ? "addiu" : "daddiu",
+                          "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+
+             p = frag_var (rs_machine_dependent, 12 + gpdel, 0,
+                           RELAX_ENCODE (20, 12 + gpdel, gpdel, 8 + gpdel, 0,
+                                         (breg == 0
+                                          ? mips_warn_about_macros
+                                          : 0)),
+                           offset_expr.X_add_symbol, (long) 0,
+                           (char *) NULL);
+           }
+         else
+           {
+             int adj, dreg;
+
+             /* If we are going to add in a base register, and the
+                target register and the base register are the same,
+                then we are using AT as a temporary register.  Since
+                we want to load the constant into AT, we add our
+                current AT (from the global offset table) and the
+                register into the register now, and pretend we were
+                not using a base register.  */
+             if (breg != treg)
+               {
+                 adj = 0;
+                 dreg = tempreg;
+               }
+             else
+               {
+                 assert (tempreg == AT);
+                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                              "nop", "");
+                 macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                              mips_isa < 3 ? "addu" : "daddu",
+                              "d,v,t", treg, AT, breg);
+                 dreg = treg;
+                 adj = 8;
+               }
+
+             /* Set mips_optimize around the lui instruction to avoid
+                inserting an unnecessary nop after the lw.  */
+             hold_mips_optimize = mips_optimize;
+             mips_optimize = 2;
+             macro_build_lui ((char *) NULL, &icnt, &expr1, AT);
+             mips_optimize = hold_mips_optimize;
+
+             macro_build ((char *) NULL, &icnt, &expr1,
+                          mips_isa < 3 ? "addiu" : "daddiu",
+                          "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", dreg, dreg, AT);
+
+             p = frag_var (rs_machine_dependent, 16 + gpdel + adj, 0,
+                           RELAX_ENCODE (24 + adj, 16 + gpdel + adj, gpdel,
+                                         8 + gpdel, 0,
+                                         (breg == 0
+                                          ? mips_warn_about_macros
+                                          : 0)),
+                           offset_expr.X_add_symbol, (long) 0,
+                           (char *) NULL);
+
+             used_at = 1;
+           }
+
+         if (gpdel > 0)
+           {
+             /* This is needed because this instruction uses $gp, but
+                 the first instruction on the main stream does not.  */
+             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+             p += 4;
+           }
+         macro_build (p, &icnt, &offset_expr,
+                      dbl ? "ld" : "lw",
+                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
+         p += 4;
+         if (expr1.X_add_number >= -0x8000
+             && expr1.X_add_number < 0x8000)
+           {
+             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+             p += 4;
+             macro_build (p, &icnt, &expr1,
+                          mips_isa < 3 ? "addiu" : "daddiu",
+                          "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+             /* FIXME: If add_number is 0, and there was no base
+                 register, the external symbol case ended with a load,
+                 so if the symbol turns out to not be external, and
+                 the next instruction uses tempreg, an unnecessary nop
+                 will be inserted.  */
+           }
+         else
+           {
+             if (breg == treg)
+               {
+                 /* We must add in the base register now, as in the
+                     external symbol case.  */
+                 assert (tempreg == AT);
+                 macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+                 p += 4;
+                 macro_build (p, &icnt, (expressionS *) NULL,
+                              mips_isa < 3 ? "addu" : "daddu",
+                              "d,v,t", treg, AT, breg);
+                 p += 4;
+                 tempreg = treg;
+                 /* We set breg to 0 because we have arranged to add
+                     it in in both cases.  */
+                 breg = 0;
+               }
+
+             macro_build_lui (p, &icnt, &expr1, AT);
+             p += 4;
+             macro_build (p, &icnt, &expr1,
+                          mips_isa < 3 ? "addiu" : "daddiu",
+                          "t,r,j", AT, AT, (int) BFD_RELOC_LO16);
+             p += 4;
+             macro_build (p, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", tempreg, tempreg, AT);
+             p += 4;
+           }
+       }
       else if (mips_pic == EMBEDDED_PIC)
        {
          /* We use
@@ -2732,30 +4329,78 @@ macro (ip)
        macro_build ((char *) NULL, &icnt, &offset_expr, "jal", "a");
       else if (mips_pic == SVR4_PIC)
        {
-         /* If this is a reference to an external symbol, we want
+         /* If this is a reference to an external symbol, and we are
+            using a small GOT, we want
               lw       $25,<sym>($gp)          (BFD_RELOC_MIPS_CALL16)
               nop
               jalr     $25
               nop
               lw       $gp,cprestore($sp)
             The cprestore value is set using the .cprestore
-            pseudo-op.  If the symbol is not external, we want
+            pseudo-op.  If we are using a big GOT, we want
+              lui      $25,<sym>               (BFD_RELOC_MIPS_CALL_HI16)
+              addu     $25,$25,$gp
+              lw       $25,<sym>($25)          (BFD_RELOC_MIPS_CALL_LO16)
+              nop
+              jalr     $25
+              nop
+              lw       $gp,cprestore($sp)
+            If the symbol is not external, we want
               lw       $25,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
               nop
               addiu    $25,$25,<sym>           (BFD_RELOC_LO16)
               jalr     $25
               nop
-              lw       $gp,cprestore($sp)
-            */
-         frag_grow (20);
-         macro_build ((char *) NULL, &icnt, &offset_expr,
-                      mips_isa < 3 ? "lw" : "ld",
-                      "t,o(b)", PIC_CALL_REG,
-                      (int) BFD_RELOC_MIPS_CALL16, GP);
-         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, (char *) NULL);
+              lw $gp,cprestore($sp) */
+         frag_grow (40);
+         if (! mips_big_got)
+           {
+             macro_build ((char *) NULL, &icnt, &offset_expr,
+                          mips_isa < 3 ? "lw" : "ld",
+                          "t,o(b)", PIC_CALL_REG,
+                          (int) BFD_RELOC_MIPS_CALL16, GP);
+             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, (char *) NULL);
+           }
+         else
+           {
+             int gpdel;
+
+             if (reg_needs_delay (GP))
+               gpdel = 4;
+             else
+               gpdel = 0;
+             macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                          PIC_CALL_REG, (int) BFD_RELOC_MIPS_CALL_HI16);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", PIC_CALL_REG, PIC_CALL_REG, GP);
+             macro_build ((char *) NULL, &icnt, &offset_expr,
+                          mips_isa < 3 ? "lw" : "ld",
+                          "t,o(b)", PIC_CALL_REG,
+                          (int) BFD_RELOC_MIPS_CALL_LO16, PIC_CALL_REG);
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          "nop", "");
+             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);
+             if (gpdel > 0)
+               {
+                 macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+                 p += 4;
+               }
+             macro_build (p, &icnt, &offset_expr,
+                          mips_isa < 3 ? "lw" : "ld",
+                          "t,o(b)", PIC_CALL_REG,
+                          (int) BFD_RELOC_MIPS_GOT16, GP);
+             p += 4;
+             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+             p += 4;
+           }                      
          macro_build (p, &icnt, &offset_expr,
                       mips_isa < 3 ? "addiu" : "daddiu",
                       "t,r,j", PIC_CALL_REG, PIC_CALL_REG,
@@ -2805,43 +4450,54 @@ 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:
       s = "lwl";
+      lr = 1;
       goto ld;
     case M_LWR_AB:
       s = "lwr";
+      lr = 1;
       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:
       s = "ldl";
+      lr = 1;
       goto ld;
     case M_LDR_AB:
       s = "ldr";
+      lr = 1;
       goto ld;
     case M_LL_AB:
       s = "ll";
@@ -2852,7 +4508,7 @@ macro (ip)
     case M_LWU_AB:
       s = "lwu";
     ld:
-      if (breg == treg || coproc)
+      if (breg == treg || coproc || lr)
        {
          tempreg = AT;
          used_at = 1;
@@ -2874,18 +4530,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:
@@ -2903,13 +4563,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:
@@ -2921,6 +4584,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
@@ -2965,7 +4629,8 @@ macro (ip)
             With a constant we always use the latter case.  */
          if (breg == 0)
            {
-             if (offset_expr.X_add_number != 0)
+             if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+                 || nopic_need_relax (offset_expr.X_add_symbol))
                p = NULL;
              else
                {
@@ -2988,7 +4653,8 @@ macro (ip)
            }
          else
            {
-             if (offset_expr.X_add_number != 0)
+             if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+                 || nopic_need_relax (offset_expr.X_add_symbol))
                p = NULL;
              else
                {
@@ -3015,7 +4681,7 @@ macro (ip)
                           (int) BFD_RELOC_LO16, tempreg);
            }
        }
-      else if (mips_pic == SVR4_PIC)
+      else if (mips_pic == SVR4_PIC && ! mips_big_got)
        {
          /* If this is a reference to an external symbol, we want
               lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
@@ -3057,6 +4723,70 @@ macro (ip)
          macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
                       (int) BFD_RELOC_LO16, tempreg);
        }
+      else if (mips_pic == SVR4_PIC)
+       {
+         int gpdel;
+
+         /* If this is a reference to an external symbol, we want
+              lui      $tempreg,<sym>          (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $tempreg,$tempreg,$gp
+              lw       $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+              <op>     $treg,0($tempreg)
+            Otherwise we want
+              lw       $tempreg,<sym>($gp)     (BFD_RELOC_MIPS_GOT16)
+              nop
+              addiu    $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+              <op>     $treg,0($tempreg)
+            If there is a base register, we add it to $tempreg before
+            the <op>.  If there is a constant, we stick it in the
+            <op> instruction.  We don't handle constants larger than
+            16 bits, because we have no way to load the upper 16 bits
+            (actually, we could handle them for the subset of cases
+            in which we are not using $at).  */
+         assert (offset_expr.X_op == O_symbol);
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000)
+           as_bad ("PIC code offset overflow (max 16 signed bits)");
+         if (reg_needs_delay (GP))
+           gpdel = 4;
+         else
+           gpdel = 0;
+         frag_grow (36);
+         macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                      tempreg, (int) BFD_RELOC_MIPS_GOT_HI16);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                      mips_isa < 3 ? "addu" : "daddu",
+                      "d,v,t", tempreg, tempreg, GP);
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      mips_isa < 3 ? "lw" : "ld",
+                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT_LO16,
+                      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);
+         if (gpdel > 0)
+           {
+             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+             p += 4;
+           }
+         macro_build (p, &icnt, &offset_expr,
+                      mips_isa < 3 ? "lw" : "ld",
+                      "t,o(b)", tempreg, (int) BFD_RELOC_MIPS_GOT16, GP);
+         p += 4;
+         macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+         p += 4;
+         macro_build (p, &icnt, &offset_expr,
+                      mips_isa < 3 ? "addiu" : "daddiu",
+                      "t,r,j", tempreg, tempreg, (int) BFD_RELOC_LO16);
+         if (breg != 0)
+           macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                        mips_isa < 3 ? "addu" : "daddu",
+                        "d,v,t", tempreg, tempreg, breg);
+         macro_build ((char *) NULL, &icnt, &expr1, s, fmt, treg,
+                      (int) BFD_RELOC_LO16, tempreg);
+       }
       else if (mips_pic == EMBEDDED_PIC)
        {
          /* If there is no base register, we want
@@ -3091,13 +4821,17 @@ macro (ip)
 
     case M_LI:
     case M_LI_S:
-      load_register (&icnt, treg, &imm_expr);
+      load_register (&icnt, treg, &imm_expr, 0);
+      return;
+
+    case M_DLI:
+      load_register (&icnt, treg, &imm_expr, 1);
       return;
 
     case M_LI_SS:
       if (imm_expr.X_op == O_constant)
        {
-         load_register (&icnt, AT, &imm_expr);
+         load_register (&icnt, AT, &imm_expr, 0);
          macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
                       "mtc1", "t,G", AT, treg);
          break;
@@ -3219,13 +4953,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
@@ -3249,6 +4983,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)
        {
@@ -3269,6 +5004,7 @@ macro (ip)
 
       s = "swc1";
       fmt = "T,o(b)";
+      /* Itbl support may require additional care here. */
       coproc = 1;
       goto ldd_std;
 
@@ -3304,7 +5040,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
@@ -3324,7 +5061,8 @@ macro (ip)
             If there is a base register, we add it to $at after the
             lui instruction.  If there is a constant, we always use
             the last case.  */
-         if (offset_expr.X_add_number != 0)
+         if ((valueT) offset_expr.X_add_number >= MAX_GPREL_OFFSET
+             || nopic_need_relax (offset_expr.X_add_symbol))
            {
              p = NULL;
              used_at = 1;
@@ -3351,6 +5089,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);
@@ -3360,6 +5099,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);
@@ -3393,6 +5133,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);
@@ -3400,11 +5141,12 @@ 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);
        }         
-      else if (mips_pic == SVR4_PIC)
+      else if (mips_pic == SVR4_PIC && ! mips_big_got)
        {
          int off;
 
@@ -3440,6 +5182,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);
@@ -3449,6 +5192,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);
@@ -3459,40 +5203,146 @@ macro (ip)
                           offset_expr.X_add_symbol, (long) 0,
                           (char *) NULL);
        }
-      else if (mips_pic == EMBEDDED_PIC)
+      else if (mips_pic == SVR4_PIC)
        {
-         /* If there is no base register, we use
-              <op>     $treg,<sym>($gp)        (BFD_RELOC_MIPS_GPREL)
-              <op>     $treg+1,<sym>+4($gp)    (BFD_RELOC_MIPS_GPREL)
-            If we have a base register, we use
-              addu     $at,$breg,$gp
-              <op>     $treg,<sym>($at)        (BFD_RELOC_MIPS_GPREL)
-              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_MIPS_GPREL)
-            */
-         if (breg == 0)
-           {
-             tempreg = GP;
-             used_at = 0;
-           }
-         else
-           {
-             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
-                          mips_isa < 3 ? "addu" : "daddu",
-                          "d,v,t", AT, breg, GP);
-             tempreg = AT;
-             used_at = 1;
-           }
+         int gpdel, off;
 
-         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;
-         macro_build ((char *) NULL, &icnt, &offset_expr, s, fmt,
-                      coproc ? treg : treg + 1,
-                      (int) BFD_RELOC_MIPS_GPREL, tempreg);
-       }
-      else
-       abort ();
+         /* If this is a reference to an external symbol, we want
+              lui      $at,<sym>               (BFD_RELOC_MIPS_GOT_HI16)
+              addu     $at,$at,$gp
+              lw       $at,<sym>($at)          (BFD_RELOC_MIPS_GOT_LO16)
+              nop
+              <op>     $treg,0($at)
+              <op>     $treg+1,4($at)
+            Otherwise we want
+              lw       $at,<sym>($gp)          (BFD_RELOC_MIPS_GOT16)
+              nop
+              <op>     $treg,<sym>($at)        (BFD_RELOC_LO16)
+              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_LO16)
+            If there is a base register we add it to $at before the
+            lwc1 instructions.  If there is a constant we include it
+            in the lwc1 instructions.  */
+         used_at = 1;
+         expr1.X_add_number = offset_expr.X_add_number;
+         offset_expr.X_add_number = 0;
+         if (expr1.X_add_number < -0x8000
+             || expr1.X_add_number >= 0x8000 - 4)
+           as_bad ("PIC code offset overflow (max 16 signed bits)");
+         if (reg_needs_delay (GP))
+           gpdel = 4;
+         else
+           gpdel = 0;
+         if (breg == 0)
+           off = 0;
+         else
+           off = 4;
+         frag_grow (56);
+         macro_build ((char *) NULL, &icnt, &offset_expr, "lui", "t,u",
+                      AT, (int) BFD_RELOC_MIPS_GOT_HI16);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                      mips_isa < 3 ? "addu" : "daddu",
+                      "d,v,t", AT, AT, GP);
+         macro_build ((char *) NULL, &icnt, &offset_expr,
+                      mips_isa < 3 ? "lw" : "ld",
+                      "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT_LO16, AT);
+         macro_build ((char *) NULL, &icnt, (expressionS *) NULL, "nop", "");
+         if (breg != 0)
+           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);
+         expr1.X_add_number += 4;
+
+         /* Set mips_optimize to 2 to avoid inserting an undesired
+             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);
+         mips_optimize = hold_mips_optimize;
+         expr1.X_add_number -= 4;
+
+         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,
+                       (char *) NULL);
+         if (gpdel > 0)
+           {
+             macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+             p += 4;
+           }
+         macro_build (p, &icnt, &offset_expr,
+                      mips_isa < 3 ? "lw" : "ld",
+                      "t,o(b)", AT, (int) BFD_RELOC_MIPS_GOT16, GP);
+         p += 4;
+         macro_build (p, &icnt, (expressionS *) NULL, "nop", "");
+         p += 4;
+         if (breg != 0)
+           {
+             macro_build (p, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "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);
+         p += 4;
+         expr1.X_add_number += 4;
+
+         /* Set mips_optimize to 2 to avoid inserting an undesired
+             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);
+         mips_optimize = hold_mips_optimize;
+       }
+      else if (mips_pic == EMBEDDED_PIC)
+       {
+         /* If there is no base register, we use
+              <op>     $treg,<sym>($gp)        (BFD_RELOC_MIPS_GPREL)
+              <op>     $treg+1,<sym>+4($gp)    (BFD_RELOC_MIPS_GPREL)
+            If we have a base register, we use
+              addu     $at,$breg,$gp
+              <op>     $treg,<sym>($at)        (BFD_RELOC_MIPS_GPREL)
+              <op>     $treg+1,<sym>+4($at)    (BFD_RELOC_MIPS_GPREL)
+            */
+         if (breg == 0)
+           {
+             tempreg = GP;
+             used_at = 0;
+           }
+         else
+           {
+             macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                          mips_isa < 3 ? "addu" : "daddu",
+                          "d,v,t", AT, breg, GP);
+             tempreg = AT;
+             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);
+       }
+      else
+       abort ();
 
       if (! used_at)
        return;
@@ -3512,8 +5362,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, ""), immed_expr)
+        {
+         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;
     }
@@ -3537,6 +5438,8 @@ macro2 (ip)
   int likely = 0;
   int dbl = 0;
   int coproc = 0;
+  int lr = 0;
+  int off;
   offsetT maxnum;
   bfd_reloc_code_real_type r;
   char *p;
@@ -3570,7 +5473,7 @@ macro2 (ip)
       /* The MIPS assembler some times generates shifts and adds.  I'm
         not trying to be that fancy. GCC should do this for us
         anyway.  */
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, dbl);
       macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dmult" : "mult",
                   "s,t", sreg, AT);
@@ -3580,7 +5483,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,
@@ -3607,7 +5510,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,
@@ -3637,9 +5540,9 @@ macro2 (ip)
 
     case M_ROL_I:
       macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", AT, sreg,
-                  imm_expr.X_add_number & 0x1f);
+                  (int) (imm_expr.X_add_number & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", dreg, sreg,
-                  (0 - imm_expr.X_add_number) & 0x1f);
+                  (int) ((0 - imm_expr.X_add_number) & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
@@ -3653,9 +5556,9 @@ macro2 (ip)
 
     case M_ROR_I:
       macro_build ((char *) NULL, &icnt, NULL, "srl", "d,w,<", AT, sreg,
-                  imm_expr.X_add_number & 0x1f);
+                  (int) (imm_expr.X_add_number & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", dreg, sreg,
-                  (0 - imm_expr.X_add_number) & 0x1f);
+                  (int) ((0 - imm_expr.X_add_number) & 0x1f));
       macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", dreg, dreg, AT);
       break;
 
@@ -3664,11 +5567,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;
 
@@ -3719,7 +5622,7 @@ macro2 (ip)
        }
       else
        {
-         load_register (&icnt, AT, &imm_expr);
+         load_register (&icnt, AT, &imm_expr, 0);
          macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
                       sreg, AT);
          used_at = 1;
@@ -3752,7 +5655,7 @@ macro2 (ip)
        }
       else
        {
-         load_register (&icnt, AT, &imm_expr);
+         load_register (&icnt, AT, &imm_expr, 0);
          macro_build ((char *) NULL, &icnt, NULL,
                       mask == M_SGE_I ? "slt" : "sltu",
                       "d,v,t", dreg, sreg, AT);
@@ -3779,7 +5682,7 @@ macro2 (ip)
     case M_SGTU_I:
       s = "sltu";
     sgti:
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg);
       break;
 
@@ -3800,7 +5703,7 @@ macro2 (ip)
     case M_SLEU_I:
       s = "sltu";
     slei:
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, NULL, s, "d,v,t", dreg, AT, sreg);
       macro_build ((char *) NULL, &icnt, &expr1, "xori", "t,r,i", dreg, dreg,
                   (int) BFD_RELOC_LO16);
@@ -3813,7 +5716,7 @@ macro2 (ip)
                       dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, NULL, "slt", "d,v,t", dreg, sreg, AT);
       break;
 
@@ -3824,7 +5727,7 @@ macro2 (ip)
                       dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, NULL, "sltu", "d,v,t", dreg, sreg,
                   AT);
       break;
@@ -3877,7 +5780,7 @@ macro2 (ip)
        }
       else
        {
-         load_register (&icnt, AT, &imm_expr);
+         load_register (&icnt, AT, &imm_expr, 0);
          macro_build ((char *) NULL, &icnt, NULL, "xor", "d,v,t", dreg,
                       sreg, AT);
          used_at = 1;
@@ -3898,7 +5801,7 @@ macro2 (ip)
                       "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, dbl);
       macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dsub" : "sub",
                   "d,v,t", dreg, sreg, AT);
@@ -3915,7 +5818,7 @@ macro2 (ip)
                       "t,r,j", dreg, sreg, (int) BFD_RELOC_LO16);
          return;
        }
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, dbl);
       macro_build ((char *) NULL, &icnt, NULL,
                   dbl ? "dsubu" : "subu",
                   "d,v,t", dreg, sreg, AT);
@@ -3939,7 +5842,7 @@ macro2 (ip)
     case M_TNE_I:
       s = "tne";
     trap:
-      load_register (&icnt, AT, &imm_expr);
+      load_register (&icnt, AT, &imm_expr, 0);
       macro_build ((char *) NULL, &icnt, NULL, s, "s,t", sreg, AT);
       break;
 
@@ -3953,7 +5856,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);
@@ -3983,11 +5886,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;
@@ -3997,69 +5900,92 @@ macro2 (ip)
       macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg, treg, AT);
       break;
 
+    case M_ULD:
+      s = "ldl";
+      s2 = "ldr";
+      off = 7;
+      goto ulw;
     case M_ULW:
-      if (offset_expr.X_add_number >= 0x7ffd)
+      s = "lwl";
+      s2 = "lwr";
+      off = 3;
+    ulw:
+      if (offset_expr.X_add_number >= 0x8000 - off)
        as_bad ("operand overflow");
-      if (byte_order == LITTLE_ENDIAN)
-       offset_expr.X_add_number += 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "lwl", "t,o(b)", treg,
+      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)
-       offset_expr.X_add_number -= 3;
+      if (! target_big_endian)
+       offset_expr.X_add_number -= off;
       else
-       offset_expr.X_add_number += 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "lwr", "t,o(b)", treg,
+       offset_expr.X_add_number += off;
+      macro_build ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       return;
 
+    case M_ULD_A:
+      s = "ldl";
+      s2 = "ldr";
+      off = 7;
+      goto ulwa;
+    case M_ULW_A:
+      s = "lwl";
+      s2 = "lwr";
+      off = 3;
+    ulwa:
+      load_address (&icnt, AT, &offset_expr);
+      if (breg != 0)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                    mips_isa < 3 ? "addu" : "daddu",
+                    "d,v,t", AT, AT, breg);
+      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 (! target_big_endian)
+       expr1.X_add_number = 0;
+      else
+       expr1.X_add_number = off;
+      macro_build ((char *) NULL, &icnt, &expr1, s2, "t,o(b)", treg,
+                  (int) BFD_RELOC_LO16, AT);
+      break;
+
     case M_ULH_A:
     case M_ULHU_A:
-    case M_ULW_A:
       load_address (&icnt, AT, &offset_expr);
-      if (mask == M_ULW_A)
-       {
-         if (byte_order == LITTLE_ENDIAN)
-           expr1.X_add_number = 3;
-         else
-           expr1.X_add_number = 0;
-         macro_build ((char *) NULL, &icnt, &expr1, "lwl", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-         if (byte_order == LITTLE_ENDIAN)
-           expr1.X_add_number = 0;
-         else
-           expr1.X_add_number = 3;
-         macro_build ((char *) NULL, &icnt, &expr1, "lwr", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-       }
+      if (breg != 0)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                    mips_isa < 3 ? "addu" : "daddu",
+                    "d,v,t", AT, AT, breg);
+      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 (target_big_endian)
+       expr1.X_add_number = 1;
       else
-       {
-         if (byte_order == 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)
-           expr1.X_add_number = 1;
-         else
-           expr1.X_add_number = 0;
-         macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
-                      (int) BFD_RELOC_LO16, AT);
-         macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg,
-                      treg, 8);
-         macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg,
-                      treg, AT);
-       }
+       expr1.X_add_number = 0;
+      macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
+                  (int) BFD_RELOC_LO16, AT);
+      macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg,
+                  treg, 8);
+      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg,
+                  treg, AT);
       break;
 
     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;
@@ -4067,67 +5993,92 @@ macro2 (ip)
                   (int) BFD_RELOC_LO16, breg);
       break;
 
+    case M_USD:
+      s = "sdl";
+      s2 = "sdr";
+      off = 7;
+      goto usw;
     case M_USW:
-      if (offset_expr.X_add_number >= 0x7ffd)
+      s = "swl";
+      s2 = "swr";
+      off = 3;
+    usw:
+      if (offset_expr.X_add_number >= 0x8000 - off)
        as_bad ("operand overflow");
-      if (byte_order == LITTLE_ENDIAN)
-       offset_expr.X_add_number += 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "swl", "t,o(b)", treg,
+      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)
-       offset_expr.X_add_number -= 3;
+      if (! target_big_endian)
+       offset_expr.X_add_number -= off;
       else
-       offset_expr.X_add_number += 3;
-      macro_build ((char *) NULL, &icnt, &offset_expr, "swr", "t,o(b)", treg,
+       offset_expr.X_add_number += off;
+      macro_build ((char *) NULL, &icnt, &offset_expr, s2, "t,o(b)", treg,
                   (int) BFD_RELOC_LO16, breg);
       return;
 
-    case M_USH_A:
+    case M_USD_A:
+      s = "sdl";
+      s2 = "sdr";
+      off = 7;
+      goto uswa;
     case M_USW_A:
+      s = "swl";
+      s2 = "swr";
+      off = 3;
+    uswa:
       load_address (&icnt, AT, &offset_expr);
-      if (mask == M_USW_A)
-       {
-         if (byte_order == LITTLE_ENDIAN)
-           expr1.X_add_number = 3;
-         else
-           expr1.X_add_number = 0;
-         macro_build ((char *) NULL, &icnt, &expr1, "swl", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-         if (byte_order == LITTLE_ENDIAN)
-           expr1.X_add_number = 0;
-         else
-           expr1.X_add_number = 3;
-         macro_build ((char *) NULL, &icnt, &expr1, "swr", "t,o(b)", treg,
-                      (int) BFD_RELOC_LO16, AT);
-       }
+      if (breg != 0)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                    mips_isa < 3 ? "addu" : "daddu",
+                    "d,v,t", AT, AT, breg);
+      if (! target_big_endian)
+       expr1.X_add_number = off;
       else
-       {
-         if (byte_order == LITTLE_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)
-           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)
-           expr1.X_add_number = 0;
-         else
-           expr1.X_add_number = 1;
-         macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
-                      (int) BFD_RELOC_LO16, AT);
-         macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg,
-                      treg, 8);
-         macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg,
-                      treg, AT);
-       }
+       expr1.X_add_number = 0;
+      macro_build ((char *) NULL, &icnt, &expr1, s, "t,o(b)", treg,
+                  (int) BFD_RELOC_LO16, AT);
+      if (! target_big_endian)
+       expr1.X_add_number = 0;
+      else
+       expr1.X_add_number = off;
+      macro_build ((char *) NULL, &icnt, &expr1, s2, "t,o(b)", treg,
+                  (int) BFD_RELOC_LO16, AT);
+      break;
+
+    case M_USH_A:
+      load_address (&icnt, AT, &offset_expr);
+      if (breg != 0)
+       macro_build ((char *) NULL, &icnt, (expressionS *) NULL,
+                    mips_isa < 3 ? "addu" : "daddu",
+                    "d,v,t", AT, AT, breg);
+      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 (! 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 (! target_big_endian)
+       expr1.X_add_number = 0;
+      else
+       expr1.X_add_number = 1;
+      macro_build ((char *) NULL, &icnt, &expr1, "lbu", "t,o(b)", AT,
+                  (int) BFD_RELOC_LO16, AT);
+      macro_build ((char *) NULL, &icnt, NULL, "sll", "d,w,<", treg,
+                  treg, 8);
+      macro_build ((char *) NULL, &icnt, NULL, "or", "d,v,t", treg,
+                  treg, AT);
       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;
     }
@@ -4135,84 +6086,328 @@ macro2 (ip)
     as_warn ("Macro used $at after \".set noat\"");
 }
 
+/* Implement macros in mips16 mode.  */
 
-/*
-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 is an address expression.
-*/
 static void
-mips_ip (str, ip)
-     char *str;
+mips16_macro (ip)
      struct mips_cl_insn *ip;
 {
-  char *s;
-  const char *args;
-  char c;
-  struct mips_opcode *insn;
-  char *argsStart;
-  unsigned int regno;
-  unsigned int lastregno = 0;
-  char *s_reset;
+  int mask;
+  int xreg, yreg, zreg, tmp;
+  int icnt;
+  expressionS expr1;
+  int dbl;
+  const char *s, *s2, *s3;
 
-  insn_error = NULL;
+  mask = ip->insn_mo->mask;
 
-  for (s = str; islower (*s) || (*s >= '0' && *s <= '3') || *s == '.'; ++s)
-    continue;
-  switch (*s)
-    {
-    case '\0':
-      break;
+  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;
 
-    case ' ':
-      *s++ = '\0';
-      break;
+  icnt = 0;
 
-    default:
-      as_fatal ("Unknown opcode: `%s'", str);
-    }
-  if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL)
-    {
-      as_warn ("`%s' not in hash table.", str);
-      insn_error = "ERROR: Unrecognized opcode";
-      return;
-    }
-  argsStart = s;
-  for (;;)
-    {
-      int insn_isa;
+  expr1.X_op = O_constant;
+  expr1.X_op_symbol = NULL;
+  expr1.X_add_symbol = NULL;
+  expr1.X_add_number = 1;
 
-      assert (strcmp (insn->name, str) == 0);
+  dbl = 0;
 
-      if (insn->pinfo == INSN_MACRO)
-       insn_isa = insn->match;
-      else if (insn->pinfo & INSN_ISA2)
-       insn_isa = 2;
-      else if (insn->pinfo & INSN_ISA3)
-       insn_isa = 3;
-      else
-       insn_isa = 1;
+  switch (mask)
+    {
+    default:
+      internalError ();
 
-      if (insn_isa > mips_isa)
-       {
-         if (insn + 1 < &mips_opcodes[NUMOPCODES]
-             && strcmp (insn->name, insn[1].name) == 0)
-           {
-             ++insn;
-             continue;
-           }
-         as_warn ("Instruction not supported on this processor");
-       }
+    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;
 
-      ip->insn_mo = insn;
-      ip->insn_opcode = insn->match;
-      for (args = insn->args;; ++args)
-       {
-         if (*s == ' ')
-           ++s;
-         switch (*args)
-           {
-           case '\0':          /* end of args */
+    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
+   is an address expression.  */
+
+static void
+mips_ip (str, ip)
+     char *str;
+     struct mips_cl_insn *ip;
+{
+  char *s;
+  const char *args;
+  char c;
+  struct mips_opcode *insn;
+  char *argsStart;
+  unsigned int regno;
+  unsigned int lastregno = 0;
+  char *s_reset;
+
+  insn_error = NULL;
+
+  for (s = str; *s != '\0' && !isspace(*s); ++s)
+    continue;
+  if ( isspace(*s) )
+    *s++ = '\0';
+    
+  if ((insn = (struct mips_opcode *) hash_find (op_hash, str)) == NULL)
+    {
+      insn_error = "unrecognized opcode";
+      return;
+    }
+  argsStart = s;
+  for (;;)
+    {
+      int insn_isa;
+
+      assert (strcmp (insn->name, str) == 0);
+
+      if (insn->pinfo == INSN_MACRO)
+       insn_isa = insn->match;
+      else if ((insn->pinfo & INSN_ISA) == INSN_ISA2)
+       insn_isa = 2;
+      else if ((insn->pinfo & INSN_ISA) == INSN_ISA3)
+       insn_isa = 3;
+      else if ((insn->pinfo & INSN_ISA) == INSN_ISA4)
+       insn_isa = 4;
+      else
+       insn_isa = 1;
+
+      if (insn_isa > mips_isa
+         || (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)
+           {
+             ++insn;
+             continue;
+           }
+         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;
+       }
+
+      ip->insn_mo = insn;
+      ip->insn_opcode = insn->match;
+      for (args = insn->args;; ++args)
+       {
+         if (*s == ' ')
+           ++s;
+         switch (*args)
+           {
+           case '\0':          /* end of args */
              if (*s == '\0')
                return;
              break;
@@ -4286,15 +6481,20 @@ mips_ip (str, ip)
              continue;
 
            case 'k':           /* cache code */
+           case 'h':           /* prefx code */
              my_getExpression (&imm_expr, s);
              check_absolute_expr (ip, &imm_expr);
              if ((unsigned long) imm_expr.X_add_number > 31)
                {
-                 as_warn ("Invalid cahce opcode (%lu)",
+                 as_warn ("Invalid value for `%s' (%lu)",
+                          ip->insn_mo->name,
                           (unsigned long) imm_expr.X_add_number);
                  imm_expr.X_add_number &= 0x1f;
                }
-             ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CACHE;
+             if (*args == 'k')
+               ip->insn_opcode |= imm_expr.X_add_number << OP_SH_CACHE;
+             else
+               ip->insn_opcode |= imm_expr.X_add_number << OP_SH_PREFX;
              imm_expr.X_op = O_absent;
              s = expr_end;
              continue;
@@ -4387,10 +6587,47 @@ mips_ip (str, ip)
                          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 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 */
+                         if (r = itbl_get_reg_val(n), 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;
                    }
-                 if (regno == AT && ! mips_noat)
+                 if (regno == AT
+                     && ! mips_noat
+                     && *args != 'E'
+                     && *args != 'G')
                    as_warn ("Used $at without \".set noat\"");
                  c = *args;
                  if (*s == ' ')
@@ -4407,6 +6644,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':
@@ -4439,6 +6679,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;
@@ -4459,6 +6704,7 @@ mips_ip (str, ip)
            case 'D':           /* floating point destination register */
            case 'S':           /* floating point source register */
            case 'T':           /* floating point target register */
+           case 'R':           /* floating point source register */
            case 'V':
            case 'W':
              s_reset = s;
@@ -4479,10 +6725,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);
 
@@ -4510,6 +6758,10 @@ mips_ip (str, ip)
                    case 'W':
                    case 'T':
                      ip->insn_opcode |= regno << 16;
+                     break;
+                   case 'R':
+                     ip->insn_opcode |= regno << 21;
+                     break;
                    }
                  lastregno = regno;
                  continue;
@@ -4527,8 +6779,9 @@ mips_ip (str, ip)
 
            case 'I':
              my_getExpression (&imm_expr, s);
-             if (imm_expr.X_op != O_big)
-               check_absolute_expr (ip, &imm_expr);
+             if (imm_expr.X_op != O_big
+                 && imm_expr.X_op != O_constant)
+               insn_error = "absolute expression required";
              s = expr_end;
              continue;
 
@@ -4598,14 +6851,13 @@ mips_ip (str, ip)
 
                if (*args == 'f'
                    || (*args == 'l'
-#ifdef GPOPT
-                       && (mips_pic == EMBEDDED_PIC
+                       && (! USE_GLOBAL_POINTER_OPT
+                           || mips_pic == EMBEDDED_PIC
                            || g_switch_value < 4)
-#endif
                        ))
                  {
                    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)
@@ -4631,28 +6883,31 @@ mips_ip (str, ip)
                      default: /* unused default case avoids warnings.  */
                      case 'L':
                        newname = RDATA_SECTION_NAME;
-#ifdef GPOPT
-                       if (g_switch_value >= 8)
+                       if (USE_GLOBAL_POINTER_OPT && g_switch_value >= 8)
                          newname = ".lit8";
-#endif
                        break;
                      case 'F':
                        newname = RDATA_SECTION_NAME;
                        break;
                      case 'l':
-#ifdef GPOPT
-                       assert (g_switch_value >= 4);
-#endif
+                       assert (!USE_GLOBAL_POINTER_OPT
+                               || g_switch_value >= 4);
                        newname = ".lit4";
                        break;
                      }
                    new_seg = subseg_new (newname, (subsegT) 0);
-                   frag_align (*args == 'l' ? 2 : 3, 0);
-#ifdef OBJ_ELF
-                   record_alignment (new_seg, 4);
-#else
-                   record_alignment (new_seg, *args == 'l' ? 2 : 3);
-#endif
+                   if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+                     bfd_set_section_flags (stdoutput, new_seg,
+                                            (SEC_ALLOC
+                                             | SEC_LOAD
+                                             | SEC_READONLY
+                                             | SEC_DATA));
+                   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);
                    if (seg == now_seg)
                      as_bad ("Can't use floating point insn in this section");
 
@@ -4678,7 +6933,7 @@ mips_ip (str, ip)
            case 'j':           /* 16 bit signed immediate */
              imm_reloc = BFD_RELOC_LO16;
              c = my_getSmallExpression (&imm_expr, s);
-             if (c)
+             if (c != '\0')
                {
                  if (c != 'l')
                    {
@@ -4686,23 +6941,29 @@ mips_ip (str, ip)
                        imm_expr.X_add_number =
                          (imm_expr.X_add_number >> 16) & 0xffff;
                      else if (c == 'h')
-                       imm_reloc = BFD_RELOC_HI16_S;
+                       {
+                         imm_reloc = BFD_RELOC_HI16_S;
+                         imm_unmatched_hi = true;
+                       }
                      else
                        imm_reloc = BFD_RELOC_HI16;
                    }
                }
-             else if (imm_expr.X_op != O_big)
-               check_absolute_expr (ip, &imm_expr);
              if (*args == 'i')
                {
-                 if (imm_expr.X_op == O_big
-                     || imm_expr.X_add_number < 0
-                     || imm_expr.X_add_number >= 0x10000)
+                 if ((c == '\0' && imm_expr.X_op != O_constant)
+                     || ((imm_expr.X_add_number < 0
+                           || imm_expr.X_add_number >= 0x10000)
+                          && imm_expr.X_op == O_constant))
                    {
                      if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
                          !strcmp (insn->name, insn[1].name))
                        break;
-                     as_bad ("16 bit expression not in range 0..65535");
+                     if (imm_expr.X_op != O_constant
+                         && imm_expr.X_op != O_big)
+                       insn_error = "absolute expression required";
+                     else
+                       as_bad ("16 bit expression not in range 0..65535");
                    }
                }
              else
@@ -4725,13 +6986,23 @@ mips_ip (str, ip)
                    max = 0x8000;
                  else
                    max = 0x10000;
-                 if (imm_expr.X_op == O_big
-                     || imm_expr.X_add_number < -0x8000
-                     || imm_expr.X_add_number >= max)
+                 if ((c == '\0' && imm_expr.X_op != O_constant)
+                     || ((imm_expr.X_add_number < -0x8000
+                           || imm_expr.X_add_number >= max)
+                          && imm_expr.X_op == O_constant)
+                     || (more
+                         && imm_expr.X_add_number < 0
+                         && mips_isa >= 3
+                         && imm_expr.X_unsigned
+                         && sizeof (imm_expr.X_add_number) <= 4))
                    {
                      if (more)
                        break;
-                     as_bad ("16 bit expression not in range -32768..32767");
+                     if (imm_expr.X_op != O_constant
+                         && imm_expr.X_op != O_big)
+                       insn_error = "absolute expression required";
+                     else
+                       as_bad ("16 bit expression not in range -32768..32767");
                    }
                }
              s = expr_end;
@@ -4793,7 +7064,10 @@ mips_ip (str, ip)
                        imm_expr.X_add_number =
                          (imm_expr.X_add_number >> 16) & 0xffff;
                      else if (c == 'h')
-                       imm_reloc = BFD_RELOC_HI16_S;
+                       {
+                         imm_reloc = BFD_RELOC_HI16_S;
+                         imm_unmatched_hi = true;
+                       }
                      else
                        imm_reloc = BFD_RELOC_HI16;
                    }
@@ -4801,31 +7075,764 @@ mips_ip (str, ip)
              s = expr_end;
              continue;
 
-           case 'a':           /* 26 bit address */
-             my_getExpression (&offset_expr, s);
-             s = expr_end;
-             offset_reloc = BFD_RELOC_MIPS_JMP;
-             continue;
+           case 'a':           /* 26 bit address */
+             my_getExpression (&offset_expr, s);
+             s = expr_end;
+             offset_reloc = BFD_RELOC_MIPS_JMP;
+             continue;
+
+           case 'N':           /* 3 bit branch condition code */
+           case 'M':           /* 3 bit compare condition code */
+             if (strncmp (s, "$fcc", 4) != 0)
+               break;
+             s += 4;
+             regno = 0;
+             do
+               {
+                 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 |= regno << OP_SH_BCC;
+             else
+               ip->insn_opcode |= regno << OP_SH_CCC;
+              continue;
+
+           default:
+             fprintf (stderr, "bad char = '%c'\n", *args);
+             internalError ();
+           }
+         break;
+       }
+      /* Args don't match.  */
+      if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
+         !strcmp (insn->name, insn[1].name))
+       {
+         ++insn;
+         s = argsStart;
+         continue;
+       }
+      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;
 
-           default:
-             fprintf (stderr, "bad char = '%c'\n", *args);
-             internalError ();
-           }
-         break;
+  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;
        }
-      /* Args don't match.  */
-      if (insn + 1 < &mips_opcodes[NUMOPCODES] &&
-         !strcmp (insn->name, insn[1].name))
+      else
        {
-         ++insn;
-         s = argsStart;
-         continue;
+         minext = - (1 << (op->extbits - 1));
+         maxext = (1 << (op->extbits - 1)) - 1;
        }
-      insn_error = "ERROR: Illegal operands";
-      return;
+      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 ')'
 
@@ -4928,6 +7935,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
@@ -4967,7 +7987,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--)
        {
@@ -4993,26 +8013,14 @@ 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
-#ifdef GPOPT
 CONST char *md_shortopts = "O::g::G:";
-#else
-CONST char *md_shortopts = "O::g::";
-#endif
+
 struct option md_longopts[] = {
 #define OPTION_MIPS1 (OPTION_MD_BASE + 1)
   {"mips0", no_argument, NULL, OPTION_MIPS1},
@@ -5021,27 +8029,57 @@ struct option md_longopts[] = {
   {"mips2", no_argument, NULL, OPTION_MIPS2},
 #define OPTION_MIPS3 (OPTION_MD_BASE + 3)
   {"mips3", no_argument, NULL, OPTION_MIPS3},
-#define OPTION_MCPU (OPTION_MD_BASE + 4)
+#define OPTION_MIPS4 (OPTION_MD_BASE + 4)
+  {"mips4", no_argument, NULL, OPTION_MIPS4},
+#define OPTION_MCPU (OPTION_MD_BASE + 5)
   {"mcpu", required_argument, NULL, OPTION_MCPU},
-#define OPTION_MEMBEDDED_PIC (OPTION_MD_BASE + 5)
+#define OPTION_MEMBEDDED_PIC (OPTION_MD_BASE + 6)
   {"membedded-pic", no_argument, NULL, OPTION_MEMBEDDED_PIC},
-#define OPTION_TRAP (OPTION_MD_BASE + 8)
+#define OPTION_TRAP (OPTION_MD_BASE + 9)
   {"trap", no_argument, NULL, OPTION_TRAP},
   {"no-break", no_argument, NULL, OPTION_TRAP},
-#define OPTION_BREAK (OPTION_MD_BASE + 9)
+#define OPTION_BREAK (OPTION_MD_BASE + 10)
   {"break", no_argument, NULL, OPTION_BREAK},
   {"no-trap", no_argument, NULL, OPTION_BREAK},
-#define OPTION_EB (OPTION_MD_BASE + 10)
+#define OPTION_EB (OPTION_MD_BASE + 11)
   {"EB", no_argument, NULL, OPTION_EB},
-#define OPTION_EL (OPTION_MD_BASE + 11)
+#define OPTION_EL (OPTION_MD_BASE + 12)
   {"EL", no_argument, NULL, OPTION_EL},
-
+#define OPTION_M4650 (OPTION_MD_BASE + 13)
+  {"m4650", no_argument, NULL, OPTION_M4650},
+#define OPTION_NO_M4650 (OPTION_MD_BASE + 14)
+  {"no-m4650", no_argument, NULL, OPTION_NO_M4650},
+#define OPTION_M4010 (OPTION_MD_BASE + 15)
+  {"m4010", no_argument, NULL, OPTION_M4010},
+#define OPTION_NO_M4010 (OPTION_MD_BASE + 16)
+  {"no-m4010", no_argument, NULL, OPTION_NO_M4010},
+#define OPTION_M4100 (OPTION_MD_BASE + 17)
+  {"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-5900 */
+#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-5900 */
+
+#define OPTION_CALL_SHARED (OPTION_MD_BASE + 7)
+#define OPTION_NON_SHARED (OPTION_MD_BASE + 8)
+#define OPTION_XGOT (OPTION_MD_BASE + 19)
+#define OPTION_32 (OPTION_MD_BASE + 20)
+#define OPTION_64 (OPTION_MD_BASE + 21)
 #ifdef OBJ_ELF
-#define OPTION_CALL_SHARED (OPTION_MD_BASE + 6)
   {"KPIC", no_argument, NULL, OPTION_CALL_SHARED},
+  {"xgot", no_argument, NULL, OPTION_XGOT},
   {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
-#define OPTION_NON_SHARED (OPTION_MD_BASE + 7)
   {"non_shared", no_argument, NULL, OPTION_NON_SHARED},
+  {"32", no_argument, NULL, OPTION_32},
+  {"64", no_argument, NULL, OPTION_64},
 #endif
 
   {NULL, no_argument, NULL, 0}
@@ -5064,29 +8102,11 @@ md_parse_option (c, arg)
       break;
 
     case OPTION_EB:
-      byte_order = BIG_ENDIAN;
-#ifdef OBJ_AOUT
-      mips_target_format = "a.out-mips-big";
-#endif
-#ifdef OBJ_ECOFF
-      mips_target_format = "ecoff-bigmips";
-#endif
-#ifdef OBJ_ELF
-      mips_target_format = "elf32-bigmips";
-#endif
+      target_big_endian = 1;
       break;
 
     case OPTION_EL:
-      byte_order = LITTLE_ENDIAN;
-#ifdef OBJ_AOUT
-      mips_target_format = "a.out-mips-little";
-#endif
-#ifdef OBJ_ECOFF
-      mips_target_format = "ecoff-littlemips";
-#endif
-#ifdef OBJ_ELF
-      mips_target_format = "elf32-littlemips";
-#endif
+      target_big_endian = 0;
       break;
 
     case 'O':
@@ -5097,8 +8117,15 @@ md_parse_option (c, arg)
       break;
 
     case 'g':
-      if (arg == NULL || arg[1] == '2')
-       mips_optimize = 0;
+      if (arg == NULL)
+       mips_debug = 2;
+      else
+       mips_debug = atoi (arg);
+      /* When the MIPS assembler sees -g or -g2, it does not do
+         optimizations which limit full symbolic debugging.  We take
+         that to be equivalent to -O0.  */
+      if (mips_debug == 2)
+       mips_optimize = 1;
       break;
 
     case OPTION_MIPS1:
@@ -5119,6 +8146,12 @@ md_parse_option (c, arg)
        mips_cpu = 4000;
       break;
 
+    case OPTION_MIPS4:
+      mips_isa = 4;
+      if (mips_cpu == -1)
+       mips_cpu = 8000;
+      break;
+
     case OPTION_MCPU:
       {
        char *p;
@@ -5130,12 +8163,29 @@ md_parse_option (c, arg)
          mips_cpu = -1;
        else
          {
+           int sv = 0;
+
+           /* We need to cope with the various "vr" prefixes for the 4300
+              processor.  */
+           if (*p == 'v' || *p == 'V')
+             {
+               sv = 1;
+               p++;
+             }
+
            if (*p == 'r' || *p == 'R')
              p++;
 
            mips_cpu = -1;
            switch (*p)
              {
+             case '1':
+               if (strcmp (p, "10000") == 0
+                   || strcmp (p, "10k") == 0
+                   || strcmp (p, "10K") == 0)
+                 mips_cpu = 10000;
+               break;
+
              case '2':
                if (strcmp (p, "2000") == 0
                    || strcmp (p, "2k") == 0
@@ -5155,10 +8205,41 @@ md_parse_option (c, arg)
                    || strcmp (p, "4k") == 0
                    || strcmp (p, "4K") == 0)
                  mips_cpu = 4000;
+               else if (strcmp (p, "4100") == 0)
+                  {
+                    mips_cpu = 4100;
+                    if (mips_4100 < 0)
+                      mips_4100 = 1;
+                  }
+               else if (strcmp (p, "4300") == 0)
+                 mips_cpu = 4300;
                else if (strcmp (p, "4400") == 0)
                  mips_cpu = 4400;
                else if (strcmp (p, "4600") == 0)
                  mips_cpu = 4600;
+               else if (strcmp (p, "4650") == 0)
+                 {
+                   mips_cpu = 4650;
+                   if (mips_4650 < 0)
+                     mips_4650 = 1;
+                 }
+               else if (strcmp (p, "4010") == 0)
+                 {
+                   mips_cpu = 4010;
+                   if (mips_4010 < 0)
+                     mips_4010 = 1;
+                 }
+               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':
@@ -5168,12 +8249,25 @@ md_parse_option (c, arg)
                  mips_cpu = 6000;
                break;
 
+             case '8':
+               if (strcmp (p, "8000") == 0
+                   || strcmp (p, "8k") == 0
+                   || strcmp (p, "8K") == 0)
+                 mips_cpu = 8000;
+               break;
+
              case 'o':
                if (strcmp (p, "orion") == 0)
                  mips_cpu = 4600;
                break;
              }
 
+           if (sv && mips_cpu != 4300 && mips_cpu != 4100 && mips_cpu != 5000)
+             {
+               as_bad ("ignoring invalid leading 'v' in -mcpu=%s switch", arg);
+               return 0;
+             }
+
            if (mips_cpu == -1)
              {
                as_bad ("invalid architecture -mcpu=%s", arg);
@@ -5183,23 +8277,69 @@ md_parse_option (c, arg)
       }
       break;
 
+    case OPTION_M4650:
+      mips_4650 = 1;
+      break;
+
+    case OPTION_NO_M4650:
+      mips_4650 = 0;
+      break;
+
+    case OPTION_M4010:
+      mips_4010 = 1;
+      break;
+
+    case OPTION_NO_M4010:
+      mips_4010 = 0;
+      break;
+
+    case OPTION_M4100:
+      mips_4100 = 1;
+      break;
+
+    case OPTION_NO_M4100:
+      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;
-#ifdef GPOPT
-      if (g_switch_seen)
+      if (USE_GLOBAL_POINTER_OPT && g_switch_seen)
        {
          as_bad ("-G may not be used with embedded PIC code");
          return 0;
        }
       g_switch_value = 0x7fffffff;
-#endif
       break;
 
-#ifdef OBJ_ELF
-  /* When generating ELF code, we permit -KPIC and -call_shared to
-     select SVR4_PIC, and -non_shared to select no PIC.  This is
-     intended to be compatible with Irix 5.  */
+      /* When generating ELF code, we permit -KPIC and -call_shared to
+        select SVR4_PIC, and -non_shared to select no PIC.  This is
+        intended to be compatible with Irix 5.  */
     case OPTION_CALL_SHARED:
+      if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+       {
+         as_bad ("-call_shared is supported only for ELF format");
+         return 0;
+       }
       mips_pic = SVR4_PIC;
       if (g_switch_seen && g_switch_value != 0)
        {
@@ -5210,13 +8350,28 @@ md_parse_option (c, arg)
       break;
 
     case OPTION_NON_SHARED:
+      if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+       {
+         as_bad ("-non_shared is supported only for ELF format");
+         return 0;
+       }
       mips_pic = NO_PIC;
       break;
-#endif /* OBJ_ELF */
 
-#ifdef GPOPT
+      /* The -xgot option tells the assembler to use 32 offsets when
+         accessing the got in SVR4_PIC mode.  It is for Irix
+         compatibility.  */
+    case OPTION_XGOT:
+      mips_big_got = 1;
+      break;
+
     case 'G':
-      if (mips_pic == SVR4_PIC || mips_pic == EMBEDDED_PIC)
+      if (! USE_GLOBAL_POINTER_OPT)
+       {
+         as_bad ("-G is not supported for this configuration");
+         return 0;
+       }
+      else if (mips_pic == SVR4_PIC || mips_pic == EMBEDDED_PIC)
        {
          as_bad ("-G may not be used with SVR4 or embedded PIC code");
          return 0;
@@ -5225,7 +8380,28 @@ md_parse_option (c, arg)
        g_switch_value = atoi (arg);
       g_switch_seen = 1;
       break;
-#endif
+
+      /* The -32 and -64 options tell the assembler to output the 32
+         bit or the 64 bit MIPS ELF format.  */
+    case OPTION_32:
+      mips_64 = 0;
+      break;
+
+    case OPTION_64:
+      {
+       const char **list, **l;
+
+       list = bfd_target_list ();
+       for (l = list; *l != NULL; l++)
+         if (strcmp (*l, "elf64-bigmips") == 0
+             || strcmp (*l, "elf64-littlemips") == 0)
+           break;
+       if (*l == NULL)
+         as_fatal ("No compiled in support for 64 bit object file format");
+       free (list);
+       mips_64 = 1;
+      }
+      break;
 
     default:
       return 0;
@@ -5250,6 +8426,19 @@ MIPS options:\n\
 -mips1, -mcpu=r{2,3}000        generate code for r2000 and r3000\n\
 -mips2, -mcpu=r6000    generate code for r6000\n\
 -mips3, -mcpu=r4000    generate code for r4000\n\
+-mips4, -mcpu=r8000    generate code for r8000\n\
+-mcpu=vr4300           generate code for vr4300\n\
+-mcpu=vr4100           generate code for vr4100\n\
+-m4650                 permit R4650 instructions\n\
+-no-m4650              do not permit R4650 instructions\n\
+-m4010                 permit R4010 instructions\n\
+-no-m4010              do not permit R4010 instructions\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\
@@ -5257,23 +8446,38 @@ MIPS options:\n\
 #ifdef OBJ_ELF
   fprintf(stream, "\
 -KPIC, -call_shared    generate SVR4 position independent code\n\
--non_shared            do not generate position independent code\n");
+-non_shared            do not generate position independent code\n\
+-xgot                  assume a 32 bit GOT\n\
+-32                    create 32 bit object file (default)\n\
+-64                    create 64 bit object file\n");
 #endif
 }
 \f
+
+void
+mips_init_after_args ()
+{
+  if (itbl_have_entries)
+    {
+      /* initialize opcodes */
+      bfd_mips_num_opcodes = bfd_mips_num_builtin_opcodes;
+      mips_opcodes = (struct mips_opcode*) mips_builtin_opcodes;
+    }
+}
+
+
 long
 md_pcrel_from (fixP)
      fixS *fixP;
 {
-#ifndef OBJ_AOUT
-  if (fixP->fx_addsy != (symbolS *) NULL
+  if (OUTPUT_FLAVOR != bfd_target_aout_flavour
+      && fixP->fx_addsy != (symbolS *) NULL
       && ! S_IS_DEFINED (fixP->fx_addsy))
     {
       /* This makes a branch to an undefined symbol be a branch to the
         current location.  */
       return 4;
     }
-#endif
 
   /* return the address of the delay slot */
   return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
@@ -5290,22 +8494,104 @@ 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.  
-     FIXME: There is no way to select anything but 32 bit mode right
-     now.  */
-  if (nbytes == 8)
+     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)
+  if (nbytes != 2 && nbytes != 4 && nbytes != 8)
     as_bad ("Unsupported reloc size %d", nbytes);
 
   fix_new_exp (frag_now, where, (int) nbytes, exp, 0,
-              nbytes == 2 ? BFD_RELOC_16 : BFD_RELOC_32);
+              (nbytes == 2
+               ? BFD_RELOC_16
+               : (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
+}
+
+/* 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
+   explicit use of the %hi modifier.  */
+
+void
+mips_frob_file ()
+{
+  struct mips_hi_fixup *l;
+
+  for (l = mips_hi_fixup_list; l != NULL; l = l->next)
+    {
+      segment_info_type *seginfo;
+      int pass;
+
+      assert (l->fixp->fx_r_type == BFD_RELOC_HI16_S);
+
+      /* Check quickly whether the next fixup happens to be a matching
+         %lo.  */
+      if (l->fixp->fx_next != NULL
+         && l->fixp->fx_next->fx_r_type == BFD_RELOC_LO16
+         && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy
+         && l->fixp->fx_offset == l->fixp->fx_next->fx_offset)
+       continue;
+
+      /* Look through the fixups for this segment for a matching %lo.
+         When we find one, move the %hi just in front of it.  We do
+         this in two passes.  In the first pass, we try to find a
+         unique %lo.  In the second pass, we permit multiple %hi
+         relocs for a single %lo (this is a GNU extension).  */
+      seginfo = seg_info (l->seg);
+      for (pass = 0; pass < 2; pass++)
+       {
+         fixS *f, *prev;
+
+         prev = NULL;
+         for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
+           {
+             /* Check whether this is a %lo fixup which matches l->fixp.  */
+             if (f->fx_r_type == BFD_RELOC_LO16
+                 && f->fx_addsy == l->fixp->fx_addsy
+                 && f->fx_offset == l->fixp->fx_offset
+                 && (pass == 1
+                     || prev == NULL
+                     || prev->fx_r_type != BFD_RELOC_HI16_S
+                     || prev->fx_addsy != f->fx_addsy
+                     || prev->fx_offset !=  f->fx_offset))
+               {
+                 fixS **pf;
+
+                 /* Move l->fixp before f.  */
+                 for (pf = &seginfo->fix_root;
+                      *pf != l->fixp;
+                      pf = &(*pf)->fx_next)
+                   assert (*pf != NULL);
+
+                 *pf = l->fixp->fx_next;
+
+                 l->fixp->fx_next = f;
+                 if (prev == NULL)
+                   seginfo->fix_root = l->fixp;
+                 else
+                   prev->fx_next = l->fixp;
+
+                 break;
+               }
+
+             prev = f;
+           }
+
+         if (f != NULL)
+           break;
+
+         if (pass == 1)
+           as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
+                          "Unmatched %%hi reloc");
+       }
+    }
 }
 
 /* When generating embedded PIC code we need to use a special
@@ -5346,9 +8632,30 @@ md_apply_fix (fixP, valueP)
   unsigned char *buf;
   long insn, value;
 
-  assert (fixP->fx_size == 4);
+  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)
@@ -5364,15 +8671,27 @@ md_apply_fix (fixP, valueP)
     case BFD_RELOC_MIPS_CALL16:
     case BFD_RELOC_MIPS_GOT16:
     case BFD_RELOC_MIPS_GPREL32:
+    case BFD_RELOC_MIPS_GOT_HI16:
+    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
@@ -5385,22 +8704,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
@@ -5413,6 +8761,15 @@ md_apply_fix (fixP, valueP)
                            value, 4);
       break;
 
+    case BFD_RELOC_16:
+      /* If we are deleting this reloc entry, we must fill in the
+         value now.  */
+      assert (fixP->fx_size == 2);
+      if (fixP->fx_done)
+       md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+                           value, 2);
+      break;
+
     case BFD_RELOC_LO16:
       /* When handling an embedded PIC switch statement, we can wind
         up deleting a LO16 reloc.  See the 'o' case in mips_ip.  */
@@ -5422,7 +8779,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);
        }
@@ -5434,27 +8791,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;
@@ -5616,8 +8963,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)
     {
@@ -5668,7 +9015,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
     {
@@ -5678,25 +9026,18 @@ s_align (x)
   demand_empty_rest_of_line ();
 }
 
-/* Handle .ascii and .asciiz.  This just calls stringer and forgets
-   that there was a previous instruction.  */
-
-static void
-s_stringer (append_zero)
-     int append_zero;
+void
+mips_flush_pending_output ()
 {
-  mips_emit_delays ();
-  insn_label = NULL;
-  stringer (append_zero);
+  mips_emit_delays (false);
+  mips_clear_insn_labels ();
 }
 
 static void
 s_change_sec (sec)
      int sec;
 {
-#ifdef GPOPT
   segT seg;
-#endif
 
   /* When generating embedded PIC code, we only use the .text, .lit8,
      .sdata and .sbss sections.  We change the .data and .rdata
@@ -5705,7 +9046,7 @@ s_change_sec (sec)
       && (sec == 'd' || sec == 'r'))
     sec = 's';
 
-  mips_emit_delays ();
+  mips_emit_delays (false);
   switch (sec)
     {
     case 't':
@@ -5720,99 +9061,75 @@ s_change_sec (sec)
       break;
 
     case 'r':
-#ifdef GPOPT
-      seg = subseg_new (RDATA_SECTION_NAME,
-                       (subsegT) get_absolute_expression ());
-#ifdef OBJ_ELF
-      bfd_set_section_flags (stdoutput, seg,
-                            (SEC_ALLOC
-                             | SEC_LOAD
-                             | SEC_READONLY
-                             | SEC_RELOC
-                             | SEC_DATA));
-      bfd_set_section_alignment (stdoutput, seg, 4);
-#endif
-      demand_empty_rest_of_line ();
-#else /* ! defined (GPOPT) */
-      as_bad ("No read only data section in this object file format");
-      demand_empty_rest_of_line ();
-      return;
-#endif /* ! defined (GPOPT) */
+      if (USE_GLOBAL_POINTER_OPT)
+       {
+         seg = subseg_new (RDATA_SECTION_NAME,
+                           (subsegT) get_absolute_expression ());
+         if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+           {
+             bfd_set_section_flags (stdoutput, seg,
+                                    (SEC_ALLOC
+                                     | SEC_LOAD
+                                     | SEC_READONLY
+                                     | SEC_RELOC
+                                     | SEC_DATA));
+             if (strcmp (TARGET_OS, "elf") != 0)
+               bfd_set_section_alignment (stdoutput, seg, 4);
+           }
+         demand_empty_rest_of_line ();
+       }
+      else
+       {
+         as_bad ("No read only data section in this object file format");
+         demand_empty_rest_of_line ();
+         return;
+       }
       break;
 
     case 's':
-#ifdef GPOPT
-      seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
-#ifdef OBJ_ELF
-      bfd_set_section_flags (stdoutput, seg,
-                            SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
-      bfd_set_section_alignment (stdoutput, seg, 4);
-#endif
-      demand_empty_rest_of_line ();
-      break;
-#else /* ! defined (GPOPT) */
-      as_bad ("Global pointers not supported; recompile -G 0");
-      demand_empty_rest_of_line ();
-      return;
-#endif /* ! defined (GPOPT) */
+      if (USE_GLOBAL_POINTER_OPT)
+       {
+         seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
+         if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+           {
+             bfd_set_section_flags (stdoutput, seg,
+                                    SEC_ALLOC | SEC_LOAD | SEC_RELOC
+                                    | SEC_DATA);
+             if (strcmp (TARGET_OS, "elf") != 0)
+               bfd_set_section_alignment (stdoutput, seg, 4);
+           }
+         demand_empty_rest_of_line ();
+         break;
+       }
+      else
+       {
+         as_bad ("Global pointers not supported; recompile -G 0");
+         demand_empty_rest_of_line ();
+         return;
+       }
     }
 
   auto_align = 1;
 }
 
-#ifdef OBJ_ELF
-
-/* Handle the ELF .section pseudo-op.  This is a wrapper around
-   obj_elf_section.  */
-
-static void
-s_elf_section (x)
-     int x;
+void
+mips_enable_auto_align ()
 {
-  mips_emit_delays ();
-  obj_elf_section (x);
   auto_align = 1;
 }
 
-#endif /* OBJ_ELF */
-
-static void
-s_cons (log_size)
-     int log_size;
-{
-  symbolS *label;
-
-  label = insn_label;
-  mips_emit_delays ();
-  if (log_size > 0 && auto_align)
-    mips_align (log_size, 0, label);
-  insn_label = NULL;
-  cons (1 << log_size);
-}
-
-static void
-s_err (x)
-     int x;
-{
-  as_fatal ("Encountered `.err', aborting assembly");
-}
-
-static void
-s_extern (x)
-     int x;
-{
-  valueT size;
-  symbolS *symbolP;
-
-  symbolP = get_symbol ();
-  if (*input_line_pointer == ',')
-    input_line_pointer++;
-  size = get_absolute_expression ();
-  S_SET_EXTERNAL (symbolP);
+static void
+s_cons (log_size)
+     int log_size;
+{
+  symbolS *label;
 
-#ifdef ECOFF_DEBUGGING
-  symbolP->ecoff_extern_size = size;
-#endif
+  label = insn_labels != NULL ? insn_labels->label : NULL;
+  mips_emit_delays (false);
+  if (log_size > 0 && auto_align)
+    mips_align (log_size, 0, label);
+  mips_clear_insn_labels ();
+  cons (1 << log_size);
 }
 
 static void
@@ -5821,9 +9138,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')
@@ -5831,7 +9148,7 @@ s_float_cons (type)
     else
       mips_align (2, 0, label);
 
-  insn_label = NULL;
+  mips_clear_insn_labels ();
 
   float_cons (type);
 }
@@ -5849,12 +9166,18 @@ s_mips_globl (x)
   char *name;
   int c;
   symbolS *symbolP;
+  flagword flag;
 
   name = input_line_pointer;
   c = get_symbol_end ();
   symbolP = symbol_find_or_make (name);
   *input_line_pointer = c;
   SKIP_WHITESPACE ();
+
+  /* On Irix 5, every global symbol that is not explicitly labelled as
+     being a function is apparently labelled as being an object.  */
+  flag = BSF_OBJECT;
+
   if (! is_end_of_line[(unsigned char) *input_line_pointer])
     {
       char *secname;
@@ -5868,9 +9191,11 @@ s_mips_globl (x)
       *input_line_pointer = c;
 
       if (sec != NULL && (sec->flags & SEC_CODE) != 0)
-       symbolP->bsym->flags |= BSF_FUNCTION;
+       flag = BSF_FUNCTION;
     }
 
+  symbolP->bsym->flags |= flag;
+
   S_SET_EXTERNAL (symbolP);
   demand_empty_rest_of_line ();
 }
@@ -5901,15 +9226,13 @@ s_option (x)
       else
        as_bad (".option pic%d not supported", i);
 
-#ifdef GPOPT
-      if (mips_pic == SVR4_PIC)
+      if (USE_GLOBAL_POINTER_OPT && mips_pic == SVR4_PIC)
        {
          if (g_switch_seen && g_switch_value != 0)
            as_warn ("-G may not be used with SVR4 PIC code");
          g_switch_value = 0;
          bfd_set_gp_size (stdoutput, 0);
        }
-#endif
     }
   else
     as_warn ("Unrecognized option \"%s\"", opt);
@@ -5931,16 +9254,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;
     }
@@ -5978,6 +9304,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;
@@ -5987,11 +9319,15 @@ s_mipsset (x)
       isa = atoi (name + 4);
       if (isa == 0)
        mips_isa = file_mips_isa;
-      else if (isa < 1 || isa > 3)
+      else if (isa < 1 || isa > 4)
        as_bad ("unknown ISA level");
       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);
@@ -6000,18 +9336,6 @@ s_mipsset (x)
   demand_empty_rest_of_line ();
 }
 
-/* The same as the usual .space directive, except that we have to
-   forget about any previous instruction.  */
-
-static void
-s_mips_space (param)
-     int param;
-{
-  mips_emit_delays ();
-  insn_label = NULL;
-  s_space (param);
-}
-
 /* Handle the .abicalls pseudo-op.  I believe this is equivalent to
    .option pic2.  It means to generate SVR4 PIC calls.  */
 
@@ -6020,11 +9344,12 @@ s_abicalls (ignore)
      int ignore;
 {
   mips_pic = SVR4_PIC;
-#ifdef GPOPT
-  if (g_switch_seen && g_switch_value != 0)
-    as_warn ("-G may not be used with SVR4 PIC code");
-  g_switch_value = 0;
-#endif
+  if (USE_GLOBAL_POINTER_OPT)
+    {
+      if (g_switch_seen && g_switch_value != 0)
+       as_warn ("-G may not be used with SVR4 PIC code");
+      g_switch_value = 0;
+    }
   bfd_set_gp_size (stdoutput, 0);
   demand_empty_rest_of_line ();
 }
@@ -6062,6 +9387,9 @@ s_cpload (ignore)
   ex.X_op_symbol = NULL;
   ex.X_add_number = 0;
 
+  /* In ELF, this symbol is implicitly an STT_OBJECT symbol.  */
+  ex.X_add_symbol->bsym->flags |= BSF_OBJECT;
+
   macro_build_lui ((char *) NULL, &icnt, &ex, GP);
   macro_build ((char *) NULL, &icnt, &ex, "addiu", "t,r,j", GP, GP,
               (int) BFD_RELOC_LO16);
@@ -6122,11 +9450,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);
 
@@ -6170,6 +9498,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.  */
@@ -6224,32 +9584,43 @@ 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));
 }
 
-/* Estimate the size of a frag before relaxing.  We are not really
-   relaxing here, and the final size is encoded in the subtype
-   information.  */
+/* Utility routine, called from above as well.  If called while the
+   input file is still being read, it's only an approximation.  (For
+   example, a symbol may later become defined which appeared to be
+   undefined earlier.)  */
 
-/*ARGSUSED*/
-int
-md_estimate_size_before_relax (fragp, segtype)
-     fragS *fragp;
-     asection *segtype;
+static int
+nopic_need_relax (sym)
+     symbolS *sym;
 {
-  int change;
+  if (sym == 0)
+    return 0;
 
-  if (mips_pic == NO_PIC)
+  if (USE_GLOBAL_POINTER_OPT)
     {
-#ifdef GPOPT
       const char *symname;
+      int change;
 
       /* Find out whether this symbol can be referenced off the GP
         register.  It can be if it is smaller than the -G size or if
         it is in the .sdata or .sbss section.  Certain symbols can
         not be referenced off the GP, although it appears as though
         they can.  */
-      symname = S_GET_NAME (fragp->fr_symbol);
+      symname = S_GET_NAME (sym);
       if (symname != (const char *) NULL
          && (strcmp (symname, "eprol") == 0
              || strcmp (symname, "etext") == 0
@@ -6261,26 +9632,268 @@ md_estimate_size_before_relax (fragp, segtype)
              || strcmp (symname, "end") == 0
              || strcmp (symname, "_gp_disp") == 0))
        change = 1;
-      else if (! S_IS_DEFINED (fragp->fr_symbol)
-              && ((fragp->fr_symbol->ecoff_extern_size != 0
-                   && fragp->fr_symbol->ecoff_extern_size <= g_switch_value)
-                  || (S_GET_VALUE (fragp->fr_symbol) != 0
-                      && S_GET_VALUE (fragp->fr_symbol) <= g_switch_value)))
+      else if (! S_IS_DEFINED (sym)
+              && (0
+#ifndef NO_ECOFF_DEBUGGING
+                  || (sym->ecoff_extern_size != 0
+                      && sym->ecoff_extern_size <= g_switch_value)
+#endif
+                  || (S_GET_VALUE (sym) != 0
+                      && S_GET_VALUE (sym) <= g_switch_value)))
        change = 0;
       else
        {
          const char *segname;
 
-         segname = segment_name (S_GET_SEGMENT (fragp->fr_symbol));
+         segname = segment_name (S_GET_SEGMENT (sym));
          assert (strcmp (segname, ".lit8") != 0
                  && strcmp (segname, ".lit4") != 0);
          change = (strcmp (segname, ".sdata") != 0
                    && strcmp (segname, ".sbss") != 0);
        }
-#else /* ! defined (GPOPT) */
-      /* We are not optimizing for the GP register.  */
-      change = 1;
-#endif /* ! defined (GPOPT) */  
+      return change;
+    }
+  else
+    /* We are not optimizing for the GP register.  */
+    return 1;
+}
+
+/* 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.  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 != fragp->fr_symbol->sy_frag; f = f->fr_next)
+           {
+             assert (f != NULL);
+             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;
+               }
+           }
+         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
+md_estimate_size_before_relax (fragp, segtype)
+     fragS *fragp;
+     asection *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)
     {
@@ -6314,6 +9927,29 @@ 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)
+    return 0;
+#endif
+  return 1;
+}
+
 /* Translate internal representation of relocation info to BFD target
    format.  */
 
@@ -6324,6 +9960,7 @@ tc_gen_reloc (section, fixp)
 {
   static arelent *retval[4];
   arelent *reloc;
+  bfd_reloc_code_real_type code;
 
   reloc = retval[0] = (arelent *) xmalloc (sizeof (arelent));
   retval[1] = NULL;
@@ -6338,9 +9975,8 @@ tc_gen_reloc (section, fixp)
         is actually the difference between the reloc address and the
         subtrahend.  */
       reloc->addend = reloc->address - S_GET_VALUE (fixp->fx_subsy);
-#ifndef OBJ_ECOFF
-      as_fatal ("Double check fx_r_type in tc-mips.c:tc_gen_reloc");
-#endif
+      if (OUTPUT_FLAVOR != bfd_target_ecoff_flavour)
+       as_fatal ("Double check fx_r_type in tc-mips.c:tc_gen_reloc");
       fixp->fx_r_type = BFD_RELOC_GPREL32;
     }
   else if (fixp->fx_r_type == BFD_RELOC_PCREL_LO16)
@@ -6371,13 +10007,12 @@ tc_gen_reloc (section, fixp)
     reloc->addend = fixp->fx_addnumber;
   else
     {
-#ifndef OBJ_AOUT
-      /* A gruesome hack which is a result of the gruesome gas reloc
-        handling.  */
-      reloc->addend = reloc->address;
-#else
-      reloc->addend = -reloc->address;
-#endif
+      if (OUTPUT_FLAVOR != bfd_target_aout_flavour)
+       /* A gruesome hack which is a result of the gruesome gas reloc
+          handling.  */
+       reloc->addend = reloc->address;
+      else
+       reloc->addend = -reloc->address;
     }
 
   /* If this is a variant frag, we may need to adjust the existing
@@ -6385,18 +10020,31 @@ tc_gen_reloc (section, fixp)
   if (fixp->fx_frag->fr_opcode != NULL
       && (fixp->fx_r_type == BFD_RELOC_MIPS_GPREL
          || fixp->fx_r_type == BFD_RELOC_MIPS_GOT16
-         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL16))
+         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL16
+         || fixp->fx_r_type == BFD_RELOC_MIPS_GOT_HI16
+         || fixp->fx_r_type == BFD_RELOC_MIPS_GOT_LO16
+         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL_HI16
+         || fixp->fx_r_type == BFD_RELOC_MIPS_CALL_LO16))
     {
       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, both of which are being replaced.  Let the
-        second one handle all of them.  */
+        GPREL relocs, or a GOT_HI16/GOT_LO16 pair, or a
+        CALL_HI16/CALL_LO16, both of which are being replaced.  Let
+        the second one handle all of them.  */
       if (fixp->fx_next != NULL
          && fixp->fx_frag == fixp->fx_next->fx_frag)
        {
-         assert (fixp->fx_r_type == BFD_RELOC_MIPS_GPREL
-                 && fixp->fx_next->fx_r_type == BFD_RELOC_MIPS_GPREL);
+         assert ((fixp->fx_r_type == BFD_RELOC_MIPS_GPREL
+                  && fixp->fx_next->fx_r_type == BFD_RELOC_MIPS_GPREL)
+                 || (fixp->fx_r_type == BFD_RELOC_MIPS_GOT_HI16
+                     && (fixp->fx_next->fx_r_type
+                         == BFD_RELOC_MIPS_GOT_LO16))
+                 || (fixp->fx_r_type == BFD_RELOC_MIPS_CALL_HI16
+                     && (fixp->fx_next->fx_r_type
+                         == BFD_RELOC_MIPS_CALL_LO16)));
          retval[0] = NULL;
          return retval;
        }
@@ -6430,37 +10078,109 @@ tc_gen_reloc (section, fixp)
        }
       else if (mips_pic == SVR4_PIC)
        {
-         if (fixp->fx_r_type != BFD_RELOC_MIPS_GOT16)
+         switch (fixp->fx_r_type)
            {
-             assert (fixp->fx_r_type == BFD_RELOC_MIPS_CALL16);
+           default:
+             abort ();
+           case BFD_RELOC_MIPS_GOT16:
+             break;
+           case BFD_RELOC_MIPS_CALL16:
+           case BFD_RELOC_MIPS_GOT_LO16:
+           case BFD_RELOC_MIPS_CALL_LO16:
              fixp->fx_r_type = BFD_RELOC_MIPS_GOT16;
+             break;
            }
        }
       else
        abort ();
     }
 
+  /* Since DIFF_EXPR_OK is defined in tc-mips.h, it is possible that
+     fixup_segment converted a non-PC relative reloc into a PC
+     relative reloc.  In such a case, we need to convert the reloc
+     code.  */
+  code = fixp->fx_r_type;
+  if (fixp->fx_pcrel)
+    {
+      switch (code)
+       {
+       case BFD_RELOC_8:
+         code = BFD_RELOC_8_PCREL;
+         break;
+       case BFD_RELOC_16:
+         code = BFD_RELOC_16_PCREL;
+         break;
+       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:
+         break;
+       default:
+         as_bad_where (fixp->fx_file, fixp->fx_line,
+                       "Cannot make %s relocation PC relative",
+                       bfd_get_reloc_code_name (code));
+       }
+    }
+
   /* To support a PC relative reloc when generating embedded PIC code
      for ECOFF, we use a Cygnus extension.  We check for that here to
      make sure that we don't let such a reloc escape normally.  */
-#ifdef OBJ_ECOFF
-  if (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+  if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour
+      && code == BFD_RELOC_16_PCREL_S2
       && mips_pic != EMBEDDED_PIC)
     reloc->howto = NULL;
   else
-#endif
-    reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+    reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
 
   if (reloc->howto == NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
-                   "Can not represent relocation in this object file format");
+                   "Can not represent %s relocation in this object file format",
+                   bfd_get_reloc_code_name (code));
       retval[0] = NULL;
     }
 
   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
@@ -6472,19 +10192,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.  */
@@ -6493,29 +10338,87 @@ void
 mips_define_label (sym)
      symbolS *sym;
 {
-  insn_label = sym;
+  struct insn_label_list *l;
+
+  if (free_insn_labels == NULL)
+    l = (struct insn_label_list *) xmalloc (sizeof *l);
+  else
+    {
+      l = free_insn_labels;
+      free_insn_labels = l->next;
+    }
+
+  l->label = sym;
+  l->next = insn_labels;
+  insn_labels = 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)
+    {
+      /* 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;
+    }
+#endif
+
+  /* Here it's OK to discard local labels.  */
+
+  return name[0] == '$';
 }
 \f
-#ifdef OBJ_ELF
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 
 /* Some special processing for a MIPS ELF file.  */
 
 void
 mips_elf_final_processing ()
 {
-  Elf32_RegInfo s;
-
-  /* Write out the .reginfo section.  */
-  s.ri_gprmask = mips_gprmask;
-  s.ri_cprmask[0] = mips_cprmask[0];
-  s.ri_cprmask[1] = mips_cprmask[1];
-  s.ri_cprmask[2] = mips_cprmask[2];
-  s.ri_cprmask[3] = mips_cprmask[3];
-  /* The gp_value field is set by the MIPS ELF backend.  */
-
-  bfd_mips_elf32_swap_reginfo_out (stdoutput, &s,
-                                  ((Elf32_External_RegInfo *)
-                                   mips_regmask_frag));
+  /* Write out the register information.  */
+  if (! mips_64)
+    {
+      Elf32_RegInfo s;
+
+      s.ri_gprmask = mips_gprmask;
+      s.ri_cprmask[0] = mips_cprmask[0];
+      s.ri_cprmask[1] = mips_cprmask[1];
+      s.ri_cprmask[2] = mips_cprmask[2];
+      s.ri_cprmask[3] = mips_cprmask[3];
+      /* The gp_value field is set by the MIPS ELF backend.  */
+
+      bfd_mips_elf32_swap_reginfo_out (stdoutput, &s,
+                                      ((Elf32_External_RegInfo *)
+                                       mips_regmask_frag));
+    }
+  else
+    {
+      Elf64_Internal_RegInfo s;
+
+      s.ri_gprmask = mips_gprmask;
+      s.ri_pad = 0;
+      s.ri_cprmask[0] = mips_cprmask[0];
+      s.ri_cprmask[1] = mips_cprmask[1];
+      s.ri_cprmask[2] = mips_cprmask[2];
+      s.ri_cprmask[3] = mips_cprmask[3];
+      /* The gp_value field is set by the MIPS ELF backend.  */
+
+      bfd_mips_elf64_swap_reginfo_out (stdoutput, &s,
+                                      ((Elf64_External_RegInfo *)
+                                       mips_regmask_frag));
+    }
 
   /* Set the MIPS ELF flag bits.  FIXME: There should probably be some
      sort of BFD interface for this.  */
@@ -6525,10 +10428,8 @@ mips_elf_final_processing ()
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC;
 }
 
-#endif /* OBJ_ELF */
+#endif /* OBJ_ELF || OBJ_MAYBE_ELF */
 \f
-#ifndef ECOFF_DEBUGGING
-
 /* These functions should really be defined by the object file format,
    since they are related to debugging information.  However, this
    code has to work for the a.out format, which does not define them,
@@ -6598,8 +10499,6 @@ md_obj_end ()
     as_warn ("missing `.end' at end of assembly");
 }
 
-extern char hex_value[];
-
 static long
 get_number ()
 {
@@ -6621,7 +10520,7 @@ get_number ()
          while (isxdigit (*input_line_pointer))
            {
              val <<= 4;
-             val |= hex_value[(int) *input_line_pointer++];
+             val |= hex_value (*input_line_pointer++);
            }
          return negative ? -val : val;
        }
@@ -6870,5 +10769,3 @@ s_loc (x)
   symbolP->sy_segment = now_seg;
 }
 #endif
-
-#endif /* ! defined (ECOFF_DEBUGGING) */
This page took 0.196331 seconds and 4 git commands to generate.