[PATCH 1/6] Gas changes for nanoMIPS support.

"Tsing" <[email protected]>
Newsgroups gmane.comp.gnu.binutils
Message-ID <[email protected]>
From: tsing <[email protected]>

/
	* configure.ac: Add nanoMIPS target.
	* config.sub: Likewise.
	* configure: Regenerate.
config/
	* mt-nanomips: New file.
gas/
	* Makefile.am: Add nanomips files.
	* Makefile.in: Regenerate.
	* config.in: Define a default architecture.
	* configure: Regenerate.
	* configure.ac: Likewise.
	* configure.tgt: Add entries for nanomips.
	* read.c: Likewise.
	* config/obj-elf.h: Likewise.
	* config/tc-nanomips.c: New file.
	* config/tc-nanomips.h: New file.
ld/
	* configure.tgt: Add nanomips entries.
---
 config/mt-nanomips       |     3 +
 configure                |     9 +
 configure.ac             |     9 +
 gas/Makefile.am          |     2 +
 gas/Makefile.in          |     5 +
 gas/config.in            |     9 +
 gas/config/obj-elf.h     |     5 +
 gas/config/tc-nanomips.c | 11667 +++++++++++++++++++++++++++++++++++++
 gas/config/tc-nanomips.h |   187 +
 gas/configure            |    54 +
 gas/configure.ac         |    45 +
 gas/configure.tgt        |     5 +
 gas/read.c               |     2 +-
 ld/configure.tgt         |     2 +
 14 files changed, 12003 insertions(+), 1 deletion(-)
 create mode 100644 config/mt-nanomips
 create mode 100644 gas/config/tc-nanomips.c
 create mode 100644 gas/config/tc-nanomips.h

diff --git a/config/mt-nanomips b/config/mt-nanomips
new file mode 100644
index 00000000000..48037c5001e
--- /dev/null
+++ b/config/mt-nanomips
@@ -0,0 +1,3 @@
+# We default to building libraries optimised for size.
+CFLAGS_FOR_TARGET += -Os -mno-gpopt
+CXXFLAGS_FOR_TARGET += -Os -mno-gpopt
diff --git a/configure b/configure
index 4c2aa195810..4efb0bacc9a 100755
--- a/configure
+++ b/configure
@@ -4194,6 +4194,12 @@ case "${target}" in
   mips*-*-*)
     noconfigdirs="$noconfigdirs gprof"
     ;;
+  nanomips*-*-elf*)
+    noconfigdirs="$noconfigdirs gprof gdb sim"
+    ;;
+  nanomips*-*-linux*)
+    noconfigdirs="$noconfigdirs gprof gdb sim"
+    ;;
   nvptx*-*-*)
     noconfigdirs="$noconfigdirs target-libssp"
     ;;
@@ -10895,6 +10901,9 @@ case "${target}" in
   mips*-*-*linux* | mips*-*-gnu*)
     target_makefile_frag="config/mt-mips-gnu"
     ;;
+  nanomips*-*-*linux*)
+    target_makefile_frag="config/mt-nanomips"
+    ;;
   *-*-linux-android*)
     target_makefile_frag="config/mt-android"
     ;;
diff --git a/configure.ac b/configure.ac
index cae0042411b..7b2481d35f4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1363,6 +1363,12 @@ case "${target}" in
   mips*-*-*)
     noconfigdirs="$noconfigdirs gprof"
     ;;
+  nanomips*-*-elf*)
+    noconfigdirs="$noconfigdirs gprof gdb sim"
+    ;;
+  nanomips*-*-linux*)
+    noconfigdirs="$noconfigdirs gprof gdb sim"
+    ;;
   nvptx*-*-*)
     noconfigdirs="$noconfigdirs target-libssp"
     ;;
@@ -3079,6 +3085,9 @@ case "${target}" in
   mips*-*-*linux* | mips*-*-gnu*)
     target_makefile_frag="config/mt-mips-gnu"
     ;;
+  nanomips*-*-*linux*)
+    target_makefile_frag="config/mt-nanomips"
+    ;;
   *-*-linux-android*)
     target_makefile_frag="config/mt-android"
     ;;
diff --git a/gas/Makefile.am b/gas/Makefile.am
index 9f90a1de50e..e954f671eb9 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -179,6 +179,7 @@ TARGET_CPU_CFILES = \
 	config/tc-metag.c \
 	config/tc-microblaze.c \
 	config/tc-mips.c \
+	config/tc-nanomips.c \
 	config/tc-mmix.c \
 	config/tc-mn10200.c \
 	config/tc-mn10300.c \
@@ -253,6 +254,7 @@ TARGET_CPU_HFILES = \
 	config/tc-metag.h \
 	config/tc-microblaze.h \
 	config/tc-mips.h \
+	config/tc-nanomips.h \
 	config/tc-mmix.h \
 	config/tc-mn10200.h \
 	config/tc-mn10300.h \
diff --git a/gas/Makefile.in b/gas/Makefile.in
index b2bb505cc69..ec8ee0fbd54 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -687,6 +687,7 @@ TARGET_CPU_CFILES = \
 	config/tc-moxie.c \
 	config/tc-msp430.c \
 	config/tc-mt.c \
+	config/tc-nanomips.c \
 	config/tc-nds32.c \
 	config/tc-ns32k.c \
 	config/tc-or1k.c \
@@ -760,6 +761,7 @@ TARGET_CPU_HFILES = \
 	config/tc-mn10300.h \
 	config/tc-msp430.h \
 	config/tc-mt.h \
+	config/tc-nanomips.h \
 	config/tc-nds32.h \
 	config/tc-ns32k.h \
 	config/tc-or1k.h \
@@ -1187,6 +1189,8 @@ config/tc-msp430.$(OBJEXT): config/$(am__dirstamp) \
 	config/$(DEPDIR)/$(am__dirstamp)
 config/tc-mt.$(OBJEXT): config/$(am__dirstamp) \
 	config/$(DEPDIR)/$(am__dirstamp)
+config/tc-nanomips.$(OBJEXT): config/$(am__dirstamp) \
+	config/$(DEPDIR)/$(am__dirstamp)
 config/tc-nds32.$(OBJEXT): config/$(am__dirstamp) \
 	config/$(DEPDIR)/$(am__dirstamp)
 config/tc-ns32k.$(OBJEXT): config/$(am__dirstamp) \
@@ -1421,6 +1425,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-moxie.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-msp430.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-mt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-nanomips.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-nds32.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-ns32k.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-or1k.Po@am__quote@
diff --git a/gas/config.in b/gas/config.in
index ae5f41f7058..cc5a759ef84 100644
--- a/gas/config.in
+++ b/gas/config.in
@@ -176,6 +176,15 @@
 /* Choose a default ABI for MIPS targets. */
 #undef MIPS_DEFAULT_ABI
 
+/* Default CPU for nanoMIPS targets. */
+#undef NANOMIPS_CPU_STRING_DEFAULT
+
+/* Generate 64-bit code by default on nanoMIPS targets. */
+#undef NANOMIPS_DEFAULT_64BIT
+
+/* Choose a default ABI for nanoMIPS targets. */
+#undef NANOMIPS_DEFAULT_ABI
+
 /* Define value for nds32_arch_name */
 #undef NDS32_DEFAULT_ARCH_NAME
 
diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
index 910b743a442..c2b0308ca1c 100644
--- a/gas/config/obj-elf.h
+++ b/gas/config/obj-elf.h
@@ -49,6 +49,11 @@ extern int alpha_flag_mdebug;
 extern int mips_flag_mdebug;
 #endif /* TC_MIPS */
 
+/* Always disable ECOFF_DEBUGGING for nanoMIPS target.  */
+#ifdef TC_NANOMIPS
+#define ECOFF_DEBUGGING 0
+#endif /* TC_NANOMIPS */
+
 #ifdef OBJ_MAYBE_ECOFF
 #ifndef ECOFF_DEBUGGING
 #define ECOFF_DEBUGGING 1
diff --git a/gas/config/tc-nanomips.c b/gas/config/tc-nanomips.c
new file mode 100644
index 00000000000..be2ab339a06
--- /dev/null
+++ b/gas/config/tc-nanomips.c
@@ -0,0 +1,11667 @@
+/* tc-nanomips.c -- assemble code for a nanoMIPS chip.
+   Copyright (C) 2018-2026 Free Software Foundation, CIP United Inc.
+   Contributed by MIPS Tech LLC.
+   Written by Faraz Shahbazker <[email protected]>
+
+   This file is part of GAS.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#include "as.h"
+#include "config.h"
+#include "subsegs.h"
+#include "safe-ctype.h"
+
+#include "opcode/nanomips.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+#include <limits.h>
+
+/* Check assumptions made in this file.  */
+typedef char static_assert1[sizeof (offsetT) < 8 ? -1 : 1];
+typedef char static_assert2[sizeof (valueT) < 8 ? -1 : 1];
+
+#ifdef DEBUG
+#define DBG(x) printf x
+#else
+#define DBG(x)
+#endif
+
+#define streq(a, b)           (strcmp (a, b) == 0)
+
+#define SKIP_SPACE_TABS(S) \
+  do { while (is_whitespace (*(S))) ++(S); } while (0)
+
+/* Clean up namespace so we can include obj-elf.h too.  */
+static int nanomips_output_flavor (void);
+static int
+nanomips_output_flavor (void)
+{
+  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 obj_frob_file
+#undef obj_frob_file_after_relocs
+#undef obj_frob_symbol
+#undef obj_pop_insert
+#undef obj_sec_sym_ok_for_reloc
+#undef OBJ_COPY_SYMBOL_ATTRIBUTES
+
+#include "obj-elf.h"
+/* Fix any of them that we actually care about.  */
+#undef OUTPUT_FLAVOR
+#define OUTPUT_FLAVOR nanomips_output_flavor()
+
+#include "elf/nanomips.h"
+
+#ifndef ECOFF_DEBUGGING
+#define NO_ECOFF_DEBUGGING
+#define ECOFF_DEBUGGING 0
+#endif
+
+#include "ecoff.h"
+
+#define ZERO 0
+#define AT 1
+#define S0  16
+#define S7  23
+#define PIC_CALL_REG 25
+#define KT0 26
+#define KT1 27
+#define GP  28
+#define SP  29
+#define FP  30
+#define RA  31
+
+#define ILLEGAL_REG (32)
+
+extern int target_big_endian;
+
+/* The name of the readonly data section.  */
+#define RDATA_SECTION_NAME ".rodata"
+
+/* Information about an instruction, including its format, operands
+   and fixups.  */
+struct nanomips_cl_insn
+{
+  /* The opcode's entry in nanomips_opcodes.  */
+  const struct nanomips_opcode *insn_mo;
+
+  /* The 16-bit or 32-bit bitstring of the instruction itself.  This is
+     a copy of INSN_MO->match with the operands filled in.  */
+  unsigned long insn_opcode;
+
+  /* The lower 32-bits of a 48-bit instruction. */
+  unsigned long insn_opcode_ext;
+
+  /* The frag that contains the instruction.  */
+  struct frag *frag;
+
+  /* The offset into FRAG of the first instruction byte.  */
+  long where;
+
+  /* The relocs associated with the instruction, if any.  */
+  fixS *fixp[3];
+
+  /* True if this entry cannot be moved from its current position.  */
+  unsigned int fixed_p:1;
+
+  /* True if this instruction occurred in a .set noreorder block.  */
+  unsigned int noreorder_p:1;
+
+  /* True if this instruction is complete.  */
+  unsigned int complete_p:1;
+};
+
+/* The ABI to use.  */
+enum nanomips_abi_level
+{
+  NO_ABI = 0,
+  P32_ABI,
+  P64_ABI,
+};
+
+/* The ABI to use.  */
+enum mc_model_type
+{
+  MC_AUTO = 0,
+  MC_MEDIUM,
+  MC_LARGE,
+};
+
+/* nanoMIPS ABI we are using for this output file.  */
+static enum nanomips_abi_level nanomips_abi = NO_ABI;
+
+/* nanoMIPS PIC level.  */
+
+enum nanomips_pic_level
+{
+  /* Do not generate PIC code.  */
+  NO_PIC,
+
+  /* Generate medium model PIC code as in the nanoMIPS ABI.  */
+  SVR4_PIC,
+
+  /* Generate large model PIC code as in the nanoMIPS ABI.  */
+  SVR4_LARGE_PIC,
+};
+
+/* This is the set of options which may be modified by the .set
+   pseudo-op.  We use a struct so that .set push and .set pop are more
+   reliable.  */
+
+struct nanomips_set_options
+{
+  /* nanoMIPS ISA (Instruction Set Architecture) level.  This is set to -1
+     if it has not been initialized.  */
+  int isa;
+
+  /* Enabled Application Specific Extensions (ASEs).  Changed by `.set
+     <asename>', by command line options, and based on the default
+     architecture.  */
+  int ase;
+
+  /* Non-zero if we should not reorder instructions.  Changed by `.set
+     reorder' and `.set noreorder'.  */
+  int noreorder;
+
+  /*  Non-zero if we should not permit the $at ($1) register to be used
+     in instructions.  Changed by `.set at' and `.set noat'.  */
+  int noat;
+
+  /* Non-zero if we should warn when a macro instruction expands into
+     more than one machine instruction.  Changed by `.set nomacro' and
+     `.set macro'.  */
+  int nomacro;
+
+  /* True if we should only emit 32-bit nanoMIPS instructions.
+     Changed by `.set insn32' and `.set noinsn32', and the -minsn32
+     and -mno-insn32 command line options.  */
+  bool insn32;
+
+  /* Restrict general purpose registers and floating point registers
+     to 32 bit.  This is initially determined when -mgp32 or -mfp32
+     is passed but can changed.  */
+  int gp;
+  int fp;
+
+  /* nanoMIPS architecture (CPU) type.  Changed by .set arch=FOO, the -march
+     command line option, and the default CPU.  */
+  int arch;
+
+  /* True if floating-point operations are not allowed.  Changed by .set
+     softfloat or .set hardfloat, by command line options -msoft-float or
+     -mhard-float.  The default is false.  */
+  bool soft_float;
+
+  /* True if only single-precision floating-point operations are allowed.
+     Changed by .set singlefloat or .set doublefloat, command-line options
+     -msingle-float or -mdouble-float.  The default is false.  */
+  bool single_float;
+
+  /* The set of ASEs that should be enabled for the user specified
+     architecture.  This cannot be inferred from 'arch' for all cores
+     as processors only have a unique 'arch' if they add architecture
+     specific instructions (UDI).  */
+  int init_ase;
+
+  /* Enable/disable balc stub optimization.  */
+  bool no_balc_stubs;
+
+  /* Enable/disable register names, only for nanoMIPS.  */
+  bool legacyregs;
+
+  /* Enable/disable PC-relative expansion.  */
+  bool pcrel;
+
+  /* Enable/disable Position Independent data.  */
+  bool pid;
+
+  /* Select PIC code.  */
+  enum nanomips_pic_level pic;
+
+  /* Code/data memory model.  */
+  enum mc_model_type mc_model;
+};
+
+/* Specifies whether module level options have been checked yet.  */
+static bool file_nanomips_opts_checked = false;
+
+/* This is the struct we use to hold the module level set of options.
+   Note that we must set the isa field to ISA_UNKNOWN and the ASE, gp and
+   fp fields to -1 to indicate that they have not been initialized.  */
+
+static struct nanomips_set_options file_nanomips_opts = {
+  /* isa */ ISA_UNKNOWN, /* ase */ 0, /* noreorder */ 0, /* at */ 0,
+  /* nomacro */ 0, /* insn32 */ false, /* gp */ -1, /* fp */ -1,
+  /* arch */ CPU_UNKNOWN, /* soft_float */ false, /* single_float */ false,
+  /* init_ase */ 0, /* no_balc_stubs */ true, /* legacyregs */ false,
+  /* pcrel */ false, /* pid */ false, /* pic */ NO_PIC,
+  /* mc_model */ MC_AUTO
+};
+
+/* This is similar to file_nanomips_opts, but for the current set of options.  */
+
+static struct nanomips_set_options nanomips_opts = {
+  /* isa */ ISA_UNKNOWN, /* ase */ 0, /* noreorder */ 0, /* at */ 0,
+  /* nomacro */ 0, /* insn32 */ false, /* gp */ -1, /* fp */ -1,
+  /* arch */ CPU_UNKNOWN, /* soft_float */ false, /* single_float */ false,
+  /* init_ase */ 0, /* no_balc_stubs */ true, /* legacyregs */ false,
+  /* pcrel */ false, /* pid */ false, /* pic */ NO_PIC,
+  /* mc_model */ MC_AUTO
+};
+
+/* Which bits of file_ase were explicitly set or cleared by ASE options.  */
+static unsigned int file_ase_explicit;
+
+/* The argument of the -march= flag.  The architecture we are assembling.  */
+static const char *nanomips_arch_string;
+
+/* The argument of the -mtune= flag.  The architecture for which we
+   are optimizing.  */
+static int nanomips_tune = CPU_UNKNOWN;
+static const char *nanomips_tune_string;
+
+/* True when generating 32-bit code for a 64-bit processor.  */
+static int nanomips_32bitmode = 0;
+
+/* True if the given ABI requires 32-bit registers.  */
+#define ABI_NEEDS_32BIT_REGS(ABI) ((ABI) == P32_ABI)
+
+/* Likewise 64-bit registers.  */
+#define ABI_NEEDS_64BIT_REGS(ABI) ((ABI) == P64_ABI)
+
+/*  Return true if ISA supports 64 bit wide gp registers.  */
+#define ISA_HAS_64BIT_REGS(ISA) ((ISA) == ISA_NANOMIPS64R6)
+
+#define GPR_SIZE nanomips_opts.gp
+
+#define HAVE_64BIT_OBJECTS (nanomips_abi == P64_ABI)
+
+/* The ABI-derived address size.  */
+#define HAVE_64BIT_ADDRESSES (GPR_SIZE == 64 && nanomips_abi == P64_ABI)
+#define HAVE_32BIT_ADDRESSES (!HAVE_64BIT_ADDRESSES)
+
+/* The size of symbolic constants (i.e., expressions of the form
+   "SYMBOL" or "SYMBOL + OFFSET").  */
+#define HAVE_32BIT_SYMBOLS \
+  (HAVE_32BIT_ADDRESSES || !HAVE_64BIT_OBJECTS)
+#define HAVE_64BIT_SYMBOLS (!HAVE_32BIT_SYMBOLS)
+
+/* Maximum symbol offset that can be encoded in a BFD_RELOC_GPREL16
+   relocation.  */
+#define MAX_GPREL_OFFSET (0x1fffc)
+
+/* Addresses are loaded in different ways, depending on the address size
+   in use.  */
+#define ADDRESS_ADD_INSN "addu"
+
+#define ADDRESS_ADDI_INSN "addiu"
+
+#define ADDRESS_LOAD_INSN "lw"
+
+#define ADDRESS_STORE_INSN "sw"
+
+#define PCREL_LOAD_INSN "lwpc"
+
+/* The minimum and maximum signed values that can be stored in a GPR.  */
+#define GPR_SMAX ((offsetT) (((valueT) 1 << (GPR_SIZE - 1)) - 1))
+#define GPR_SMIN (-GPR_SMAX - 1)
+
+/* 1 if trap instructions should used for overflow rather than break
+   instructions.  */
+static int nanomips_trap = 0;
+
+/* 1 if double width floating point constants should not be constructed
+   by assembling two single width halves within the instruction stream,
+   but instead loaded from memory.  */
+static int nanomips_disable_float_construction;
+
+/* Non-zero if .set [no]relax directive was used */
+static bool toggle_linkrelax_p = false;
+static bool nanomips_linkrelax_p = false;
+
+/* The size of objects in the small data section.  */
+static unsigned int g_switch_value = 8;
+/* Whether the -G option was used.  */
+static int g_switch_seen = 0;
+
+/* 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 function can only provide a guess, but it seems to work for
+   gcc output.  It needs to guess right for gcc, otherwise gcc
+   will put what it thinks is a GP-relative instruction in a branch
+   delay slot.  */
+static int nopic_need_relax (symbolS *, int);
+
+/* handle of the OPCODE hash table */
+static htab_t op_hash = NULL;
+
+/* The opcode hash table we use for the nanoMIPS ASE.  */
+static htab_t nanomips_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[] = "#";
+
+/* This array holds the chars that only start a comment at the beginning of
+   a line.  If the line seems to have the form '# 123 filename'
+   .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+   first line of the input file.  This is because the compiler outputs
+   #NO_APP at the beginning of its output.  */
+/* Also note that C style comments are always supported.  */
+const char line_comment_chars[] = "#";
+
+/* This array holds machine specific line separator characters.  */
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or    0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+   changed in read.c .  Ideally it shouldn't have to know about it at all,
+   but nothing is ideal around here.  */
+
+/* Types of printf format used for instruction-related error messages.
+   "I" means int ("%d") and "S" means string ("%s").  */
+enum nanomips_insn_error_format
+{
+  ERR_FMT_PLAIN,
+  ERR_FMT_I,
+  ERR_FMT_SS,
+  ERR_FMT_SI,
+};
+
+/* Information about an error that was found while assembling the current
+   instruction.  */
+struct nanomips_insn_error
+{
+  /* We sometimes need to match an instruction against more than one
+     opcode table entry.  Errors found during this matching are reported
+     against a particular syntactic argument rather than against the
+     instruction as a whole.  We grade these messages so that errors
+     against argument N have a greater priority than an error against
+     any argument < N, since the former implies that arguments up to N
+     were acceptable and that the opcode entry was therefore a closer match.
+     If several matches report an error against the same argument,
+     we only use that error if it is the same in all cases.
+
+     min_argnum is the minimum argument number for which an error message
+     should be accepted.  It is 0 if MSG is against the instruction as
+     a whole.  */
+  int min_argnum;
+
+  /* The printf()-style message, including its format and arguments.  */
+  enum nanomips_insn_error_format format;
+  const char *msg;
+  union
+  {
+    int i;
+    const char *ss[2];
+    struct
+    {
+      const char *s1;
+      int u1;
+    } si;
+  } u;
+};
+
+/* The error that should be reported for the current instruction.  */
+static struct nanomips_insn_error insn_error;
+
+static int auto_align = 1;
+
+static int nanomips_gp_register = GP;
+
+/* Debugging level.  -g sets this to 2.  -gN sets this to N.  -g0 is
+   equivalent to seeing no -g option at all.  */
+static int nanomips_debug = 0;
+
+/* A list of previous instructions, with index 0 being the most recent.
+   We need to look back MAX_NOPS instructions when filling delay slots
+   or working around processor errata.  We need to look back one
+   instruction further if we're thinking about using history to
+   fill a branch delay slot.  */
+static struct nanomips_cl_insn history;
+
+/* Arrays of operands for each instruction.  */
+#define MAX_OPERANDS 6
+struct nanomips_operand_array
+{
+  const struct nanomips_operand *operand[MAX_OPERANDS];
+};
+static struct nanomips_operand_array *nanomips_operands;
+
+/* Nop instructions used by emit_nop.  */
+static struct nanomips_cl_insn nanomips_nop16_insn;
+static struct nanomips_cl_insn nanomips_nop32_insn;
+
+/* The appropriate nop for the current mode.  */
+#define NOP_INSN (nanomips_opts.insn32		\
+		  ? &nanomips_nop32_insn	\
+		  : &nanomips_nop16_insn)
+
+/* The size of NOP_INSN in bytes.  */
+#define NOP_INSN_SIZE (nanomips_opts.insn32? 4 : 2)
+
+/* 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;
+
+/* Map 3-bit register numbers to normal nanoMIPS register numbers.  */
+
+static const unsigned int nanomips_to_32_reg_d_map[] = {
+  16, 17, 18, 19, 4, 5, 6, 7
+};
+
+/* The expansion of many macros depends on the type of symbol that
+   they refer to.  For example, when generating position-dependent code,
+   a macro that refers to a symbol may have two different expansions,
+   one which uses GP-relative addresses and one which uses absolute
+   addresses.  When generating SVR4-style PIC, a macro may have
+   different expansions for local and global symbols.
+
+   We handle these situations by generating both sequences and putting
+   them in variant frags.  In position-dependent code, the first sequence
+   will be the GP-relative one and the second sequence will be the
+   absolute one.  In SVR4 PIC, the first sequence will be for global
+   symbols and the second will be for local symbols.
+
+   The frag's "subtype" is RELAX_ENCODE (FIRST, SECOND), where FIRST and
+   SECOND are the lengths of the two sequences in bytes.  These fields
+   can be extracted using RELAX_FIRST() and RELAX_SECOND().  In addition,
+   the subtype has the following flags:
+
+   RELAX_USE_SECOND
+	Set if it has been decided that we should use the second
+	sequence instead of the first.
+
+   RELAX_SECOND_LONGER
+	Set in the first variant frag if the macro's second implementation
+	is longer than its first.  This refers to the macro as a whole,
+	not an individual relaxation.
+
+   RELAX_NOMACRO
+	Set in the first variant frag if the macro appeared in a .set nomacro
+	block and if one alternative requires a warning but the other does not.
+
+   The frag's "opcode" points to the first fixup for relaxable code.
+
+   Relaxable macros are generated using a sequence such as:
+
+      relax_start (SYMBOL);
+      ... generate first expansion ...
+      relax_switch ();
+      ... generate second expansion ...
+      relax_end ();
+
+   The code and fixups for the unwanted alternative are discarded
+   by md_convert_frag.  */
+#define RELAX_ENCODE(FIRST, SECOND) (((FIRST) << 8) | (SECOND))
+
+#define RELAX_FIRST(X) (((X) >> 8) & 0xff)
+#define RELAX_SECOND(X) ((X) & 0xff)
+#define RELAX_USE_SECOND 0x10000
+#define RELAX_SECOND_LONGER 0x20000
+#define RELAX_NOMACRO 0x40000
+
+/* For nanoMIPS code, we use relaxation similar to one we use for
+   microMIPS code.  Some instructions that take immediate values support
+   two encodings: a small one which takes some small value, and a
+   larger one which takes a 16 bit value.  As some branches also follow
+   this pattern, relaxing these values is required.
+
+   There are no delayed branches in nanoMIPS and we do not relax
+   32-bit branch instructions that do not fit within 32-bit range at
+   assembly, so attributes related to those features are removed.
+   Instead we relax 32-bit calls that do not fit within 16-bit range
+   in to calls to 32-bit unconditional branch stubs which can fit within
+   16-bit range.
+
+   USESTUB and ONCESTUB track calls which can be relaxed to stubs. KEEPSTUB
+   tracks stubs that have sufficient calls (at least 3)  against them to be
+   instantiated for reducing code size.
+
+   TOOFAR16/NEGOFF are for relaxation of the ADDIU opcode in to
+   32/NEG/R2/RS5 variants.
+   !TOOFAR16 && NEGOFF  => ADDIU[RS5], consequent to operand constraints
+   !TOOFAR16 && !NEGOFF => ADDIU[R2], consequent to operand constraints
+   TOOFAR16  && NEGOFF  => ADDIU[NEG]
+   TOOFAR16 &&  !NEGOFF => ADDIU[32]
+*/
+
+enum
+{
+  RT_ADDIU = 1,
+  RT_BRANCH_UCND,
+  RT_BRANCH_CNDZ,
+  RT_BRANCH_CND,
+  RT_BALC_STUB,
+};
+
+#define RELAX_MD_ENCODE(type, link, ext)			\
+  (0x20000000							\
+   | ((type) & 0xff)						\
+   | ((ext) ? 0x100 : 0)					\
+   | ((link) ? 0x2000 : 0))
+#define RELAX_MD_P(i) (((i) & 0xe0000000) == 0x20000000)
+#define RELAX_MD_TYPE(i) ((i) & 0xff)
+#define RELAX_MD_LINK(i) (((i) & 0x100) != 0)
+
+#define RELAX_MD_BALC_STUB_P(i) \
+  (RELAX_MD_P (i) && ((i) & 0xff) == RT_BALC_STUB)
+
+#define RELAX_MD_BRANCH_P(i)					\
+  (RELAX_MD_P (i) && (((i) & 0xff) == RT_BRANCH_UCND		\
+		  	   || ((i) & 0xff) == RT_BRANCH_CNDZ	\
+		  	   || ((i) & 0xff) == RT_BRANCH_CND))
+
+#define RELAX_MD_ADDIU_P(i)					\
+  (RELAX_MD_P (i) && ((i) & 0xff) == RT_ADDIU)
+
+#define RELAX_MD_KEEPSTUB(i) (((i) & 0x200) != 0)
+#define RELAX_MD_MARK_KEEPSTUB(i) ((i) | 0x200)
+#define RELAX_MD_CLEAR_KEEPSTUB(i) ((i) & ~0x200)
+
+#define RELAX_MD_USESTUB(i) (((i) & 0x400) != 0)
+#define RELAX_MD_MARK_USESTUB(i) ((i) | 0x400)
+#define RELAX_MD_CLEAR_USESTUB(i) ((i) & ~0x400)
+
+#define RELAX_MD_ONCESTUB(i) (((i) & 0x800) != 0)
+#define RELAX_MD_MARK_ONCESTUB(i) ((i) | 0x800)
+#define RELAX_MD_CLEAR_ONCESTUB(i) ((i) & ~0x800)
+
+#define RELAX_MD_TOOFAR16(i) (((i) & 0x1000) != 0)
+#define RELAX_MD_MARK_TOOFAR16(i) ((i) | 0x1000)
+#define RELAX_MD_CLEAR_TOOFAR16(i) ((i) & ~0x1000)
+
+#define RELAX_MD_TOOFAR32(i) (((i) & 0x2000) != 0)
+#define RELAX_MD_MARK_TOOFAR32(i) ((i) | 0x2000)
+#define RELAX_MD_CLEAR_TOOFAR32(i) ((i) & ~0x2000)
+
+#define RELAX_MD_NEGOFF(i) (((i) & 0x4000) != 0)
+#define RELAX_MD_MARK_NEGOFF(i) ((i) | 0x4000)
+#define RELAX_MD_CLEAR_NEGOFF(i) ((i) & ~0x4000)
+
+#define RELAX_MD_FIXED(i) (((i) & 0x1000) != 0)
+
+/* Sign-extend 16-bit value X.  */
+#define SEXT_16BIT(X) ((((X) + 0x8000) & 0xffff) - 0x8000)
+
+/* Is the given value a sign-extended 32-bit value?  */
+#define IS_SEXT_32BIT_NUM(x)						\
+  (((x) &~ (offsetT) 0x7fffffff) == 0					\
+   || (((x) &~ (offsetT) 0x7fffffff) == ~ (offsetT) 0x7fffffff))
+
+/* Is the given value an unsigned 16-bit value?  */
+#define IS_SEXT_16BIT_UINT(x)  (((x) &~ (offsetT) 0xffff) == 0)
+
+/* Is the given value a sign-extended 12-bit value?  */
+#define IS_SEXT_12BIT_NUM(x) (((((x) & 0xfff) ^ 0x800LL) - 0x800LL) == (x))
+
+/* Is the given value a sign-extended 9-bit value?  */
+#define IS_SEXT_9BIT_NUM(x) (((((x) & 0x1ff) ^ 0x100LL) - 0x100LL) == (x))
+
+/* Is the given value a zero-extended 32-bit value?  Or a negated one?  */
+#define IS_ZEXT_32BIT_NUM(x)						\
+  (((x) &~ (offsetT) 0xffffffff) == 0					\
+   || (((x) &~ (offsetT) 0xffffffff) == ~ (offsetT) 0xffffffff))
+
+/* Extract bits MASK << SHIFT from STRUCT and shift them right
+   SHIFT places.  */
+#define EXTRACT_BITS(STRUCT, MASK, SHIFT) \
+  (((STRUCT) >> (SHIFT)) & (MASK))
+
+/* Global variables used when generating relaxable macros.  See the
+   comment above RELAX_ENCODE for more details about how relaxation
+   is used.  */
+static struct
+{
+  /* 0 if we're not emitting a relaxable macro.
+     1 if we're emitting the first of the two relaxation alternatives.
+     2 if we're emitting the second alternative.  */
+  int sequence;
+
+  /* The first relaxable fixup in the current frag.  (In other words,
+     the first fixup that refers to relaxable code.)  */
+  fixS *first_fixup;
+
+  /* sizes[0] says how many bytes of the first alternative are stored in
+     the current frag.  Likewise sizes[1] for the second alternative.  */
+  unsigned int sizes[2];
+
+  /* The symbol on which the choice of sequence depends.  */
+  symbolS *symbol;
+} nanomips_relax;
+
+/* Global variables used to decide whether a macro needs a warning.  */
+static struct
+{
+  /* For relaxable macros, sizes[0] is the length of the first alternative
+     in bytes and sizes[1] is the length of the second alternative.
+     For non-relaxable macros, both elements give the length of the
+     macro in bytes.  */
+  unsigned int sizes[2];
+
+  /* For relaxable macros, first_insn_sizes[0] is the length of the first
+     instruction of the first alternative in bytes and first_insn_sizes[1]
+     is the length of the first instruction of the second alternative.
+     For non-relaxable macros, both elements give the length of the first
+     instruction in bytes.
+
+     Set to zero if we haven't yet seen the first instruction.  */
+  unsigned int first_insn_sizes[2];
+
+  /* For relaxable macros, insns[0] is the number of instructions for the
+     first alternative and insns[1] is the number of instructions for the
+     second alternative.
+
+     For non-relaxable macros, both elements give the number of
+     instructions for the macro.  */
+  unsigned int insns[2];
+
+  /* The first variant frag for this macro.  */
+  fragS *first_frag;
+} nanomips_macro_warning;
+
+/* Prototypes for static functions.  */
+
+enum nanomips_regclass
+  { NANOMIPS_GR_REG, NANOMIPS_FP_REG };
+
+static void append_insn
+  (struct nanomips_cl_insn *, expressionS *, bfd_reloc_code_real_type *,
+   bool expansionp);
+static void macro_build (expressionS *, const char *, const char *, ...);
+static void load_register (int, expressionS *, int);
+static void macro_start (void);
+static void macro_end (bool);
+static void macro (struct nanomips_cl_insn *ip, char *str);
+static void nanomips_ip (char *str, struct nanomips_cl_insn *ip);
+static size_t my_getSmallExpression
+  (expressionS *, bfd_reloc_code_real_type *, char *);
+static void my_getExpression (expressionS *, char *);
+static void s_align (int);
+static void s_change_sec (int);
+static void s_change_section (int);
+static void s_cons (int);
+static void s_float_cons (int);
+static void s_nanomipsset (int);
+static void s_cpsetup (int);
+static void s_dtprelword (int);
+static void s_dtpreldword (int);
+static void s_ehword (int);
+static void s_insn (int);
+static void s_module (int);
+static void s_nanomips_ent (int);
+static void s_nanomips_end (int);
+static void s_nanomips_frame (int);
+static void s_nanomips_mask (int reg_type);
+static void s_nanomips_file (int);
+static void s_nanomips_loc (int);
+static void s_linkrelax (int);
+static bool pic_need_relax (symbolS *, asection *);
+static void file_check_options (void);
+static void stubgroup_new (asection *);
+static void s_sign_cons (int);
+static bool nanomips_allow_local_subtract_symbols (symbolS *,
+						   symbolS *,
+						   bool);
+
+/* Table and functions used to map between CPU/ISA names, and
+   ISA levels, and CPU numbers.  */
+
+struct nanomips_cpu_info
+{
+  const char *name;		/* CPU or ISA name.  */
+  int flags;			/* NANOMIPS_CPU_* flags.  */
+  int ase;			/* Set of ASEs implemented by the CPU.  */
+  int isa;			/* ISA level.  */
+  int cpu;			/* CPU number (default CPU if ISA).  */
+};
+
+#define NANOMIPS_CPU_IS_ISA	0x0001	/* Is this an ISA?  (If 0, a CPU.) */
+
+static const struct nanomips_cpu_info *nanomips_parse_cpu
+  (const char *, const char *);
+static const struct nanomips_cpu_info *nanomips_cpu_info_from_isa
+  (int, bool);
+static const struct nanomips_cpu_info *nanomips_cpu_info_from_arch (int);
+
+/* Command-line options.  */
+const char md_shortopts[] = "O::g::G:";
+
+enum options
+{
+  OPTION_MARCH = OPTION_MD_BASE,
+  OPTION_MTUNE,
+  OPTION_NO_MIPS16,
+  OPTION_MT,
+  OPTION_NO_MT,
+  OPTION_VIRT,
+  OPTION_NO_VIRT,
+  OPTION_NO_MICROMIPS,
+  OPTION_TRAP,
+  OPTION_BREAK,
+  OPTION_EB,
+  OPTION_EL,
+  OPTION_CONSTRUCT_FLOATS,
+  OPTION_NO_CONSTRUCT_FLOATS,
+  OPTION_INSN32,
+  OPTION_NO_INSN32,
+  OPTION_SOFT_FLOAT,
+  OPTION_HARD_FLOAT,
+  OPTION_SINGLE_FLOAT,
+  OPTION_DOUBLE_FLOAT,
+  OPTION_32,
+  OPTION_64,
+  OPTION_M32,
+  OPTION_M64,
+  OPTION_BALC_STUBS,
+  OPTION_NO_BALC_STUBS,
+  OPTION_LEGACY_REGS,
+  OPTION_NO_LEGACY_REGS,
+  OPTION_LINKRELAX,
+  OPTION_PCREL,
+  OPTION_NO_PCREL,
+  OPTION_PID,
+  OPTION_NO_PID,
+  OPTION_PIC,
+  OPTION_NOPIC,
+  OPTION_LARGE_PIC,
+  OPTION_MCMODEL,
+  OPTION_END_OF_ENUM
+};
+
+const struct option md_longopts[] =
+{
+  /* Options which specify architecture.  */
+  {"march", required_argument, NULL, OPTION_MARCH},
+  {"mtune", required_argument, NULL, OPTION_MTUNE},
+
+  /* Options which specify Application Specific Extensions (ASEs).  */
+  {"mmt", no_argument, NULL, OPTION_MT},
+  {"mno-mt", no_argument, NULL, OPTION_NO_MT},
+  {"mvirt", no_argument, NULL, OPTION_VIRT},
+  {"mno-virt", no_argument, NULL, OPTION_NO_VIRT},
+
+  /* Miscellaneous options.  */
+  {"32", no_argument, NULL, OPTION_32},
+  {"64", no_argument, NULL, OPTION_64},
+  {"m32", no_argument, NULL, OPTION_M32},
+  {"m64", no_argument, NULL, OPTION_M64},
+  {"EB", no_argument, NULL, OPTION_EB},
+  {"EL", no_argument, NULL, OPTION_EL},
+  {"mcmodel", required_argument, NULL, OPTION_MCMODEL},
+  {"mpic", no_argument, NULL, OPTION_PIC},
+  {"mno-pic", no_argument, NULL, OPTION_NOPIC},
+  {"mPIC", no_argument, NULL, OPTION_LARGE_PIC},
+  {"mno-PIC", no_argument, NULL, OPTION_NOPIC},
+  {"mpid", no_argument, NULL, OPTION_PID},
+  {"mno-pid", no_argument, NULL, OPTION_NO_PID},
+  {"mpcrel", no_argument, NULL, OPTION_PCREL},
+  {"mno-pcrel", no_argument, NULL, OPTION_NO_PCREL},
+  {"minsn32", no_argument, NULL, OPTION_INSN32},
+  {"mno-insn32", no_argument, NULL, OPTION_NO_INSN32},
+  {"mdouble-float", no_argument, NULL, OPTION_DOUBLE_FLOAT},
+  {"mhard-float", no_argument, NULL, OPTION_HARD_FLOAT},
+  {"msingle-float", no_argument, NULL, OPTION_SINGLE_FLOAT},
+  {"msoft-float", no_argument, NULL, OPTION_SOFT_FLOAT},
+  {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS},
+  {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS},
+  {"mbalc-stubs", no_argument, NULL, OPTION_BALC_STUBS},
+  {"mno-balc-stubs", no_argument, NULL, OPTION_NO_BALC_STUBS},
+  {"mlegacyregs", no_argument, NULL, OPTION_LEGACY_REGS},
+  {"mno-legacyregs", no_argument, NULL, OPTION_NO_LEGACY_REGS},
+  {"linkrelax", no_argument, NULL, OPTION_LINKRELAX},
+  {"break", no_argument, NULL, OPTION_BREAK},
+  {"no-break", no_argument, NULL, OPTION_TRAP},
+  {"trap", no_argument, NULL, OPTION_TRAP},
+  {"no-trap", no_argument, NULL, OPTION_BREAK},
+
+  {NULL, no_argument, NULL, 0}
+};
+
+const size_t md_longopts_size = sizeof (md_longopts);
+
+/* Information about either an Application Specific Extension or an
+   optional architecture feature that, for simplicity, we treat in the
+   same way as an ASE.  */
+struct nanomips_ase
+{
+  /* The name of the ASE, used in both the command-line and .set options.  */
+  const char *name;
+
+  /* The associated ASE_* flags.  If the ASE is available on both 32-bit
+     and 64-bit architectures, the flags here refer to the subset that
+     is available on both.  */
+  unsigned int flags;
+
+  /* The ASE_* flag used for instructions that are available on 64-bit
+     architectures but that are not included in FLAGS.  */
+  unsigned int flags64;
+
+  /* The command-line options that turn the ASE on and off.  */
+  int option_on;
+  int option_off;
+
+  int nanomips32_rev;
+  int nanomips64_rev;
+
+  /* The architecture where the ASE was removed or -1 if the extension has not
+     been removed.  */
+  int rem_rev;
+};
+
+/* A table of all supported ASEs.  */
+static const struct nanomips_ase nanomips_ases[] = {
+  {"mt", ASE_MT, 0,
+   OPTION_MT, OPTION_NO_MT,
+   6, 6,
+   -1},
+
+  {"virt", ASE_VIRT, ASE_VIRT64,
+   OPTION_VIRT, OPTION_NO_VIRT,
+   6, 6,
+   -1},
+};
+
+/* Pseudo-op table.  */
+
+static const pseudo_typeS nanomips_pseudo_table[] = {
+  /* nanoMIPS specific pseudo-ops.  */
+  {"set", s_nanomipsset, 0},
+  {"rdata", s_change_sec, 'r'},
+  {"sdata", s_change_sec, 's'},
+  {"cpload", s_ignore, 0},
+  {"cpsetup", s_cpsetup, 0},
+  {"cplocal", s_ignore, 0},
+  {"cprestore", s_ignore, 0},
+  {"cpreturn", s_ignore, 0},
+  {"dtprelword", s_dtprelword, 0},
+  {"dtpreldword", s_dtpreldword, 0},
+  {"ehword", s_ehword, 0},
+  {"cpadd", s_ignore, 0},
+  {"insn", s_insn, 0},
+  {"module", s_module, 0},
+
+  /* Relatively generic pseudo-ops that happen to be used on MIPS.  */
+  {"bss", s_change_sec, 'b'},
+  {"err", s_err, 0},
+  {"half", s_cons, 1},
+  {"dword", s_cons, 3},
+  {"origin", s_org, 0},
+  {"repeat", s_rept, 0},
+
+  /* This is non-standard, but we define it for consistency.  */
+  {"sbss", s_change_sec, 'B'},
+
+  /* These pseudo-ops are defined in read.c, but must be overridden
+     here for one reason or another.  */
+  {"align", s_align, 0},
+  {"byte", s_cons, 0},
+  {"data", s_change_sec, 'd'},
+  {"double", s_float_cons, 'd'},
+  {"float", s_float_cons, 'f'},
+  {"hword", s_cons, 1},
+  {"int", s_cons, 2},
+  {"long", s_cons, 2},
+  {"octa", s_cons, 4},
+  {"quad", s_cons, 3},
+  {"section", s_change_section, 0},
+  {"short", s_cons, 1},
+  {"single", s_float_cons, 'f'},
+  {"text", s_change_sec, 't'},
+  {"word", s_cons, 2},
+  {"uleb128", s_leb128, 0},
+  {"sleb128", s_leb128, 1},
+
+  {"extern", ecoff_directive_extern, 0},
+
+  /* These psuedo-ops are specific to nanoMIPS relocatable expression
+     for jump offset tables.  */
+  {"sbyte", s_sign_cons, 0},
+  {"shword", s_sign_cons, 1},
+
+  /* Control linker-relaxation for nanoMIPS */
+  {"linkrelax", s_linkrelax, 0},
+
+  {NULL, NULL, 0},
+};
+
+static const pseudo_typeS nanomips_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_nanomips_ent, 1},
+  {"bgnb", s_ignore, 0},
+  {"end", s_nanomips_end, 0},
+  {"endb", s_ignore, 0},
+  {"ent", s_nanomips_ent, 0},
+  {"file", s_nanomips_file, 0},
+  {"fmask", s_nanomips_mask, 'F'},
+  {"frame", s_nanomips_frame, 0},
+  {"loc", s_nanomips_loc, 0},
+  {"mask", s_nanomips_mask, 'R'},
+  {"verstamp", s_ignore, 0},
+  {NULL, NULL, 0},
+};
+
+/* Data structures for BALC stub optimization for nanoMIPS ISA.  */
+/* List of call-sites to be resolved by a stub.  */
+struct call_list
+{
+  bfd_vma callsite;
+  struct call_list *next;
+};
+
+/* Tracking info for each planned stub.  */
+struct balc_stub
+{
+  unsigned int numcalls;
+  struct call_list *first_call;
+  struct call_list *last_call;
+  fragS *fragp;
+  symbolS *sym;
+};
+
+/* Doubly linked list of all planned stubs groups. */
+typedef struct stub_group_s
+{
+  htab_t stubtable;
+  bfd_vma next_offset;
+  fragS *fragp;
+  struct stub_group_s *prev;
+  struct stub_group_s *next;
+  asection *seg;
+} stub_group;
+
+/* Master-table of all stubgroups by section-name.  */
+static htab_t balc_stubgroup_table = NULL;
+/* Quick pointer to the stubgroup currently under consideration
+   for relaxation.  */
+static stub_group *stubg_now = NULL;
+/* Flag to check when we are in function-less mode.  */
+static bool stub_funcless_mode = false;
+/* BC32 to be used as a call-stub.  */
+static struct nanomips_cl_insn nanomips_bc32_insn;
+typedef struct proc
+{
+  symbolS *func_sym;
+  symbolS *func_end_sym;
+  unsigned long reg_mask;
+  unsigned long reg_offset;
+  unsigned long fpreg_mask;
+  unsigned long fpreg_offset;
+  unsigned long frame_offset;
+  unsigned long frame_reg;
+  unsigned long pc_reg;
+} procS;
+static procS *cur_proc_ptr;
+
+/* Tracking state for signed cons expression.  */
+static bool sign_cons = false;
+
+/* Local and reversed copy of the explicit relocation list.  */
+static struct reloc_list *reloc_list_copy;
+/* Last explicit relocation pointer matched to a frag.  */
+static struct reloc_list *reloc_list_iter;
+/* Whether the order of explicit relocations in the source  match
+   the order of the corresponding offset symbols.  The order is 
+   typically guaranteed by how the compiler generates explicit
+   relocations, we provide a fail-safe for handwritten assembly.  */
+bool reloc_ordered = true;
+
+/* Export the ABI address size for use by TC_ADDRESS_BYTES for the
+   purpose of the `.dc.a' internal pseudo-op.  */
+
+int
+nanomips_address_bytes (void)
+{
+  file_check_options ();
+  return HAVE_64BIT_ADDRESSES ? 8 : 4;
+}
+
+void
+nanomips_pop_insert (void)
+{
+  pop_insert (nanomips_pseudo_table);
+  if (!ECOFF_DEBUGGING)
+    pop_insert (nanomips_nonecoff_pseudo_table);
+}
+
+/* Symbols labelling the current insn.  */
+
+struct insn_label_list
+{
+  struct insn_label_list *next;
+  symbolS *label;
+};
+
+static struct insn_label_list *free_insn_labels;
+#define label_list tc_segment_info_data
+
+static void nanomips_clear_insn_labels (void);
+
+static inline void
+nanomips_clear_insn_labels (void)
+{
+  register struct insn_label_list **pl;
+  segment_info_type *si;
+
+  if (now_seg)
+    {
+      for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
+	;
+
+      si = seg_info (now_seg);
+      *pl = si->label_list;
+      si->label_list = NULL;
+    }
+}
+
+static char *expr_parse_end;
+
+/* An expression in a macro instruction.  This is set by nanomips_ip and
+    when populated is always an O_constant.  */
+
+static expressionS imm_expr;
+
+/* The relocatable field in an instruction and the relocs associated
+   with it.  These variables are used for instructions like LUI and
+   JAL as well as true offsets.  They are also used for address
+   operands in macros.  */
+
+static expressionS offset_expr;
+static bfd_reloc_code_real_type offset_reloc[3]
+  = { BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED };
+
+/* This is set to the resulting size of the instruction to be produced
+   by nanomips_ip if an explicit size is supplied.  */
+
+static unsigned int forced_insn_length;
+
+/* This is set if the mnemonic includes a specific [xx] format
+   notation from the architecture reference manual.  */
+
+static bool forced_insn_format;
+
+/* The default target format to use.  */
+
+#if defined (TE_FreeBSD)
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX ENDIAN "nanomips-freebsd"
+#elif defined (TE_TMIPS)
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX "trad" ENDIAN "nanomips"
+#else
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX ENDIAN "nanomips"
+#endif
+
+#define ELF_NTARGET(PREFIX, ENDIAN) PREFIX ENDIAN "nanomips"
+
+const char *
+nanomips_target_format (void)
+{
+  switch (OUTPUT_FLAVOR)
+    {
+    case bfd_target_elf_flavour:
+      return (target_big_endian
+	      ? (HAVE_64BIT_OBJECTS
+		 ? ELF_NTARGET ("elf64-", "big")
+		 : ELF_NTARGET ("elf32-", "big"))
+	      : (HAVE_64BIT_OBJECTS
+		 ? ELF_NTARGET ("elf64-", "little")
+		 : ELF_NTARGET ("elf32-", "little")));
+    default:
+      abort ();
+      return NULL;
+    }
+}
+
+/* Return the ISA revision that is currently in use.  */
+
+static inline int
+nanomips_isa_rev (void)
+{
+  return 6;
+}
+
+/* Check whether the current ISA supports ASE.  Issue a warning if
+   appropriate.  */
+
+static void
+check_isa_supports_ase (const struct nanomips_ase *ase)
+{
+  int min_rev, size;
+
+  if (ISA_HAS_64BIT_REGS (nanomips_opts.isa))
+    min_rev = ase->nanomips64_rev;
+  else
+    min_rev = ase->nanomips32_rev;
+
+  if (min_rev < 0 || nanomips_isa_rev () < min_rev)
+    {
+      size = ISA_HAS_64BIT_REGS (nanomips_opts.isa) ? 64 : 32;
+      if (min_rev < 0)
+	as_warn (_("the %d-bit nanoMIPS architecture does not support the"
+		   " `%s' extension"), size, ase->name);
+      else
+	as_warn (_("the `%s' extension requires nanoMIPS%d revision %d or "
+		   "greater"), ase->name, size, min_rev);
+    }
+}
+
+/* Check all enabled ASEs to see whether they are supported by the
+   chosen architecture.  */
+
+static void
+check_isa_supports_ases (void)
+{
+  unsigned int i, mask;
+
+  for (i = 0; i < ARRAY_SIZE (nanomips_ases); i++)
+    {
+      mask = nanomips_ases[i].flags;
+      if ((nanomips_opts.ase & mask) == nanomips_ases[i].flags)
+	check_isa_supports_ase (&nanomips_ases[i]);
+    }
+}
+
+/* Set the state of ASE to ENABLED_P.  Return the mask of ASE_* flags
+   that were affected.  */
+
+static unsigned int
+nanomips_set_ase (const struct nanomips_ase *ase,
+		  struct nanomips_set_options *opts, bool enabled_p)
+{
+  unsigned int mask;
+
+  mask = ase->flags;
+  opts->ase &= ~mask;
+
+  if (enabled_p)
+    opts->ase |= ase->flags;
+
+  return mask;
+}
+
+/* Return the ASE called NAME, or null if none.  */
+
+static const struct nanomips_ase *
+nanomips_lookup_ase (const char *name)
+{
+  unsigned int i;
+
+  for (i = 0; i < ARRAY_SIZE (nanomips_ases); i++)
+    if (strcmp (name, nanomips_ases[i].name) == 0)
+      return &nanomips_ases[i];
+  return NULL;
+}
+
+/* Return the length of a nanoMIPS instruction in bytes. */
+
+static inline unsigned int
+insn_length (const struct nanomips_opcode *mo)
+{
+  return (opcode_48bit_p (mo) ? 6 : (nanomips_opcode_32bit_p (mo) ? 4 : 2));
+}
+
+/* Initialise INSN from opcode entry MO.  Leave its position unspecified.  */
+
+static void
+create_insn (struct nanomips_cl_insn *insn, const struct nanomips_opcode *mo)
+{
+  size_t i;
+
+  insn->insn_mo = mo;
+  insn->insn_opcode = mo->match;
+  insn->frag = NULL;
+  insn->where = 0;
+  for (i = 0; i < ARRAY_SIZE (insn->fixp); i++)
+    insn->fixp[i] = NULL;
+  insn->fixed_p = (nanomips_opts.noreorder > 0);
+  insn->noreorder_p = (nanomips_opts.noreorder > 0);
+  insn->complete_p = 0;
+}
+
+/* Get a list of all the operands in INSN.  */
+
+static const struct nanomips_operand_array *
+insn_operands (const struct nanomips_cl_insn *insn)
+{
+  if (insn->insn_mo >= &nanomips_opcodes[0]
+      && insn->insn_mo < &nanomips_opcodes[bfd_nanomips_num_opcodes])
+    return &nanomips_operands[insn->insn_mo - &nanomips_opcodes[0]];
+
+  abort ();
+}
+
+/* Install UVAL as the value of OPERAND in INSN.  */
+
+static inline void
+insn_insert_operand (struct nanomips_cl_insn *insn,
+		     const struct nanomips_operand *operand,
+		     unsigned int uval)
+{
+  insn->insn_opcode = nanomips_insert_operand (operand, insn->insn_opcode,
+					       uval);
+}
+
+/* Extract the value of OPERAND from INSN.  */
+
+static inline unsigned
+insn_extract_operand (const struct nanomips_cl_insn *insn,
+		      const struct nanomips_operand *operand)
+{
+  return nanomips_extract_operand (operand, insn->insn_opcode);
+}
+
+/* Read a compressed instruction of length LENGTH.  */
+
+static unsigned long
+read_compressed_insn (char *buf, unsigned int length)
+{
+  unsigned long insn;
+  unsigned int i;
+
+  insn = 0;
+  for (i = 0; i < length; i += 2)
+    {
+      insn <<= 16;
+      if (target_big_endian)
+	insn |= bfd_getb16 ((char *) buf);
+      else
+	insn |= bfd_getl16 ((char *) buf);
+      buf += 2;
+    }
+  return insn;
+}
+
+/* Write compressed instruction INSN to BUF, given that the
+   instruction is LENGTH bytes long.  Return a pointer to the next byte.  */
+
+static char *
+write_compressed_insn (char *buf, unsigned int insn, unsigned int length)
+{
+  unsigned int i;
+
+  for (i = 0; i < length; i += 2)
+    md_number_to_chars (buf + i, insn >> ((length - i - 2) * 8), 2);
+
+  return buf + length;
+}
+
+/* Install INSN at the location specified by its "frag" and "where" fields.  */
+
+static void
+install_insn (const struct nanomips_cl_insn *insn)
+{
+  char *f = insn->frag->fr_literal + insn->where;
+
+  if (insn_length (insn->insn_mo) == 6)
+    {
+      write_compressed_insn (f, insn->insn_opcode, 2);
+      write_compressed_insn (f + 2, insn->insn_opcode_ext, 4);
+    }
+  else
+    write_compressed_insn (f, insn->insn_opcode, insn_length (insn->insn_mo));
+}
+
+static void
+toggle_linkrelax (fragS *frag, long where)
+{
+  nanomips_linkrelax_p = !nanomips_linkrelax_p;
+  toggle_linkrelax_p = false;
+  fix_new (frag, where, 0, &abs_symbol, 0, false,
+	   (nanomips_linkrelax_p
+	    ? BFD_RELOC_NANOMIPS_RELAX
+	    : BFD_RELOC_NANOMIPS_NORELAX));
+}
+
+/* Move INSN to offset WHERE in FRAG.  Adjust the fixups accordingly
+   and install the opcode in the new location.  */
+
+static void
+move_insn (struct nanomips_cl_insn *insn, fragS *frag, long where)
+{
+  size_t i;
+
+  insn->frag = frag;
+  insn->where = where;
+
+  if (toggle_linkrelax_p)
+    toggle_linkrelax (frag, where);
+
+  for (i = 0; i < ARRAY_SIZE (insn->fixp); i++)
+    if (insn->fixp[i] != NULL)
+      {
+	insn->fixp[i]->fx_frag = frag;
+	insn->fixp[i]->fx_where = where;
+      }
+  install_insn (insn);
+}
+
+/* Add INSN to the end of the output.  */
+
+static void
+add_fixed_insn (struct nanomips_cl_insn *insn)
+{
+  char *f = frag_more (insn_length (insn->insn_mo));
+  move_insn (insn, frag_now, f - frag_now->fr_literal);
+}
+
+/* Start a variant frag and move INSN to the start of the variant part,
+   marking it as fixed.  The other arguments are as for frag_var.  */
+
+static void
+add_relaxed_insn (struct nanomips_cl_insn *insn, int max_chars, int var,
+		  relax_substateT subtype, symbolS *symbol, offsetT offset)
+{
+  frag_grow (max_chars);
+  move_insn (insn, frag_now, frag_more (0) - frag_now->fr_literal);
+  insn->fixed_p = 1;
+  frag_var (rs_machine_dependent, max_chars, var, subtype, symbol,
+	    offset, NULL);
+}
+
+/* Insert N copies of INSN into the history buffer, starting at
+   position FIRST.  Neither FIRST nor N need to be clipped.  */
+
+static void
+insert_into_history (const struct nanomips_cl_insn *insn)
+{
+  if (nanomips_relax.sequence != 2)
+    history = *insn;
+}
+
+/* Clear the error in insn_error.  */
+
+static void
+clear_insn_error (void)
+{
+  memset (&insn_error, 0, sizeof (insn_error));
+}
+
+/* Possibly record error message MSG for the current instruction.
+   If the error is about a particular argument, ARGNUM is the 1-based
+   number of that argument, otherwise it is 0.  FORMAT is the format
+   of MSG.  Return true if MSG was used, false if the current message
+   was kept.  */
+
+static bool
+set_insn_error_format (int argnum, enum nanomips_insn_error_format format,
+		       const char *msg)
+{
+  if (argnum == 0)
+    {
+      /* Give priority to errors against specific arguments, and to
+	 the first whole-instruction message.  */
+      if (insn_error.msg)
+	return false;
+    }
+  else
+    {
+      /* Keep insn_error if it is against a later argument.  */
+      if (argnum < insn_error.min_argnum)
+	return false;
+
+      /* If both errors are against the same argument but are different,
+	 give up on reporting a specific error for this argument.
+	 See the comment about nanomips_insn_error for details.  */
+      if (argnum == insn_error.min_argnum
+	  && insn_error.msg
+	  && strcmp (insn_error.msg, msg) != 0)
+	{
+	  insn_error.msg = 0;
+	  insn_error.min_argnum += 1;
+	  return false;
+	}
+    }
+  insn_error.min_argnum = argnum;
+  insn_error.format = format;
+  insn_error.msg = msg;
+  return true;
+}
+
+/* Record an instruction error with no % format fields.  ARGNUM and MSG are
+   as for set_insn_error_format.  */
+
+static void
+set_insn_error (int argnum, const char *msg)
+{
+  set_insn_error_format (argnum, ERR_FMT_PLAIN, msg);
+}
+
+/* Record an instruction error with one %d field I.  ARGNUM and MSG are
+   as for set_insn_error_format.  */
+
+static void
+set_insn_error_i (int argnum, const char *msg, int i)
+{
+  if (set_insn_error_format (argnum, ERR_FMT_I, msg))
+    insn_error.u.i = i;
+}
+
+/* Record an instruction error with two %s fields S1 and S2.  ARGNUM and MSG
+   are as for set_insn_error_format.  */
+
+static void
+set_insn_error_ss (int argnum, const char *msg, const char *s1,
+		   const char *s2)
+{
+  if (set_insn_error_format (argnum, ERR_FMT_SS, msg))
+    {
+      insn_error.u.ss[0] = s1;
+      insn_error.u.ss[1] = s2;
+    }
+}
+
+static void
+set_insn_error_si (int argnum, const char *msg, const char *s1, const int i)
+{
+  if (set_insn_error_format (argnum, ERR_FMT_SI, msg))
+    {
+      insn_error.u.si.s1 = s1;
+      insn_error.u.si.u1 = i;
+    }
+}
+
+/* Report the error in insn_error, which is against assembly code STR.  */
+
+static void
+report_insn_error (const char *str)
+{
+  const char *msg = concat (insn_error.msg, " `%s'", (const char *) NULL);
+
+  switch (insn_error.format)
+    {
+    case ERR_FMT_PLAIN:
+      as_bad (msg, str);
+      break;
+
+    case ERR_FMT_I:
+      as_bad (msg, insn_error.u.i, str);
+      break;
+
+    case ERR_FMT_SS:
+      as_bad (msg, insn_error.u.ss[0], insn_error.u.ss[1], str);
+      break;
+
+    case ERR_FMT_SI:
+      as_bad (msg, insn_error.u.si.s1, insn_error.u.si.u1, str);
+      break;
+    }
+}
+
+struct regname
+{
+  const char *name;
+  unsigned int num;
+};
+
+#define RNUM_MASK	0x00003ff
+#define RTYPE_MASK	0x7fffc00
+#define RTYPE_NUM	0x0000400
+#define RTYPE_FPU	0x0000800
+#define RTYPE_FCC	0x0001000
+#define RTYPE_VEC	0x0002000
+#define RTYPE_GP	0x0004000
+#define RTYPE_CP0	0x0008000
+#define RTYPE_PC	0x0010000
+#define RTYPE_ACC	0x0020000
+#define RTYPE_CCC	0x0040000
+#define RTYPE_CP0SEL	0x0300000
+#define RTYPE_CP0SEL_EVEN	0x0100000
+#define RTYPE_CP0SEL_ODD	0x0200000
+#define RWARN		0x8000000
+
+#define GENERIC_REGISTER_NUMBERS \
+    {"$0",	RTYPE_NUM | 0},  \
+    {"$1",	RTYPE_NUM | 1},  \
+    {"$2",	RTYPE_NUM | 2},  \
+    {"$3",	RTYPE_NUM | 3},  \
+    {"$4",	RTYPE_NUM | 4},  \
+    {"$5",	RTYPE_NUM | 5},  \
+    {"$6",	RTYPE_NUM | 6},  \
+    {"$7",	RTYPE_NUM | 7},  \
+    {"$8",	RTYPE_NUM | 8},  \
+    {"$9",	RTYPE_NUM | 9},  \
+    {"$10",	RTYPE_NUM | 10}, \
+    {"$11",	RTYPE_NUM | 11}, \
+    {"$12",	RTYPE_NUM | 12}, \
+    {"$13",	RTYPE_NUM | 13}, \
+    {"$14",	RTYPE_NUM | 14}, \
+    {"$15",	RTYPE_NUM | 15}, \
+    {"$16",	RTYPE_NUM | 16}, \
+    {"$17",	RTYPE_NUM | 17}, \
+    {"$18",	RTYPE_NUM | 18}, \
+    {"$19",	RTYPE_NUM | 19}, \
+    {"$20",	RTYPE_NUM | 20}, \
+    {"$21",	RTYPE_NUM | 21}, \
+    {"$22",	RTYPE_NUM | 22}, \
+    {"$23",	RTYPE_NUM | 23}, \
+    {"$24",	RTYPE_NUM | 24}, \
+    {"$25",	RTYPE_NUM | 25}, \
+    {"$26",	RTYPE_NUM | 26}, \
+    {"$27",	RTYPE_NUM | 27}, \
+    {"$28",	RTYPE_NUM | 28}, \
+    {"$29",	RTYPE_NUM | 29}, \
+    {"$30",	RTYPE_NUM | 30}, \
+    {"$31",	RTYPE_NUM | 31}
+
+#define FPU_REGISTER_NAMES       \
+    {"$f0",	RTYPE_FPU | 0},  \
+    {"$f1",	RTYPE_FPU | 1},  \
+    {"$f2",	RTYPE_FPU | 2},  \
+    {"$f3",	RTYPE_FPU | 3},  \
+    {"$f4",	RTYPE_FPU | 4},  \
+    {"$f5",	RTYPE_FPU | 5},  \
+    {"$f6",	RTYPE_FPU | 6},  \
+    {"$f7",	RTYPE_FPU | 7},  \
+    {"$f8",	RTYPE_FPU | 8},  \
+    {"$f9",	RTYPE_FPU | 9},  \
+    {"$f10",	RTYPE_FPU | 10}, \
+    {"$f11",	RTYPE_FPU | 11}, \
+    {"$f12",	RTYPE_FPU | 12}, \
+    {"$f13",	RTYPE_FPU | 13}, \
+    {"$f14",	RTYPE_FPU | 14}, \
+    {"$f15",	RTYPE_FPU | 15}, \
+    {"$f16",	RTYPE_FPU | 16}, \
+    {"$f17",	RTYPE_FPU | 17}, \
+    {"$f18",	RTYPE_FPU | 18}, \
+    {"$f19",	RTYPE_FPU | 19}, \
+    {"$f20",	RTYPE_FPU | 20}, \
+    {"$f21",	RTYPE_FPU | 21}, \
+    {"$f22",	RTYPE_FPU | 22}, \
+    {"$f23",	RTYPE_FPU | 23}, \
+    {"$f24",	RTYPE_FPU | 24}, \
+    {"$f25",	RTYPE_FPU | 25}, \
+    {"$f26",	RTYPE_FPU | 26}, \
+    {"$f27",	RTYPE_FPU | 27}, \
+    {"$f28",	RTYPE_FPU | 28}, \
+    {"$f29",	RTYPE_FPU | 29}, \
+    {"$f30",	RTYPE_FPU | 30}, \
+    {"$f31",	RTYPE_FPU | 31}
+
+#define FPU_CONDITION_CODE_NAMES \
+    {"$fcc0",	RTYPE_FCC | 0},  \
+    {"$fcc1",	RTYPE_FCC | 1},  \
+    {"$fcc2",	RTYPE_FCC | 2},  \
+    {"$fcc3",	RTYPE_FCC | 3},  \
+    {"$fcc4",	RTYPE_FCC | 4},  \
+    {"$fcc5",	RTYPE_FCC | 5},  \
+    {"$fcc6",	RTYPE_FCC | 6},  \
+    {"$fcc7",	RTYPE_FCC | 7}
+
+#define COPROC_CONDITION_CODE_NAMES         \
+    {"$cc0",	RTYPE_FCC | RTYPE_CCC | 0}, \
+    {"$cc1",	RTYPE_FCC | RTYPE_CCC | 1}, \
+    {"$cc2",	RTYPE_FCC | RTYPE_CCC | 2}, \
+    {"$cc3",	RTYPE_FCC | RTYPE_CCC | 3}, \
+    {"$cc4",	RTYPE_FCC | RTYPE_CCC | 4}, \
+    {"$cc5",	RTYPE_FCC | RTYPE_CCC | 5}, \
+    {"$cc6",	RTYPE_FCC | RTYPE_CCC | 6}, \
+    {"$cc7",	RTYPE_FCC | RTYPE_CCC | 7}
+
+#define NANOMIPS_SYMBOLIC_REG_NAMES \
+    {"$zero",	RTYPE_GP | 0},  \
+    {"$at",	RTYPE_GP | 1},  \
+    {"$t4",	RTYPE_GP | 2},  \
+    {"$t5",	RTYPE_GP | 3},  \
+    {"$a0",	RTYPE_GP | 4},  \
+    {"$a1",	RTYPE_GP | 5},  \
+    {"$a2",	RTYPE_GP | 6},  \
+    {"$a3",	RTYPE_GP | 7},  \
+    {"$a4",	RTYPE_GP | 8},  \
+    {"$a5",	RTYPE_GP | 9},  \
+    {"$a6",	RTYPE_GP | 10}, \
+    {"$a7",	RTYPE_GP | 11}, \
+    {"$t0",	RTYPE_GP | 12}, \
+    {"$t1",	RTYPE_GP | 13}, \
+    {"$t2",	RTYPE_GP | 14}, \
+    {"$t3",	RTYPE_GP | 15}, \
+    {"$s0",	RTYPE_GP | 16}, \
+    {"$s1",	RTYPE_GP | 17}, \
+    {"$s2",	RTYPE_GP | 18}, \
+    {"$s3",	RTYPE_GP | 19}, \
+    {"$s4",	RTYPE_GP | 20}, \
+    {"$s5",	RTYPE_GP | 21}, \
+    {"$s6",	RTYPE_GP | 22}, \
+    {"$s7",	RTYPE_GP | 23}, \
+    {"$t8",	RTYPE_GP | 24}, \
+    {"$t9",	RTYPE_GP | 25}, \
+    {"$k0",	RTYPE_GP | 26}, \
+    {"$k1",	RTYPE_GP | 27}, \
+    {"$gp",	RTYPE_GP | 28}, \
+    {"$sp",	RTYPE_GP | 29}, \
+    {"$fp",	RTYPE_GP | 30}, \
+    {"$ra",	RTYPE_GP | 31}, \
+    {"$AT",	RTYPE_GP | 1}
+
+#define NANOMIPS_NUMERIC_REG_NAMES \
+    {"$r0",	RTYPE_GP | 0},  \
+    {"$r1",	RTYPE_GP | 1},  \
+    {"$r2",	RTYPE_GP | 2},  \
+    {"$r3",	RTYPE_GP | 3},  \
+    {"$r4",	RTYPE_GP | 4},  \
+    {"$r5",	RTYPE_GP | 5},  \
+    {"$r6",	RTYPE_GP | 6},  \
+    {"$r7",	RTYPE_GP | 7},  \
+    {"$r8",	RTYPE_GP | 8},  \
+    {"$r9",	RTYPE_GP | 9},  \
+    {"$r10",	RTYPE_GP | 10}, \
+    {"$r11",	RTYPE_GP | 11}, \
+    {"$r12",	RTYPE_GP | 12}, \
+    {"$r13",	RTYPE_GP | 13}, \
+    {"$r14",	RTYPE_GP | 14}, \
+    {"$r15",	RTYPE_GP | 15}, \
+    {"$r16",	RTYPE_GP | 16}, \
+    {"$r17",	RTYPE_GP | 17}, \
+    {"$r18",	RTYPE_GP | 18}, \
+    {"$r19",	RTYPE_GP | 19}, \
+    {"$r20",	RTYPE_GP | 20}, \
+    {"$r21",	RTYPE_GP | 21}, \
+    {"$r22",	RTYPE_GP | 22}, \
+    {"$r23",	RTYPE_GP | 23}, \
+    {"$r24",	RTYPE_GP | 24}, \
+    {"$r25",	RTYPE_GP | 25}, \
+    {"$r26",	RTYPE_GP | 26}, \
+    {"$r27",	RTYPE_GP | 27}, \
+    {"$r28",	RTYPE_GP | 28}, \
+    {"$r29",	RTYPE_GP | 29}, \
+    {"$r30",	RTYPE_GP | 30}, \
+    {"$r31",	RTYPE_GP | 31}
+
+#define NANOMIPS_DSP_ACCUMULATOR_NAMES \
+    {"$ac0",	RTYPE_ACC | 0}, \
+    {"$ac1",	RTYPE_ACC | 1}, \
+    {"$ac2",	RTYPE_ACC | 2}, \
+    {"$ac3",	RTYPE_ACC | 3}
+
+static const struct regname nanomips_reg_names[] = {
+  GENERIC_REGISTER_NUMBERS,
+  NANOMIPS_SYMBOLIC_REG_NAMES,
+  NANOMIPS_NUMERIC_REG_NAMES,
+  FPU_REGISTER_NAMES,
+  FPU_CONDITION_CODE_NAMES,
+  COPROC_CONDITION_CODE_NAMES,
+  NANOMIPS_DSP_ACCUMULATOR_NAMES,
+  {0, 0}
+};
+
+/* Register symbols $v0 and $v1 map to GPRs 2 and 3, but they can also be
+   interpreted as vector registers 0 and 1.  If SYMVAL is the value of one
+   of these register symbols, return the associated vector register,
+   otherwise return SYMVAL itself.  */
+
+static unsigned int
+nanomips_prefer_vec_regno (unsigned int symval)
+{
+  if ((symval & -2) == (RTYPE_GP | 2))
+    return RTYPE_VEC | (symval & 1);
+  return symval;
+}
+
+/* Return true if string [S, E) is a valid register name, storing its
+   symbol value in *SYMVAL_PTR if so.  */
+
+static bool
+nanomips_parse_register_1 (char *s, char *e, unsigned int *symval_ptr)
+{
+  char save_c;
+  symbolS *symbol;
+
+  /* Terminate name.  */
+  save_c = *e;
+  *e = '\0';
+
+  /* Look up the name.  */
+  symbol = symbol_find (s);
+  *e = save_c;
+
+  if (!symbol || S_GET_SEGMENT (symbol) != reg_section)
+    return false;
+
+  *symval_ptr = S_GET_VALUE (symbol);
+  return true;
+}
+
+/* Return true if the string at *SPTR is a valid register name.
+
+   When returning true, move *SPTR past the register, store the
+   register's symbol value in *SYMVAL_PTR.  */
+
+static bool
+nanomips_parse_register (char **sptr, unsigned int *symval_ptr)
+{
+  char *s, *e;
+  unsigned int symval;
+
+  /* Find end of name.  */
+  s = e = *sptr;
+  if (is_name_beginner (*e))
+    ++e;
+  while (is_part_of_name (*e))
+    ++e;
+
+  if (!nanomips_parse_register_1 (s, e, &symval))
+    return false;
+
+  *sptr = e;
+  *symval_ptr = symval;
+  return true;
+}
+
+/* Check if SPTR points at a valid register specifier according to TYPES.
+   If so, then return 1, advance S to consume the specifier and store
+   the register's number in REGNOP, otherwise return 0.  */
+
+static int
+reg_lookup (char **s, unsigned int types, unsigned int *regnop)
+{
+  unsigned int regno;
+
+  if (nanomips_parse_register (s, &regno))
+    {
+      if (types & RTYPE_VEC)
+	regno = nanomips_prefer_vec_regno (regno);
+      if (regno & types)
+	regno &= RNUM_MASK;
+      else
+	regno = ~0;
+    }
+  else
+    {
+      if (types & RWARN)
+	as_warn (_("unrecognized register name `%s'"), *s);
+      regno = ~0;
+    }
+  if (regnop)
+    *regnop = regno;
+  return regno <= RNUM_MASK;
+}
+
+/* Token types for parsed operand lists.  */
+enum nanomips_operand_token_type
+{
+  /* A plain register, e.g. $f2.  */
+  OT_REG,
+
+  /* A constant vector index, e.g. [1].  */
+  OT_INTEGER_INDEX,
+
+  /* A register vector index, e.g. [$2].  */
+  OT_REG_INDEX,
+
+  /* A continuous range of registers, e.g. $s0-$s4.  */
+  OT_REG_RANGE,
+
+  /* A (possibly relocated) expression.  */
+  OT_INTEGER,
+
+  /* A floating-point value.  */
+  OT_FLOAT,
+
+  /* A single character.  This can be '(', ')' or ',', but '(' only appears
+     before OT_REGs.  */
+  OT_CHAR,
+
+  /* A doubled character, either "--" or "++".  */
+  OT_DOUBLE_CHAR,
+
+  /* The end of the operand list.  */
+  OT_END
+};
+
+/* A parsed operand token.  */
+struct nanomips_operand_token
+{
+  /* The type of token.  */
+  enum nanomips_operand_token_type type;
+  union
+  {
+    /* The register symbol value for an OT_REG or OT_REG_INDEX.  */
+    unsigned int regno;
+
+    /* The integer value of an OT_INTEGER_INDEX.  */
+    addressT index;
+
+    /* The two register symbol values involved in an OT_REG_RANGE.  */
+    struct
+    {
+      unsigned int regno1;
+      unsigned int regno2;
+    } reg_range;
+
+    /* The value of an OT_INTEGER.  The value is represented as an
+       expression and the relocation operators that were applied to
+       that expression.  The reloc entries are BFD_RELOC_UNUSED if no
+       relocation operators were used.  */
+    struct
+    {
+      expressionS value;
+      bfd_reloc_code_real_type relocs[3];
+    } integer;
+
+    /* The binary data for an OT_FLOAT constant, and the number of bytes
+       in the constant.  */
+    struct
+    {
+      unsigned char data[8];
+      int length;
+    } flt;
+
+    /* The character represented by an OT_CHAR or OT_DOUBLE_CHAR.  */
+    char ch;
+  } u;
+};
+
+/* An obstack used to construct lists of nanomips_operand_tokens.  */
+static struct obstack nanomips_operand_tokens;
+
+/* Give TOKEN type TYPE and add it to nanomips_operand_tokens.  */
+
+static void
+nanomips_add_token (struct nanomips_operand_token *token,
+		    enum nanomips_operand_token_type type)
+{
+  token->type = type;
+  obstack_grow (&nanomips_operand_tokens, token, sizeof (*token));
+}
+
+/* Check whether S is '(' followed by a register name.  Add OT_CHAR
+   and OT_REG tokens for them if so, and return a pointer to the first
+   unconsumed character.  Return null otherwise.  */
+
+static char *
+nanomips_parse_base_start (char *s)
+{
+  struct nanomips_operand_token token;
+  unsigned int regno;
+  bool decrement_p;
+
+  if (*s != '(')
+    return 0;
+
+  ++s;
+  SKIP_SPACE_TABS (s);
+
+  /* Only match "--" as part of a base expression.  In other contexts "--X"
+     is a double negative.  */
+  decrement_p = (s[0] == '-' && s[1] == '-');
+  if (decrement_p)
+    {
+      s += 2;
+      SKIP_SPACE_TABS (s);
+    }
+
+  /* Allow a channel specifier because that leads to better error messages
+     than treating something like "$vf0x++" as an expression.  */
+  if (!nanomips_parse_register (&s, &regno))
+    return 0;
+
+  token.u.ch = '(';
+  nanomips_add_token (&token, OT_CHAR);
+
+  if (decrement_p)
+    {
+      token.u.ch = '-';
+      nanomips_add_token (&token, OT_DOUBLE_CHAR);
+    }
+
+  token.u.regno = regno;
+  nanomips_add_token (&token, OT_REG);
+
+  /* For consistency, only match "++" as part of base expressions too.  */
+  SKIP_SPACE_TABS (s);
+  if (s[0] == '+' && s[1] == '+')
+    {
+      s += 2;
+      token.u.ch = '+';
+      nanomips_add_token (&token, OT_DOUBLE_CHAR);
+    }
+  return s;
+}
+
+/* Parse one or more tokens from S.  Return a pointer to the first
+   unconsumed character on success.  Return null if an error was found
+   and store the error text in insn_error.  FLOAT_FORMAT is as for
+   nanomips_parse_arguments.  */
+
+static char *
+nanomips_parse_argument_token (char *s, char float_format)
+{
+  char *end, *save_in;
+  const char *err;
+  unsigned int regno1, regno2;
+  struct nanomips_operand_token token;
+
+  /* First look for "($reg", since we want to treat that as an
+     OT_CHAR and OT_REG rather than an expression.  */
+  end = nanomips_parse_base_start (s);
+  if (end)
+    return end;
+
+  /* Handle other characters that end up as OT_CHARs.  */
+  if (*s == ')' || *s == ',')
+    {
+      token.u.ch = *s;
+      nanomips_add_token (&token, OT_CHAR);
+      ++s;
+      return s;
+    }
+
+  /* Handle tokens that start with a register.  */
+  if (nanomips_parse_register (&s, &regno1))
+    {
+      SKIP_SPACE_TABS (s);
+      if (*s == '-')
+	{
+	  /* A register range.  */
+	  ++s;
+	  SKIP_SPACE_TABS (s);
+	  if (!nanomips_parse_register (&s, &regno2))
+	    {
+	      set_insn_error (0, _("invalid register range"));
+	      return 0;
+	    }
+
+	  token.u.reg_range.regno1 = regno1;
+	  token.u.reg_range.regno2 = regno2;
+	  nanomips_add_token (&token, OT_REG_RANGE);
+	  return s;
+	}
+
+      /* Add the register itself.  */
+      token.u.regno = regno1;
+      nanomips_add_token (&token, OT_REG);
+
+      /* Check for a vector index.  */
+      if (*s == '[')
+	{
+	  ++s;
+	  SKIP_SPACE_TABS (s);
+	  if (nanomips_parse_register (&s, &token.u.regno))
+	    nanomips_add_token (&token, OT_REG_INDEX);
+	  else
+	    {
+	      expressionS element;
+
+	      my_getExpression (&element, s);
+	      if (element.X_op != O_constant)
+		{
+		  set_insn_error (0, _("vector element must be constant"));
+		  return 0;
+		}
+	      s = expr_parse_end;
+	      token.u.index = element.X_add_number;
+	      nanomips_add_token (&token, OT_INTEGER_INDEX);
+	    }
+	  SKIP_SPACE_TABS (s);
+	  if (*s != ']')
+	    {
+	      set_insn_error (0, _("missing `]'"));
+	      return 0;
+	    }
+	  ++s;
+	}
+      return s;
+    }
+
+  if (float_format)
+    {
+      /* First try to treat expressions as floats.  */
+      save_in = input_line_pointer;
+      input_line_pointer = s;
+      err = md_atof (float_format, (char *) token.u.flt.data,
+		     &token.u.flt.length);
+      end = input_line_pointer;
+      input_line_pointer = save_in;
+      if (err && *err)
+	{
+	  set_insn_error (0, err);
+	  return 0;
+	}
+      if (s != end)
+	{
+	  nanomips_add_token (&token, OT_FLOAT);
+	  return end;
+	}
+    }
+
+  /* Treat everything else as an integer expression.  */
+  token.u.integer.relocs[0] = BFD_RELOC_UNUSED;
+  token.u.integer.relocs[1] = BFD_RELOC_UNUSED;
+  token.u.integer.relocs[2] = BFD_RELOC_UNUSED;
+  my_getSmallExpression (&token.u.integer.value, token.u.integer.relocs, s);
+  s = expr_parse_end;
+  nanomips_add_token (&token, OT_INTEGER);
+  return s;
+}
+
+/* S points to the operand list for an instruction.  FLOAT_FORMAT is 'f'
+   if expressions should be treated as 32-bit floating-point constants,
+   'd' if they should be treated as 64-bit floating-point constants,
+   or 0 if they should be treated as integer expressions (the usual case).
+
+   Return a list of tokens on success, otherwise return 0.  The caller
+   must obstack_free the list after use.  */
+
+static struct nanomips_operand_token *
+nanomips_parse_arguments (char *s, char float_format)
+{
+  struct nanomips_operand_token token;
+
+  SKIP_SPACE_TABS (s);
+  while (*s)
+    {
+      s = nanomips_parse_argument_token (s, float_format);
+      if (!s)
+	{
+	  obstack_free (&nanomips_operand_tokens,
+			obstack_base (&nanomips_operand_tokens));
+	  return 0;
+	}
+      SKIP_SPACE_TABS (s);
+    }
+  nanomips_add_token (&token, OT_END);
+  return obstack_finish (&nanomips_operand_tokens);
+}
+
+/* Return true if opcode MO is valid for the currently selected
+   floating point configuration.  */
+
+static bool
+is_opcode_valid_for_fp (const struct nanomips_opcode *mo)
+{
+  int fp_s, fp_d;
+
+  /* Check whether the instruction or macro requires single-precision or
+     double-precision floating-point support.  Note that this information is
+     stored differently in the opcode table for insns and macros.  */
+  if (mo->pinfo == INSN_MACRO)
+    {
+      fp_s = mo->pinfo2 & INSN2_M_FP_S;
+      fp_d = mo->pinfo2 & INSN2_M_FP_D;
+    }
+  else
+    {
+      fp_s = mo->pinfo & INSN_FP_S;
+      fp_d = mo->pinfo & INSN_FP_D;
+    }
+
+  if (fp_d && (nanomips_opts.soft_float || nanomips_opts.single_float))
+    return false;
+
+  if (fp_s && nanomips_opts.soft_float)
+    return false;
+
+  return true;
+}
+
+/* Return true if opcode MO is valid for the currently selected ISA
+   with the specified ASE.  */
+
+static bool
+is_opcode_valid_for_ase (const struct nanomips_opcode *mo, int ase)
+{
+  int isa = nanomips_opts.isa;
+  unsigned int i;
+
+  if (ISA_HAS_64BIT_REGS (isa))
+    for (i = 0; i < ARRAY_SIZE (nanomips_ases); i++)
+      if ((ase & nanomips_ases[i].flags) == nanomips_ases[i].flags)
+	ase |= nanomips_ases[i].flags64;
+
+  return nanomips_opcode_is_member (mo, isa, ase, nanomips_opts.arch);
+}
+
+/* Return true if opcode MO is valid for the currently selected ISA,
+   ASE and floating point configuration.  */
+
+static bool
+is_opcode_valid (const struct nanomips_opcode *mo)
+{
+  return (is_opcode_valid_for_ase (mo, nanomips_opts.ase)
+	  && is_opcode_valid_for_fp (mo));
+}
+
+/* Return true if opcode MO is valid for the currently selected ISA
+   and architecture with a default ASE selection.  */
+
+static bool
+is_opcode_valid_def_ase (const struct nanomips_opcode *mo)
+{
+  const struct nanomips_cpu_info *info;
+  int ase;
+
+  info = nanomips_cpu_info_from_isa (nanomips_opts.isa,
+				     (nanomips_opts.ase & ASE_xNMS) == 0);
+  ase = info->ase | nanomips_opts.ase;
+
+  return is_opcode_valid_for_ase (mo, ase);
+}
+
+/* Return true if the size of the nanoMIPS opcode MO matches one
+   explicitly requested.  */
+
+static bool
+is_size_valid (const struct nanomips_opcode *mo)
+{
+  if (nanomips_opts.insn32 && mo->pinfo != INSN_MACRO && insn_length (mo) != 4)
+    return false;
+
+  if (!forced_insn_length)
+    return true;
+
+  if (mo->pinfo == INSN_MACRO)
+    return false;
+
+  return forced_insn_length == insn_length (mo);
+}
+
+/* For consistency checking, verify that all bits of OPCODE are specified
+   either by the match/mask part of the instruction definition, or by the
+   operand list.  Also build up a list of operands in OPERANDS.
+
+   INSN_BITS says which bits of the instruction are significant.
+   DECODE_OPERAND rovides the nanomips_operand description of each operand.  */
+
+static int
+validate_insn (const struct nanomips_opcode *opcode, unsigned long insn_bits,
+	       struct nanomips_operand_array *operands)
+{
+  const char *s;
+  unsigned long used_bits, doubled, undefined, opno, mask;
+  const struct nanomips_operand *operand;
+
+  mask = (opcode->pinfo == INSN_MACRO ? 0 : opcode->mask);
+  if ((mask & opcode->match) != opcode->match)
+    {
+      as_bad (_("internal: bad nanomips opcode (mask error): %s %s"),
+	      opcode->name, opcode->args);
+      return 0;
+    }
+  used_bits = 0;
+  opno = 0;
+
+  for (s = opcode->args; *s; ++s)
+    switch (*s)
+      {
+      case ',':
+      case '(':
+      case ')':
+	break;
+
+      case '#':
+	s++;
+	break;
+
+      default:
+	operand = decode_nanomips_operand (s);
+	if (!operand && opcode->pinfo != INSN_MACRO)
+	  {
+	    as_bad (_("internal: unknown operand type: %s %s"),
+		    opcode->name, opcode->args);
+	    return 0;
+	  }
+	gas_assert (opno < MAX_OPERANDS);
+	operands->operand[opno] = operand;
+	if (operand != NULL)
+	  used_bits = nanomips_insert_operand (operand, used_bits, -1);
+	/* Skip prefix characters.  */
+	if (*s == '+' || *s == 'm' || *s == '-')
+	  ++s;
+	opno += 1;
+	break;
+      }
+  doubled = used_bits & mask & insn_bits;
+  if (doubled)
+    {
+      as_bad (_("internal: bad nanomips opcode (bits 0x%08lx doubly defined):"
+		" %s %s"), doubled, opcode->name, opcode->args);
+      return 0;
+    }
+  used_bits |= mask;
+  undefined = ~used_bits & insn_bits;
+  if (opcode->pinfo != INSN_MACRO && undefined)
+    {
+      as_bad (_("internal: bad nanomips opcode (bits 0x%08lx undefined): "
+		"%s %s"), undefined, opcode->name, opcode->args);
+      return 0;
+    }
+  used_bits &= ~insn_bits;
+  if (used_bits)
+    {
+      as_bad (_("internal: bad nanomips opcode (bits 0x%08lx defined): "
+		"%s %s"), used_bits, opcode->name, opcode->args);
+      return 0;
+    }
+  return 1;
+}
+
+static int
+validate_nanomips_insn (const struct nanomips_opcode *opc,
+			struct nanomips_operand_array *operands)
+{
+  unsigned long insn_bits;
+  unsigned long major;
+  unsigned int length;
+
+  if (opc->pinfo == INSN_MACRO)
+    return validate_insn (opc, 0xffffffff, operands);
+
+  length = insn_length (opc);
+  if (length != 2 && length != 4 && length != 6)
+    {
+      as_bad (_("internal error: bad nanoMIPS opcode (incorrect length: %u): "
+		"%s %s"), length, opc->name, opc->args);
+      return 0;
+    }
+
+  if (length == 6)
+    /* 48-bit instruction is encoded as 16-bits in the opcode table.  */
+    major = opc->match >> 10;
+  else
+    major = opc->match >> (10 + 8 * (length - 2));
+
+  if ((length == 2 && !(major & 4))
+      || (length == 4 && (major & 4))
+      || (length == 6 && !(major & 18)))
+    {
+      as_bad (_("internal error: bad nanoMIPS opcode "
+		"(opcode/length mismatch): %s %s"), opc->name, opc->args);
+      return 0;
+    }
+
+  if (length == 6)
+    /* 48-bit instruction is encoded as 16-bits in the opcode table.  */
+    insn_bits = 1 << 16;
+  else
+    {
+      /* Shift piecewise to avoid an overflow where unsigned long is 32-bit.  */
+      insn_bits = 1 << 4 * length;
+      insn_bits <<= 4 * length;
+    }
+
+  insn_bits -= 1;
+  return validate_insn (opc, insn_bits, operands);
+}
+
+/* This function is called once, at assembler startup time.  It should set up
+   all the tables, etc. that the MD part of the assembler will need.  */
+
+void
+md_begin (void)
+{
+  int i = 0;
+  int broken = 0;
+  int count_marker = 0;
+
+  if (nanomips_opts.pic != NO_PIC)
+    {
+      if (g_switch_seen && g_switch_value != 0)
+	as_bad (_("-G may not be used in position-independent code"));
+      g_switch_value = 0;
+    }
+
+  if (!bfd_set_arch_mach (stdoutput, bfd_arch_nanomips,
+			  file_nanomips_opts.arch))
+    as_warn (_("could not set architecture and machine"));
+
+  op_hash = str_htab_create ();
+
+  nanomips_op_hash = str_htab_create ();
+  nanomips_operands = XCNEWVEC (struct nanomips_operand_array,
+				bfd_nanomips_num_opcodes);
+
+  i = 0;
+  while (i < bfd_nanomips_num_opcodes)
+    {
+      const char *name = nanomips_opcodes[i].name;
+
+      if (str_hash_insert (nanomips_op_hash, name, &nanomips_opcodes[i], 0))
+	      as_fatal (_("duplicate %s"), name);
+      do
+	{
+	  struct nanomips_cl_insn *insn;
+
+	  if (!validate_nanomips_insn (&nanomips_opcodes[i],
+				       &nanomips_operands[i]))
+	    broken = 1;
+
+	  if (count_marker < 3 && nanomips_opcodes[i].pinfo != INSN_MACRO)
+	    {
+	      if (nanomips_nop16_insn.insn_mo == NULL
+		  && insn_length (&nanomips_opcodes[i]) == 2
+		  && strcmp (name, "nop") == 0)
+		insn = &nanomips_nop16_insn;
+	      else if (nanomips_nop32_insn.insn_mo == NULL
+		       && insn_length (&nanomips_opcodes[i]) == 4
+		       && strcmp (name, "nop") == 0)
+		insn = &nanomips_nop32_insn;
+	      else if (nanomips_bc32_insn.insn_mo == NULL
+		       && insn_length (&nanomips_opcodes[i]) == 4
+		       && strcmp (name, "bc") == 0)
+		insn = &nanomips_bc32_insn;
+	      else
+		continue;
+
+	      create_insn (insn, nanomips_opcodes + i);
+	      insn->fixed_p = 1;
+	      count_marker++;
+	    }
+	}
+      while (++i < bfd_nanomips_num_opcodes
+	     && strcmp (nanomips_opcodes[i].name, name) == 0);
+    }
+
+  i = 0;
+  while (i < bfd_nanomips_num_opcodes)
+    {
+      const struct nanomips_opcode *opcode = &nanomips_opcodes[i];
+      if (opcode->suffix[0] != '\0')
+	{
+	  char *name;
+	  name = xmalloc (strlen (opcode->name) + strlen (opcode->suffix) + 1);
+	  name = strcpy (name, opcode->name);
+	  name = strcat (name, opcode->suffix);
+
+	  if (str_hash_find (nanomips_op_hash, name) == NULL)
+	    {
+	      if (str_hash_insert (nanomips_op_hash, name, &nanomips_opcodes[i], 0))
+		      as_fatal (_("duplicate %s"), name);
+	    }
+	}
+      i++;
+    }
+
+  if (broken)
+    as_fatal (_("broken assembler, no assembly attempted"));
+
+  /* We add all the general register names to the symbol table.  This
+     helps us detect invalid uses of them.  */
+  for (i = 0; nanomips_reg_names[i].name; i++)
+    {
+      symbolS *regsym = symbol_new (nanomips_reg_names[i].name, reg_section,
+				    &zero_address_frag, 
+				    nanomips_reg_names[i].num);
+      symbolS *defsym = symbol_find (nanomips_reg_names[i].name);
+      symbol_table_insert (regsym);
+      if (defsym)
+	as_warn ("Attempt to define internal register symbol %s ignored",
+		 nanomips_reg_names[i].name);
+    }
+
+  for (i = 0; nanomips_cp0_3264r6[i].name; i++)
+    {
+      /* 10-bit symbol value for CP0 named register consists of a 5-bit
+	 register number and 5-bit fixed select value.  */
+      unsigned int value = (RTYPE_CP0
+			    | (nanomips_cp0_3264r6[i].num
+			       << NANOMIPSOP_SH_CP0SEL)
+			    | nanomips_cp0_3264r6[i].sel);
+      symbolS *regsym = symbol_new (nanomips_cp0_3264r6[i].name, reg_section,
+				    &zero_address_frag, value);
+      symbolS *defsym = symbol_find (nanomips_cp0_3264r6[i].name);
+      symbol_table_insert (regsym);
+      if (defsym)
+	as_warn ("Attempt to define internal register symbol %s ignored",
+		 nanomips_cp0_3264r6[i].name);
+    }
+
+  for (i = 0; nanomips_cp0sel_3264r6[i].name; i++)
+    {
+      symbolS *regsym, *defsym;
+      unsigned value = nanomips_cp0sel_3264r6[i].num;
+
+      switch (nanomips_cp0sel_3264r6[i].selmask)
+	{
+	case NANOMIPS_CP0SEL_MASK_EVEN:
+	  value |= RTYPE_CP0SEL_EVEN;
+	  break;
+	case NANOMIPS_CP0SEL_MASK_ODD:
+	  value |= RTYPE_CP0SEL_ODD;
+	  break;
+	case NANOMIPS_CP0SEL_MASK_ANY:
+	  value |= RTYPE_CP0SEL;
+	  break;
+	default:
+	  break;
+	}
+
+      regsym = symbol_new (nanomips_cp0sel_3264r6[i].name, reg_section,
+			   &zero_address_frag, value);
+      defsym = symbol_find (nanomips_cp0sel_3264r6[i].name);
+      symbol_table_insert (regsym);
+      if (defsym)
+	as_warn ("Attempt to define internal register symbol %s ignored",
+		 nanomips_cp0sel_3264r6[i].name);
+    }
+
+  obstack_init (&nanomips_operand_tokens);
+
+  nanomips_flush_pending_output ();
+
+  bfd_set_gp_size (stdoutput, g_switch_value);
+
+  {
+    segT seg;
+    subsegT subseg;
+    segT sec;
+
+    seg = now_seg;
+    subseg = now_subseg;
+
+    subseg_set (seg, subseg);
+  }
+}
+
+static inline void
+fpabi_incompatible_with (int fpabi, const char *what)
+{
+  as_warn (_(".gnu_attribute %d,%d is incompatible with `%s'"),
+	   Tag_GNU_NANOMIPS_ABI_FP, fpabi, what);
+}
+
+static inline void
+fpabi_requires (int fpabi, const char *what)
+{
+  as_warn (_(".gnu_attribute %d,%d requires `%s'"),
+	   Tag_GNU_NANOMIPS_ABI_FP, fpabi, what);
+}
+
+/* Check -mabi and register sizes against the specified FP ABI.  */
+static void
+check_fpabi (int fpabi)
+{
+  switch (fpabi)
+    {
+    case Val_GNU_NANOMIPS_ABI_FP_DOUBLE:
+      if (file_nanomips_opts.soft_float)
+	fpabi_incompatible_with (fpabi, "softfloat");
+      else if (file_nanomips_opts.single_float)
+	fpabi_incompatible_with (fpabi, "singlefloat");
+      break;
+
+    case Val_GNU_NANOMIPS_ABI_FP_SINGLE:
+      if (file_nanomips_opts.soft_float)
+	fpabi_incompatible_with (fpabi, "softfloat");
+      else if (!file_nanomips_opts.single_float)
+	fpabi_requires (fpabi, "singlefloat");
+      break;
+
+    case Val_GNU_NANOMIPS_ABI_FP_SOFT:
+      if (!file_nanomips_opts.soft_float)
+	fpabi_requires (fpabi, "softfloat");
+      break;
+
+    default:
+      as_warn (_(".gnu_attribute %d,%d is not a recognized"
+		 " floating-point ABI"), Tag_GNU_NANOMIPS_ABI_FP, fpabi);
+      break;
+    }
+}
+
+/* Perform consistency checks on the module level options exactly once.
+   This is a deferred check that happens:
+     at the first .set directive
+     or, at the first pseudo op that generates code (inc .dc.a)
+     or, at the first instruction
+     or, at the end.  */
+
+static void
+file_check_options (void)
+{
+  const struct nanomips_cpu_info *arch_info = 0;
+  if (file_nanomips_opts_checked)
+    return;
+
+  /* No user specified integer register size.  */
+  /* Infer the integer register size from the ABI.  */
+  if (file_nanomips_opts.gp < 0)
+    file_nanomips_opts.gp = ((ABI_NEEDS_32BIT_REGS (nanomips_abi)) ? 32 : 64);
+
+  if (file_nanomips_opts.fp < 0)
+    /* No user specified float register size.  */
+    /* nanoMIPS implies 64-bit float registers.  */
+    file_nanomips_opts.fp = 64;
+
+  /* This flag is set when we have a 64-bit capable CPU but use only
+     32-bit wide registers.  Note that EABI does not use it.  */
+  if (ISA_HAS_64BIT_REGS (file_nanomips_opts.isa)
+      && ((nanomips_abi == NO_ABI && file_nanomips_opts.gp == 32)
+	  || nanomips_abi == P32_ABI))
+    nanomips_32bitmode = 1;
+
+  /* If the user didn't explicitly select or deselect a particular ASE,
+     use the default setting for the CPU.  */
+  file_nanomips_opts.ase |=
+    (file_nanomips_opts.init_ase & ~file_ase_explicit);
+
+  /* Set up the current options.  These may change throughout assembly.  */
+  nanomips_opts = file_nanomips_opts;
+
+  check_isa_supports_ases ();
+  file_nanomips_opts_checked = true;
+
+  if (!bfd_set_arch_mach (stdoutput, bfd_arch_nanomips,
+			  file_nanomips_opts.arch))
+    as_warn (_("could not set architecture and machine"));
+}
+
+void
+md_assemble (char *str)
+{
+  struct nanomips_cl_insn insn;
+  bfd_reloc_code_real_type unused_reloc[3]
+    = { BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED };
+
+  file_check_options ();
+
+  imm_expr.X_op = O_absent;
+  offset_expr.X_op = O_absent;
+  offset_reloc[0] = BFD_RELOC_UNUSED;
+  offset_reloc[1] = BFD_RELOC_UNUSED;
+  offset_reloc[2] = BFD_RELOC_UNUSED;
+
+  if (cur_proc_ptr == NULL && !stub_funcless_mode)
+    {
+      stubgroup_new (now_seg);
+      stub_funcless_mode = true;
+    }
+
+  clear_insn_error ();
+  nanomips_ip (str, &insn);
+
+  if (insn_error.msg)
+    report_insn_error (str);
+  else if (insn.insn_mo->pinfo == INSN_MACRO)
+    {
+      macro_start ();
+      macro (&insn, str);
+      macro_end ((insn.insn_mo->pinfo2 & INSN2_MACRO) != 0);
+    }
+  else
+    {
+      if (offset_expr.X_op != O_absent)
+	append_insn (&insn, &offset_expr, offset_reloc, false);
+      else
+	append_insn (&insn, NULL, unused_reloc, false);
+    }
+}
+
+static inline bool
+nanomips_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  switch (reloc)
+    {
+    case BFD_RELOC_NANOMIPS_GPREL19_S2:
+    case BFD_RELOC_NANOMIPS_GPREL18_S3:
+    case BFD_RELOC_NANOMIPS_GPREL16_S2:
+    case BFD_RELOC_NANOMIPS_HI20:
+    case BFD_RELOC_NANOMIPS_LO12:
+    case BFD_RELOC_NANOMIPS_PCREL_HI20:
+    case BFD_RELOC_NANOMIPS_7_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_10_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_11_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_21_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_25_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_14_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_GOT_CALL:
+    case BFD_RELOC_NANOMIPS_GOTPC_HI20:
+    case BFD_RELOC_NANOMIPS_GOT_LO12:
+    case BFD_RELOC_NANOMIPS_GOT_PAGE:
+    case BFD_RELOC_NANOMIPS_GOT_OFST:
+    case BFD_RELOC_NANOMIPS_GOT_DISP:
+    case BFD_RELOC_NANOMIPS_IMM16:
+    case BFD_RELOC_NANOMIPS_NEG12:
+    case BFD_RELOC_NANOMIPS_I32:
+    case BFD_RELOC_NANOMIPS_TLS_GD:
+    case BFD_RELOC_NANOMIPS_TLS_GD_I32:
+    case BFD_RELOC_NANOMIPS_TLS_LD:
+    case BFD_RELOC_NANOMIPS_TLS_LD_I32:
+    case BFD_RELOC_NANOMIPS_TLS_DTPREL12:
+    case BFD_RELOC_NANOMIPS_TLS_DTPREL16:
+    case BFD_RELOC_NANOMIPS_TLS_DTPREL_I32:
+    case BFD_RELOC_NANOMIPS_TLS_TPREL12:
+    case BFD_RELOC_NANOMIPS_TLS_TPREL16:
+    case BFD_RELOC_NANOMIPS_TLS_TPREL_I32:
+    case BFD_RELOC_NANOMIPS_TLS_GOTTPREL:
+    case BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32:
+    case BFD_RELOC_NANOMIPS_PC_I32:
+    case BFD_RELOC_NANOMIPS_GOTPC_I32:
+    case BFD_RELOC_NANOMIPS_GPREL_I32:
+    case BFD_RELOC_NANOMIPS_GPREL18:
+    case BFD_RELOC_NANOMIPS_GPREL17_S1:
+    case BFD_RELOC_NANOMIPS_GPREL_HI20:
+    case BFD_RELOC_NANOMIPS_GPREL_LO12:
+    case BFD_RELOC_NANOMIPS_SIGNED_9:
+      return true;
+
+    default:
+      return false;
+    }
+}
+
+static inline bool
+nanomips_48bit_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return (reloc == BFD_RELOC_NANOMIPS_I32
+	  || reloc == BFD_RELOC_NANOMIPS_PC_I32
+	  || reloc == BFD_RELOC_NANOMIPS_GPREL_I32
+	  || reloc == BFD_RELOC_NANOMIPS_GOTPC_I32
+	  || reloc == BFD_RELOC_NANOMIPS_TLS_GD_I32
+	  || reloc == BFD_RELOC_NANOMIPS_TLS_LD_I32
+	  || reloc == BFD_RELOC_NANOMIPS_TLS_DTPREL_I32
+	  || reloc == BFD_RELOC_NANOMIPS_TLS_TPREL_I32
+	  || reloc == BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32);
+}
+
+static inline bool
+hi_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return (reloc == BFD_RELOC_HI16_S
+	  || reloc == BFD_RELOC_NANOMIPS_HI20
+	  || reloc == BFD_RELOC_NANOMIPS_GOTPC_HI20
+	  || reloc == BFD_RELOC_NANOMIPS_GPREL_HI20);
+}
+
+static inline bool
+lo_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return (reloc == BFD_RELOC_LO16
+	  || reloc == BFD_RELOC_NANOMIPS_LO12
+	  || reloc == BFD_RELOC_NANOMIPS_GPREL_LO12
+	  || reloc == BFD_RELOC_NANOMIPS_GOT_LO12);
+}
+
+static inline bool
+gprel_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return (reloc == BFD_RELOC_GPREL16
+	  || reloc == BFD_RELOC_NANOMIPS_GPREL7_S2
+	  || reloc == BFD_RELOC_NANOMIPS_GPREL18
+	  || reloc == BFD_RELOC_NANOMIPS_GPREL19_S2
+	  || reloc == BFD_RELOC_NANOMIPS_GPREL16_S2
+	  || reloc == BFD_RELOC_NANOMIPS_GPREL18_S3
+	  || reloc == BFD_RELOC_NANOMIPS_GPREL17_S1
+	  || reloc == BFD_RELOC_NANOMIPS_TLS_GD
+	  || reloc == BFD_RELOC_NANOMIPS_TLS_LD);
+}
+
+static inline bool
+pcrel16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return (reloc == BFD_RELOC_NANOMIPS_4_PCREL_S1
+	  || reloc == BFD_RELOC_NANOMIPS_7_PCREL_S1
+	  || reloc == BFD_RELOC_NANOMIPS_10_PCREL_S1);
+}
+
+static inline bool
+pcrel32_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return (reloc == BFD_RELOC_32_PCREL
+	  || reloc == BFD_RELOC_NANOMIPS_11_PCREL_S1
+	  || reloc == BFD_RELOC_NANOMIPS_14_PCREL_S1
+	  || reloc == BFD_RELOC_NANOMIPS_21_PCREL_S1
+	  || reloc == BFD_RELOC_NANOMIPS_25_PCREL_S1
+	  || reloc == BFD_RELOC_NANOMIPS_PCREL_HI20
+	  || reloc == BFD_RELOC_NANOMIPS_GOTPC_HI20);
+}
+
+static inline bool
+pcrel_branch_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return (pcrel16_reloc_p (reloc)
+	  || (pcrel32_reloc_p (reloc)
+	      && reloc != BFD_RELOC_NANOMIPS_PCREL_HI20
+	      && reloc != BFD_RELOC_NANOMIPS_GOTPC_HI20));
+}
+
+static inline bool
+pcrel48_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return (reloc == BFD_RELOC_NANOMIPS_PC_I32
+	  || reloc == BFD_RELOC_NANOMIPS_GOTPC_I32
+	  || reloc == BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32);
+}
+
+static inline bool
+pcrel_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return (pcrel16_reloc_p (reloc)
+	  || pcrel32_reloc_p (reloc)
+	  || pcrel48_reloc_p (reloc));
+}
+
+/* Return true if RELOC is a PC-relative relocation that does not have
+   full address range.  */
+
+static inline bool
+limited_pcrel_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  switch (reloc)
+    {
+    case BFD_RELOC_NANOMIPS_4_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_7_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_10_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_11_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_14_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_21_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_25_PCREL_S1:
+      return true;
+
+    case BFD_RELOC_32_PCREL:
+    case BFD_RELOC_NANOMIPS_PCREL_HI20:
+      return HAVE_64BIT_ADDRESSES;
+
+    default:
+      return false;
+    }
+}
+
+/* Return true if RELOC is a place-holder for linker relaxation and
+   not associated with a particular instruction.  */
+
+static inline bool
+linkrelax_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  switch (reloc)
+    {
+    case BFD_RELOC_NANOMIPS_ALIGN:
+    case BFD_RELOC_NANOMIPS_FILL:
+    case BFD_RELOC_NANOMIPS_MAX:
+    case BFD_RELOC_NANOMIPS_FIXED:
+    case BFD_RELOC_NANOMIPS_RELAX:
+    case BFD_RELOC_NANOMIPS_NORELAX:
+      return true;
+    default:
+      return false;
+    }
+}
+
+/* Return true if R is an initial selected relocation that has multiple
+   size variants.  */
+
+static bool
+flex_reloc_p (bfd_reloc_code_real_type r)
+{
+  return (r == BFD_RELOC_NANOMIPS_GPREL18
+	  || r == BFD_RELOC_NANOMIPS_TLS_LD
+	  || r == BFD_RELOC_NANOMIPS_TLS_GD
+	  || r == BFD_RELOC_NANOMIPS_TLS_TPREL12
+	  || r == BFD_RELOC_NANOMIPS_TLS_DTPREL12);
+}
+
+
+/* Return true if R is GP-relative relocation that takes 32-bit
+   immediate displacement.  */
+
+static bool
+gprel_i32_reloc_p (bfd_reloc_code_real_type r)
+{
+  return (r == BFD_RELOC_NANOMIPS_GPREL_I32
+	  || r == BFD_RELOC_NANOMIPS_TLS_LD_I32
+	  || r == BFD_RELOC_NANOMIPS_TLS_GD_I32);
+}
+
+/* Return the type of %lo() reloc needed by RELOC, given that
+   reloc_needs_lo_p.  */
+
+static inline bfd_reloc_code_real_type
+matching_lo_reloc (bfd_reloc_code_real_type reloc ATTRIBUTE_UNUSED)
+{
+  return (BFD_RELOC_LO16);
+}
+
+/* Return true if the given fixup is followed by a matching %lo
+   relocation.  */
+
+static inline bool
+fixup_has_matching_lo_p (fixS *fixp)
+{
+  return (fixp->fx_next != NULL
+	  && fixp->fx_next->fx_r_type == matching_lo_reloc (fixp->fx_r_type)
+	  && fixp->fx_addsy == fixp->fx_next->fx_addsy
+	  && fixp->fx_offset == fixp->fx_next->fx_offset);
+}
+
+/* Move all labels in LABELS to the current insertion point.  */
+
+static void
+nanomips_move_labels (struct insn_label_list *labels)
+{
+  struct insn_label_list *l;
+  valueT val;
+  segment_info_type *si = seg_info (now_seg);
+
+  if (toggle_linkrelax_p)
+    toggle_linkrelax (frag_now, frag_now_fix ());
+
+  for (l = si->label_list; l != NULL; l = l->next)
+    {
+      gas_assert (S_GET_SEGMENT (l->label) == now_seg);
+      symbol_set_frag (l->label, frag_now);
+      val = (valueT) frag_now_fix ();
+      S_SET_VALUE (l->label, val);
+    }
+}
+
+static bool
+s_is_linkonce (symbolS *sym, segT from_seg)
+{
+  bool linkonce = false;
+  segT symseg = S_GET_SEGMENT (sym);
+
+  if (symseg != from_seg && !S_IS_LOCAL (sym))
+    {
+      if ((bfd_section_flags (symseg) & SEC_LINK_ONCE))
+	linkonce = true;
+      /* The GNU toolchain uses an extension for ELF: a section
+	 beginning with the magic string .gnu.linkonce is a
+	 linkonce section.  */
+      if (strncmp (segment_name (symseg), ".gnu.linkonce",
+		   sizeof ".gnu.linkonce" - 1) == 0)
+	linkonce = true;
+    }
+  return linkonce;
+}
+
+/* End the current frag.  Make it a variant frag and record the
+   relaxation info.  */
+
+static void
+relax_close_frag (void)
+{
+  nanomips_macro_warning.first_frag = frag_now;
+  frag_var (rs_machine_dependent, 0, 0,
+	    RELAX_ENCODE (nanomips_relax.sizes[0], nanomips_relax.sizes[1]),
+	    nanomips_relax.symbol, 0, (char *) nanomips_relax.first_fixup);
+
+  memset (&nanomips_relax.sizes, 0, sizeof (nanomips_relax.sizes));
+  nanomips_relax.first_fixup = 0;
+}
+
+/* Start a new relaxation sequence whose expansion depends on SYMBOL.
+   See the comment above RELAX_ENCODE for more details.  */
+
+static void
+relax_start (symbolS *symbol)
+{
+  gas_assert (nanomips_relax.sequence == 0);
+  nanomips_relax.sequence = 1;
+  nanomips_relax.symbol = symbol;
+}
+
+/* Start generating the second version of a relaxable sequence.
+   See the comment above RELAX_ENCODE for more details.  */
+
+static void
+relax_switch (void)
+{
+  gas_assert (nanomips_relax.sequence == 1);
+  nanomips_relax.sequence = 2;
+}
+
+/* End the current relaxable sequence.  */
+
+static void
+relax_end (void)
+{
+  gas_assert (nanomips_relax.sequence == 2);
+  relax_close_frag ();
+  nanomips_relax.sequence = 0;
+}
+
+/* Return true if IP is a compact branch or jump.  */
+
+static inline bool
+compact_branch_p (const struct nanomips_cl_insn *ip)
+{
+  return (ip->insn_mo->pinfo2 & (INSN2_UNCOND_BRANCH
+				 | INSN2_COND_BRANCH)) != 0;
+}
+
+/* Return true if IP is an unconditional branch or jump.  */
+
+static inline bool
+uncond_branch_p (const struct nanomips_cl_insn *ip)
+{
+  return ((ip->insn_mo->pinfo2 & INSN2_UNCOND_BRANCH) != 0);
+}
+
+/* Information about an instruction argument that we're trying to match.  */
+struct nanomips_arg_info
+{
+  /* The instruction so far.  */
+  struct nanomips_cl_insn *insn;
+
+  /* The first unconsumed operand token.  */
+  struct nanomips_operand_token *token;
+
+  /* The 1-based operand number, in terms of insn->insn_mo->args.  */
+  int opnum;
+
+  /* The 1-based argument number, for error reporting.  This does not
+     count elided optional registers, etc..  */
+  int argnum;
+
+  /* The last OP_REG operand seen, or ILLEGAL_REG if none.  */
+  unsigned int last_regno;
+
+  /* If the first operand was an OP_REG, this is the register that it
+     specified, otherwise it is ILLEGAL_REG.  */
+  unsigned int dest_regno;
+
+  /* The value of the last OP_INT operand.  Used for OP_MSB,
+     where it gives the lsb position and to check stack frame size
+     for save/restore register list. */
+  unsigned int last_op_int;
+
+  /* If true, match routines should assume that no later instruction
+     alternative matches and should therefore be as accomodating as
+     possible.  Match routines should not report errors if something
+     is only invalid for !LAX_MATCH.  */
+  bool lax_match;
+
+  /* True if a reference to the current AT register was seen.  */
+  bool seen_at;
+
+  /* CP0 select register mask.  */
+  unsigned int select_mask;
+};
+
+/* Record that the argument is out of range.  */
+
+static void
+match_out_of_range (struct nanomips_arg_info *arg)
+{
+  set_insn_error_i (arg->argnum, _("operand %d out of range"), arg->argnum);
+}
+
+/* Record that the argument isn't constant but needs to be.  */
+
+static void
+match_not_constant (struct nanomips_arg_info *arg)
+{
+  set_insn_error_i (arg->argnum, _("operand %d must be constant"),
+		    arg->argnum);
+}
+
+/* Try to match an OT_CHAR token for character CH.  Consume the token
+   and return true on success, otherwise return false.  */
+
+static bool
+match_char (struct nanomips_arg_info *arg, char ch)
+{
+  if (arg->token->type == OT_CHAR && arg->token->u.ch == ch)
+    {
+      ++arg->token;
+      if (ch == ',')
+	arg->argnum += 1;
+      return true;
+    }
+  return false;
+}
+
+/* Try to get an expression from the next tokens in ARG.  Consume the
+   tokens and return true on success, storing the expression value in
+   VALUE and relocation types in R.  */
+
+static bool
+match_expression (struct nanomips_arg_info *arg, expressionS *value,
+		  bfd_reloc_code_real_type *r)
+{
+  /* If the next token is a '(' that was parsed as being part of a base
+     expression, assume we have an elided offset.  The later match will fail
+     if this turns out to be wrong.  */
+  if (arg->token->type == OT_CHAR && arg->token->u.ch == '(')
+    {
+      value->X_op = O_constant;
+      value->X_add_number = 0;
+      r[0] = r[1] = r[2] = BFD_RELOC_UNUSED;
+      return true;
+    }
+
+  /* Reject register-based expressions such as "0+$2" and "(($2))".
+     For plain registers the default error seems more appropriate.  */
+  if (arg->token->type == OT_INTEGER
+      && arg->token->u.integer.value.X_op == O_register)
+    {
+      set_insn_error (arg->argnum, _("register value used as expression"));
+      return false;
+    }
+
+  if (arg->token->type == OT_INTEGER)
+    {
+      *value = arg->token->u.integer.value;
+      memcpy (r, arg->token->u.integer.relocs, 3 * sizeof (*r));
+      ++arg->token;
+      return true;
+    }
+
+  set_insn_error_i
+    (arg->argnum, _("operand %d must be an immediate expression"),
+     arg->argnum);
+  return false;
+}
+
+/* Try to get a constant expression from the next tokens in ARG.  Consume
+   the tokens and return return true on success, storing the constant value
+   in *VALUE.  Use FALLBACK as the value if the match succeeded with an
+   error.  */
+
+static bool
+match_const_int (struct nanomips_arg_info *arg, offsetT *value)
+{
+  expressionS ex;
+  bfd_reloc_code_real_type r[3];
+
+  if (!match_expression (arg, &ex, r))
+    return false;
+
+  if (r[0] == BFD_RELOC_UNUSED && ex.X_op == O_constant)
+    *value = ex.X_add_number;
+  else
+    {
+      match_not_constant (arg);
+      return false;
+    }
+  return true;
+}
+
+/* Return the RTYPE_* flags for a register operand of type TYPE that
+   appears in instruction OPCODE.  */
+
+static unsigned int
+convert_reg_type (const struct nanomips_opcode *opcode,
+		  enum nanomips_reg_operand_type type)
+{
+  switch (type)
+    {
+    case OP_REG_GP:
+    default:
+      if (!nanomips_opts.legacyregs)
+	return RTYPE_GP;
+      else
+	return RTYPE_NUM | RTYPE_GP;
+
+    case OP_REG_FP:
+      return RTYPE_FPU;
+
+    case OP_REG_ACC:
+      return RTYPE_ACC;
+
+    case OP_REG_COPRO:
+      if (opcode->name[strlen (opcode->name) - 1] == '0')
+	return RTYPE_NUM | RTYPE_CP0;
+      return RTYPE_NUM;
+
+    case OP_REG_HW:
+      return RTYPE_NUM;
+
+    case OP_REG_CP0:
+      /* CP0 register without select must be symbolic.  */
+      return RTYPE_CP0;
+
+    case OP_REG_CP0SEL:
+      /* CP0 register with select may be numeric.  */
+      return RTYPE_CP0SEL | RTYPE_NUM;
+    }
+  abort ();
+}
+
+/* ARG is register REGNO, of type TYPE.  Warn about any dubious registers.  */
+
+static void
+check_regno (struct nanomips_arg_info *arg,
+	     enum nanomips_reg_operand_type type, unsigned int regno)
+{
+  if (AT && type == OP_REG_GP && regno == AT)
+    arg->seen_at = true;
+}
+
+/* ARG is a register with symbol value SYMVAL.  Try to interpret it as
+   a register of type TYPE.  Return true on success, storing the register
+   number in *REGNO and warning about any dubious uses.  */
+
+static bool
+match_regno (struct nanomips_arg_info *arg,
+	     enum nanomips_reg_operand_type type, unsigned int symval,
+	     unsigned int *regno)
+{
+  if (!(symval & convert_reg_type (arg->insn->insn_mo, type)))
+    return false;
+
+  /* Remember if the register name matches a specific select mask type.
+     Otherwise fall-back to allowing all select values.  */
+  if (type == OP_REG_CP0SEL)
+    switch (symval & RTYPE_CP0SEL)
+      {
+      case RTYPE_CP0SEL_EVEN:
+	arg->select_mask = NANOMIPS_CP0SEL_MASK_EVEN;
+	break;
+      case RTYPE_CP0SEL_ODD:
+	arg->select_mask = NANOMIPS_CP0SEL_MASK_ODD;
+	break;
+      case RTYPE_CP0SEL:
+      default:
+	arg->select_mask = NANOMIPS_CP0SEL_MASK_ANY;
+	break;
+      }
+
+  *regno = symval & RNUM_MASK;
+  check_regno (arg, type, *regno);
+  return true;
+}
+
+/* Try to interpret the next token in ARG as a register of type TYPE.
+   Consume the token and return true on success, storing the register
+   number in *REGNO.  Return false on failure.  */
+
+static bool
+match_reg (struct nanomips_arg_info *arg, enum nanomips_reg_operand_type type,
+	   unsigned int *regno)
+{
+  if (arg->token->type == OT_REG
+      && match_regno (arg, type, arg->token->u.regno, regno))
+    {
+      ++arg->token;
+      return true;
+    }
+  return false;
+}
+
+/* Try to interpret the next token in ARG as a range of registers of type TYPE.
+   Consume the token and return true on success, storing the register numbers
+   in *REGNO1 and *REGNO2.  Return false on failure.  */
+
+static bool
+match_reg_range (struct nanomips_arg_info *arg,
+		 enum nanomips_reg_operand_type type, unsigned int *regno1,
+		 unsigned int *regno2)
+{
+  if (match_reg (arg, type, regno1))
+    {
+      *regno2 = *regno1;
+      return true;
+    }
+  if (arg->token->type == OT_REG_RANGE
+      && match_regno (arg, type, arg->token->u.reg_range.regno1, regno1)
+      && match_regno (arg, type, arg->token->u.reg_range.regno2, regno2)
+      && *regno1 <= *regno2)
+    {
+      ++arg->token;
+      return true;
+    }
+  return false;
+}
+
+/* OP_INT matcher.  */
+
+static bool
+match_relocatable_int_operand (const struct nanomips_operand *operand_base)
+{
+  const struct nanomips_int_operand *operand;
+  unsigned int op_size;
+
+  operand = (const struct nanomips_int_operand *) operand_base;
+  op_size = operand_base->size;
+
+  if ((op_size == 7 && operand->shift == 2)
+      || (op_size == 4 && operand->shift == 2)
+      || op_size == 12
+      || op_size == 14
+      || op_size == 16
+      || op_size == 17
+      || op_size == 18
+      || op_size == 19
+      || op_size == 20)
+    return true;
+
+  return false;
+}
+
+/* Negative 12-bit constant matcher.  */
+
+static bool
+match_negative_int_operand (struct nanomips_arg_info *arg,
+			    const struct nanomips_operand *operand_base)
+{
+  offsetT sval;
+
+  if (!match_const_int (arg, &sval))
+    return false;
+  if ((int) sval > 0 || (int) sval < -4095)
+    return false;
+  sval = -(int) sval;
+
+  insn_insert_operand (arg->insn, operand_base, sval);
+  return true;
+}
+
+/* Check if OPERAND_BASE matches a suitable variant of relocation RTYPE.  */
+
+static bool
+match_flex_reloc_for_int_operand (const struct nanomips_operand *operand_base,
+				  bfd_reloc_code_real_type rtype)
+{
+  size_t i;
+  int opmask = nanomips_operand_mask (operand_base);
+  const bfd_reloc_code_real_type gp_relocs[] = {
+    BFD_RELOC_NANOMIPS_GPREL18,
+    BFD_RELOC_NANOMIPS_GPREL19_S2,
+    BFD_RELOC_NANOMIPS_GPREL18_S3,
+    BFD_RELOC_NANOMIPS_GPREL16_S2,
+    BFD_RELOC_NANOMIPS_GPREL17_S1,
+    BFD_RELOC_NANOMIPS_GPREL7_S2
+  };
+  const bfd_reloc_code_real_type tp_relocs[] = {
+    BFD_RELOC_NANOMIPS_TLS_TPREL12,
+    BFD_RELOC_NANOMIPS_TLS_TPREL16
+  };
+  const bfd_reloc_code_real_type dtp_relocs[] = {
+    BFD_RELOC_NANOMIPS_TLS_DTPREL12,
+    BFD_RELOC_NANOMIPS_TLS_DTPREL16
+  };
+  size_t rcount;
+  const bfd_reloc_code_real_type *rlist;
+
+  switch (rtype)
+    {
+    case BFD_RELOC_NANOMIPS_GPREL18:
+      rlist = gp_relocs;
+      rcount = ARRAY_SIZE (gp_relocs);
+      break;
+    case BFD_RELOC_NANOMIPS_TLS_TPREL12:
+      rlist = tp_relocs;
+      rcount = ARRAY_SIZE (tp_relocs);
+      break;
+    case BFD_RELOC_NANOMIPS_TLS_DTPREL12:
+      rlist = dtp_relocs;
+      rcount = ARRAY_SIZE (dtp_relocs);
+      break;
+    default:
+      rlist = &rtype;
+      rcount = 1;
+      break;
+    }
+
+  for (i = 0; i < rcount; i++)
+    {
+      reloc_howto_type *howto;
+      howto = bfd_reloc_type_lookup (stdoutput, rlist[i]);
+
+      if ((howto->dst_mask & opmask) == howto->dst_mask
+	  && howto->bitsize == operand_base->size)
+	return true;
+    }
+  return false;
+}
+
+/* Generic INT matcher.  */
+
+static bool
+match_int_operand (struct nanomips_arg_info *arg,
+		   const struct nanomips_operand *operand_base)
+{
+  const struct nanomips_int_operand *operand;
+  unsigned int uval;
+  int min_val, max_val, factor;
+  offsetT sval;
+
+  operand = (const struct nanomips_int_operand *) operand_base;
+  factor = 1 << operand->shift;
+  min_val = nanomips_int_operand_min (operand);
+  max_val = nanomips_int_operand_max (operand);
+
+  if (match_relocatable_int_operand (operand_base))
+    {
+      /* The operand can be relocated.  */
+      if (!match_expression (arg, &offset_expr, offset_reloc))
+	return false;
+
+      if (offset_reloc[0] != BFD_RELOC_UNUSED
+	  && !nanomips_48bit_reloc_p (offset_reloc[0]))
+	/* Relocation operators were used.  Check if relocation
+	   destination mask matches operand bits.  */
+	{
+	  if (flex_reloc_p (offset_reloc[0]))
+	    return match_flex_reloc_for_int_operand (operand_base,
+						     offset_reloc[0]);
+	  else
+	    {
+	      int opmask;
+	      reloc_howto_type *howto;
+
+	      if (offset_reloc[0] == BFD_RELOC_NANOMIPS_LO12
+		  && (forced_insn_length == 2
+		      || (forced_insn_format
+			  && insn_length (arg->insn->insn_mo) == 2)))
+		offset_reloc[0] = BFD_RELOC_NANOMIPS_LO4_S2;
+
+	      howto = bfd_reloc_type_lookup (stdoutput, offset_reloc[0]);
+	      opmask = nanomips_operand_mask (operand_base);
+	      /* This is not an direct mask comparison.  In some cases
+		 the relocation targets only a part of the operand bits.  */
+	      return ((howto->dst_mask & opmask) == howto->dst_mask);
+	    }
+	}
+
+      if (offset_expr.X_op != O_constant)
+	{
+	  /* Accept non-constant operands if no later alternative matches,
+	     leaving it for the caller to process.  */
+	  if (operand_base->size == 16 && offset_expr.X_op == O_subtract)
+	    {
+	      offset_reloc[0] = BFD_RELOC_UNUSED + RT_ADDIU;
+	      return true;
+	    }
+	  else
+	    {
+	      offset_reloc[0] = BFD_RELOC_NANOMIPS_LO12;
+	      return arg->lax_match;
+	    }
+	}
+
+      /* Clear the global state; we're going to install the operand
+	  ourselves.  */
+      if (min_val < 0)
+	sval = (int) offset_expr.X_add_number;
+      else
+	sval = offset_expr.X_add_number;
+
+      offset_expr.X_op = O_absent;
+
+      /* For compatibility with older assemblers, we accept
+	 0x8000-0xffff as signed 16-bit numbers when only
+	 signed numbers are allowed.  */
+      if (sval > max_val)
+	{
+	  max_val = ((1 << operand_base->size) - 1) << operand->shift;
+	  if (!arg->lax_match && sval <= max_val)
+	    return false;
+	}
+    }
+  else
+    {
+      if (!match_const_int (arg, &sval))
+	return false;
+      if (min_val < 0 && IS_ZEXT_32BIT_NUM (sval))
+	sval = (int) sval;
+    }
+
+  arg->last_op_int = sval;
+
+  if (sval < min_val || sval > max_val || sval % factor)
+    {
+      match_out_of_range (arg);
+      return false;
+    }
+
+  uval = (unsigned int) sval >> operand->shift;
+  uval -= operand->bias;
+
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return true;
+}
+
+/* Match integer operand with no relocation.  */
+static bool
+match_imm_int_operand (struct nanomips_arg_info *arg,
+		       const struct nanomips_operand *operand_base)
+{
+  if (!match_int_operand (arg, operand_base))
+    return false;
+
+  return ((offset_expr.X_op == O_absent || offset_expr.X_op == O_constant)
+	  && *offset_reloc == BFD_RELOC_UNUSED);
+}
+
+#define MAX_ADDI_OFFSET (0xffff)
+
+#define MIN_ADDI_OFFSET (-4095)
+
+static bool
+match_int_word (struct nanomips_arg_info *arg,
+		const struct nanomips_operand *operand ATTRIBUTE_UNUSED)
+{
+  if (match_expression (arg, &offset_expr, offset_reloc))
+    {
+      if (offset_reloc[0] != BFD_RELOC_UNUSED
+	  && !nanomips_48bit_reloc_p (offset_reloc[0])
+	  && !flex_reloc_p (offset_reloc[0]))
+	/* Any other relocation operators not allowed in this position.  */
+	return false;
+
+      if (offset_reloc[0] == BFD_RELOC_UNUSED)
+	offset_reloc[0] = BFD_RELOC_NANOMIPS_I32;
+
+      /* We don't match symbol-difference expressions here.  */
+      if (offset_expr.X_op != O_constant && offset_expr.X_op_symbol == NULL)
+	{
+	  arg->insn->insn_opcode_ext = 0;
+	  return true;
+	}
+      else
+	if ((forced_insn_length == 6
+	     || forced_insn_format
+	     || offset_expr.X_add_number > MAX_ADDI_OFFSET
+	     || offset_expr.X_add_number < MIN_ADDI_OFFSET)
+	    && offset_expr.X_op != O_big)
+	/* We don't match the 48-bit instruction, when we could make
+	   do with a 32-bit one, unless explicitly required.  */
+	{
+	  arg->last_op_int = offset_expr.X_add_number;
+	  arg->insn->insn_opcode_ext = 0;
+	  return true;
+	}
+    }
+  return false;
+}
+
+/* Match an immediate 32-bit value with possible bias.  */
+static bool
+match_imm_word (struct nanomips_arg_info *arg,
+		const struct nanomips_operand *operand_base)
+{
+  const struct nanomips_int_operand *operand;
+  operand = (const struct nanomips_int_operand *) operand_base;
+
+  if (match_expression (arg, &offset_expr, offset_reloc))
+    {
+      if (offset_expr.X_op != O_constant || *offset_reloc != BFD_RELOC_UNUSED)
+	return false;
+
+      if (offset_expr.X_op != O_big)
+	{
+	  int sval = offset_expr.X_add_number - operand->bias;
+	  arg->last_op_int = sval;
+	  arg->insn->insn_opcode_ext = 0;
+	  arg->insn->insn_opcode_ext = (((sval >> 16) & 0xffff)
+					| (sval << 16));
+	  offset_expr.X_op = O_absent;
+	  return true;
+	}
+    }
+  return false;
+}
+
+static bool
+match_pcrel_word (struct nanomips_arg_info *arg,
+		  const struct nanomips_operand *operand ATTRIBUTE_UNUSED)
+{
+  if (match_expression (arg, &offset_expr, offset_reloc))
+    {
+      if (offset_reloc[0] != BFD_RELOC_UNUSED
+	  && !nanomips_48bit_reloc_p (offset_reloc[0]))
+	/* Any other relocation operators not allowed in this position.  */
+	return false;
+
+      if (offset_reloc[0] == BFD_RELOC_UNUSED)
+	offset_reloc[0] = BFD_RELOC_NANOMIPS_PC_I32;
+
+      arg->insn->insn_opcode_ext = 0;
+      return true;
+    }
+  return false;
+}
+
+static bool
+match_gprel_word (struct nanomips_arg_info *arg,
+		  const struct nanomips_operand *operand ATTRIBUTE_UNUSED)
+{
+  if (match_expression (arg, &offset_expr, offset_reloc))
+    {
+      if (offset_reloc[0] != BFD_RELOC_UNUSED
+	  && !gprel_i32_reloc_p (offset_reloc[0])
+	  && (forced_insn_length != 6 || !gprel_reloc_p (offset_reloc[0])))
+	/* Any other relocation operators not allowed in this position.  */
+	return false;
+
+      if (offset_reloc[0] == BFD_RELOC_UNUSED)
+	offset_reloc[0] = BFD_RELOC_NANOMIPS_GPREL_I32;
+
+      arg->insn->insn_opcode_ext = 0;
+      return true;
+    }
+  return false;
+}
+
+/* OP_MAPPED_INT matcher.  */
+static bool
+match_mapped_int_operand (struct nanomips_arg_info *arg,
+			  const struct nanomips_operand *operand_base)
+{
+  const struct nanomips_mapped_int_operand *operand;
+  unsigned int uval, num_vals;
+  offsetT sval;
+
+  operand = (const struct nanomips_mapped_int_operand *) operand_base;
+  if (!match_const_int (arg, &sval))
+    return false;
+
+  num_vals = 1 << operand_base->size;
+  for (uval = 0; uval < num_vals; uval++)
+    if (operand->int_map[uval] == sval)
+      break;
+  if (uval == num_vals)
+    {
+      match_out_of_range (arg);
+      return false;
+    }
+
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return true;
+}
+
+/* OP_MSB matcher.  */
+
+static bool
+match_msb_operand (struct nanomips_arg_info *arg,
+		   const struct nanomips_operand *operand_base)
+{
+  const struct nanomips_msb_operand *operand;
+  int min_val, max_val, max_high;
+  offsetT size, sval, high;
+
+  operand = (const struct nanomips_msb_operand *) operand_base;
+  min_val = operand->bias;
+  max_val = min_val + (1 << operand_base->size) - 1;
+  max_high = operand->opsize;
+
+  if (!match_const_int (arg, &size))
+    return false;
+
+  high = size + arg->last_op_int;
+  sval = operand->add_lsb ? high : size;
+
+  if (size < 0 || high > max_high || sval < min_val || sval > max_val)
+    {
+      match_out_of_range (arg);
+      return false;
+    }
+  insn_insert_operand (arg->insn, operand_base, sval - min_val);
+  return true;
+}
+
+/* OP_REG matcher.  */
+
+static bool
+match_reg_operand (struct nanomips_arg_info *arg,
+		   const struct nanomips_operand *operand_base)
+{
+  const struct nanomips_reg_operand *operand;
+  unsigned int regno, uval, num_vals;
+
+  operand = (const struct nanomips_reg_operand *) operand_base;
+  if (!match_reg (arg, operand->reg_type, &regno))
+    return false;
+
+  if (operand->reg_map)
+    {
+      num_vals = 1 << operand->root.size;
+      for (uval = 0; uval < num_vals; uval++)
+	if (operand->reg_map[uval] == regno)
+	  break;
+      if (num_vals == uval)
+	return false;
+    }
+  else
+    uval = regno;
+
+  if (operand_base->size > 0
+      && uval >= (unsigned int) (1 << operand_base->size))
+    {
+      match_out_of_range (arg);
+      return false;
+    }
+
+  arg->last_regno = regno;
+  if (arg->opnum == 1)
+    arg->dest_regno = regno;
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return true;
+}
+
+/* OP_REG_PAIR matcher.  */
+
+static bool
+match_reg_pair_operand (struct nanomips_arg_info *arg,
+			const struct nanomips_operand *operand_base)
+{
+  const struct nanomips_reg_pair_operand *operand;
+  unsigned int regno1, regno2, uval, num_vals;
+
+  operand = (const struct nanomips_reg_pair_operand *) operand_base;
+  if (!match_reg (arg, operand->reg_type, &regno1)
+      || !match_char (arg, ',')
+      || !match_reg (arg, operand->reg_type, &regno2))
+    return false;
+
+  num_vals = 1 << operand_base->size;
+  for (uval = 0; uval < num_vals; uval++)
+    if (operand->reg1_map[uval] == regno1
+	&& operand->reg2_map[uval] == regno2)
+      break;
+  if (uval == num_vals)
+    return false;
+
+  insn_insert_operand (arg->insn, operand_base, uval);
+  return true;
+}
+
+/* OP_PCREL matcher.  The caller chooses the relocation type.  */
+
+static bool
+match_pcrel_operand (struct nanomips_arg_info *arg)
+{
+  bfd_reloc_code_real_type r[3];
+
+  return match_expression (arg, &offset_expr, r) && r[0] == BFD_RELOC_UNUSED;
+}
+
+static bool
+match_non_zero_pcrel_operand (struct nanomips_arg_info *arg,
+			      const struct nanomips_operand *operand_base)
+{
+  bfd_reloc_code_real_type r[3];
+
+  if (!match_expression (arg, &offset_expr, r) || r[0] != BFD_RELOC_UNUSED)
+    return false;
+
+  if (!offset_expr.X_add_symbol && offset_expr.X_add_number == 0)
+    {
+      set_insn_error (arg->argnum,
+		      _("the PC relative offset must not be $0"));
+      return false;
+    }
+
+  /* Enforce operand bits in the instruction encoding as non-zero
+     so that it can be distinguished from another instructions with the same
+     encoding and 0 in these bit positions.  Requires the relocation
+     scheme to be RELA, which is trivially true only for nanoMIPS.  The check
+     for ISA just to be on the safe side.  */
+  insn_insert_operand (arg->insn, operand_base,
+		       (1 << operand_base->size) - 1);
+  return true;
+}
+
+/* OP_HI20_INT matcher.  */
+
+#define UNSIGNED_OPVALUE(OP)					\
+{ { OP_INT, OP->size, OP->lsb, OP->size_top, OP->lsb_top },	\
+    ((1 << OP->size) - 1), 0, 0, false }
+
+#define SIGNEX_OPVALUE(OP) { OP_INT, OP->size - 1, OP->lsb, 0, 0 }
+
+static bool
+match_hi20_int_operand (struct nanomips_arg_info *arg,
+			const struct nanomips_operand *operand_base
+			ATTRIBUTE_UNUSED)
+{
+  offsetT uval = 0;
+  const struct nanomips_int_operand op_enc = {
+    {OP_INT, 20, 2, 1, 0}, (1 << 20) - 1, 12, 0, false
+  };
+  const struct nanomips_int_operand op_shuffle = {
+    {OP_INT, 19, 10, 10, 0}, (1 << 19) - 1, 12, 0, false
+  };
+
+  if (!match_expression (arg, &offset_expr, offset_reloc))
+    return false;
+
+  /* 16-bit immediate without %hi reloc  */
+  if (offset_reloc[0] == BFD_RELOC_UNUSED && offset_expr.X_op == O_constant)
+    {
+      if ((offset_expr.X_add_number & ~0xffff) == 0)
+	uval = offset_expr.X_add_number << 4;
+      else
+	{
+	  match_out_of_range (arg);
+	  return false;
+	}
+    }
+  /* 20-bit high part using %hi  */
+  else if (hi_reloc_p (offset_reloc[0]))
+    uval = offset_expr.X_add_number >> 12;
+  else
+    {
+      /* Accept non-constant operands if no later alternative matches,
+	 leaving it for the caller to process.  */
+      if (arg->lax_match)
+	offset_reloc[0] = BFD_RELOC_NANOMIPS_HI20;
+      return arg->lax_match;
+    }
+
+  if (offset_expr.X_op == O_constant)
+    {
+      offset_expr.X_op = O_absent;
+      /* Re-shuffle and insert lower 19-bits, exluding sign.  */
+      uval = nanomips_insert_operand (&op_shuffle.root,
+				      uval & 0x80000, uval & 0x7ffff);
+
+      /* Insert 20-bits in to instruction.  */
+      insn_insert_operand (arg->insn, &op_enc.root, uval);
+    }
+  return true;
+}
+
+/* OP_HI20_PCREL matcher.  Relocation must be specified.  */
+static bool
+match_hi20_pcrel_operand (struct nanomips_arg_info *arg)
+{
+  const struct nanomips_int_operand op_enc = {
+    {OP_INT, 20, 2, 1, 0 }, (1 << 20) - 1, 0, 0, false
+  };
+  const struct nanomips_int_operand op_shuffle = {
+    {OP_INT, 19, 10, 10, 0}, (1 << 19) - 1, 0, 0, false
+  };
+  /* Encode full 20-bit signed value.  */
+  if (!match_expression (arg, &offset_expr, offset_reloc))
+    return false;
+
+  if (offset_reloc[0] == BFD_RELOC_UNUSED
+      || (hi_reloc_p (offset_reloc[0]) && offset_expr.X_op == O_constant))
+    {
+      unsigned int uval = offset_expr.X_add_number;
+
+      if (hi_reloc_p (offset_reloc[0]))
+	uval = uval >> 12;
+
+      /* Re-shuffle and insert lower 19-bits, exluding sign.  */
+      uval = nanomips_insert_operand (&op_shuffle.root,
+				      uval & 0x80000, uval & 0x7ffff);
+      /* Insert 20-bits in to instruction.  */
+      insn_insert_operand (arg->insn, &op_enc.root, uval);
+      /* Expression is completely handled, we don't want append_insn
+	 messing up the opcode.  */
+      offset_expr.X_op = O_absent;
+      return true;
+    }
+  else
+    return (offset_reloc[0] == BFD_RELOC_NANOMIPS_PCREL_HI20
+	    || offset_reloc[0] == BFD_RELOC_NANOMIPS_GOTPC_HI20);
+}
+
+static bool
+match_hi20_scaled_operand (struct nanomips_arg_info *arg)
+{
+  const struct nanomips_int_operand op_enc = {
+    {OP_INT, 20, 2, 1, 0}, (1 << 20) - 1, 12, 0, false
+  };
+  const struct nanomips_int_operand op_shuffle = {
+    {OP_INT, 19, 10, 10, 0}, (1 << 19) - 1, 12, 0,  false
+  };
+  offsetT uval = 0;
+
+  if (!match_const_int (arg, &uval) || (uval & 0xfff) != 0)
+    return false;
+
+  uval = uval >> 12;
+  offset_expr.X_op = O_absent;
+
+  /* Re-shuffle and insert lower 19-bits, exluding sign.  */
+  uval = nanomips_insert_operand (&op_shuffle.root,
+				  uval & 0x80000, uval & 0x7ffff);
+
+  /* Insert 20-bits in to instruction.  */
+  insn_insert_operand (arg->insn, &op_enc.root, uval);
+
+  return true;
+}
+
+/* OP_CHECK_PREV matcher.  */
+
+static bool
+match_check_prev_operand (struct nanomips_arg_info *arg,
+			  const struct nanomips_operand *operand_base)
+{
+  const struct nanomips_check_prev_operand *operand;
+  unsigned int regno;
+
+  operand = (const struct nanomips_check_prev_operand *) operand_base;
+
+  if (!match_reg (arg, OP_REG_GP, &regno))
+    return false;
+
+  if (!operand->zero_ok && regno == 0)
+    return false;
+
+  if ((operand->less_than_ok && regno < arg->last_regno)
+      || (operand->greater_than_ok && regno > arg->last_regno)
+      || (operand->equal_ok && regno == arg->last_regno))
+    {
+      arg->last_regno = regno;
+      insn_insert_operand (arg->insn, operand_base, regno);
+      return true;
+    }
+  return false;
+}
+
+/* OP_MAPPED_CHECK_PREV matcher.  */
+
+static bool
+match_mapped_check_prev_operand (struct nanomips_arg_info *arg,
+				 const struct nanomips_operand *operand_base)
+{
+  unsigned char uval, last_uval;
+  const struct nanomips_mapped_check_prev_operand *operand
+    = (const struct nanomips_mapped_check_prev_operand *) operand_base;
+
+  last_uval = nanomips_encode_reg_operand (operand_base, arg->last_regno);
+
+  if (!match_reg_operand (arg, operand_base))
+    return false;
+
+  if (!operand->zero_ok && arg->last_regno == 0)
+    return false;
+
+  uval = nanomips_encode_reg_operand (operand_base, arg->last_regno);
+
+  if ((operand->less_than_ok && uval < last_uval)
+      || (operand->greater_than_ok && uval > last_uval)
+      || (operand->equal_ok && uval == last_uval))
+    return true;
+
+  return false;
+}
+
+/* OP_SAVE_RESTORE_FP_LIST matcher for nanoMIPS.  */
+
+static bool
+match_save_restore_fp_list_operand (struct nanomips_arg_info *arg,
+				    const struct nanomips_operand *operand)
+{
+  unsigned int opval;
+  unsigned int regno1, regno2;
+
+  if (!match_reg_range (arg, OP_REG_FP, &regno1, &regno2))
+    return false;
+
+  if (regno1 != 0 || regno2 > 15)
+    return false;
+
+  opval = (regno2 - regno1);
+
+  insn_insert_operand (arg->insn, operand, opval);
+  return true;
+}
+
+/* OP_SAVE_RESTORE_LIST matcher.  */
+
+static bool
+match_save_restore_list_operand (struct nanomips_arg_info *arg,
+				 const struct nanomips_operand *operand)
+{
+  unsigned int opval, count;
+  unsigned first_reg, last_reg;
+  bool mode16 = (insn_length (arg->insn->insn_mo) == 2);
+  bool gp = false;
+
+  count = 0;
+  first_reg = last_reg = RNUM_MASK + 1;
+  do
+    {
+      unsigned int regno1 = 0, regno2 = 0;
+      bool first_range = false;
+
+      if (!match_reg_range (arg, OP_REG_GP, &regno1, &regno2))
+	{
+	  arg->token--;
+	  if (regno2 != 0 && regno2 < regno1)
+	    set_insn_error_ss (count,
+			       "register range (%s-%s) is not in "
+			       "increasing order",
+			       nanomips_reg_names[32 + regno1].name,
+			       nanomips_reg_names[32 + regno2].name);
+	  return false;
+	}
+
+      if (regno1 <= 29 && regno2 >= 29)
+	{
+	  set_insn_error_ss (regno1,
+			     "%s register list should not contain $sp%s",
+			     arg->insn->insn_mo->name, "");
+	  return false;
+	}
+
+      if (first_reg == RNUM_MASK + 1)
+	{
+	  first_reg = last_reg = regno1;
+	  first_range = true;
+	}
+
+      if ((regno1 != ((first_reg & 0x10) | (last_reg + 1) % 32))
+	  || first_range)
+	{
+	  /* a non-contiguous sequence */
+	  if (regno1 == 28 && !mode16 && (nanomips_opts.ase & ASE_xNMS) != 0)
+	    /* enable GP special casing if possible */
+	    gp = true;
+	  else if (regno1 == 28 && (nanomips_opts.ase & ASE_xNMS) == 0)
+	    {
+	      set_insn_error_ss (count,
+				 "non-contiguous %s of $gp is not available "
+				 "for nanoMIPS subset%s",
+				 arg->insn->insn_mo->name, "");
+	      return false;
+	    }
+	  else if (regno1 == 28 && mode16)
+	    {
+	      set_insn_error_ss (count,
+				 "non-contiguous %s of $gp is not available "
+				 "for 16-bit instructions%s",
+				 arg->insn->insn_mo->name, "");
+	      return false;
+	    }
+	  else if (first_range)
+	    ;
+	  else
+	    {
+	      if (regno1 == 30 && first_reg != 30)
+		set_insn_error (count, "$fp must be the first register in "
+				"the list");
+	      else if (regno1 == 31 && first_reg != 30 && first_reg != 31)
+		set_insn_error (count, "$ra must be the first register in "
+				"the list");
+	      else if (last_reg == 31 && regno1 != 16)
+		set_insn_error_ss (count + 1, "%s must be followed by %s",
+				   nanomips_reg_names[32 + last_reg].name,
+				   nanomips_reg_names[32 + 16].name);
+	      else
+		set_insn_error_ss (regno1,
+				   "register list not contiguous between "
+				   "%s and %s",
+				   nanomips_reg_names[32 + last_reg].name,
+				   nanomips_reg_names[32 + regno1].name);
+	      return false;
+	    }
+	}
+
+      last_reg = regno2;
+
+      /* sequence  */
+      while (regno1++ <= regno2)
+	count += 1;
+    }
+  while (match_char (arg, ','));
+
+  /* too few or too many registers in list */
+  if (count >= 16)
+    {
+      set_insn_error_si (count,
+			 "%s can handle a maximum of 16 register, %d requested",
+			 arg->insn->insn_mo->name, count);
+      return false;
+    }
+
+  if (mode16 && first_reg < 30)
+    {
+      set_insn_error_ss (count + 1,
+			 "first register for 16-bit %s must be $fp or $ra%s",
+			 arg->insn->insn_mo->name, "");
+      return false;
+    }
+
+  if (count * (GPR_SIZE / 8) > arg->last_op_int)
+    as_warn ("frame too small for save/restore register list");
+
+  if (mode16)
+    opval = ((first_reg & 0x1) << 4) | count;
+  else
+    opval = ((first_reg << 6) | (count << 1) | gp);
+
+  /* Finally build the instruction.  */
+  insn_insert_operand (arg->insn, operand, opval);
+
+  return true;
+}
+
+/* OP_NON_ZERO_REG matcher.  */
+
+static bool
+match_non_zero_reg_operand (struct nanomips_arg_info *arg,
+			    const struct nanomips_operand *operand)
+{
+  unsigned int regno;
+
+  if (!match_reg (arg, OP_REG_GP, &regno))
+    return false;
+
+  if (regno == 0)
+    {
+      const unsigned long pinfo = arg->insn->insn_mo->pinfo;
+      unsigned int regtype;
+      const char *regstr[] = { "source", "target" };
+
+      if ((arg->opnum == 1 && (pinfo & INSN_WRITE_1) != 0)
+	  || (arg->opnum == 2 && (pinfo & INSN_WRITE_2) != 0))
+	regtype = 1;
+      else
+	regtype = 0;
+
+      set_insn_error_ss (arg->argnum, _("the %s register must not be $0%s"),
+			 regstr[regtype], "");
+      return false;
+    }
+
+  arg->last_regno = regno;
+  insn_insert_operand (arg->insn, operand, regno);
+  return true;
+}
+
+/* OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG matcher.  OTHER_REGNO is the
+   register that we need to match.  */
+
+static bool
+match_tied_reg_operand (struct nanomips_arg_info *arg,
+			unsigned int other_regno)
+{
+  unsigned int regno;
+
+  return match_reg (arg, OP_REG_GP, &regno) && regno == other_regno;
+}
+
+/* OP_BASE_CHECK_OFFSET matcher.  */
+
+static bool
+match_base_checked_offset_operand (struct nanomips_arg_info *arg,
+				   const struct nanomips_operand *operand_base)
+{
+  unsigned int regno;
+  const struct nanomips_base_check_offset_operand *operand
+    = (const struct nanomips_base_check_offset_operand *) operand_base;
+
+  if (!match_reg (arg, OP_REG_GP, &regno))
+    return false;
+
+  if ((operand->const_ok
+       && (offset_expr.X_op == O_constant || offset_expr.X_op == O_absent))
+      || (operand->expr_ok
+	  && (offset_expr.X_op == O_symbol || offset_expr.X_op == O_absent))
+      || (regno == 0)
+      || lo_reloc_p (offset_reloc[0]))
+    {
+      arg->last_regno = regno;
+      insn_insert_operand (arg->insn, operand_base, regno);
+      return true;
+    }
+  return false;
+}
+
+/* Read a floating-point constant from S for LI.S or LI.D.  LENGTH is
+   the length of the value in bytes (4 for float, 8 for double) and
+   USING_GPRS says whether the destination is a GPR rather than an FPR.
+
+   Return the constant in IMM and OFFSET as follows:
+
+   - If the constant should be loaded via memory, set IMM to O_absent and
+     OFFSET to the memory address.
+
+   - Otherwise, if the constant should be loaded into two 32-bit registers,
+     set IMM to the O_constant to load into the high register and OFFSET
+     to the corresponding value for the low register.
+
+   - Otherwise, set IMM to the full O_constant and set OFFSET to O_absent.
+
+   These constants only appear as the last operand in an instruction,
+   and every instruction that accepts them in any variant accepts them
+   in all variants.  This means we don't have to worry about backing out
+   any changes if the instruction does not match.  We just match
+   unconditionally and report an error if the constant is invalid.  */
+
+static bool
+match_float_constant (struct nanomips_arg_info *arg, expressionS *imm,
+		      expressionS *offset, unsigned int length,
+		      bool using_gprs)
+{
+  char *p;
+  segT seg;
+  subsegT subseg;
+  unsigned char *data;
+
+  /* Where the constant is placed is based on how the nanoMIPS assembler
+     does things:
+
+     length == 4 && using_gprs  -- immediate value only
+     length == 8 && using_gprs  -- .rdata or immediate value
+     length <= -Gnum -- .sdata
+     length >= -Gnum  -- .rodata
+
+     The .sdata sections are only used if permitted by the -G argument.  */
+  if (arg->token->type != OT_FLOAT)
+    {
+      set_insn_error (arg->argnum, _("floating-point expression required"));
+      return false;
+    }
+
+  gas_assert (arg->token->u.flt.length == (int) length);
+  data = arg->token->u.flt.data;
+  ++arg->token;
+
+  /* Handle 32-bit constants -- immediate value is best.  */
+  if (length == 4
+      && !nanomips_disable_float_construction)
+    {
+      imm->X_op = O_constant;
+      if (!target_big_endian)
+	imm->X_add_number = bfd_getl32 (data);
+      else
+	imm->X_add_number = bfd_getb32 (data);
+      offset->X_op = O_absent;
+      return true;
+    }
+
+  /* Handle 64-bit constants for which an immediate value is best.  */
+  if (length == 8
+      && !nanomips_disable_float_construction
+      && ((data[0] == 0 && data[1] == 0)
+	  || (data[2] == 0 && data[3] == 0)
+	  || ((nanomips_opts.ase & ASE_xNMS) != 0 && using_gprs))
+      && ((data[4] == 0 && data[5] == 0)
+	  || (data[6] == 0 && data[7] == 0)
+	  || ((nanomips_opts.ase & ASE_xNMS) != 0 && using_gprs)))
+    {
+      /* The value is simple enough to load with a couple of instructions.
+	 If using 32-bit registers, set IMM to the high order 32 bits and
+	 OFFSET to the low order 32 bits.  Otherwise, set IMM to the entire
+	 64 bit constant.  */
+      if (GPR_SIZE == 32)
+	{
+	  imm->X_op = O_constant;
+	  offset->X_op = O_constant;
+	  if (!target_big_endian)
+	    {
+	      imm->X_add_number = bfd_getl32 (data + 4);
+	      offset->X_add_number = bfd_getl32 (data);
+	    }
+	  else
+	    {
+	      imm->X_add_number = bfd_getb32 (data);
+	      offset->X_add_number = bfd_getb32 (data + 4);
+	    }
+	  if (offset->X_add_number == 0)
+	    offset->X_op = O_absent;
+	}
+      else
+	{
+	  imm->X_op = O_constant;
+	  if (!target_big_endian)
+	    imm->X_add_number = bfd_getl64 (data);
+	  else
+	    imm->X_add_number = bfd_getb64 (data);
+	  offset->X_op = O_absent;
+	}
+      return true;
+    }
+
+  /* Switch to the right section.  */
+  seg = now_seg;
+  subseg = now_subseg;
+
+  if (!using_gprs && g_switch_value >= length && nanomips_opts.pic == NO_PIC)
+    s_change_sec ('s');
+  else
+    s_change_sec ('r');
+
+  if (seg == now_seg)
+    as_bad (_("cannot use `%s' in this section"), arg->insn->insn_mo->name);
+
+  /* Set the argument to the current address in the section.  */
+  imm->X_op = O_absent;
+  offset->X_op = O_symbol;
+  offset->X_add_symbol = symbol_temp_new_now ();
+  offset->X_add_number = 0;
+
+  /* Put the floating point number into the section.  */
+  p = frag_more (length);
+  memcpy (p, data, length);
+
+  /* Switch back to the original section.  */
+  subseg_set (seg, subseg);
+  return true;
+}
+
+/* Copy bits from one part of an instruction to another part.
+   Source bits are specified by size & lsb.  Destination bits
+   are specified by size_top and lsb_top.  */
+static bool
+match_copy_bits (struct nanomips_arg_info *arg,
+		 const struct nanomips_operand *operand)
+{
+  const struct nanomips_operand op_src = {
+    OP_INT, operand->size, operand->lsb, 0, 0
+  };
+  const struct nanomips_operand op_dest = {
+    OP_INT, operand->size_top, operand->lsb_top, 0, 0
+  };
+  unsigned int uval;
+
+  uval = nanomips_extract_operand (&op_src, arg->insn->insn_opcode);
+  insn_insert_operand (arg->insn, &op_dest, uval);
+  return true;
+}
+
+/* Select value for a named COP0 register.  */
+static bool
+match_cp0_select (struct nanomips_arg_info *arg,
+		  const struct nanomips_operand *operand)
+{
+  offsetT uval;
+
+  /* Expect an immediate select value between 0 & 31.  */
+  if (!match_const_int (arg, &uval)
+      || uval < 0
+      || uval >= (1 << NANOMIPSOP_SH_CP0SEL))
+    return false;
+
+  /* Check if this select value is allowed by the saved mask of the last
+     seen CP0 register.  */
+  if (((1 << uval) & arg->select_mask) == 0)
+    return false;
+
+  insn_insert_operand (arg->insn, operand, uval);
+  return true;
+}
+
+/* S is the text seen for ARG.  Match it against OPERAND.  Return the end
+   of the argument text if the match is successful, otherwise return null.  */
+
+static bool
+match_operand (struct nanomips_arg_info *arg,
+	       const struct nanomips_operand *operand)
+{
+  switch (operand->type)
+    {
+    case OP_INT:
+      return match_int_operand (arg, operand);
+
+    case OP_MAPPED_INT:
+      return match_mapped_int_operand (arg, operand);
+
+    case OP_MSB:
+      return match_msb_operand (arg, operand);
+
+    case OP_REG:
+    case OP_OPTIONAL_REG:
+      return match_reg_operand (arg, operand);
+
+    case OP_REG_PAIR:
+      return match_reg_pair_operand (arg, operand);
+
+    case OP_PCREL:
+      return match_pcrel_operand (arg);
+
+    case OP_SAVE_RESTORE_LIST:
+      return match_save_restore_list_operand (arg, operand);
+
+    case OP_SAVE_RESTORE_FP_LIST:
+      return match_save_restore_fp_list_operand (arg, operand);
+
+    case OP_REPEAT_DEST_REG:
+      return match_tied_reg_operand (arg, arg->dest_regno);
+
+    case OP_REPEAT_PREV_REG:
+      return match_tied_reg_operand (arg, arg->last_regno);
+
+    case OP_CHECK_PREV:
+      return match_check_prev_operand (arg, operand);
+
+    case OP_NON_ZERO_REG:
+      return match_non_zero_reg_operand (arg, operand);
+
+    case OP_NON_ZERO_PCREL_S1:
+      return match_non_zero_pcrel_operand (arg, operand);
+
+    case OP_HI20_PCREL:
+      return match_hi20_pcrel_operand (arg);
+
+    case OP_HI20_INT:
+      return match_hi20_int_operand (arg, operand);
+
+    case OP_HI20_SCALE:
+      return match_hi20_scaled_operand (arg);
+
+    case OP_MAPPED_CHECK_PREV:
+      return match_mapped_check_prev_operand (arg, operand);
+
+    case OP_UINT_WORD:
+    case OP_INT_WORD:
+      return match_int_word (arg, operand);
+
+    case OP_PC_WORD:
+      return match_pcrel_word (arg, operand);
+
+    case OP_GPREL_WORD:
+      return match_gprel_word (arg, operand);
+
+    case OP_DONT_CARE:
+      return false;
+
+    case OP_NEG_INT:
+      return match_negative_int_operand (arg, operand);
+
+    case OP_IMM_INT:
+      return match_imm_int_operand (arg, operand);
+
+    case OP_IMM_WORD:
+      return match_imm_word (arg, operand);
+
+    case OP_BASE_CHECK_OFFSET:
+      return match_base_checked_offset_operand (arg, operand);
+
+    case OP_COPY_BITS:
+      return match_copy_bits (arg, operand);
+
+    case OP_CP0SEL:
+      return match_cp0_select (arg, operand);
+
+    default:
+      abort ();
+    }
+}
+
+#define INSN_ERET	0x42000018
+#define INSN_DERET	0x4200001f
+#define INSN_DMULT	0x1c
+#define INSN_DMULTU	0x1d
+
+#define BASE_REG_EQ(INSN1, INSN2) 	\
+  ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
+      == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
+
+/* For nanoMIPS macros, we need to generate a local number label
+   as the target of branches.  */
+#define NANOMIPS_LABEL_CHAR		'\037'
+static unsigned long nanomips_target_label;
+static char nanomips_target_name[32];
+
+static char *
+nanomips_label_name (void)
+{
+  char *p = nanomips_target_name;
+  char symbol_name_temporary[24];
+  unsigned long l;
+  int i;
+
+  if (*p)
+    return p;
+
+  i = 0;
+  l = nanomips_target_label;
+#ifdef LOCAL_LABEL_PREFIX
+  *p++ = LOCAL_LABEL_PREFIX;
+#endif
+  *p++ = 'L';
+  *p++ = NANOMIPS_LABEL_CHAR;
+  do
+    {
+      symbol_name_temporary[i++] = l % 10 + '0';
+      l /= 10;
+    }
+  while (l != 0);
+  while (i > 0)
+    *p++ = symbol_name_temporary[--i];
+  *p = '\0';
+
+  return nanomips_target_name;
+}
+
+static void
+nanomips_label_expr (expressionS *label_expr)
+{
+  label_expr->X_op = O_symbol;
+  label_expr->X_add_symbol = symbol_find_or_make (nanomips_label_name ());
+  label_expr->X_add_number = 0;
+}
+
+static void
+nanomips_label_inc (void)
+{
+  nanomips_target_label++;
+  *nanomips_target_name = '\0';
+}
+
+static void
+nanomips_add_label (void)
+{
+  colon (nanomips_label_name ());
+  nanomips_label_inc ();
+}
+
+/* If assembling nanoMIPS code, then return the nanoMIPS
+   reloc corresponding to the requested one if any.  Otherwise
+   return the reloc unchanged.
+*/
+
+static bfd_reloc_code_real_type
+nanomips_map_reloc (bfd_reloc_code_real_type reloc)
+{
+  static const bfd_reloc_code_real_type relocs[][2] = {
+    /* Keep sorted incrementally by the left-hand key.  */
+    {BFD_RELOC_GPREL16, BFD_RELOC_NANOMIPS_GPREL18},
+    {BFD_RELOC_HI16, BFD_RELOC_NANOMIPS_HI20},
+    {BFD_RELOC_HI16_S, BFD_RELOC_NANOMIPS_HI20},
+    {BFD_RELOC_LO16, BFD_RELOC_NANOMIPS_LO12},
+  };
+  bfd_reloc_code_real_type r;
+  size_t i;
+
+  for (i = 0; i < ARRAY_SIZE (relocs); i++)
+    {
+      r = relocs[i][0];
+      if (r > reloc)
+	return reloc;
+      if (r == reloc)
+	return relocs[i][1];
+    }
+  return reloc;
+}
+
+/* Remove first call from call-list and decrement numcalls.  */
+static void
+stublist_pop_call (struct balc_stub *stub)
+{
+  struct call_list *ptr = stub->first_call;
+
+  if (ptr)
+    {
+      if (stub->last_call == ptr)
+	stub->last_call = NULL;
+
+      stub->first_call = ptr->next;
+      xfree (ptr);
+      stub->numcalls -= 1;
+    }
+  return;
+}
+
+/* Remove all calls greater than or equal to CALLSITE
+   from call-list and decrement numcalls.  LIST is guaranteed
+   to have at least 2 elements when this is called.  */
+static void
+stublist_trunc_calls (struct balc_stub *stub, bfd_vma callsite)
+{
+  struct call_list *list = stub->first_call;
+
+  while (list && list->next)
+    {
+      if (list->next->callsite < callsite)
+	list = list->next;
+      else
+	break;
+    }
+
+  if (list)
+    {
+      stub->last_call = list;
+      list = list->next;
+      stub->last_call->next = NULL;
+      while (list)
+	{
+	  struct call_list *ptr = list;
+	  list = list->next;
+	  stub->numcalls -= 1;
+	  xfree (ptr);
+	}
+    }
+  return;
+}
+
+/* Add call to call-list.  */
+static void
+stublist_append_call (struct balc_stub *stub, bfd_vma callsite)
+{
+  struct call_list *next
+    = (struct call_list *) xmalloc (sizeof (struct call_list));
+  memset (next, 0, sizeof (struct call_list));
+  next->callsite = callsite;
+
+  if (stub->first_call == NULL)
+    stub->first_call = stub->last_call = next;
+  else if (callsite < stub->first_call->callsite)
+    {
+      next->next = stub->first_call;
+      stub->first_call = next;
+    }
+  else if (callsite > stub->last_call->callsite)
+    {
+      stub->last_call->next = next;
+      stub->last_call = next;
+    }
+  else
+    {
+      struct call_list *list = stub->first_call;
+
+      while (list && list->next)
+	{
+	  if (list->next->callsite < callsite)
+	    list = list->next;
+	  else
+	    break;
+	}
+
+      next->next = list->next;
+      list->next = next;
+    }
+
+  stub->numcalls += 1;
+  return;
+}
+
+static void
+stublist_merge_forward_calls (struct balc_stub *dest, struct balc_stub *src)
+{
+  src->last_call->next = dest->first_call;
+  dest->first_call = src->first_call;
+  if (dest->last_call == NULL)
+    dest->last_call = dest->first_call;
+  dest->numcalls += src->numcalls;
+}
+
+static void
+stublist_merge_backward_calls (struct balc_stub *dest, struct balc_stub *src)
+{
+  dest->last_call->next = src->first_call;
+  dest->last_call = src->last_call;
+  dest->numcalls += src->numcalls;
+}
+
+/* FIXME: Using a hash-table as a stubtable for now. We ideally want
+   these to be sorted on first_offset, so a different data structure
+   is required to get the best possible results.  */
+#define stubtable_find str_hash_find
+#define stubtable_create str_htab_create
+#define stubtable_insert str_hash_insert
+#define stubtable_delete str_hash_delete
+#define stubtable_traverse htab_traverse
+
+static bool
+balc_in_stub_range (const bfd_vma callsite, const stub_group *stubg)
+{
+  fragS *fragp;
+  bfd_vma minaddr;
+
+  if (stubg == NULL
+      || stubg->fragp == NULL
+      || stubg->fragp->fr_address + stubg->fragp->fr_fix == 0)
+    return false;
+
+  fragp = stubg->fragp;
+
+  /* 10-bit left shifted +ve offset taken from NextPC
+     gives a range of 1024 from current call-site.  */
+  if (fragp->fr_address + fragp->fr_fix >= 1024)
+    minaddr = (fragp->fr_address + fragp->fr_fix - 1024);
+  else
+    minaddr = 0;
+
+  /* Only the lowest address of the stub needs to be reachable.  */
+  if (stubg->next_offset != 0)
+    minaddr += (stubg->next_offset - 4);
+
+  /* 10-bit left shifted -ve offset taken from NextPC
+     gives a range of 1022 from the current call-site.  */
+  if (callsite > (fragp->fr_address + fragp->fr_fix + 1022)
+      || callsite < minaddr)
+    return false;
+
+  return true;
+}
+
+static bool
+balc_in_stub_group (const char *func, const bfd_vma callsite,
+		    const stub_group *stubg, struct balc_stub **stub)
+{
+  gas_assert (func != NULL && stubg != NULL);
+
+  if (!balc_in_stub_range (callsite, stubg) || (stubg->stubtable == NULL))
+    return false;
+
+  *stub = stubtable_find (stubg->stubtable, func);
+  return (*stub != NULL);
+}
+
+static bool
+balc_add_stub (symbolS *sym, stub_group *stubg)
+{
+  struct balc_stub *stub = NULL;
+  const char *func = S_GET_NAME (sym);
+
+  gas_assert (func != NULL && stubg != NULL);
+
+  if (!stubg->stubtable)
+    stubg->stubtable = stubtable_create ();
+
+  stub = (struct balc_stub *) stubtable_find (stubg->stubtable, func);
+
+  if (stub == NULL)
+    {
+      stub = (struct balc_stub *) xmalloc (sizeof (struct balc_stub));
+
+      if (stub == NULL)
+	return false;
+
+      memset (stub, 0, sizeof (struct balc_stub));
+      stub->sym = sym;
+    }
+  return true;
+}
+
+/* Consolidate this stub with a preceding or succeeding stub if all
+   calls to this stub can reach the alternative just as well.  */
+static bool
+balc_merge_stub (const char *func, stub_group *stubg,
+		 struct balc_stub **stub_ptr_ptr)
+{
+  stub_group *next = stubg->next;
+  stub_group *prev = stubg->prev;
+  struct balc_stub *prevstub = NULL;
+  struct balc_stub *nextstub = NULL;
+  struct balc_stub *stub = *stub_ptr_ptr;
+  bfd_vma callsite = stub->first_call->callsite;
+
+  /* Correction to account for the fact that the 4-byte stub will also move
+     forward from its current position to end of target stub-group.  */
+  if (stub->fragp && RELAX_MD_KEEPSTUB (stub->fragp->fr_subtype))
+    callsite += 4;
+
+  /* Any forward stub that we find can be the target for merge.  */
+  while (next && next->stubtable && balc_in_stub_range (callsite, next))
+    {
+      nextstub = (struct balc_stub *) stubtable_find (next->stubtable, func);
+
+      if (nextstub)
+	break;
+      next = next->next;
+    }
+
+  callsite = stub->last_call->callsite;
+
+  /* Correction to account for the fact that the 4-byte stub will also move
+     backward from its current position to end of target stub-group.  */
+  if (stub->fragp && RELAX_MD_KEEPSTUB (stub->fragp->fr_subtype)
+      && callsite > stub->fragp->fr_address)
+    callsite -= 4;
+
+  /* A previous stub can be a target only if it already has calls.  */
+  while (prev && prev->stubtable && balc_in_stub_range (callsite, prev))
+    {
+      struct balc_stub *pstub
+	= (struct balc_stub *) stubtable_find (prev->stubtable, func);
+
+      if (pstub && pstub->numcalls)
+	{
+	  prevstub = pstub;
+	  break;
+	}
+      prev = prev->prev;
+    }
+
+  /* If we have 2 candidates, then we favour the preceding one only if it
+     already has more calls than the succeeding one.  */
+  if (prevstub)
+    {
+      if (nextstub && (nextstub->numcalls >= prevstub->numcalls))
+	{
+	  stublist_merge_forward_calls (nextstub, stub);
+	  *stub_ptr_ptr = nextstub;
+	}
+      else
+	{
+	  stublist_merge_backward_calls (prevstub, stub);
+	  *stub_ptr_ptr = prevstub;
+	}
+      stubtable_delete (stubg->stubtable, func);
+      return true;
+    }
+  else if (nextstub)
+    {
+      stublist_merge_forward_calls (nextstub, stub);
+      *stub_ptr_ptr = nextstub;
+      stubtable_delete (stubg->stubtable, func);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* hash traverse - add stub for each function to current stub-group.  */
+static void
+balc_add_traverse (const char *key, void *value)
+{
+  struct balc_stub *stub = (struct balc_stub *) value;
+
+  if (stub->fragp != NULL)
+    {
+      symbolS *sym = stub->fragp->fr_symbol;
+      gas_assert (strcmp (key, S_GET_NAME (sym)) == 0);
+      balc_add_stub (sym, stubg_now);
+    }
+  return;
+}
+
+/* hash traverse - create a frag stub for each function.  */
+static void
+balc_frag_traverse (const char *key ATTRIBUTE_UNUSED, void *value)
+{
+  struct balc_stub *stub = (struct balc_stub *) value;
+  symbolS *l;
+  fragS *old_frag = stub->fragp;
+
+  if (old_frag)
+    {
+      old_frag->fr_subtype
+	= RELAX_MD_CLEAR_KEEPSTUB (old_frag->fr_subtype);
+      stub->sym = old_frag->fr_symbol;
+    }
+
+  frag_grow (4);
+  l = symbol_new (nanomips_label_name (), now_seg, frag_now, frag_now->fr_fix);
+  nanomips_label_inc ();
+  stub->fragp = frag_now;
+  add_relaxed_insn (&nanomips_bc32_insn, 4, 0,
+		    RELAX_MD_ENCODE (RT_BALC_STUB, 0, 1),
+		    stub->sym, 0);
+  stub->sym = l;
+  if (stub->numcalls >= 3)
+    stub->fragp->fr_subtype
+      = RELAX_MD_MARK_KEEPSTUB (stub->fragp->fr_subtype);
+  if (stubg_now->fragp == NULL)
+    stubg_now->fragp = stub->fragp;
+
+  return;
+}
+
+/* Find an elligible stub for a function call.  */
+static bool
+balc_find_stub_inrange (const char *func, const bfd_vma callsite,
+			stub_group *stubg, struct balc_stub **stub)
+{
+  stub_group *plink;
+  stub_group *nlink;
+  struct balc_stub *pstub = NULL;
+  struct balc_stub *nstub = NULL;
+
+  /* Search backwards within range.  */
+  plink = stubg->prev;
+  while (plink != NULL
+	 && !balc_in_stub_group (func, callsite, plink, &pstub)
+	 && balc_in_stub_range (callsite, plink))
+    plink = plink->prev;
+
+  /* Search forwards within range.  */
+  nlink = stubg;
+  while (nlink != NULL
+	 && !balc_in_stub_group (func, callsite, nlink, &nstub)
+	 && balc_in_stub_range (callsite, nlink))
+    nlink = nlink->next;
+
+  /* When 2 possible stub candidates are available, choose the preceeding one
+     only if it already has more associated calls than the suceeding one.  */
+  if (pstub)
+    {
+      if (nstub && (pstub->numcalls <= nstub->numcalls))
+	*stub = nstub;
+      else
+	*stub = pstub;
+
+      return true;
+    }
+  else if (nstub)
+    {
+      *stub = nstub;
+      return true;
+    }
+  return false;
+}
+
+/* Find closest elligible stub for this function call.  */
+static bool
+balc_get_stub_for_symbol (const char *func, const bfd_vma callsite,
+			  stub_group *stubg, symbolS **symbol)
+{
+  stub_group *plink = stubg->prev;
+  stub_group *nlink = stubg;
+  struct balc_stub *stub;
+
+  while (plink != NULL || nlink != NULL)
+    {
+      if (((nlink && balc_in_stub_group (func, callsite, nlink, &stub))
+	   || (plink && balc_in_stub_group (func, callsite, plink, &stub)))
+	  && stub->numcalls >= 3)
+	{
+	  *symbol = stub->sym;
+	  return true;
+	}
+
+      if (plink != NULL)
+	plink = plink->prev;
+      if (nlink != NULL)
+	nlink = nlink->next;
+    }
+  return false;
+}
+
+/* Initialize new stub-group at entry to a function.  */
+static void
+stubgroup_new (asection *sec)
+{
+  stub_group *stubg = NULL;
+
+  /* Create the master table.  */
+  if (balc_stubgroup_table == NULL)
+    balc_stubgroup_table = str_htab_create ();
+
+  if (stubg_now)
+    {
+      if (stubg_now->seg != sec)
+	{
+	  /* Change of sections, find the end of correct list.  */
+	  stubg_now = str_hash_find (balc_stubgroup_table, sec->name);
+	  while (stubg_now && stubg_now->next)
+	    stubg_now = stubg_now->next;
+	}
+
+      /* Re-cycle the previous stubgroup, if unused.  */
+      if (stubg_now && stubg_now->stubtable == NULL)
+	stubg = stubg_now;
+    }
+
+  if (stubg == NULL)
+    {
+      stubg = (stub_group *) xmalloc (sizeof (stub_group));
+      memset (stubg, 0, sizeof (stub_group));
+      stubg->seg = sec;
+    }
+
+  if (stubg_now == NULL)
+    /* First stubgroup for this section, add to master table.  */
+    str_hash_insert (balc_stubgroup_table, sec->name,  stubg, 0);
+  else if (stubg_now != stubg)
+    {
+      /* Link in with existing stub-groups for this section  */
+      stubg_now->next = stubg;
+      stubg->prev = stubg_now;
+    }
+  stubg_now = stubg;
+}
+
+/* Finalize the stub-group at the end of each function.  */
+static void
+stubgroup_wane (void)
+{
+  frag_wane (frag_now);
+  frag_new (0);
+  /* Carry forward stubs for all previous function calls within
+     this section.  */
+  if (stubg_now != NULL
+      && stubg_now->prev != NULL
+      && stubg_now->prev->stubtable != NULL
+      && stubg_now->seg == now_seg)
+    stubtable_traverse (stubg_now->prev->stubtable, balc_add_traverse, 0);
+
+  /* Create a frag for each stub - this must be done now and for *all*
+     possible called functions, because frags can not be inserted in a
+     position retroactively. This is also the reason why we include all
+     previously called functions within the section - to allow for forward
+     consolidation of stubs, knowing that many of these stubs will eventually
+     not be instantiated, due to either paucity of calls or consolidation.  */
+  if (stubg_now->stubtable && stubg_now->seg == now_seg)
+    stubtable_traverse (stubg_now->stubtable, balc_frag_traverse, 0);
+}
+
+/* Check whether the last encountered label carried an
+   explicit relocation.  */
+
+static bool
+explicit_reloc_label_p (fragS *fragP, asection *sec)
+{
+  if (reloc_list != NULL
+      && symbol_get_frag (reloc_list->u.a.offset_sym) == fragP)
+    {
+      segment_info_type *si = seg_info (sec);
+
+      struct insn_label_list *iter = si->label_list;
+      while (iter != NULL)
+	{
+	  if (iter->label == reloc_list->u.a.offset_sym)
+	    return true;
+	  iter = iter->next;
+	}
+    }
+  return false;
+}
+
+/* Check whether the last encountered label carried an explicit
+   relocation of the specified type.  */
+
+static bool
+explicit_reloc_type_p (fragS *fragP, asection *sec,
+		       unsigned rtype)
+{
+  return (explicit_reloc_label_p (fragP, sec)
+	  && reloc_list->u.a.howto->type == rtype);
+}
+
+/* Output an instruction.  IP is the instruction information.
+   ADDRESS_EXPR is an operand of the instruction to be used with
+   RELOC_TYPE.  EXPANSIONP is true if the instruction is part of
+   a macro expansion.  */
+
+static void
+append_insn (struct nanomips_cl_insn *ip, expressionS *address_expr,
+	     bfd_reloc_code_real_type *reloc_type,
+	     bool expansionp ATTRIBUTE_UNUSED)
+{
+  unsigned long pinfo;
+
+  pinfo = ip->insn_mo->pinfo;
+
+  if (address_expr == NULL)
+    ip->complete_p = 1;
+  else if (reloc_type[0] <= BFD_RELOC_UNUSED
+	   && reloc_type[1] == BFD_RELOC_UNUSED
+	   && reloc_type[2] == BFD_RELOC_UNUSED
+	   && address_expr->X_op == O_constant)
+    {
+      switch (*reloc_type)
+	{
+	case BFD_RELOC_NANOMIPS_HI20:
+	case BFD_RELOC_NANOMIPS_GPREL_HI20:
+	  ip->insn_opcode |= ((((address_expr->X_add_number >> 12) & 0x1ff)
+			       << 12)
+			      | (((address_expr->X_add_number >> 21) & 0x3ff)
+				 << 2)
+			      | ((address_expr->X_add_number >> 31) & 1));
+	  ip->complete_p = 1;
+	  break;
+
+	case BFD_RELOC_NANOMIPS_LO12:
+	case BFD_RELOC_NANOMIPS_GOT_LO12:
+	  ip->insn_opcode |= address_expr->X_add_number & 0xfff;
+	  ip->complete_p = 1;
+	  break;
+
+	case BFD_RELOC_NANOMIPS_IMM16:
+	  ip->insn_opcode |= address_expr->X_add_number & 0xffff;
+	  ip->complete_p = 1;
+	  break;
+
+	case BFD_RELOC_NANOMIPS_NEG12:
+	  ip->insn_opcode |= (-address_expr->X_add_number) & 0xfff;
+	  ip->complete_p = 1;
+	  break;
+
+	case BFD_RELOC_NANOMIPS_I32:
+	case BFD_RELOC_NANOMIPS_GPREL_I32:
+	  ip->insn_opcode_ext = (((address_expr->X_add_number >> 16) & 0xffff)
+				 | (address_expr->X_add_number << 16));
+	  ip->complete_p = 1;
+	  break;
+
+	case BFD_RELOC_NANOMIPS_GOT_CALL:
+	  ip->complete_p = 1;
+	case BFD_RELOC_NANOMIPS_PC_I32:
+	case BFD_RELOC_NANOMIPS_GOTPC_I32:
+	  break;
+
+	case BFD_RELOC_NANOMIPS_SIGNED_9:
+	  ip->insn_opcode |= ((address_expr->X_add_number & 0xff)
+			      | ((address_expr->X_add_number & 0x100) << 7));
+	  ip->complete_p = 1;
+	  break;
+
+	default:
+	  break;
+	}
+    }
+
+  dwarf2_emit_insn (0);
+
+  if (address_expr 
+	   && *reloc_type >= BFD_RELOC_UNUSED + RT_BRANCH_UCND 
+	   && !forced_insn_format)
+    {
+      int type = *reloc_type - BFD_RELOC_UNUSED;
+      int al = pinfo & INSN_WRITE_GPR_31;
+
+      gas_assert (address_expr != NULL);
+      gas_assert (!nanomips_relax.sequence);
+
+      add_relaxed_insn (ip, 4, 2,
+			RELAX_MD_ENCODE (type, al, 0),
+			address_expr->X_add_symbol,
+			address_expr->X_add_number);
+      /* Track this call for balcp-to-stub relaxation.  */
+      if (!nanomips_opts.no_balc_stubs
+	  && stubg_now != NULL
+	  && type == RT_BRANCH_UCND
+	  && (ip->insn_mo->pinfo & INSN_WRITE_GPR_31) != 0)
+	balc_add_stub (address_expr->X_add_symbol, stubg_now);
+
+      *reloc_type = BFD_RELOC_UNUSED;
+    }
+  else if (address_expr
+	   && *reloc_type == BFD_RELOC_UNUSED + RT_ADDIU
+	   && address_expr->X_op == O_subtract
+	   && !forced_insn_format)
+    {
+      int type = RT_ADDIU;
+      bool fixed = (nanomips_opts.insn32 || (forced_insn_length == 4));
+
+      gas_assert (address_expr != NULL);
+      gas_assert (!nanomips_relax.sequence);
+      add_relaxed_insn (ip, 6, 4,
+			RELAX_MD_ENCODE (type, 0, fixed),
+			make_expr_symbol (address_expr), 0);
+      *reloc_type = BFD_RELOC_UNUSED;
+    }
+  else if (address_expr
+	   && *reloc_type >= BFD_RELOC_UNUSED + RT_BRANCH_UCND
+	   && forced_insn_format)
+    {
+      const bfd_reloc_code_real_type rtype[] =
+	{ BFD_RELOC_NANOMIPS_10_PCREL_S1,
+	  BFD_RELOC_NANOMIPS_7_PCREL_S1,
+	  BFD_RELOC_NANOMIPS_4_PCREL_S1 };
+      *reloc_type = rtype[*reloc_type - BFD_RELOC_UNUSED - RT_BRANCH_UCND];
+      add_fixed_insn (ip);
+    }
+  else
+    {
+      if (nanomips_relax.sequence)
+	{
+	  /* If we've reached the end of this frag, turn it into a variant
+	     frag and record the information for the instructions we've
+	     written so far.  */
+	  if (frag_room () < insn_length (ip->insn_mo))
+	    relax_close_frag ();
+	  nanomips_relax.sizes[nanomips_relax.sequence - 1] +=
+	    insn_length (ip->insn_mo);
+	}
+
+      if (nanomips_relax.sequence != 2)
+	{
+	  if (nanomips_macro_warning.first_insn_sizes[0] == 0)
+	    nanomips_macro_warning.first_insn_sizes[0] =
+	      insn_length (ip->insn_mo);
+	  nanomips_macro_warning.sizes[0] += insn_length (ip->insn_mo);
+	  nanomips_macro_warning.insns[0]++;
+	}
+      if (nanomips_relax.sequence != 1)
+	{
+	  if (nanomips_macro_warning.first_insn_sizes[1] == 0)
+	    nanomips_macro_warning.first_insn_sizes[1] =
+	      insn_length (ip->insn_mo);
+	  nanomips_macro_warning.sizes[1] += insn_length (ip->insn_mo);
+	  nanomips_macro_warning.insns[1]++;
+	}
+      add_fixed_insn (ip);
+    }
+
+  if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED)
+    {
+      bfd_reloc_code_real_type final_type[3];
+      reloc_howto_type *howto0;
+      reloc_howto_type *howto;
+      int i;
+      unsigned where;
+
+      /* Perform any necessary conversion to nanoMIPS relocations
+         and find out how many relocations there actually are.  */
+      for (i = 0; i < 3 && reloc_type[i] != BFD_RELOC_UNUSED; i++)
+	final_type[i] = nanomips_map_reloc (reloc_type[i]);
+
+      /* In a compound relocation, it is the final (outermost)
+         operator that determines the relocated field.  */
+      howto = howto0 = bfd_reloc_type_lookup (stdoutput, final_type[i - 1]);
+      if (!howto)
+	abort ();
+
+      if (i > 1)
+	howto0 = bfd_reloc_type_lookup (stdoutput, final_type[0]);
+
+      where = ip->where;
+      /* For 48-bit nanoMIPS instructions, we want the relocation
+         to be on the lower 32-bits of the instruction.  */
+      if (nanomips_48bit_reloc_p (final_type[0]))
+	where = ip->where + 2;
+
+      ip->fixp[0] = fix_new_exp (ip->frag, where,
+				 bfd_get_reloc_size (howto),
+				 address_expr,
+				 howto0 && howto0->pc_relative,
+				 final_type[0]);
+      /* Remember the first fix-up in this frag.  */
+      if (ip->frag->tc_frag_data == NULL)
+	ip->frag->tc_frag_data = ip->fixp[0];
+
+      if (nanomips_relax.sequence)
+	{
+	  if (nanomips_relax.first_fixup == 0)
+	    nanomips_relax.first_fixup = ip->fixp[0];
+	}
+
+      /* Add fixups for the second and third relocations, if given.
+         Note that the ABI allows the second relocation to be
+         against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC.  At the
+         moment we only use RSS_UNDEF, but we could add support
+         for the others if it ever becomes necessary.  */
+      for (i = 1; i < 3; i++)
+	if (reloc_type[i] != BFD_RELOC_UNUSED)
+	  {
+	    ip->fixp[i] = fix_new (ip->frag, where,
+				   ip->fixp[0]->fx_size, NULL, 0,
+				   false, final_type[i]);
+
+	    /* Use fx_tcbit to mark compound relocs.  */
+	    ip->fixp[0]->fx_tcbit = 1;
+	    ip->fixp[i]->fx_tcbit = 1;
+	  }
+
+      if (nanomips_linkrelax_p && (insn_length (ip->insn_mo) != 6))
+	{
+	  if (forced_insn_format)
+	    fix_new (ip->frag, ip->where, 0, &abs_symbol, 0, false,
+		     BFD_RELOC_NANOMIPS_FIXED);
+	  else if (forced_insn_length > 0)
+	    fix_new (ip->frag, ip->where, 0, &abs_symbol, 0, false,
+		     (forced_insn_length == 2 ? BFD_RELOC_NANOMIPS_INSN16
+		      : BFD_RELOC_NANOMIPS_INSN32));
+	}
+    }
+
+  install_insn (ip);
+  insert_into_history (ip);
+
+  /* If we have just completed an unconditional branch, clear the history.  */
+  if ((compact_branch_p (&history) && uncond_branch_p (&history))
+      && !(history.insn_mo->pinfo2 & INSN2_CONVERTED_TO_COMPACT))
+    nanomips_flush_pending_output ();
+
+  /* We just output an insn, so the next one doesn't have a label.  */
+  nanomips_clear_insn_labels ();
+}
+
+/* Forget that there was any previous instruction or label.
+   When BRANCH is true, the branch history is also flushed.  */
+
+void
+nanomips_flush_pending_output (void)
+{
+  prev_nop_frag = NULL;
+  insert_into_history (NOP_INSN);
+  nanomips_clear_insn_labels ();
+}
+
+/* Start a (possibly nested) noreorder block.  */
+
+static void
+start_noreorder (void)
+{
+  nanomips_opts.noreorder++;
+}
+
+/* End a nested noreorder block.  */
+
+static void
+end_noreorder (void)
+{
+  nanomips_opts.noreorder--;
+}
+
+/* Sign-extend 32-bit mode constants that have bit 31 set and all
+   higher bits unset.  */
+
+static void
+normalize_constant_expr (expressionS *ex)
+{
+  if (ex->X_op == O_constant && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+    ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+			- 0x80000000);
+}
+
+/* Sign-extend 32-bit mode address offsets that have bit 31 set and
+   all higher bits unset.  */
+
+static void
+normalize_address_expr (expressionS *ex)
+{
+  if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES)
+       || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS))
+      && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+    ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+			- 0x80000000);
+}
+
+struct gprel_insn_match
+{
+  /* Instruction.  */
+  const char *str;
+  /* Arch word size 32/64 if it matters, else 0.  */
+  int gpwidth;
+  /* Relocation.  */
+  bfd_reloc_code_real_type reloc;
+};
+
+static const struct gprel_insn_match nanomips_gprel_map[] = {
+  {"lw[gp]", 0, BFD_RELOC_NANOMIPS_GPREL19_S2},
+  {"sw[gp]", 0, BFD_RELOC_NANOMIPS_GPREL19_S2},
+  {"lh[gp]", 0, BFD_RELOC_NANOMIPS_GPREL17_S1},
+  {"lhu[gp]", 0, BFD_RELOC_NANOMIPS_GPREL17_S1},
+  {"sh[gp]", 0, BFD_RELOC_NANOMIPS_GPREL17_S1},
+  {"lb[gp]", 0, BFD_RELOC_NANOMIPS_GPREL18},
+  {"lbu[gp]", 0, BFD_RELOC_NANOMIPS_GPREL18},
+  {"sb[gp]", 0, BFD_RELOC_NANOMIPS_GPREL18},
+  {"addiu.b", 0, BFD_RELOC_NANOMIPS_GPREL18},
+  {"addiu[gp.b]", 0, BFD_RELOC_NANOMIPS_GPREL18},
+  {"addiu.w", 0, BFD_RELOC_NANOMIPS_GPREL19_S2},
+  {"addiu[gp.w]", 0, BFD_RELOC_NANOMIPS_GPREL19_S2},
+  {"ldc1[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2},
+  {"sdc1[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2},
+  {"l.d[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2},
+  {"s.d[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2},
+  {"swc1[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2},
+  {"lwc1[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2},
+  {"s.s[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2},
+  {"l.s[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2},
+  {"lwu[gp]", 0, BFD_RELOC_NANOMIPS_GPREL16_S2},
+  {"ld[gp]", 32, BFD_RELOC_NANOMIPS_GPREL19_S2},
+  {"sd[gp]", 32, BFD_RELOC_NANOMIPS_GPREL19_S2},
+  {"ld[gp]", 64, BFD_RELOC_NANOMIPS_GPREL18_S3},
+  {"sd[gp]", 64, BFD_RELOC_NANOMIPS_GPREL18_S3},
+};
+
+static bfd_reloc_code_real_type
+gprel_for_insn (const struct nanomips_opcode *insn)
+{
+  unsigned int i;
+
+  if (forced_insn_length == 2
+      || (forced_insn_format && insn_length (insn) == 2))
+    return BFD_RELOC_NANOMIPS_GPREL7_S2;
+  if (insn_length (insn) == 6)
+    return BFD_RELOC_NANOMIPS_GPREL_I32;
+
+  for (i = 0; i < ARRAY_SIZE (nanomips_gprel_map); i++)
+    if (strncasecmp (insn->name, nanomips_gprel_map[i].str,
+		     strlen (insn->name)) == 0
+	&& strcmp (insn->suffix,
+		   nanomips_gprel_map[i].str + strlen (insn->name)) == 0)
+      {
+	if (nanomips_gprel_map[i].gpwidth
+	    && nanomips_gprel_map[i].gpwidth != GPR_SIZE)
+	  continue;
+
+	return nanomips_gprel_map[i].reloc;
+      }
+
+  return BFD_RELOC_UNUSED;
+}
+
+static bfd_reloc_code_real_type
+tlsrel_for_insn (const struct nanomips_opcode *insn,
+		 bfd_reloc_code_real_type rtype)
+{
+  unsigned int i;
+  static const struct
+  {
+    bfd_reloc_code_real_type key;
+    bfd_reloc_code_real_type value;
+  } reloc_map[] = {
+    { BFD_RELOC_NANOMIPS_TLS_GD, BFD_RELOC_NANOMIPS_TLS_GD_I32 },
+    { BFD_RELOC_NANOMIPS_TLS_LD, BFD_RELOC_NANOMIPS_TLS_LD_I32 },
+    { BFD_RELOC_NANOMIPS_TLS_TPREL12, BFD_RELOC_NANOMIPS_TLS_TPREL_I32 },
+    { BFD_RELOC_NANOMIPS_TLS_DTPREL12, BFD_RELOC_NANOMIPS_TLS_DTPREL_I32 }
+  };
+
+  if (insn_length (insn) == 6)
+    for (i = 0; i < ARRAY_SIZE (reloc_map); i++)
+      if (rtype == reloc_map[i].key)
+	return reloc_map[i].value;
+
+  if (strncmp (insn->name, "addiu", 5) == 0
+      || strncmp (insn->name, "li", 2) == 0)
+    {
+      if (rtype == BFD_RELOC_NANOMIPS_TLS_TPREL12)
+	return BFD_RELOC_NANOMIPS_TLS_TPREL16;
+      if (rtype == BFD_RELOC_NANOMIPS_TLS_DTPREL12)
+	return BFD_RELOC_NANOMIPS_TLS_DTPREL16;
+    }
+  return rtype;
+}
+
+static bool
+match_insn (struct nanomips_cl_insn *insn,
+	    const struct nanomips_opcode *opcode,
+	    struct nanomips_operand_token *tokens,
+	    unsigned int opcode_extra, bool lax_match)
+{
+  const char *args;
+  struct nanomips_arg_info arg;
+  const struct nanomips_operand *operand;
+  char c;
+
+  imm_expr.X_op = O_absent;
+  offset_expr.X_op = O_absent;
+  offset_reloc[0] = BFD_RELOC_UNUSED;
+  offset_reloc[1] = BFD_RELOC_UNUSED;
+  offset_reloc[2] = BFD_RELOC_UNUSED;
+
+  create_insn (insn, opcode);
+  /* When no opcode suffix is specified, assume ".xyzw". */
+  insn->insn_opcode |= opcode_extra;
+  memset (&arg, 0, sizeof (arg));
+  arg.insn = insn;
+  arg.token = tokens;
+  arg.argnum = 1;
+  arg.last_regno = ILLEGAL_REG;
+  arg.dest_regno = ILLEGAL_REG;
+  arg.lax_match = lax_match;
+  arg.select_mask = 0;
+
+  for (args = opcode->args;; ++args)
+    {
+      if (arg.token->type == OT_END)
+	{
+	  /* Handle unary instructions in which only one operand is given.
+	     The source is then the same as the destination.  */
+	  if ((*args == ',') || (arg.opnum == 0 && *args != 0))
+	    {
+	      operand = decode_nanomips_operand (args + (arg.opnum ? 1 : 0));
+	      if (operand && nanomips_optional_operand_p (operand))
+		{
+		  arg.token = tokens;
+		  arg.argnum = 1;
+		  continue;
+		}
+	      /* These optional types appear only as the last operand.  */
+	      else if (operand && (operand->type == OP_CP0SEL
+		       || operand->type == OP_DONT_CARE))
+		return true;
+	    }
+
+	  /* Treat elided base registers as $0.  */
+	  if (strcmp (args, "(b)") == 0 || strcmp (args, "(c)") == 0)
+	    args += 3;
+
+	  if ((args[0] == '+' && args[1] == 'N')
+	      || (args[0] == 'm' && args[1] == 'K'))
+	    /* The register suffix is optional. */
+	    args += 2;
+
+	  /* Fail the match if there were too few operands.  */
+	  if (*args)
+	    return false;
+
+	  clear_insn_error ();
+	  return true;
+	}
+
+      /* Fail the match if the line has too many operands.   */
+      if (*args == 0)
+	return false;
+
+      /* Handle characters that need to match exactly.  */
+      if (*args == '(' || *args == ')' || *args == ',')
+	{
+	  if (match_char (&arg, *args))
+	    continue;
+	  return false;
+	}
+      if (*args == '#')
+	{
+	  ++args;
+	  if (arg.token->type == OT_DOUBLE_CHAR && arg.token->u.ch == *args)
+	    {
+	      ++arg.token;
+	      continue;
+	    }
+	  return false;
+	}
+
+      /* Handle special macro operands.  Work out the properties of
+         other operands.  */
+      arg.opnum += 1;
+
+      switch (*args)
+	{
+	case 'I':
+	  if (!match_const_int (&arg, &imm_expr.X_add_number))
+	    return false;
+	  imm_expr.X_op = O_constant;
+	  if (GPR_SIZE == 32)
+	    normalize_constant_expr (&imm_expr);
+	  continue;
+
+	case 'A':
+	  if (arg.token->type == OT_CHAR && arg.token->u.ch == '(')
+	    {
+	      /* Assume that the offset has been elided and that what
+	         we saw was a base register.  The match will fail later
+	         if that assumption turns out to be wrong.  */
+	      offset_expr.X_op = O_constant;
+	      offset_expr.X_add_number = 0;
+	    }
+	  else
+	    {
+	      if (!match_expression (&arg, &offset_expr, offset_reloc))
+		return false;
+	      normalize_address_expr (&offset_expr);
+	    }
+	  continue;
+
+	case 'F':
+	  if (!match_float_constant (&arg, &imm_expr, &offset_expr, 8, true))
+	    return false;
+	  continue;
+
+	case 'L':
+	  if (!match_float_constant (&arg, &imm_expr, &offset_expr, 8, false))
+	    return false;
+	  continue;
+
+	case 'f':
+	  if (!match_float_constant (&arg, &imm_expr, &offset_expr, 4, true))
+	    return false;
+	  continue;
+
+	case 'l':
+	  if (!match_float_constant (&arg, &imm_expr, &offset_expr, 4, false))
+	    return false;
+	  continue;
+	}
+
+      operand = decode_nanomips_operand (args);
+      if (!operand)
+	abort ();
+
+      if (operand->type == OP_PCREL
+	  || operand->type == OP_NON_ZERO_PCREL_S1
+	  || operand->type == OP_HI20_PCREL
+	  || operand->type == OP_INT_WORD
+	  || operand->type == OP_UINT_WORD
+	  || operand->type == OP_PC_WORD
+	  || operand->type == OP_INT
+	  || operand->type == OP_GPREL_WORD)
+	switch (*args)
+	  {
+	  case '+':
+	    switch (args[1])
+	      {
+	      case 'u':
+		*offset_reloc = BFD_RELOC_NANOMIPS_25_PCREL_S1;
+		break;
+
+	      case 'r':
+		*offset_reloc = BFD_RELOC_NANOMIPS_21_PCREL_S1;
+		break;
+
+	      case 'Q':
+	      case 'R':
+		*offset_reloc = BFD_RELOC_NANOMIPS_I32;
+		break;
+	      }
+	    break;
+
+	  case 'a':
+	    *offset_reloc = BFD_RELOC_NANOMIPS_25_PCREL_S1;
+	    break;
+
+	  case 'p':
+	    *offset_reloc = BFD_RELOC_NANOMIPS_14_PCREL_S1;
+	    break;
+
+	  case '~':
+	    *offset_reloc = BFD_RELOC_NANOMIPS_11_PCREL_S1;
+	    break;
+
+	  case 'm':
+	    c = args[1];
+	    switch (c)
+	      {
+	      case 'D':
+		*offset_reloc = (forced_insn_length
+				 ? BFD_RELOC_NANOMIPS_10_PCREL_S1
+				 : BFD_RELOC_UNUSED + RT_BRANCH_UCND);
+		break;
+	      case 'E':
+		*offset_reloc = (forced_insn_length
+				 ? BFD_RELOC_NANOMIPS_7_PCREL_S1
+				 : BFD_RELOC_UNUSED + RT_BRANCH_CNDZ);
+		break;
+	      case 'F':
+		*offset_reloc = (forced_insn_length
+				 ? BFD_RELOC_NANOMIPS_4_PCREL_S1
+				 : BFD_RELOC_UNUSED + RT_BRANCH_CND);
+		break;
+	      }
+	    break;
+	  }
+
+      /* Skip prefixes.  */
+      if (*args == '+' || *args == 'm' || *args == '-' || *args == '`')
+	args++;
+
+      if (nanomips_optional_operand_p (operand)
+	  && args[1] == ','
+	  && (arg.token[0].type != OT_REG || arg.token[1].type == OT_END))
+	{
+	  /* Assume that the register has been elided and is the
+	     same as the first operand.  */
+	  arg.token = tokens;
+	  arg.argnum = 1;
+	}
+
+      if (operand->type == OP_COPY_BITS)
+	args++;
+
+      if (!match_operand (&arg, operand))
+	return false;
+
+      if (*offset_reloc == BFD_RELOC_NANOMIPS_GPREL18)
+	*offset_reloc = gprel_for_insn (insn->insn_mo);
+      else if (flex_reloc_p (*offset_reloc))
+	*offset_reloc = tlsrel_for_insn (insn->insn_mo, *offset_reloc);
+    }
+}
+
+/* Record that the current instruction is invalid for the current ISA.  */
+
+static void
+match_invalid_for_isa (void)
+{
+  set_insn_error_ss
+    (0, _("opcode not supported on this processor: %s (%s)"),
+     nanomips_cpu_info_from_arch (nanomips_opts.arch)->name,
+     nanomips_cpu_info_from_isa (nanomips_opts.isa, false));
+}
+
+static bool
+match_insns (struct nanomips_cl_insn *insn,
+		      const struct nanomips_opcode *first,
+		      const struct nanomips_opcode *past,
+		      struct nanomips_operand_token *tokens,
+		      int opcode_extra, bool lax_match)
+{
+  const struct nanomips_opcode *opcode;
+  bool seen_valid_for_isa, seen_valid_for_size;
+  bool seen_valid_for_ase, seen_valid_for_fp;
+
+  seen_valid_for_isa = false;
+  seen_valid_for_size = false;
+  seen_valid_for_ase = false;
+  seen_valid_for_fp = false;
+  opcode = first;
+
+  /* Search for a match, ignoring alternatives that don't satisfy the
+     current ISA or forced_length.  */
+  do
+    {
+      gas_assert (strcmp (opcode->name, first->name) == 0);
+      if (!forced_insn_format || strcmp (opcode->suffix, first->suffix) == 0)
+	{
+	  if (is_opcode_valid (opcode))
+	    {
+	      seen_valid_for_isa = true;
+	      if (is_size_valid (opcode))
+		{
+		  seen_valid_for_size = true;
+		  if (match_insn (insn, opcode, tokens, opcode_extra,
+				  lax_match))
+		    return true;
+		}
+	    }
+	  else
+	    {
+	      /* If instruction is not valid for current ISA,  try to guess
+		 why for better error reporting.  */
+	      if (is_opcode_valid_def_ase (opcode))
+		seen_valid_for_ase = true;
+	      if (is_opcode_valid_for_fp (opcode))
+		seen_valid_for_fp = true;
+	    }
+	}
+      ++opcode;
+    }
+  while (opcode < past && strcmp (opcode->name, first->name) == 0);
+
+  /* Handle the case where we didn't try to match an instruction because
+     all the alternatives were incompatible with the current ISA.  */
+  if (!seen_valid_for_isa)
+    {
+      match_invalid_for_isa ();
+      return true;
+    }
+
+  /* Handle the case where we didn't try to match an instruction because
+     all the alternatives were of the wrong size.  */
+  if (!seen_valid_for_size)
+    {
+      if (nanomips_opts.insn32)
+	set_insn_error (0, _("opcode not supported in the `insn32' mode"));
+      else
+	set_insn_error_i
+	  (0, _("unrecognized %d-bit version of nanoMIPS opcode"),
+	   8 * forced_insn_length);
+      return true;
+    }
+  return false;
+}
+
+/* Set up global variables for the start of a new macro.  */
+
+static void
+macro_start (void)
+{
+  memset (&nanomips_macro_warning.sizes, 0,
+	  sizeof (nanomips_macro_warning.sizes));
+  memset (&nanomips_macro_warning.first_insn_sizes, 0,
+	  sizeof (nanomips_macro_warning.first_insn_sizes));
+  memset (&nanomips_macro_warning.insns, 0,
+	  sizeof (nanomips_macro_warning.insns));
+  nanomips_macro_warning.first_frag = NULL;
+}
+
+/* Finish up a macro.  Emit warnings as appropriate.  */
+
+static void
+macro_end (bool allow_expansion)
+{
+  /* Relaxation warning flags.  */
+  relax_substateT subtype = 0;
+
+  /* Check instruction count requirements.  */
+  if (nanomips_macro_warning.insns[0] > 1
+      || nanomips_macro_warning.insns[1] > 1)
+    {
+      if (nanomips_macro_warning.insns[1] > nanomips_macro_warning.insns[0])
+	subtype |= RELAX_SECOND_LONGER;
+      if (nanomips_opts.nomacro)
+	subtype |= RELAX_NOMACRO;
+    }
+
+  /* If both implementations are longer than 1 instruction, then emit the
+     warning now.  */
+  if (nanomips_macro_warning.insns[0] > 1
+      && nanomips_macro_warning.insns[1] > 1)
+    {
+      if (nanomips_opts.nomacro)
+	{
+	  if (allow_expansion)
+	    as_warn (_("macro instruction expanded into multiple "
+		       "instructions"));
+	  else
+	    as_bad (_("macro instruction expanded into multiple "
+		      "instructions"));
+	}
+
+      subtype &= ~RELAX_NOMACRO;
+    }
+
+  /* If any flags still set, then one implementation might need a warning
+     and the other either will need one of a different kind or none at all.
+     Pass any remaining flags over to relaxation.  */
+  if (nanomips_macro_warning.first_frag != NULL)
+    nanomips_macro_warning.first_frag->fr_subtype |= subtype;
+}
+
+static const char *const brk_fmt[2] = { "mK", "+J" };
+static const char *const addiu_fmt[2] = { "+t,r,j", "t,r,i" };
+static const char *const mfhl_fmt[2] = { "mj", "s" };
+
+#define BRK_FMT (brk_fmt[nanomips_opts.insn32])
+#define COP12_FMT "E,+j(b)"
+#define JALR_FMT "t,s"
+#define LUI_FMT "t,u"
+#define DLUI_FMT "mp,+Q"
+
+#define ADDRESS_ADD_FMT "d,v,t"
+#define ADDRESS_ADDI32_FMT "mp,mt,+R"
+
+#define ADDIU_FMT (addiu_fmt[HAVE_32BIT_ADDRESSES? 0 : 1])
+#define ADDIUGP_FMT "t,ma,."
+#define LWGP_FMT "t,.(ma)"
+#define LDGP_FMT "t,.(ma)"
+#define LL_SC_FMT "t,+p(b)"
+#define LLD_SCD_FMT "t,+q(b)"
+#define LLE_SCE_FMT "t,+p(b)"
+#define LWU_FMT "t,o(b)"
+#define MFHL_FMT (mfhl_fmt[nanomips_opts.insn32])
+#define SHFT_FMT "t,r,<"
+#define TRAP_FMT "s,t,|"
+#define ACLR_FMT "\\,+j(b)"
+#define BCONDZ1_FMT "s,p"
+#define BCONDZ2_FMT "t,p"
+#define B_FMT "+u"
+#define JAL_FMT "+u"
+#define OP_IMM_FMT "t,r,i"
+#define BITOP_IMM_FMT "t,r,g"
+#define PREF_FMT "k,+j(b)"
+#define BITW_FMT "d,v,t"
+#define DIV_FMT "d,v,t"
+#define MOVE_FMT "mb,mj"
+#define ISA_OFFBITS 12
+#define ISA_ADD_OFFBITS (HAVE_32BIT_ADDRESSES ? 16 : 12)
+#define ISA_SIGNED_OFFBITS 9
+#define ISA_MULTI_OFFBITS 9
+#define ISA_COP2_OFFBITS 9
+#define ISA_LLSC_OFFBITS 9
+
+#define ISA_LLDSCD_OFFBITS 6
+
+#define ISA_CACHE_OFFBITS ISA_LLSC_OFFBITS
+
+#define MAX_PIC_OFFSET 0x1fffc
+
+#define MIN_PIC_OFFSET 0
+
+#define ISA_UNSIGNED_LDST_RELOC BFD_RELOC_NANOMIPS_LO12
+#define ISA_SIGNED_LDST_RELOC BFD_RELOC_NANOMIPS_SIGNED_9
+
+#define ISA_SIGNED_LDST_FMT "t,+j(b)"
+#define ISA_MULTI_LDST_FMT "t,+j(b),|"
+#define ISA_UNSIGNED_LDST_FMT "t,o(b)"
+#define ISA_UNSIGNED_COP1_FMT "T,o(b)"
+#define ISA_SIGNED_COP1_FMT "T,+j(b)"
+
+/* Read a macro's relocation codes from *ARGS and store them in *R.
+   The first argument in *ARGS will be either the code for a single
+   relocation or -1 followed by the three codes that make up a
+   composite relocation.  */
+
+static void
+macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
+{
+  int i, next;
+
+  next = va_arg (*args, int);
+  if (next >= 0)
+    r[0] = next;
+  else
+    {
+      for (i = 0; i < 3; i++)
+	r[i] = va_arg (*args, int);
+      /* This function is only used for 16-bit relocation fields.
+         To make the macro code simpler, treat an unrelocated value
+         in the same way as BFD_RELOC_LO16.  */
+    }
+  if (r[0] == BFD_RELOC_UNUSED)
+    r[0] = BFD_RELOC_LO16;
+}
+
+/* Map an <format,reloc> pair to nanoMIPS relocs.  */
+
+static void
+macro_match_reloc (const char *fmt, bfd_reloc_code_real_type *r)
+{
+  if (*r == BFD_RELOC_LO16)
+    {
+      if (*fmt == 'o' || *fmt == 'g')
+	*r = BFD_RELOC_NANOMIPS_LO12;
+      else if (*fmt == 'j')
+	*r = BFD_RELOC_NANOMIPS_IMM16;
+      else if (*fmt == 'h')
+	*r = BFD_RELOC_NANOMIPS_NEG12;
+    }
+  else if (*r == BFD_RELOC_GPREL16)
+    {
+      if (*fmt == '.')
+	*r = BFD_RELOC_NANOMIPS_GPREL19_S2;
+      else if (*fmt == '+' && *(fmt + 1) == '2')
+	*r = BFD_RELOC_NANOMIPS_GPREL16_S2;
+      else if (*fmt == '+' && *(fmt + 1) == '1')
+	*r = BFD_RELOC_NANOMIPS_GPREL18;
+      else if (*fmt == '+' && *(fmt + 1) == '3')
+	*r = BFD_RELOC_NANOMIPS_GPREL17_S1;
+      else if (*fmt == '+' && *(fmt + 1) == '4')
+	*r = BFD_RELOC_NANOMIPS_GPREL18_S3;
+      else
+	gas_assert (false);
+    }
+  if (*r == _dummy_first_bfd_reloc_code_real)
+    *r = BFD_RELOC_UNUSED;
+  else if (!nanomips_reloc_p (*r))
+    {
+      *r = nanomips_map_reloc (*r);
+      gas_assert (nanomips_reloc_p (*r));
+    }
+}
+
+/* Build an instruction created by a macro expansion.  This is passed
+   a pointer to the count of instructions created so far, an
+   expression, the name of the instruction to build, an operand format
+   string, and corresponding arguments.  */
+
+static void
+macro_build (expressionS *ep, const char *name, const char *fmt, ...)
+{
+  const struct nanomips_opcode *mo = NULL;
+  bfd_reloc_code_real_type r[3];
+  const struct nanomips_opcode *amo;
+  const struct nanomips_operand *operand;
+  htab_t hash;
+  struct nanomips_cl_insn insn;
+  va_list args;
+  unsigned int uval;
+
+  va_start (args, fmt);
+
+  r[0] = BFD_RELOC_UNUSED;
+  r[1] = BFD_RELOC_UNUSED;
+  r[2] = BFD_RELOC_UNUSED;
+  hash = nanomips_op_hash;
+
+  amo = str_hash_find (hash, name);
+  gas_assert (amo);
+  gas_assert (strcmp (name, amo->name) == 0);
+
+  do
+    {
+      /* Search until we get a match for NAME.  */
+      if (strcmp (fmt, amo->args) == 0
+	  && amo->pinfo != INSN_MACRO
+	  && is_opcode_valid (amo)
+	  && is_size_valid (amo))
+	{
+	  if (!mo)
+	    mo = amo;
+	}
+
+      ++amo;
+      gas_assert (amo->name);
+    }
+  while (strcmp (name, amo->name) == 0);
+
+  gas_assert (mo);
+  create_insn (&insn, mo);
+  for (; *fmt; ++fmt)
+    {
+      switch (*fmt)
+	{
+	case ',':
+	case '(':
+	case ')':
+	case 'z':
+	  continue;
+
+	case 'i':
+	case 'g':
+	case 'j':
+	case 'h':
+	  macro_read_relocs (&args, r);
+	  macro_match_reloc (fmt, r);
+	  continue;
+
+	case '~':
+	case 'o':
+	  macro_read_relocs (&args, r);
+	  macro_match_reloc (fmt, r);
+	  continue;
+
+	case 'u':
+	  macro_read_relocs (&args, r);
+	  gas_assert (ep != NULL
+		      && (ep->X_op == O_constant
+			  || (ep->X_op == O_symbol && hi_reloc_p (*r))));
+	  macro_match_reloc (fmt, r);
+	  continue;
+
+	case '+':
+	  if ((*(fmt + 1) == 'm'
+	       || *(fmt + 1) == 'j'
+	       || *(fmt + 1) == '1'
+	       || *(fmt + 1) == '2'
+	       || *(fmt + 1) == '3'
+	       || *(fmt + 1) == '4'
+	       || *(fmt + 1) == 'K'
+	       || *(fmt + 1) == 'Q'
+	       || *(fmt + 1) == 'R'
+	       || *(fmt + 1) == 'S')
+	      && ep != NULL)
+	    {
+	      macro_read_relocs (&args, r);
+	      macro_match_reloc (fmt, r);
+	      if (*(fmt + 1) == 'Q' || *(fmt + 1) == 'R' || *(fmt + 1) == 'S')
+		break;
+	      fmt++;
+	      continue;
+	    }
+
+	  if (*(fmt + 1) == 'r' || *(fmt + 1) == 'u')
+	    {
+	      if (*(fmt + 1) == 'u')
+		r[0] = BFD_RELOC_NANOMIPS_25_PCREL_S1;
+	      else if (*(fmt + 1) == 'r')
+		r[0] = BFD_RELOC_NANOMIPS_21_PCREL_S1;
+	      fmt++;
+	      continue;
+	    }
+	  break;
+
+	case 'p':
+	  gas_assert (ep != NULL);
+
+	  /*
+	   * This allows macro() to pass an immediate expression for
+	   * creating short branches without creating a symbol.
+	   *
+	   * We don't allow branch relaxation for these branches, as
+	   * they should only appear in ".set nomacro" anyway.
+	   */
+	  *r = BFD_RELOC_NANOMIPS_14_PCREL_S1;
+	  macro_match_reloc (fmt, r);
+	  continue;
+
+	case 'm':
+	  if (*(fmt + 1) == 'Q')
+	    {
+	      macro_read_relocs (&args, r);
+	      macro_match_reloc (fmt, r);
+	      fmt++;
+	      continue;
+	    }
+	  break;
+
+	case '.':
+	  macro_read_relocs (&args, r);
+	  macro_match_reloc (fmt, r);
+	  continue;
+
+	case '-':
+	  if (*(fmt + 1) == 'i')
+	    macro_read_relocs (&args, r);
+	  /* Fall through.  */
+
+	default:
+	  break;
+	}
+
+      operand = decode_nanomips_operand (fmt);
+      if (!operand)
+	abort ();
+
+      uval = va_arg (args, int);
+
+      if (operand->type == OP_INT_WORD
+	  || operand->type == OP_UINT_WORD
+	  || operand->type == OP_PC_WORD
+	  || operand->type == OP_GPREL_WORD
+	  || operand->type == OP_IMM_WORD)
+	insn.insn_opcode_ext = 0;
+      else if (operand->type == OP_NEG_INT)
+	insn_insert_operand (&insn, operand, -uval);
+      else
+	insn_insert_operand (&insn, operand, uval);
+
+      if ((*fmt == '+') || *fmt == 'm' || *fmt == '-' || *fmt == '`')
+	++fmt;
+    }
+
+  va_end (args);
+  gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+  append_insn (&insn, ep, r, true);
+}
+
+/*
+ * Generate an lui/dlui instruction.
+ */
+static void
+macro_build_lui (expressionS *ep, int regnum)
+{
+  if (ep->X_op != O_constant)
+    gas_assert (ep->X_op == O_symbol);
+
+  macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_NANOMIPS_HI20);
+}
+
+/* Return the high part that should be loaded in order to make the low
+   part of VALUE accessible using an offset of OFFBITS bits.  */
+
+static offsetT
+offset_high_part (offsetT value, unsigned int offbits)
+{
+  offsetT bias;
+  addressT low_mask;
+
+  if (offbits == 0)
+    return value;
+  bias = 1 << (offbits - 1);
+  low_mask = bias * 2 - 1;
+  if (offbits == ISA_OFFBITS)
+    /* Low part is 12-bit unsigned, so no bias necessary.  */
+    return value & ~low_mask;
+  else
+    return (value + bias) & ~low_mask;
+}
+
+/* Return the high part that should be loaded in order to make the low
+   part of VALUE accessible using an offset of OFFBITS bits.  */
+
+static offsetT
+offset_high_unsigned (offsetT value, unsigned int offbits)
+{
+  addressT low_mask;
+
+  if (offbits == 0)
+    return value;
+  low_mask = (1 << (offbits - 1)) * 2 - 1;
+  return value & ~low_mask;
+}
+
+/*			set_at()
+ * Generates code to set the $at register to true (one)
+ * if reg is less than the immediate expression.
+ */
+static void
+set_at (int reg, int unsignedp)
+{
+  if (offset_high_part (imm_expr.X_add_number, ISA_OFFBITS) == 0)
+    macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", OP_IMM_FMT,
+		 AT, reg, BFD_RELOC_LO16);
+  else
+    {
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
+    }
+}
+
+/* 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)
+
+/*
+ *  This routine generates the least number of instructions necessary to load
+ *  an absolute expression value into a register.
+ */
+static void
+load_register (int reg, expressionS *ep, int dbl)
+{
+  if (ep->X_op != O_big)
+    {
+      gas_assert (ep->X_op == O_constant);
+
+      /* Sign-extending 32-bit constants makes their handling easier.  */
+      if (!dbl)
+	normalize_constant_expr (ep);
+
+      if ((int)ep->X_add_number >= -1
+	  && ep->X_add_number <= 126
+	  && (reg >> 2 == 1 || reg >> 2 == 4))
+	{
+	  /* 7-bit values loaded using LI[16].  */
+	  macro_build (NULL, "li", "md,mI", reg, (int) ep->X_add_number);
+	  return;
+	}
+      if (IS_SEXT_16BIT_UINT (ep->X_add_number))
+	{
+	  /* We can handle 16 bit unsigned values with an addiu to
+	     $zero.  No need to ever use daddiu here, since $zero and
+	     the result are always correct in 32 bit mode.  */
+	  macro_build (ep, ADDRESS_ADDI_INSN, ADDIU_FMT, reg, 0,
+		       BFD_RELOC_NANOMIPS_IMM16);
+	  return;
+	}
+      if (offset_high_unsigned (-ep->X_add_number, ISA_OFFBITS) == 0)
+	{
+	  macro_build (ep, "addiu", "t,r,h", reg, 0, BFD_RELOC_NANOMIPS_NEG12);
+	  return;
+	}
+      else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
+	{
+	  if ((nanomips_opts.ase & ASE_xNMS) != 0
+	      && (ep->X_add_number & 0xfff) != 0
+	      && (*offset_reloc == BFD_RELOC_UNUSED
+		  || pcrel_reloc_p (*offset_reloc))
+	      && !nanomips_opts.insn32)
+	    macro_build (ep, "li", "mp,+Q", reg, BFD_RELOC_NANOMIPS_I32);
+	  else
+	    {
+	      /* 32 bit values require an lui.  */
+	      macro_build (ep, "lui", "t,u", reg, BFD_RELOC_NANOMIPS_HI20);
+	      if ((ep->X_add_number & 0xfff) != 0
+		  && !hi_reloc_p (*offset_reloc))
+		macro_build (ep, "ori", BITOP_IMM_FMT, reg, reg,
+			     BFD_RELOC_NANOMIPS_LO12);
+	    }
+	  return;
+	}
+    }
+
+  return;
+}
+
+/* Move the contents of register SOURCE into register DEST.  */
+
+static void
+move_register (int dest, int source)
+{
+  /* Prefer to use a 16-bit instruction unless a 32-bit one
+     is specifically requested.  */
+  if (!nanomips_opts.insn32)
+    macro_build (NULL, "move", MOVE_FMT, dest, source);
+  else
+    macro_build (NULL, "or", "d,v,t", dest, source, 0);
+}
+
+/* Emit a two-argument branch macro specified by TYPE, using SREG as
+   the register tested.  EP specifies the branch target.  */
+
+static void
+macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
+{
+  const char *br;
+
+  switch (type)
+    {
+    case M_BGEZ:
+      br = "bgez";
+      break;
+    case M_BGTZ:
+      br = "bgtz";
+      break;
+    case M_BLEZ:
+      br = "blez";
+      break;
+    case M_BLTZ:
+      br = "bltz";
+      break;
+    default:
+      abort ();
+    }
+
+  if (type == M_BGTZ || type == M_BLEZ)
+    macro_build (ep, br, BCONDZ2_FMT, sreg);
+  else
+    macro_build (ep, br, BCONDZ1_FMT, sreg);
+}
+
+/* Emit a three-argument branch macro specified by TYPE, using SREG and
+   TREG as the registers tested.  EP specifies the branch target.  */
+
+static void
+macro_build_branch_rsrt (int type, expressionS *ep,
+			 unsigned int sreg, unsigned int treg)
+{
+  const char *br;
+
+  switch (type)
+    {
+    case M_BEQ:
+    case M_BEQ_I:
+      br = "beq";
+      break;
+    case M_BNE:
+    case M_BNE_I:
+      br = "bne";
+      break;
+    default:
+      abort ();
+    }
+
+  if (treg == 0)
+    macro_build (ep, br, "s,t,p", treg, sreg);
+  else
+    macro_build (ep, br, "s,t,p", sreg, treg);
+}
+
+/* Emit a three-argument branch macro specified by TYPE, using SREG and
+   TREG as the registers tested.  EP specifies the branch target.  */
+
+static void
+macro_build_branch_rtim (int type, expressionS *ep,
+			 unsigned int sreg, expressionS *imex)
+{
+  const char *br;
+  int imm = imex->X_add_number;
+
+  switch (type)
+    {
+    case M_BEQ_I:
+      br = "beqic";
+      break;
+    case M_BGE_I:
+      br = "bgeic";
+      break;
+    case M_BGEU_I:
+    case M_BGTU_I:
+      br = "bgeiuc";
+      break;
+    case M_BLT_I:
+    case M_BLE_I:
+      br = "bltic";
+      break;
+    case M_BLTU_I:
+    case M_BLEU_I:
+      br = "bltiuc";
+      break;
+    case M_BNE_I:
+      br = "bneic";
+      break;
+    default:
+      abort ();
+    }
+
+  macro_build (ep, br, "t,+9,~", sreg, imm, BFD_RELOC_NANOMIPS_11_PCREL_S1);
+}
+
+/* Return true if the value stored in offset_expr and offset_reloc
+   fits into a signed offset of OFFBITS bits.  RANGE is the maximum
+   amount that the caller wants to add without inducing overflow
+   and ALIGN is the known alignment of the value in bytes.  */
+
+static bool
+small_offset_p (unsigned int range, unsigned int align, unsigned int offbits)
+{
+  if (offbits == 16)
+    {
+      /* Accept any relocation operator if overflow isn't a concern.  */
+      if (range < align && *offset_reloc != BFD_RELOC_UNUSED)
+	return true;
+
+      /* These relocations are guaranteed not to overflow in correct links.  */
+      if (gprel_reloc_p (*offset_reloc))
+	return true;
+    }
+
+  if (offset_expr.X_op == O_constant
+      && offset_high_part (offset_expr.X_add_number, offbits) == 0
+      && offset_high_part (offset_expr.X_add_number + range, offbits) == 0
+      && offset_expr.X_add_number % align == 0
+      && range % align == 0)
+    return true;
+
+  return false;
+}
+
+static bool
+small_poffset_p (unsigned int range, unsigned align, unsigned int offbits)
+{
+  if (offset_expr.X_op == O_constant
+      && offset_expr.X_add_number >= 0
+      && offset_expr.X_add_number + range < (1 << offbits)
+      && offset_expr.X_add_number % align == 0
+      && range % align == 0)
+    return true;
+
+  if (lo_reloc_p (*offset_reloc))
+    return true;
+
+  return false;
+}
+
+static bool
+small_noffset_p (unsigned int range, unsigned int offbits)
+{
+  if (offset_expr.X_op == O_constant
+      && offset_expr.X_add_number < 0
+      && -offset_expr.X_add_number + range < (1 << offbits))
+    return true;
+
+  return false;
+}
+
+/* Check if RANGE fits in positive/negative add-immediate limits.  */
+
+static bool
+small_add_offset_p (unsigned int range)
+{
+  return (small_poffset_p (range, 1, ISA_ADD_OFFBITS)
+	  || (small_noffset_p (range, ISA_OFFBITS)));
+}
+
+/* Check whether EXP is a valid expression for macro expansions
+   to handle in the current arch mode.  */
+
+static bool
+macro_expression_valid (expressionS *exp, bfd_reloc_code_real_type rtype)
+{
+  if ((exp->X_op != O_constant && exp->X_op != O_symbol)
+      || (exp->X_op == O_symbol && hi_reloc_p (rtype)))
+    {
+      as_bad (_("expression too complex"));
+      exp->X_op = O_constant;
+      return false;
+    }
+
+  if (HAVE_64BIT_ADDRESSES && nanomips_opts.insn32)
+    {
+      as_bad (_("Macro expansions for 64-bit addresses not supported"
+		"in insn32 mode"));
+      return false;
+    }
+
+  if (exp->X_op == O_constant && hi_reloc_p (rtype))
+    exp->X_add_number = offset_high_part (exp->X_add_number, ISA_OFFBITS);
+}
+
+/* Expand the LA macro for PC-relative addressing.  */
+
+static void
+macro_pcrel_la (unsigned int dest, expressionS *an_expr,
+		bfd_reloc_code_real_type r)
+{
+  if ((nanomips_opts.ase & ASE_xNMS) != 0
+      && r == BFD_RELOC_UNUSED
+      && !nanomips_opts.insn32)
+    macro_build (an_expr, "lapc", "mp,+S", dest,
+		 BFD_RELOC_NANOMIPS_PC_I32);
+  else
+    {
+      macro_build (an_expr, "aluipc", "t,+K", dest,
+		   BFD_RELOC_NANOMIPS_PCREL_HI20);
+      macro_build (an_expr, ADDRESS_ADDI_INSN, ADDIU_FMT,
+		   dest, dest, BFD_RELOC_NANOMIPS_LO12);
+    }
+}
+
+/* Expand the LA macro for absolute addressing.  */
+
+static void
+macro_absolute_la (unsigned int dest)
+{
+  if ((nanomips_opts.ase & ASE_xNMS) != 0
+      && *offset_reloc == BFD_RELOC_UNUSED
+      && !nanomips_opts.insn32
+      && !HAVE_64BIT_SYMBOLS)
+    macro_build (&offset_expr, "li", "mp,+Q", dest, BFD_RELOC_NANOMIPS_I32);
+  else
+    {
+      macro_build_lui (&offset_expr, dest);
+      if (!hi_reloc_p (*offset_reloc))
+	macro_build (&offset_expr, "ori", BITOP_IMM_FMT, dest, dest,
+		     ISA_UNSIGNED_LDST_RELOC);
+    }
+}
+
+/* Expand the LA macro.  */
+
+static void
+macro_la (unsigned int op[], unsigned int breg, int *used_at,
+	  bool gprel_ok)
+{
+  unsigned int tempreg;
+
+  if (small_poffset_p (0, 1, ISA_ADD_OFFBITS))
+    {
+      macro_build (&offset_expr, ADDRESS_ADDI_INSN, ADDIU_FMT, op[0], breg,
+		   -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+      return;
+    }
+  else if (small_noffset_p (0, ISA_OFFBITS))
+    {
+      macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,h", op[0], breg,
+		   -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+      return;
+    }
+
+  if (!nanomips_opts.noat && (op[0] == breg))
+    {
+      tempreg = AT;
+      *used_at = 1;
+    }
+  else
+    tempreg = op[0];
+
+  if (!macro_expression_valid (&offset_expr, offset_reloc[0]))
+    return;
+
+  if (offset_expr.X_op == O_constant)
+    load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES);
+  else if (nanomips_opts.pic == NO_PIC)
+    {
+      if (gprel_ok
+	  && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+	  && !nopic_need_relax (offset_expr.X_add_symbol, 1)
+	  && (nanomips_opts.mc_model == MC_MEDIUM
+	      || nanomips_opts.mc_model == MC_AUTO))
+	{
+	  relax_start (offset_expr.X_add_symbol);
+	  macro_build (&offset_expr, ADDRESS_ADDI_INSN, ADDIUGP_FMT,
+		       op[0], nanomips_gp_register, BFD_RELOC_GPREL16);
+	  relax_switch ();
+	}
+
+      if (nanomips_opts.pcrel)
+	macro_pcrel_la (op[0], &offset_expr, *offset_reloc);
+      else
+	macro_absolute_la (op[0]);
+
+      if (nanomips_relax.sequence)
+	relax_end ();
+    }
+  else
+    {
+      relax_start (offset_expr.X_add_symbol);
+
+      if (nanomips_opts.pic == SVR4_PIC || linkrelax)
+	macro_build (&offset_expr, ADDRESS_LOAD_INSN, LWGP_FMT, tempreg,
+		     BFD_RELOC_NANOMIPS_GOT_DISP, nanomips_gp_register);
+      else if ((nanomips_opts.ase & ASE_xNMS) != 0 && !nanomips_opts.insn32)
+	macro_build (&offset_expr, PCREL_LOAD_INSN, "mp,+S", tempreg,
+		     BFD_RELOC_NANOMIPS_GOTPC_I32);
+      else
+	{
+	  macro_build (&offset_expr, "aluipc", "t,+K", op[0],
+		       BFD_RELOC_NANOMIPS_GOTPC_HI20);
+	  macro_build (&offset_expr, ADDRESS_LOAD_INSN, ISA_UNSIGNED_LDST_FMT,
+		       tempreg, BFD_RELOC_NANOMIPS_GOT_LO12, op[0]);
+	}
+
+      relax_switch ();
+      /* Local symbols can be PC-relative.  */
+      macro_pcrel_la (op[0], &offset_expr, *offset_reloc);
+      relax_end ();
+    }
+
+  if (breg != 0)
+    macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", op[0], tempreg, breg);
+}
+
+static void
+macro_absolute_ld_st (const char *s, const char *fmt, unsigned int op[],
+		      unsigned int tempreg, unsigned int breg)
+{
+  macro_build_lui (&offset_expr, tempreg);
+
+  if (breg != 0)
+    macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg);
+
+  macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_LO16, tempreg);
+}
+
+static void
+macro_pcrel_ld_st (const char *s, const char *fmt, unsigned int op[],
+		   unsigned int tempreg, bool coproc)
+{
+  if ((nanomips_opts.ase & ASE_xNMS) != 0
+      && *offset_reloc == BFD_RELOC_UNUSED
+      && !nanomips_opts.insn32
+      && !coproc
+      && (s[0] == 's' || s[0] == 'l')
+      && (s[1] == 'w' || s[1] == 'd')
+      && s[2] == 0)
+    {
+      char insn[] = "xxpc";
+      strncpy (insn, s, 2);
+      macro_build (&offset_expr, insn, "mp,+S", op[0],
+		   BFD_RELOC_NANOMIPS_PC_I32);
+    }
+  else
+    {
+      macro_build (&offset_expr, "aluipc", "t,+K", tempreg,
+		   BFD_RELOC_NANOMIPS_PCREL_HI20);
+      macro_build (&offset_expr, s, fmt,
+		   op[0], BFD_RELOC_NANOMIPS_LO12, tempreg);
+    }
+}
+
+static void
+macro_ld_st (const char *s, const char *fmt, unsigned int op[],
+	     const char  *gpfmt, int align, int offbits, unsigned int breg,
+	     unsigned int tempreg, int *used_at, bool coproc)
+{
+  expressionS expr1;
+  expr1.X_op = O_constant;
+  expr1.X_op_symbol = NULL;
+  expr1.X_add_symbol = NULL;
+  expr1.X_add_number = 1;
+
+  if (small_add_offset_p (0))
+    {
+      /* The first case exists for M_LD_AB and M_SD_AB, which are
+         macros for o32 but which should act like normal instructions
+         otherwise.  */
+      if ((offbits == ISA_OFFBITS || strncmp (s, "ua", 2) == 0)
+	  && small_poffset_p (0, align, offbits))
+	macro_build (&offset_expr, s, fmt, op[0], offset_reloc[0], breg);
+      else if (small_offset_p (0, align, offbits))
+	macro_build (NULL, s, fmt, op[0],
+		     (int) offset_expr.X_add_number, breg);
+      else
+	{
+	  /* Source destroying loads do not need AT for expansion.  */
+	  if (s[0] == 'l' && op[0] == breg && op[0] != 0)
+	    tempreg = breg;
+	  else if (tempreg == AT)
+	    *used_at = 1;
+
+	  if (small_poffset_p (0, 1, ISA_ADD_OFFBITS))
+	    macro_build (&offset_expr, ADDRESS_ADDI_INSN, ADDIU_FMT,
+			 tempreg, breg, -1, offset_reloc[0],
+			 offset_reloc[1], offset_reloc[2]);
+	  else if (small_noffset_p (0, ISA_OFFBITS))
+	    macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,h",
+			 tempreg, breg, -1, offset_reloc[0],
+			 offset_reloc[1], offset_reloc[2]);
+	  macro_build (NULL, s, fmt, op[0], 0, tempreg);
+	}
+      return;
+    }
+
+  if (tempreg == AT)
+    *used_at = 1;
+
+  if (!macro_expression_valid (&offset_expr, offset_reloc[0]))
+    return;
+
+  /* A constant expression in PIC code can be handled just as it
+     is in non PIC code.  */
+  if (offset_expr.X_op == O_constant)
+    {
+      if (op[0] == breg
+	  && op[0] != 0
+	  && (nanomips_opts.ase & ASE_xNMS) != 0
+	  && (offset_high_part (offset_expr.X_add_number, offbits)
+	      != offset_expr.X_add_number))
+	{
+	  *used_at = 0;
+	  macro_build (&offset_expr, "addiu", "mp,mt,+R", op[0], op[0],
+		       BFD_RELOC_NANOMIPS_I32);
+	  macro_build (NULL, s, fmt, op[0], 0, op[0]);
+	}
+      else
+	{
+	  expr1.X_add_number = offset_high_part (offset_expr.X_add_number,
+						 offbits);
+	  if (offbits != ISA_OFFBITS && (expr1.X_add_number & 0xfff) != 0)
+	    expr1 = offset_expr;
+	  offset_expr.X_add_number -= expr1.X_add_number;
+
+	  load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
+	  if (breg != 0)
+	    macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg,
+			 tempreg, breg);
+
+	  if (offbits == ISA_OFFBITS)
+	    macro_build (&offset_expr, s, fmt, op[0],
+			 BFD_RELOC_NANOMIPS_LO12, tempreg);
+	  else
+	    macro_build (NULL, s, fmt, op[0], (int) offset_expr.X_add_number,
+			 tempreg);
+	}
+    }
+  else if (offbits != ISA_OFFBITS)
+    {
+      /* The offset field is too narrow to be used for a low-part
+         relocation, so load the whole address into the auxillary
+         register.  */
+      macro_la (&tempreg, 0, used_at, strncmp (s, "ua", 2) != 0);
+      if (breg != 0)
+	macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg, breg);
+      macro_build (NULL, s, fmt, op[0], 0, tempreg);
+    }
+  else if (nanomips_opts.pic == NO_PIC)
+    {
+      if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+	  && !nopic_need_relax (offset_expr.X_add_symbol, 1)
+	  && (nanomips_opts.mc_model == MC_MEDIUM
+	      || nanomips_opts.mc_model == MC_AUTO))
+	{
+	  relax_start (offset_expr.X_add_symbol);
+	  if (gpfmt == NULL)
+	    gpfmt = fmt;
+	  macro_build (&offset_expr, s, gpfmt, op[0],
+		       BFD_RELOC_GPREL16, nanomips_gp_register);
+	  relax_switch ();
+	}
+
+      if (nanomips_opts.pcrel)
+	macro_pcrel_ld_st (s, fmt, op, tempreg, coproc);
+      else
+	macro_absolute_ld_st (s, fmt, op, tempreg, breg);
+
+      if (nanomips_relax.sequence)
+	relax_end ();
+    }
+  else
+    {
+      gas_assert (offset_expr.X_op == O_symbol);
+
+      gpfmt = "t,.(ma)";
+      relax_start (offset_expr.X_add_symbol);
+
+      if (nanomips_opts.pic == SVR4_PIC || linkrelax)
+	macro_build (&offset_expr, ADDRESS_LOAD_INSN, gpfmt, tempreg,
+		     BFD_RELOC_NANOMIPS_GOT_DISP, nanomips_gp_register);
+      else if ((nanomips_opts.ase & ASE_xNMS) != 0 && !nanomips_opts.insn32)
+	macro_build (&offset_expr, PCREL_LOAD_INSN, "mp,+S", tempreg,
+		     BFD_RELOC_NANOMIPS_GOTPC_I32);
+      else
+	{
+	  macro_build (&offset_expr, "aluipc", "t,+K", tempreg,
+		       BFD_RELOC_NANOMIPS_GOTPC_HI20);
+	  macro_build (&offset_expr, ADDRESS_LOAD_INSN, ISA_UNSIGNED_LDST_FMT,
+		       tempreg, BFD_RELOC_NANOMIPS_GOT_LO12, tempreg);
+	}
+      macro_build (NULL, s, fmt, op[0], 0, tempreg);
+
+      relax_switch ();
+      macro_pcrel_ld_st (s, fmt, op, tempreg, coproc);
+      relax_end ();
+    }
+}
+
+/* Generate a pair of loads or stores, either as a multi-word instruction
+   derived from S or 2 subsequent instructions: S.  If MULTI_OK, then
+   the EP admits no additional relocation, R can be ignored.  */
+static void
+macro_ld_st_pair (const char *s, const char *fmt, unsigned int op[],
+		  unsigned int breg, expressionS *ep,
+		  bfd_reloc_code_real_type r, bool multi_ok)
+{
+  if (multi_ok && (nanomips_opts.ase & ASE_xNMS) != 0)
+    {
+      char ms[10];
+      if (snprintf (ms, sizeof(ms), "%sm", s) >= (int)sizeof(ms))
+	gas_assert (false);
+      macro_build (ep, ms, ISA_MULTI_LDST_FMT, op[0], r, breg, 2);
+    }
+  else
+    {
+      macro_build (ep, s, fmt, op[0], r, breg);
+      ep->X_add_number += 4;
+      macro_build (ep, s, fmt, op[0] + 1, r, breg);
+    }
+}
+
+static void
+macro_absolute_ldd_std (const char *s, const char *fmt,unsigned int op[],
+			unsigned int breg, unsigned int offbits)
+{
+  if ((nanomips_opts.ase & ASE_xNMS) != 0 && !nanomips_opts.insn32)
+    {
+      macro_build (&offset_expr, "li", "mp,+Q", AT, BFD_RELOC_NANOMIPS_I32);
+      offset_expr.X_op = O_constant;
+      offset_expr.X_add_number = 0;
+    }
+  else
+    {
+      macro_build_lui (&offset_expr, AT);
+
+      if (offbits != ISA_OFFBITS
+	  || (offset_high_part (offset_expr.X_add_number, ISA_OFFBITS)
+	      != offset_high_part (offset_expr.X_add_number + 4, ISA_OFFBITS)))
+	{
+	  macro_build (&offset_expr, "ori", BITOP_IMM_FMT, AT, AT,
+		       BFD_RELOC_NANOMIPS_LO12);
+	  offset_expr.X_op = O_constant;
+	  offset_expr.X_add_number = 0;
+	}
+    }
+
+  if (breg != 0)
+    macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+
+  macro_ld_st_pair (s, fmt, op, AT, &offset_expr, BFD_RELOC_NANOMIPS_LO12,
+		    offset_expr.X_op == O_constant);
+}
+
+static void
+macro_pcrel_ldd_std (const char *s, const char *fmt,
+		     unsigned int op[], unsigned int offbits)
+{
+  if (offbits != ISA_OFFBITS)
+    {
+      expressionS an_expr;
+      an_expr.X_op = O_constant;
+      an_expr.X_op_symbol = NULL;
+      an_expr.X_add_symbol = NULL;
+      an_expr.X_add_number = 4;
+      macro_pcrel_la (AT, &offset_expr, *offset_reloc);
+
+      macro_ld_st_pair (s, fmt, op, AT, &an_expr,
+			BFD_RELOC_NANOMIPS_SIGNED_9, true);
+    }
+  else if ((nanomips_opts.ase & ASE_xNMS) != 0
+      && *offset_reloc == BFD_RELOC_UNUSED
+      && !nanomips_opts.insn32)
+    {
+      const char *insn = (strcmp (s, "lw") == 0 ? "lwpc" : "swpc");
+      macro_build (&offset_expr, insn, "mp,+S", op[0],
+		   BFD_RELOC_NANOMIPS_PC_I32);
+      offset_expr.X_add_number += 4;
+      macro_build (&offset_expr, insn, "mp,+S", op[0] + 1,
+		   BFD_RELOC_NANOMIPS_PC_I32);
+    }
+  else
+    {
+      macro_build (&offset_expr, "aluipc", "t,+K", AT,
+		   BFD_RELOC_NANOMIPS_PCREL_HI20);
+      macro_ld_st_pair (s, fmt, op, AT, &offset_expr,
+			BFD_RELOC_NANOMIPS_LO12, false);
+    }
+}
+
+static void
+macro_ldd_std (const char *s, const char *fmt, unsigned int op[], int align,
+	       int offbits, unsigned int breg, int *used_at)
+{
+  expressionS expr1;
+  expressionS *ep;
+
+  expr1.X_op = O_constant;
+  expr1.X_op_symbol = NULL;
+  expr1.X_add_symbol = NULL;
+  expr1.X_add_number = 1;
+
+  if (small_add_offset_p (0))
+    {
+      ep = &offset_expr;
+      if (offbits == ISA_OFFBITS && small_poffset_p (4, align, ISA_OFFBITS))
+	offset_reloc[0] = ISA_UNSIGNED_LDST_RELOC;
+      else if (offbits == ISA_SIGNED_OFFBITS
+	       && small_offset_p (4, align, ISA_SIGNED_OFFBITS))
+	offset_reloc[0] = ISA_SIGNED_LDST_RELOC;
+      else
+	{
+	  if (small_poffset_p (0, align, ISA_ADD_OFFBITS))
+	    macro_build (&offset_expr, ADDRESS_ADDI_INSN, ADDIU_FMT, AT,
+			 breg, offset_reloc[0]);
+	  else if (small_noffset_p (0, ISA_OFFBITS))
+	    macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,h", AT,
+			 breg, offset_reloc[0]);
+
+	  expr1.X_add_number = 0;
+	  ep = &expr1;
+	  breg = AT;
+	  *used_at = 1;
+	  offset_reloc[0] = BFD_RELOC_UNUSED;
+	}
+
+      if (strstr (s, "lw") != NULL && op[0] == breg)
+	{
+	  ep->X_add_number += 4;
+	  macro_build (ep, s, fmt, op[0] + 1, offset_reloc[0], breg);
+	  ep->X_add_number -= 4;
+	  macro_build (ep, s, fmt, op[0], offset_reloc[0], breg);
+	}
+      else
+	macro_ld_st_pair (s, fmt, op, breg, ep, offset_reloc[0],
+			  (small_offset_p (0, align, ISA_MULTI_OFFBITS)
+			   || expr1.X_add_number == 0));
+      return;
+    }
+
+  if (!macro_expression_valid (&offset_expr, offset_reloc[0]))
+    return;
+
+  if (nanomips_opts.pic == NO_PIC || offset_expr.X_op == O_constant)
+    {
+      if (offset_expr.X_op == O_symbol
+	  && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+	  && strncmp (s, "ua", 2) != 0
+	  && (breg == 0
+	      && (gprel_reloc_p (*offset_reloc)
+		  || *offset_reloc == BFD_RELOC_UNUSED))
+	  && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+	{
+	  const char *sfmt = fmt;
+	  unsigned int tempreg;
+	  relax_start (offset_expr.X_add_symbol);
+	  if (breg == 0)
+	    {
+	      tempreg = nanomips_gp_register;
+	      sfmt = "t,.(ma)";
+	    }
+	  else
+	    {
+	      macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+			   AT, breg, nanomips_gp_register);
+	      tempreg = AT;
+	      *used_at = 1;
+	    }
+
+	  macro_build (&offset_expr, s, sfmt, op[0], BFD_RELOC_GPREL16,
+		       tempreg);
+	  offset_expr.X_add_number += 4;
+	  macro_build (&offset_expr, s, sfmt, op[0] + 1,
+		       BFD_RELOC_GPREL16, tempreg);
+	  relax_switch ();
+
+	  offset_expr.X_add_number -= 4;
+	}
+
+      *used_at = 1;
+      if (nanomips_opts.pcrel)
+	macro_pcrel_ldd_std (s, fmt, op, offbits);
+      else
+	macro_absolute_ldd_std (s, fmt, op, breg, offbits);
+
+
+      if (nanomips_relax.sequence)
+	relax_end ();
+    }
+  else if (offbits == ISA_OFFBITS)
+    {
+      *used_at = 1;
+      expr1.X_add_number = 0;
+      relax_start (offset_expr.X_add_symbol);
+      macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,.(ma)", AT,
+		   BFD_RELOC_NANOMIPS_GOT_DISP, nanomips_gp_register);
+      if (breg != 0)
+	macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+
+      macro_ld_st_pair (s, fmt, op, AT, &expr1, BFD_RELOC_NANOMIPS_LO12, true);
+
+      relax_switch ();
+      macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,.(ma)", AT,
+		   BFD_RELOC_NANOMIPS_GOT_PAGE, nanomips_gp_register);
+      if (breg != 0)
+	macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+
+      macro_build (&offset_expr, s, fmt, op[0],
+		   BFD_RELOC_NANOMIPS_GOT_OFST, AT);
+      offset_expr.X_add_number += 4;
+      macro_build (&offset_expr, s, fmt, op[0] + 1,
+		   BFD_RELOC_NANOMIPS_GOT_OFST, AT);
+      relax_end ();
+    }
+  else
+    {
+      *used_at = 1;
+      expr1.X_add_number = 0;
+      macro_la (&nanomips_opts.noat, 0, used_at, true);
+      macro_ld_st_pair (s, fmt, op, AT, &expr1,
+			BFD_RELOC_NANOMIPS_LO12, true);
+    }
+}
+
+/* Build macros  */
+
+static void
+macro (struct nanomips_cl_insn *ip, char *str ATTRIBUTE_UNUSED)
+{
+  const struct nanomips_operand_array *operands;
+
+  unsigned int breg, i;
+  unsigned int tempreg;
+  int mask;
+  int used_at = 0;
+  expressionS label_expr;
+  expressionS expr1;
+  const char *s = ip->insn_mo->name;
+  const char *s2 = NULL;
+  const char *fmt;
+  const char *gpfmt = NULL;
+  int coproc = 0;
+  int offbits = ISA_OFFBITS;
+  bool dbl = false;
+  int lp = 0;
+  unsigned int align;
+  unsigned int op[MAX_OPERANDS];
+
+  operands = insn_operands (ip);
+  for (i = 0; i < MAX_OPERANDS; i++)
+    if (operands->operand[i])
+      op[i] = insn_extract_operand (ip, operands->operand[i]);
+    else
+      op[i] = -1;
+
+  mask = ip->insn_mo->mask;
+
+  label_expr.X_op = O_constant;
+  label_expr.X_op_symbol = NULL;
+  label_expr.X_add_symbol = NULL;
+  label_expr.X_add_number = 0;
+
+  expr1.X_op = O_constant;
+  expr1.X_op_symbol = NULL;
+  expr1.X_add_symbol = NULL;
+  expr1.X_add_number = 1;
+  align = 1;
+
+  switch (mask)
+    {
+    case M_ABS:
+      /*    bgez    $a0,1f
+	    move    v0,$a0
+	    sub     v0,$zero,$a0
+	 1:
+       */
+
+      nanomips_label_expr (&label_expr);
+      if (op[0] != op[1])
+	move_register (op[0], op[1]);
+      macro_build (&label_expr, "bgezc", BCONDZ1_FMT, op[1]);
+      macro_build (NULL, "subu", "d,v,t", op[0], 0, op[1]);
+      nanomips_add_label ();
+      break;
+
+    case M_ADD_I:
+      s = "addiu";
+      s2 = "add";
+      if (imm_expr.X_add_number == 0)
+	goto do_addi;
+      else
+	goto do_addi_i;
+    case M_ADDU_I:
+      s = "addiu";
+      s2 = "addu";
+    do_addi:
+      if (offset_high_unsigned (imm_expr.X_add_number, 16) == 0)
+	{
+	  macro_build (&imm_expr, s, ADDIU_FMT, op[0], op[1],
+		       BFD_RELOC_NANOMIPS_IMM16);
+	  break;
+	}
+      else if (offset_high_unsigned (-imm_expr.X_add_number, 12) == 0)
+	{
+	  macro_build (&imm_expr, s, "t,r,h", op[0], op[1],
+		       BFD_RELOC_NANOMIPS_NEG12);
+	  break;
+	}
+      else if ((nanomips_opts.ase & ASE_xNMS) != 0
+	       && op[0] == op[1]
+	       && !nanomips_opts.insn32)
+	{
+	  macro_build (&imm_expr, s, "mp,mt,+R", op[0], op[0],
+		       BFD_RELOC_NANOMIPS_I32);
+	  break;
+	}
+    do_addi_i:
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
+      break;
+
+    case M_AND_I:
+      s = "andi";
+      s2 = "and";
+      goto do_bit;
+    case M_OR_I:
+      s = "ori";
+      s2 = "or";
+      goto do_bit;
+    case M_NOR_I:
+      s = "";
+      s2 = "nor";
+      goto do_load_op;
+    case M_XOR_I:
+      s = "xori";
+      s2 = "xor";
+    do_bit:
+      if (offset_high_unsigned (imm_expr.X_add_number, 12) == 0)
+	{
+	  if (mask != M_NOR_I)
+	    macro_build (&imm_expr, s, BITOP_IMM_FMT, op[0], op[1],
+			 BFD_RELOC_LO16);
+	  else
+	    {
+	      macro_build (&imm_expr, "ori", BITOP_IMM_FMT, op[0], op[1],
+			   BFD_RELOC_LO16);
+	      macro_build (NULL, "nor", "d,v,t", op[0], op[0], 0);
+	    }
+	  break;
+	}
+    do_load_op:
+      used_at = 1;
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
+      break;
+
+    case M_BEQ_I:
+    case M_BNE_I:
+      if (imm_expr.X_add_number == 0)
+	op[1] = 0;
+      else if (offset_high_unsigned (imm_expr.X_add_number, 7) == 0)
+	{
+	  macro_build_branch_rtim (mask, &offset_expr, op[0], &imm_expr);
+	  break;
+	}
+      else
+	{
+	  op[1] = AT;
+	  used_at = 1;
+	  load_register (op[1], &imm_expr, GPR_SIZE == 64);
+	}
+      macro_build_branch_rsrt (mask, &offset_expr, op[0], op[1]);
+      break;
+
+    case M_BGE:
+      if (op[1] == 0)
+	macro_build_branch_rs (M_BGEZ, &offset_expr, op[0]);
+      else if (op[0] == 0)
+	macro_build_branch_rs (M_BLEZ, &offset_expr, op[1]);
+      else
+	{
+	  used_at = 1;
+	  macro_build (NULL, "slt", "d,v,t", AT, op[0], op[1]);
+	  macro_build_branch_rsrt (M_BEQ, &offset_expr, AT, ZERO);
+	}
+      break;
+
+    case M_BGT:
+      if (op[1] == 0)
+	macro_build_branch_rs (M_BGTZ, &offset_expr, op[0]);
+      else if (op[0] == 0)
+	macro_build_branch_rs (M_BLTZ, &offset_expr, op[1]);
+      else
+	{
+	  used_at = 1;
+	  macro_build (NULL, "slt", "d,v,t", AT, op[1], op[0]);
+	  macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO);
+	}
+      break;
+
+    case M_BLE:
+      if (op[1] == 0)
+	macro_build_branch_rs (M_BLEZ, &offset_expr, op[0]);
+      else if (op[0] == 0)
+	macro_build_branch_rs (M_BGEZ, &offset_expr, op[1]);
+      else
+	{
+	  used_at = 1;
+	  macro_build (NULL, "slt", "d,v,t", AT, op[1], op[0]);
+	  macro_build_branch_rsrt (M_BEQ, &offset_expr, AT, ZERO);
+	}
+      break;
+
+    case M_BLT:
+      if (op[1] == 0)
+	macro_build_branch_rs (M_BLTZ, &offset_expr, op[0]);
+      else if (op[0] == 0)
+	macro_build_branch_rs (M_BGTZ, &offset_expr, op[1]);
+      else
+	{
+	  used_at = 1;
+	  macro_build (NULL, "slt", "d,v,t", AT, op[0], op[1]);
+	  macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO);
+	}
+      break;
+
+    case M_BGEZAL:
+      s = "bltzc";
+      goto brcond_al;
+    case M_BLTZAL:
+      s = "bgezc";
+    brcond_al:
+      nanomips_label_expr (&label_expr);
+      macro_build (&label_expr, s, BCONDZ1_FMT, op[0]);
+      macro_build (&offset_expr, "bal", B_FMT);
+      nanomips_add_label ();
+      break;
+
+    case M_BGT_I:
+      /* Check for > max integer.  */
+      if (imm_expr.X_add_number >= GPR_SMAX)
+	{
+	do_false:
+	  /* Result is always false.  */
+	  macro_build (NULL, "nop", "");
+	  break;
+	}
+      ++imm_expr.X_add_number;
+      /* Fall through. */
+    case M_BGE_I:
+      if (imm_expr.X_add_number == 0)
+	macro_build_branch_rs (M_BGEZ, &offset_expr, op[0]);
+      else if (imm_expr.X_add_number == 1)
+	macro_build_branch_rs (M_BGTZ, &offset_expr, op[0]);
+      else if (imm_expr.X_add_number <= GPR_SMIN)
+	{
+	do_true:
+	  /* result is always true */
+	  as_warn (_("branch %s is always true"), ip->insn_mo->name);
+	  macro_build (&offset_expr, "b", B_FMT);
+	  break;
+	}
+      else if (offset_high_unsigned (imm_expr.X_add_number, 7) == 0)
+	macro_build_branch_rtim (M_BGE_I, &offset_expr, op[0], &imm_expr);
+      else
+	{
+	  used_at = 1;
+	  set_at (op[0], 0);
+	  macro_build_branch_rsrt (M_BEQ, &offset_expr, AT, ZERO);
+	}
+      break;
+
+    case M_BGTU_I:
+      if (op[0] == 0 || (GPR_SIZE == 32 && imm_expr.X_add_number == -1))
+	goto do_false;
+      ++imm_expr.X_add_number;
+      /* Fall through. */
+    case M_BGEU_I:
+      if (imm_expr.X_add_number == 0)
+	goto do_true;
+      else if (imm_expr.X_add_number == 1)
+	macro_build_branch_rsrt (M_BNE, &offset_expr, op[0], ZERO);
+      else if (offset_high_unsigned (imm_expr.X_add_number, 7) == 0)
+	macro_build_branch_rtim (mask, &offset_expr, op[0], &imm_expr);
+      else
+	{
+	  used_at = 1;
+	  set_at (op[0], 1);
+	  macro_build_branch_rsrt (M_BEQ, &offset_expr, AT, ZERO);
+	}
+      break;
+
+    case M_BGTU:
+      if (op[1] == 0)
+	macro_build_branch_rsrt (M_BNE, &offset_expr, op[0], ZERO);
+      else if (op[0] == 0)
+	goto do_false;
+      else
+	{
+	  used_at = 1;
+	  macro_build (NULL, "sltu", "d,v,t", AT, op[1], op[0]);
+	  macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO);
+	}
+      break;
+
+    case M_BLE_I:
+      if (imm_expr.X_add_number >= GPR_SMAX)
+	goto do_true;
+      ++imm_expr.X_add_number;
+      /* Fall through. */
+    case M_BLT_I:
+      if (imm_expr.X_add_number == 0)
+	macro_build_branch_rs (M_BLTZ, &offset_expr, op[0]);
+      else if (imm_expr.X_add_number == 1)
+	macro_build_branch_rs (M_BLEZ, &offset_expr, op[0]);
+      else if (offset_high_unsigned (imm_expr.X_add_number, 7) == 0)
+	macro_build_branch_rtim (mask, &offset_expr, op[0], &imm_expr);
+      else
+	{
+	  used_at = 1;
+	  set_at (op[0], 0);
+	  macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO);
+	}
+      break;
+
+    case M_BLEU:
+      if (op[1] == 0)
+	macro_build_branch_rsrt (M_BEQ, &offset_expr, op[0], ZERO);
+      else if (op[0] == 0)
+	goto do_true;
+      else
+	{
+	  used_at = 1;
+	  macro_build (NULL, "sltu", "d,v,t", AT, op[1], op[0]);
+	  macro_build_branch_rsrt (M_BEQ, &offset_expr, AT, ZERO);
+	}
+      break;
+
+    case M_BLEU_I:
+      if (op[0] == 0 || (GPR_SIZE == 32 && imm_expr.X_add_number == -1))
+	goto do_true;
+      ++imm_expr.X_add_number;
+      /* Fall through. */
+    case M_BLTU_I:
+      if (imm_expr.X_add_number == 0)
+	goto do_false;
+      else if (imm_expr.X_add_number == 1)
+	macro_build_branch_rsrt (M_BEQ, &offset_expr, op[0], ZERO);
+      else if (offset_high_unsigned (imm_expr.X_add_number, 7) == 0)
+	macro_build_branch_rtim (mask, &offset_expr, op[0], &imm_expr);
+      else
+	{
+	  used_at = 1;
+	  set_at (op[0], 1);
+	  macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO);
+	}
+      break;
+
+    case M_BLTU:
+      if (op[1] == 0)
+	goto do_false;
+      else if (op[0] == 0)
+	macro_build_branch_rsrt (M_BNE, &offset_expr, ZERO, op[1]);
+      else
+	{
+	  used_at = 1;
+	  macro_build (NULL, "sltu", "d,v,t", AT, op[0], op[1]);
+	  macro_build_branch_rsrt (M_BNE, &offset_expr, AT, ZERO);
+	}
+      break;
+
+    case M_EXT:
+      if (31 - op[2] - op[3] > 0)
+	{
+	  macro_build (NULL, "sll", SHFT_FMT, op[0], op[1],
+		       31 - op[2] - op[3]);
+	  macro_build (NULL, "srl", SHFT_FMT, op[0], op[0], 31 - op[3]);
+	}
+      else
+	macro_build (NULL, "srl", SHFT_FMT, op[0], op[1], op[2]);
+      break;
+
+    case M_INS:
+      if (op[0] == op[1] && op[2] == 0)
+	{
+	  macro_build (NULL, "nop", "");
+	  break;
+	}
+      else if (op[0] == op[1])
+	{
+	  used_at = 1;
+	  macro_build (NULL, "extw", "d,s,t,+I", AT, op[0], op[0], op[2]);
+	  macro_build (NULL, "extw", "d,s,t,+I", op[0], AT, op[0],
+		       1 + op[3] - op[2]);
+	}
+      else if (op[2] == 0 && op[3] == 31)
+	{
+	  move_register (op[0], op[1]);
+	  break;
+	}
+      else
+	{
+	  if (op[2] != 0)
+	    macro_build (NULL, "extw", "d,s,t,+I", op[0], op[0],
+			 op[0], op[2]);
+	  macro_build (NULL, "extw", "d,s,t,+I", op[0], op[0], op[1],
+		       1 + op[3] - op[2]);
+	}
+      if (op[3] < 31)
+	macro_build (NULL, "extw", "d,s,t,+I", op[0], op[0], op[0],
+		     31 - op[3]);
+      break;
+
+    case M_DIV_I:
+    case M_DIVU_I:
+      goto do_divi;
+
+    case M_REM_3I:
+    case M_MOD_I:
+      s = "mod";
+    do_divi:
+      if (imm_expr.X_add_number == 0)
+	{
+	  as_warn (_("divide by zero"));
+	  if (nanomips_trap)
+	    macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
+	  else
+	    macro_build (NULL, "break", BRK_FMT, 7);
+	  break;
+	}
+      if (imm_expr.X_add_number == 1)
+	{
+	  if (strncmp (s, "div", 3) == 0)
+	    move_register (op[0], op[1]);
+	  else
+	    move_register (op[0], ZERO);
+	  break;
+	}
+      if (imm_expr.X_add_number == -1 && s[strlen (s) - 1] != 'u')
+	{
+	  if (strncmp (s, "div", 3) == 0)
+	    macro_build (NULL, "neg", "d,w", op[0], op[1]);
+	  else
+	    move_register (op[0], ZERO);
+	  break;
+	}
+
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, s, DIV_FMT, op[0], op[1], AT);
+      break;
+
+    case M_MODU_I:
+      s = "modu";
+      goto do_divi;
+
+    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.  */
+      breg = op[2];
+      macro_la (op, breg, &used_at, true);
+      break;
+
+    case M_J_A:
+      /* The j instruction may not be used in PIC code, since it
+         requires an absolute address.  We convert it to a b
+         instruction.  */
+      /* Likewise for nanoMIPS which has no absolute jump instruction.  */
+      macro_build (&offset_expr, "bc", B_FMT);
+      break;
+
+      /* The jal instructions must be handled as macros because when
+         generating PIC code they expand to multi-instruction
+         sequences.  Normally they are simple instructions.  */
+    case M_JAL_A:
+      if (nanomips_opts.pic == NO_PIC)
+	macro_build (&offset_expr, "jal", B_FMT);
+      else
+	{
+	  macro_build (&offset_expr, ADDRESS_LOAD_INSN, LWGP_FMT,
+		       AT, BFD_RELOC_NANOMIPS_GOT_CALL, nanomips_gp_register);
+
+	  if (nanomips_linkrelax_p)
+	    {
+	      if (nanomips_opts.insn32)
+		macro_build (&offset_expr, "jalrc", "s,-i", AT,
+			     BFD_RELOC_NANOMIPS_JALR32);
+	      else
+		macro_build (&offset_expr, "jalrc", "mp,-i", AT,
+			     BFD_RELOC_NANOMIPS_JALR16);
+	    }
+	  else
+	    macro_build (NULL, "jalrc", (nanomips_opts.insn32 ? "s" : "mp"),
+			 AT);
+	}
+
+      break;
+
+    case M_LB_AC:
+    case M_LBU_AC:
+    case M_LH_AC:
+    case M_LHU_AC:
+    case M_LW_AC:
+      fmt = ISA_UNSIGNED_LDST_FMT;
+      switch (s[1])
+	{
+	case 'b':
+	  gpfmt = "t,+1(ma)";
+	  break;
+	case 'd':
+	  gpfmt = "t,+4(ma)";
+	  break;
+	case 'h':
+	  gpfmt = "t,+3(ma)";
+	  break;
+	default:
+	  if (s[2] == 'u')
+	    gpfmt = "t,+2(ma)";
+	  else
+	    gpfmt = "t,.(ma)";
+	  break;
+	}
+      goto ld;
+
+    case M_LL_AC:
+      fmt = LL_SC_FMT;
+      align = 4;
+      offbits = ISA_SIGNED_OFFBITS;
+      goto ld;
+
+    ld:
+      if (op[2] == op[0] + lp)
+	goto ld_st;
+      else
+	tempreg = op[0] + lp;
+      goto ld_noat;
+
+    case M_LBUE_AC:
+    case M_LHUE_AC:
+    case M_LBE_AC:
+    case M_LHE_AC:
+    case M_SHE_AC:
+    case M_SWE_AC:
+    case M_LWE_AC:
+    case M_SBE_AC:
+      fmt = ISA_SIGNED_LDST_FMT;
+      goto ld_st_signed_off;
+
+    case M_SB_AC:
+    case M_SH_AC:
+      fmt = ISA_UNSIGNED_LDST_FMT;
+      gpfmt = "t,+1(ma)";
+      goto ld_st;
+
+    case M_SW_AC:
+      fmt = ISA_UNSIGNED_LDST_FMT;
+      gpfmt = "t,.(ma)";
+      goto ld_st;
+
+    case M_LLE_AC:
+    case M_SCE_AC:
+    case M_SC_AC:
+      fmt = LL_SC_FMT;
+      align = 4;
+      goto ld_st_signed_off;
+
+    case M_CACHE_AC:
+    case M_PREF_AC:
+    case M_CACHEE_AC:
+    case M_PREFE_AC:
+      fmt = PREF_FMT;
+      goto ld_st_signed_off;
+
+    case M_SWM_AC:
+    case M_SDM_AC:
+    case M_LWM_AC:
+    case M_LDM_AC:
+      fmt = ISA_SIGNED_LDST_FMT ",|";
+
+    ld_st_signed_off:
+      offbits = ISA_SIGNED_OFFBITS;
+    ld_st:
+      tempreg = AT;
+    ld_noat:
+      breg = op[2];
+      macro_ld_st (s, fmt, op, gpfmt, align, offbits, breg, tempreg,
+		   &used_at, coproc);
+      break;
+
+    case M_JRADDIUSP:
+      offset_expr = imm_expr;
+      if (small_poffset_p (0, 1, ISA_ADD_OFFBITS))
+	macro_build (&imm_expr, "addiu", ADDIU_FMT, SP, SP,
+		     BFD_RELOC_NANOMIPS_IMM16);
+      else if (small_noffset_p (0, ISA_OFFBITS))
+	macro_build (&imm_expr, "addiu", ADDIU_FMT, SP, SP,
+		     BFD_RELOC_NANOMIPS_NEG12);
+      else
+	{
+	  used_at = 1;
+	  load_register (AT, &imm_expr, GPR_SIZE == 64);
+	  macro_build (NULL, "addu", "d,v,t", SP, SP, AT);
+	}
+      macro_build (NULL, "jrc", (nanomips_opts.insn32 ? "s" : "mp"), RA);
+      break;
+
+    case M_LI:
+      if (imm_expr.X_op != O_absent)
+	load_register (op[0], &imm_expr, 0);
+      else
+	macro_la (op, 0, &used_at, false);
+      break;
+
+    case M_LI_S:
+      if (imm_expr.X_op == O_constant)
+	{
+	  load_register (op[0], &imm_expr, 0);
+	  break;
+	}
+
+      /* Loading from data section, fall-back to load-from-label
+	 macro expansion.  */
+      used_at = 1;
+      align = 4;
+      offbits = ISA_OFFBITS;
+      fmt = ISA_UNSIGNED_LDST_FMT;
+      op[2] = 0;
+      s = "lw";
+      gpfmt = LWGP_FMT;
+      tempreg = (op[0] != 0) ? op[0] : AT;
+      goto ld_noat;
+
+    case M_LI_D:
+      /* Check if we have a constant in IMM_EXPR.  If the GPRs are 64 bits
+         wide, IMM_EXPR is the entire value.  Otherwise IMM_EXPR is the high
+         order 32 bits of the value and the low order 32 bits are either
+         zero or in OFFSET_EXPR.  */
+      if (imm_expr.X_op == O_constant)
+	{
+	  int hreg, lreg;
+
+	  if (target_big_endian)
+	    {
+	      hreg = op[0];
+	      lreg = op[0] + 1;
+	    }
+	  else
+	    {
+	      hreg = op[0] + 1;
+	      lreg = op[0];
+	    }
+
+	  if (hreg <= 31)
+	    load_register (hreg, &imm_expr, 0);
+	  if (lreg <= 31)
+	    {
+	      if (offset_expr.X_op == O_absent)
+		move_register (lreg, 0);
+	      else
+		{
+		  gas_assert (offset_expr.X_op == O_constant);
+		  load_register (lreg, &offset_expr, 0);
+		}
+	    }
+	  break;
+	}
+
+      /* Loading from data section, fall-back to load-from-label
+	 macro expansion.  */
+      gas_assert (imm_expr.X_op == O_absent);
+      used_at = 1;
+      align = 4;
+      offbits = ISA_OFFBITS;
+      fmt = ISA_UNSIGNED_LDST_FMT;
+      op[2] = 0;
+      s = "lw";
+      gpfmt = LWGP_FMT;
+      tempreg = op[0];
+      goto ldd_std;
+
+    case M_LD_AC:
+      fmt = ISA_UNSIGNED_LDST_FMT;
+      gpfmt = "t,+2(ma)";
+      s = "lw";
+      goto ldd_std;
+
+    case M_SD_AC:
+      fmt = ISA_UNSIGNED_LDST_FMT;
+      gpfmt = "t,+2(ma)";
+      s = "sw";
+
+    ldd_std:
+      breg = op[2];
+      macro_ldd_std (s, fmt, op, align, offbits, breg, &used_at);
+      break;
+
+    case M_MUL:
+      macro_build (NULL, "multu", "s,t", op[1], op[2]);
+      macro_build (NULL, "mflo", MFHL_FMT, op[0]);
+      break;
+
+    case M_MUL_I:
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, "mul", "d,v,t", op[0], op[1], AT);
+      break;
+
+    case M_ROL:
+      if (op[0] == op[1])
+	{
+	  tempreg = AT;
+	  used_at = 1;
+	}
+      else
+	tempreg = op[0];
+      macro_build (NULL, "negu", "d,w", tempreg, op[2]);
+      macro_build (NULL, "rorv", BITW_FMT, op[0], op[1], tempreg);
+      break;
+
+    case M_ROL_I:
+      {
+	unsigned int rot;
+
+	rot = imm_expr.X_add_number & 0x1f;
+	macro_build (NULL, "ror", SHFT_FMT, op[0], op[1], (32 - rot) & 0x1f);
+	break;
+      }
+
+    case M_ROR_I:
+      {
+	unsigned int rot;
+
+	rot = imm_expr.X_add_number & 0x1f;
+	macro_build (NULL, "ror", SHFT_FMT, op[0], op[1], rot);
+	break;
+      }
+
+    case M_SEQ:
+      if (op[1] == 0)
+	macro_build (&expr1, "sltiu", OP_IMM_FMT, op[0], op[2],
+		     BFD_RELOC_LO16);
+      else if (op[2] == 0)
+	macro_build (&expr1, "sltiu", OP_IMM_FMT, op[0], op[1],
+		     BFD_RELOC_LO16);
+      else
+	{
+	  macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]);
+	  macro_build (&expr1, "sltiu", OP_IMM_FMT, op[0], op[0],
+		       BFD_RELOC_LO16);
+	}
+      break;
+
+    case M_SEQ_I:
+      if (imm_expr.X_add_number == 0)
+	macro_build (&expr1, "sltiu", OP_IMM_FMT, op[0], op[1],
+		     BFD_RELOC_LO16);
+      else if (op[1] == 0)
+	{
+	  as_warn (_("instruction %s: result is always false"),
+		   ip->insn_mo->name);
+	  move_register (op[0], 0);
+	}
+      else if (offset_high_unsigned (imm_expr.X_add_number, ISA_OFFBITS) == 0)
+	macro_build (&imm_expr, "seqi", OP_IMM_FMT, op[0], op[1],
+		     BFD_RELOC_LO16);
+      else
+	{
+	  if (offset_high_unsigned (-imm_expr.X_add_number,
+				    ISA_ADD_OFFBITS) == 0)
+	    {
+	      imm_expr.X_add_number = -imm_expr.X_add_number;
+	      macro_build (&imm_expr, "addiu", ADDIU_FMT,
+			   op[0], op[1], BFD_RELOC_LO16);
+	    }
+	  else
+	    {
+	      used_at = 1;
+	      load_register (AT, &imm_expr, GPR_SIZE == 64);
+	      macro_build (NULL, "xor", "d,v,t", op[0], op[1], AT);
+	    }
+	  macro_build (&expr1, "sltiu", OP_IMM_FMT, op[0], op[0],
+		       BFD_RELOC_LO16);
+	}
+      break;
+
+    case M_SGE:		/* X >= Y  <==>  not (X < Y) */
+      s = "slt";
+      goto sge;
+    case M_SGEU:
+      s = "sltu";
+    sge:
+      macro_build (NULL, s, "d,v,t", op[0], op[1], op[2]);
+      macro_build (&expr1, "xori", BITOP_IMM_FMT, op[0], op[0],
+		   BFD_RELOC_LO16);
+      break;
+
+    case M_SGE_I:		/* X >= I  <==>  not (X < I) */
+    case M_SGEU_I:
+      if (offset_high_unsigned (imm_expr.X_add_number, ISA_OFFBITS) == 0)
+	macro_build (&imm_expr, mask == M_SGE_I ? "slti" : "sltiu",
+		     OP_IMM_FMT, op[0], op[1], BFD_RELOC_LO16);
+      else
+	{
+	  used_at = 1;
+	  load_register (AT, &imm_expr, GPR_SIZE == 64);
+	  macro_build (NULL, mask == M_SGE_I ? "slt" : "sltu", "d,v,t",
+		       op[0], op[1], AT);
+	}
+      macro_build (&expr1, "xori", BITOP_IMM_FMT, op[0], op[0],
+		   BFD_RELOC_LO16);
+      break;
+
+    case M_SGT:		/* X > Y  <==>  Y < X */
+      s = "slt";
+      goto sgt;
+    case M_SGTU:
+      s = "sltu";
+    sgt:
+      macro_build (NULL, s, "d,v,t", op[0], op[2], op[1]);
+      break;
+
+    case M_SGT_I:		/* X > I  <==>  I < X */
+      s = "slt";
+      goto sgti;
+    case M_SGTU_I:
+      s = "sltu";
+    sgti:
+      used_at = 1;
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, s, "d,v,t", op[0], AT, op[1]);
+      break;
+
+    case M_SLE:		/* X <= Y  <==>  Y >= X  <==>  not (Y < X) */
+      s = "slt";
+      goto sle;
+    case M_SLEU:
+      s = "sltu";
+    sle:
+      macro_build (NULL, s, "d,v,t", op[0], op[2], op[1]);
+      macro_build (&expr1, "xori", BITOP_IMM_FMT, op[0], op[0],
+		   BFD_RELOC_LO16);
+      break;
+
+    case M_SLE_I:	/* X <= I  <==>  I >= X  <==>  not (I < X) */
+      s = "slt";
+      goto slei;
+    case M_SLEU_I:
+      s = "sltu";
+    slei:
+      used_at = 1;
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, s, "d,v,t", op[0], AT, op[1]);
+      macro_build (&expr1, "xori", BITOP_IMM_FMT, op[0], op[0],
+		   BFD_RELOC_LO16);
+      break;
+
+    case M_SLT_I:
+      if (offset_high_unsigned (imm_expr.X_add_number, ISA_OFFBITS) == 0)
+	{
+	  macro_build (&imm_expr, "slti", OP_IMM_FMT, op[0], op[1],
+		       BFD_RELOC_LO16);
+	  break;
+	}
+      used_at = 1;
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, "slt", "d,v,t", op[0], op[1], AT);
+      break;
+
+    case M_SLTU_I:
+      if (offset_high_unsigned (imm_expr.X_add_number, ISA_OFFBITS) == 0)
+	{
+	  macro_build (&imm_expr, "sltiu", OP_IMM_FMT, op[0], op[1],
+		       BFD_RELOC_LO16);
+	  break;
+	}
+      used_at = 1;
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, "sltu", "d,v,t", op[0], op[1], AT);
+      break;
+
+    case M_SNE:
+      if (op[1] == 0)
+	macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[2]);
+      else if (op[2] == 0)
+	macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[1]);
+      else
+	{
+	  macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]);
+	  macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[0]);
+	}
+      break;
+
+    case M_SNE_I:
+      if (imm_expr.X_add_number == 0)
+	macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[1]);
+      else if (op[1] == 0)
+	{
+	  as_warn (_("instruction %s: result is always true"),
+		   ip->insn_mo->name);
+	  load_register (op[0], &expr1, GPR_SIZE == 64);
+	}
+      else
+	{
+	  if (offset_high_unsigned (imm_expr.X_add_number,
+				    ISA_ADD_OFFBITS) == 0)
+	    macro_build (&imm_expr, "xori", BITOP_IMM_FMT, op[0], op[1],
+			 BFD_RELOC_LO16);
+	  else if (offset_high_unsigned (-imm_expr.X_add_number,
+					 ISA_ADD_OFFBITS) == 0)
+	    {
+	      imm_expr.X_add_number = -imm_expr.X_add_number;
+	      macro_build (&imm_expr, "addiu", ADDIU_FMT, op[0], op[1],
+			   BFD_RELOC_LO16);
+	    }
+	  else
+	    {
+	      used_at = 1;
+	      load_register (AT, &imm_expr, GPR_SIZE == 64);
+	      macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]);
+	    }
+	  macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[0]);
+	}
+      break;
+
+    case M_SUB_I:
+      s = "addiu";
+      s2 = "sub";
+      goto do_subi_i;
+    case M_SUBU_I:
+      s = "addiu";
+      s2 = "subu";
+      if (offset_high_unsigned (-imm_expr.X_add_number, 16) == 0)
+	{
+	  imm_expr.X_add_number = -imm_expr.X_add_number;
+	  macro_build (&imm_expr, s, ADDIU_FMT, op[0], op[1],
+		       BFD_RELOC_NANOMIPS_IMM16);
+	  break;
+	}
+      else if (offset_high_unsigned (imm_expr.X_add_number, 12) == 0)
+	{
+	  imm_expr.X_add_number = -imm_expr.X_add_number;
+	  macro_build (&imm_expr, s, "t,r,h", op[0], op[1],
+		       BFD_RELOC_NANOMIPS_NEG12);
+	  break;
+	}
+      else if ((nanomips_opts.ase & ASE_xNMS) != 0
+	       && op[0] == op[1]
+	       && !nanomips_opts.insn32)
+	{
+	  imm_expr.X_add_number = -imm_expr.X_add_number;
+	  macro_build (&imm_expr, s, "mp,mt,+R", op[0], op[0],
+		       BFD_RELOC_NANOMIPS_I32);
+	  break;
+	}
+
+    do_subi_i:
+      used_at = 1;
+      load_register (AT, &imm_expr, dbl);
+      macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
+      break;
+
+    case M_TEQ_I:
+    case M_TNE_I:
+      used_at = 1;
+      load_register (AT, &imm_expr, GPR_SIZE == 64);
+      macro_build (NULL, s, "s,t", op[0], AT);
+      break;
+
+    case M_ULH_AC:
+      s = "ualh";
+      goto uld_st;
+
+    case M_ULW_AC:
+      s = "ualw";
+      goto uld_st;
+
+    case M_USH_AC:
+      s = "uash";
+      goto uld_st;
+
+    case M_USW_AC:
+      s = "uasw";
+      goto uld_st;
+
+    case M_ULD_AC:
+      s = "uald";
+      s2 = "ualw";
+      goto uld_st;
+
+    case M_USD_AC:
+      s = "uasd";
+      s2 = "uasw";
+
+    uld_st:
+      fmt = ISA_SIGNED_LDST_FMT;
+      offbits = ISA_SIGNED_OFFBITS;
+      if (s[3] == 'd' && GPR_SIZE == 32)
+	{
+	  s = s2;
+	  goto ldd_std;
+	}
+      else
+	goto ld_st;
+      break;
+
+    default:
+      as_bad (_("macro %s not implemented yet"), ip->insn_mo->name);
+      break;
+    }
+  if (nanomips_opts.noat && used_at)
+    as_bad (_("macro used $at after \".set noat\""));
+}
+
+/* Look up instruction [START, START + LENGTH) in HASH.  Record any extra
+   opcode bits in *OPCODE_EXTRA.  */
+
+static struct nanomips_opcode *
+nanomips_lookup_insn (htab_t hash, const char *start,
+		      ssize_t length,
+		      unsigned int *opcode_extra ATTRIBUTE_UNUSED)
+{
+  char *name, *dot;
+  unsigned int suffix;
+  ssize_t opend;
+  struct nanomips_opcode *insn;
+
+  /* Make a copy of the instruction so that we can fiddle with it.  */
+  name = xstrndup (start, length);
+
+  forced_insn_format = (strchr (start, '[') != NULL
+			&& strchr (strchr (start, '['), ']') != NULL);
+
+  /* Look up the instruction as-is.  */
+  insn = str_hash_find (hash, name);
+  if (insn)
+    return insn;
+
+  if (strchr (name, ']'))
+    dot = strchr (strchr (name, ']'), '.');
+  else
+    dot = strchr (name, '.');
+
+  /* See if there's an instruction size override suffix,
+     either `16' or `32', at the end of the mnemonic proper,
+     that defines the operation, i.e. before the first `.'
+     character if any.  Strip it and retry.  */
+  opend = dot != NULL ? dot - name : length;
+  if (opend >= 3 && name[opend - 2] == '1' && name[opend - 1] == '6')
+    suffix = 2;
+  else if (opend >= 2 && name[opend - 2] == '3' && name[opend - 1] == '2')
+    suffix = 4;
+  else if (opend >= 3 && name[opend - 2] == '4' && name[opend - 1] == '8')
+    suffix = 6;
+  else
+    suffix = 0;
+  if (suffix)
+    {
+      memmove (name + opend - 2, name + opend, length - opend + 1);
+      insn = str_hash_find (hash, name);
+      while (insn)
+	if (insn_length (insn) == suffix)
+	  {
+	    forced_insn_length = suffix;
+	    return insn;
+	  }
+	else if (forced_insn_format)
+	  break;
+	else
+	  insn++;
+    }
+  return NULL;
+}
+
+/* Assemble an instruction into its binary format.  If the instruction
+   is a macro, set imm_expr and offset_expr to the values associated
+   with "I" and "A" operands respectively.  Otherwise store the value
+   of the relocatable field (if any) in offset_expr.  In both cases
+   set offset_reloc to the relocation operators applied to offset_expr.  */
+
+static void
+nanomips_ip (char *str, struct nanomips_cl_insn *insn)
+{
+  const struct nanomips_opcode *first, *past;
+  htab_t hash;
+  char format;
+  size_t end;
+  struct nanomips_operand_token *tokens;
+  unsigned int opcode_extra;
+
+  hash = nanomips_op_hash;
+  past = &nanomips_opcodes[bfd_nanomips_num_opcodes];
+  forced_insn_length = 0;
+  forced_insn_format = false;
+  opcode_extra = 0;
+
+  /* We first try to match an instruction up to a space or to the end.  */
+  for (end = 0; !is_end_of_stmt (str[end]) && !is_whitespace (str[end]); end++)
+    continue;
+
+  first = nanomips_lookup_insn (hash, str, end, &opcode_extra);
+  if (first == NULL)
+    {
+      set_insn_error (0, _("unrecognized opcode"));
+      return;
+    }
+
+  if (strcmp (first->name, "li.s") == 0)
+    format = 'f';
+  else if (strcmp (first->name, "li.d") == 0)
+    format = 'd';
+  else
+    format = 0;
+  tokens = nanomips_parse_arguments (str + end, format);
+  if (!tokens)
+    return;
+
+  if (!match_insns (insn, first, past, tokens, opcode_extra, false)
+      && !match_insns (insn, first, past, tokens, opcode_extra, true))
+    set_insn_error (0, _("invalid operands"));
+
+  obstack_free (&nanomips_operand_tokens, tokens);
+}
+
+struct percent_op_match
+{
+  const char *str;
+  bfd_reloc_code_real_type reloc;
+};
+
+static const struct percent_op_match nanomips_percent_op[] = {
+  {"%lo", BFD_RELOC_NANOMIPS_LO12},
+  {"%call16", BFD_RELOC_NANOMIPS_GOT_CALL},
+  {"%got_call", BFD_RELOC_NANOMIPS_GOT_CALL},
+  {"%got_disp", BFD_RELOC_NANOMIPS_GOT_DISP},
+  {"%got_page", BFD_RELOC_NANOMIPS_GOT_PAGE},
+  {"%got_ofst", BFD_RELOC_NANOMIPS_GOT_OFST},
+  {"%got", BFD_RELOC_NANOMIPS_GOT_DISP},
+  {"%gp_rel", BFD_RELOC_NANOMIPS_GPREL18},
+  {"%gprel", BFD_RELOC_NANOMIPS_GPREL18},
+  {"%gprel_hi", BFD_RELOC_NANOMIPS_GPREL_HI20},
+  {"%gprel_lo", BFD_RELOC_NANOMIPS_GPREL_LO12},
+  {"%gprel32", BFD_RELOC_NANOMIPS_GPREL_I32},
+  {"%hi", BFD_RELOC_NANOMIPS_HI20},
+  {"%pcrel_hi", BFD_RELOC_NANOMIPS_PCREL_HI20},
+  {"%pcrel_lo", BFD_RELOC_NANOMIPS_LO12},
+  {"%got_pcrel_hi", BFD_RELOC_NANOMIPS_GOTPC_HI20},
+  {"%got_pcrel_lo", BFD_RELOC_NANOMIPS_GOT_LO12},
+  {"%got_lo", BFD_RELOC_NANOMIPS_GOT_LO12},
+  {"%got_pcrel32", BFD_RELOC_NANOMIPS_GOTPC_I32},
+  {"%tlsgd", BFD_RELOC_NANOMIPS_TLS_GD},
+  {"%tlsld", BFD_RELOC_NANOMIPS_TLS_LD},
+  {"%dtprel", BFD_RELOC_NANOMIPS_TLS_DTPREL12},
+  {"%tprel", BFD_RELOC_NANOMIPS_TLS_TPREL12},
+  {"%gottprel", BFD_RELOC_NANOMIPS_TLS_GOTTPREL},
+  {"%gottprel_pc32", BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32},
+  /* These are currently not supported for nanoMIPS.  */
+  {"%call_hi", BFD_RELOC_UNUSED},
+  {"%call_lo", BFD_RELOC_UNUSED}
+};
+
+/* Return true if *STR points to a relocation operator.  When returning true,
+   move *STR over the operator and store its relocation code in *RELOC.
+   Leave both *STR and *RELOC alone when returning false.  */
+
+static bool
+parse_relocation (char **str, bfd_reloc_code_real_type *reloc)
+{
+  const struct percent_op_match *percent_op;
+  size_t limit, i;
+
+  percent_op = nanomips_percent_op;
+  limit = ARRAY_SIZE (nanomips_percent_op);
+
+  for (i = 0; i < limit; i++)
+    if (strncasecmp (*str, percent_op[i].str, strlen (percent_op[i].str))
+	== 0)
+      {
+	int len = strlen (percent_op[i].str);
+
+	if (!is_end_of_stmt ((*str)[len])
+		    && !is_whitespace ((*str)[len])
+		    && (*str)[len] != '(')
+	  continue;
+
+	*str += strlen (percent_op[i].str);
+	*reloc = percent_op[i].reloc;
+
+	/* Check whether the output BFD supports this relocation.
+	   If not, issue an error and fall back on something safe.  */
+	if (!bfd_reloc_type_lookup (stdoutput, percent_op[i].reloc))
+	  {
+	    as_bad (_("relocation %s isn't supported by the current ABI"),
+		    percent_op[i].str);
+	    *reloc = BFD_RELOC_UNUSED;
+	  }
+	return true;
+      }
+  return false;
+}
+
+/* Parse string STR as a 16-bit relocatable operand.  Store the
+   expression in *EP and the relocations in the array starting
+   at RELOC.  Return the number of relocation operators used.
+
+   On exit, EXPR_END points to the first character after the expression.  */
+
+static size_t
+my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
+		       char *str)
+{
+  bfd_reloc_code_real_type reversed_reloc[3];
+  size_t reloc_index, i;
+  int crux_depth, str_depth;
+  char *crux;
+
+  memset (reversed_reloc, 0, sizeof (reversed_reloc));
+  /* Search for the start of the main expression, recoding relocations
+     in REVERSED_RELOC.  End the loop with CRUX pointing to the start
+     of the main expression and with CRUX_DEPTH containing the number
+     of open brackets at that point.  */
+  reloc_index = -1;
+  str_depth = 0;
+  do
+    {
+      reloc_index++;
+      crux = str;
+      crux_depth = str_depth;
+
+      /* Skip over whitespace and brackets, keeping count of the number
+         of brackets.  */
+      while (is_whitespace (*str) || *str == '(')
+	if (*str++ == '(')
+	  str_depth++;
+    }
+  while (*str == '%'
+	 && reloc_index < 1
+	 && parse_relocation (&str, &reversed_reloc[reloc_index]));
+
+  my_getExpression (ep, crux);
+  str = expr_parse_end;
+
+  /* Match every open bracket.  */
+  while (crux_depth > 0 && (*str == ')' || is_whitespace (*str)))
+    if (*str++ == ')')
+      crux_depth--;
+
+  if (crux_depth > 0)
+    as_bad (_("unclosed '('"));
+
+  expr_parse_end = str;
+
+  for (i = 0; i < reloc_index; i++)
+	reloc[i] = reversed_reloc[reloc_index - 1 - i];
+
+  return reloc_index;
+}
+
+static void
+my_getExpression (expressionS *ep, char *str)
+{
+  char *save_in;
+
+  save_in = input_line_pointer;
+  input_line_pointer = str;
+  expression (ep);
+  expr_parse_end = input_line_pointer;
+  input_line_pointer = save_in;
+}
+
+const char *
+md_atof (int type, char *litP, int *sizeP)
+{
+  return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+  if (target_big_endian)
+    number_to_chars_bigendian (buf, val, n);
+  else
+    number_to_chars_littleendian (buf, val, n);
+}
+
+static int
+support_64bit_objects (void)
+{
+  const char **list, **l;
+  int yes;
+
+  list = bfd_target_list ();
+  for (l = list; *l != NULL; l++)
+    if (strcmp (*l, ELF_NTARGET ("elf64-", "big")) == 0
+	|| strcmp (*l, ELF_NTARGET ("elf64-", "little")) == 0)
+      break;
+  yes = (*l != NULL);
+  free (list);
+  return yes;
+}
+
+/* Set STRING_PTR (either &nanomips_arch_string or &nanomips_tune_string) to
+   NEW_VALUE.  Warn if another value was already specified.  Note:
+   we have to defer parsing the -march and -mtune arguments in order
+   to handle 'from-abi' correctly, since the ABI might be specified
+   in a later argument.  */
+
+static void
+nanomips_set_option_string (const char **string_ptr, const char *new_value)
+{
+  if (*string_ptr != 0 && strcasecmp (*string_ptr, new_value) != 0)
+    as_warn (_("a different %s was already specified, is now %s"),
+	     string_ptr == &nanomips_arch_string ? "-march" : "-mtune",
+	     new_value);
+
+  *string_ptr = new_value;
+}
+
+static void
+nanomips_set_mcmodel (enum mc_model_type *model, const char *arg)
+{
+  if (strcmp (arg, "large"))
+    *model = MC_LARGE;
+  else if (strcmp (arg, "medium"))
+    *model = MC_LARGE;
+  else if (strcmp (arg, "auto"))
+    *model = MC_AUTO;
+  else
+    as_fatal (_("Request for unsupported memory model %s"), arg);
+}
+
+int
+md_parse_option (int c, const char *arg)
+{
+  unsigned int i;
+
+  for (i = 0; i < ARRAY_SIZE (nanomips_ases); i++)
+    if (c == nanomips_ases[i].option_on || c == nanomips_ases[i].option_off)
+      {
+	file_ase_explicit |=
+	  nanomips_set_ase (&nanomips_ases[i], &file_nanomips_opts,
+			    c == nanomips_ases[i].option_on);
+	return 1;
+      }
+
+  switch (c)
+    {
+    case OPTION_CONSTRUCT_FLOATS:
+      nanomips_disable_float_construction = 0;
+      break;
+
+    case OPTION_NO_CONSTRUCT_FLOATS:
+      nanomips_disable_float_construction = 1;
+      break;
+
+    case OPTION_TRAP:
+      nanomips_trap = 1;
+      break;
+
+    case OPTION_BREAK:
+      nanomips_trap = 0;
+      break;
+
+    case OPTION_EB:
+      target_big_endian = 1;
+      break;
+
+    case OPTION_EL:
+      target_big_endian = 0;
+      break;
+
+    case 'g':
+      if (arg == NULL)
+	nanomips_debug = 2;
+      else
+	nanomips_debug = atoi (arg);
+      break;
+
+    case OPTION_MTUNE:
+      nanomips_set_option_string (&nanomips_tune_string, arg);
+      break;
+
+    case OPTION_MARCH:
+      nanomips_set_option_string (&nanomips_arch_string, arg);
+      break;
+
+    case OPTION_NO_MICROMIPS:
+    case OPTION_NO_MIPS16:
+      break;
+
+    case OPTION_INSN32:
+      file_nanomips_opts.insn32 = true;
+      break;
+
+    case OPTION_NO_INSN32:
+      file_nanomips_opts.insn32 = false;
+      break;
+
+    case OPTION_PIC:
+      file_nanomips_opts.pic = SVR4_PIC;
+      break;
+
+    case OPTION_NOPIC:
+      file_nanomips_opts.pic = NO_PIC;
+      break;
+
+    case OPTION_LARGE_PIC:
+      file_nanomips_opts.pic = SVR4_LARGE_PIC;
+      break;
+
+    case 'G':
+      g_switch_value = atoi (arg);
+      g_switch_seen = 1;
+      break;
+
+      /* The -32, -n32 and -64 options are shortcuts for -mabi=32, -mabi=n32
+         and -mabi=64.  */
+    case OPTION_32:
+    case OPTION_64:
+      as_fatal (_("Request for unsupported ABI"));
+      break;
+
+    case OPTION_M32:
+      nanomips_abi = P32_ABI;
+      break;
+
+    case OPTION_M64:
+      nanomips_abi = P64_ABI;
+      if (!support_64bit_objects ())
+	as_fatal (_("no compiled in support for 64 bit object file format"));
+      break;
+
+    case OPTION_BALC_STUBS:
+      file_nanomips_opts.no_balc_stubs = false;
+      break;
+
+    case OPTION_NO_BALC_STUBS:
+      file_nanomips_opts.no_balc_stubs = true;
+      break;
+
+    case OPTION_LEGACY_REGS:
+      file_nanomips_opts.legacyregs = true;
+      break;
+
+    case OPTION_NO_LEGACY_REGS:
+      file_nanomips_opts.legacyregs = false;
+      break;
+
+    case OPTION_LINKRELAX:
+      s_linkrelax (0);
+      break;
+
+    case OPTION_SINGLE_FLOAT:
+      file_nanomips_opts.single_float = 1;
+      break;
+
+    case OPTION_DOUBLE_FLOAT:
+      file_nanomips_opts.single_float = 0;
+      break;
+
+    case OPTION_SOFT_FLOAT:
+      file_nanomips_opts.soft_float = 1;
+      break;
+
+    case OPTION_HARD_FLOAT:
+      file_nanomips_opts.soft_float = 0;
+      break;
+
+    case OPTION_PCREL:
+      file_nanomips_opts.pcrel = true;
+      break;
+
+    case OPTION_NO_PCREL:
+      file_nanomips_opts.pcrel = false;
+      break;
+
+    case OPTION_PID:
+      file_nanomips_opts.pid = true;
+      break;
+
+    case OPTION_NO_PID:
+      file_nanomips_opts.pid = false;
+      break;
+
+    case OPTION_MCMODEL:
+      nanomips_set_mcmodel (&file_nanomips_opts.mc_model, arg);
+      break;
+
+    default:
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Set up globals to tune for the ISA or processor described by INFO.  */
+
+static void
+nanomips_set_tune (const struct nanomips_cpu_info *info)
+{
+  if (info != 0)
+    nanomips_tune = info->cpu;
+}
+
+void
+nanomips_after_parse_args (void)
+{
+  const struct nanomips_cpu_info *arch_info = 0;
+  const struct nanomips_cpu_info *tune_info = 0;
+
+  if (nanomips_arch_string != 0)
+    arch_info = nanomips_parse_cpu ("-march", nanomips_arch_string);
+
+  if (arch_info == 0)
+    {
+      arch_info = nanomips_parse_cpu ("default CPU",
+				      NANOMIPS_CPU_STRING_DEFAULT);
+      gas_assert (arch_info);
+    }
+
+  if (nanomips_abi == NO_ABI)
+    nanomips_abi = NANOMIPS_DEFAULT_ABI;
+
+  if (ABI_NEEDS_64BIT_REGS (nanomips_abi)
+      && !ISA_HAS_64BIT_REGS (arch_info->isa))
+    as_bad (_("-march=%s is not compatible with the selected ABI"),
+	    arch_info->name);
+
+  file_nanomips_opts.arch = arch_info->cpu;
+  file_nanomips_opts.isa = arch_info->isa;
+  file_nanomips_opts.init_ase = arch_info->ase;
+
+  /* Set up initial nanomips_opts state.  */
+  nanomips_opts = file_nanomips_opts;
+
+  /* Optimize for file_nanomips_opts.arch, unless -mtune selects a different
+     processor.  */
+  if (nanomips_tune_string != 0)
+    tune_info = nanomips_parse_cpu ("-mtune", nanomips_tune_string);
+
+  if (tune_info == 0)
+    nanomips_set_tune (arch_info);
+  else
+    nanomips_set_tune (tune_info);
+}
+
+/* This is called before the symbol table is processed.  In order to
+   work with gcc when using mips-tfile, we must keep all local labels.
+   However, in other cases, we want to discard them.  If we were
+   called with -g, but we didn't see any debugging information, it may
+   mean that gcc is smuggling debugging information through to
+   mips-tfile, in which case we must generate all local labels.  */
+
+void
+nanomips_frob_file_before_adjust (void)
+{
+#ifndef NO_ECOFF_DEBUGGING
+  if (ECOFF_DEBUGGING && nanomips_debug != 0 && !ecoff_debugging_seen)
+    flag_keep_locals = 1;
+#endif
+}
+
+static bool
+frag_in_section_p (fragS *fragp, const char *secname)
+{
+  asection *sec = bfd_get_section_by_name (stdoutput, secname);
+
+  while (sec != NULL)
+    {
+      fragS *iter = seg_info (sec)->frchainP->frch_root;
+      while (iter != NULL)
+	{
+	  if (iter == fragp)
+	    return true;
+	  iter = iter->fr_next;
+	}
+      sec = bfd_get_next_section_by_name (NULL, sec);
+    }
+  return false;
+}
+
+int
+nanomips_force_relocation (fixS *fixp)
+{
+  if (generic_force_reloc (fixp))
+    return 1;
+
+  if (linkrelax_reloc_p (fixp->fx_r_type)
+      || fixp->fx_r_type == BFD_RELOC_NANOMIPS_INSN32
+      || fixp->fx_r_type == BFD_RELOC_NANOMIPS_INSN16
+      || fixp->fx_r_type == BFD_RELOC_NANOMIPS_SAVERESTORE
+      || fixp->fx_r_type == BFD_RELOC_NANOMIPS_JALR32
+      || fixp->fx_r_type == BFD_RELOC_NANOMIPS_JALR16)
+    return 1;
+
+  /* and for nanoMIPS expansions */
+  if (pcrel_branch_reloc_p (fixp->fx_r_type)
+      || fixp->fx_r_type == BFD_RELOC_NANOMIPS_PCREL_HI20
+      || fixp->fx_r_type == BFD_RELOC_NANOMIPS_PC_I32
+      || fixp->fx_r_type == BFD_RELOC_NANOMIPS_GOTPC_HI20
+      || fixp->fx_r_type == BFD_RELOC_NANOMIPS_GOTPC_I32
+      || fixp->fx_r_type == BFD_RELOC_NANOMIPS_GOT_LO12)
+    return 1;
+
+  if (linkrelax
+      && fixp->fx_subsy
+      && (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_CODE) != 0
+      && (S_GET_SEGMENT (fixp->fx_subsy)->flags & SEC_CODE) != 0)
+    {
+      /* For lable-difference relocations in .debug_info sections, we
+	 want to force a relocation, even if the difference is link-time
+	 invariant so that the linker has the opportunity to zero-out
+	 AT_high_pc expressions when the corresponding sections get
+	 garbage-collected.  Spurious non-zero AT_high_pc fields of
+	 garbage-collected sections can trip-up GDB source disassembly.  */
+      if (frag_in_section_p (fixp->fx_frag, ".debug_info"))
+	return 1;
+      else
+	return (!nanomips_allow_local_subtract_symbols (fixp->fx_addsy,
+							fixp->fx_subsy,
+							false));
+    }
+  return 0;
+}
+
+/* Find the size of an instruction relocated by RELOC, default to 4.  */
+static unsigned int
+get_reloc_size (bfd_reloc_code_real_type reloc)
+{
+  if (pcrel16_reloc_p (reloc))
+    return 2;
+  else
+    return 4;
+}
+
+/* Read the instruction associated with RELOC from BUF.  */
+
+static unsigned int
+read_reloc_insn (char *buf, bfd_reloc_code_real_type reloc)
+{
+  return read_compressed_insn (buf, get_reloc_size (reloc));
+}
+
+/* Write instruction INSN to BUF, given that it has been relocated
+   by RELOC.  */
+
+static void
+write_reloc_insn (char *buf, bfd_reloc_code_real_type reloc,
+		  unsigned long insn)
+{
+  write_compressed_insn (buf, insn, get_reloc_size (reloc));
+}
+
+/* Apply a fixup to the object file.  */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+  char *buf;
+  unsigned long insn;
+  reloc_howto_type *howto = NULL;
+
+  /* We ignore generic BFD relocations we don't know about.  */
+  howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+  if (! howto)
+
+  if (fixP->fx_pcrel)
+    switch (fixP->fx_r_type)
+      {
+      case BFD_RELOC_NANOMIPS_PC_I32:
+      case BFD_RELOC_NANOMIPS_GOTPC_HI20:
+      case BFD_RELOC_NANOMIPS_GOTPC_I32:
+      case BFD_RELOC_NANOMIPS_GOT_LO12:
+      case BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32:
+	break;
+
+      case BFD_RELOC_8:
+        fixP->fx_r_type = BFD_RELOC_8_PCREL;
+	break;
+      }
+
+  if (linkrelax_reloc_p (fixP->fx_r_type)
+      || fixP->fx_r_type == BFD_RELOC_NANOMIPS_INSN32
+      || fixP->fx_r_type == BFD_RELOC_NANOMIPS_INSN16
+      || fixP->fx_r_type == BFD_RELOC_NANOMIPS_SAVERESTORE
+      || fixP->fx_r_type == BFD_RELOC_NANOMIPS_JALR32
+      || fixP->fx_r_type == BFD_RELOC_NANOMIPS_JALR16)
+    {
+      fixP->fx_addnumber = *valP;
+      return;
+    }
+
+  gas_assert (fixP->fx_size == 2
+	      || fixP->fx_size == 4
+	      || fixP->fx_r_type == BFD_RELOC_NANOMIPS_I32
+	      || fixP->fx_r_type == BFD_RELOC_8
+	      || fixP->fx_r_type == BFD_RELOC_16
+	      || fixP->fx_r_type == BFD_RELOC_64
+	      || fixP->fx_r_type == BFD_RELOC_CTOR
+	      || fixP->fx_r_type == BFD_RELOC_NANOMIPS_NEG
+	      || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+	      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+	      || fixP->fx_r_type == BFD_RELOC_NANOMIPS_UNSIGNED_8
+	      || fixP->fx_r_type == BFD_RELOC_NANOMIPS_SIGNED_8
+	      || fixP->fx_r_type == BFD_RELOC_NANOMIPS_ASHIFTR_1
+	      || (fixP->fx_size == 1 && fixP->fx_r_type == BFD_RELOC_32));
+
+  buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+  /* Don't treat parts of a composite relocation as done.  There are two
+     reasons for this:
+
+     (1) The second and third parts will be against 0 (RSS_UNDEF) but
+     should nevertheless be emitted if the first part is.
+
+     (2) In normal usage, composite relocations are never assembly-time
+     constants.  The easiest way of dealing with the pathological
+     exceptions is to generate a relocation against STN_UNDEF and
+     leave everything up to the linker.  */
+  if (fixP->fx_addsy == NULL && !fixP->fx_pcrel && fixP->fx_tcbit == 0)
+    fixP->fx_done = 1;
+
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_NANOMIPS_TLS_DTPREL:
+    case BFD_RELOC_NANOMIPS_TLS_TPREL:
+    case BFD_RELOC_NANOMIPS_TLS_GD:
+    case BFD_RELOC_NANOMIPS_TLS_LD:
+    case BFD_RELOC_NANOMIPS_TLS_GD_I32:
+    case BFD_RELOC_NANOMIPS_TLS_LD_I32:
+    case BFD_RELOC_NANOMIPS_TLS_DTPREL12:
+    case BFD_RELOC_NANOMIPS_TLS_DTPREL16:
+    case BFD_RELOC_NANOMIPS_TLS_DTPREL_I32:
+    case BFD_RELOC_NANOMIPS_TLS_TPREL12:
+    case BFD_RELOC_NANOMIPS_TLS_TPREL16:
+    case BFD_RELOC_NANOMIPS_TLS_TPREL_I32:
+    case BFD_RELOC_NANOMIPS_TLS_GOTTPREL:
+    case BFD_RELOC_NANOMIPS_TLS_GOTTPREL_PC_I32:
+      if (!fixP->fx_addsy)
+	{
+	  as_bad_where (fixP->fx_file, fixP->fx_line,
+			_("TLS relocation against a constant"));
+	  break;
+	}
+      S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      /* fall through */
+
+    case BFD_RELOC_HI16:
+    case BFD_RELOC_HI16_S:
+    case BFD_RELOC_LO16:
+    case BFD_RELOC_GPREL16:
+    case BFD_RELOC_GPREL32:
+    case BFD_RELOC_NANOMIPS_EH:
+    case BFD_RELOC_NANOMIPS_NEG:
+    case BFD_RELOC_NANOMIPS_GOT_CALL:
+    case BFD_RELOC_NANOMIPS_GOT_DISP:
+    case BFD_RELOC_NANOMIPS_GOT_PAGE:
+    case BFD_RELOC_NANOMIPS_GOT_OFST:
+    case BFD_RELOC_NANOMIPS_GPREL19_S2:
+    case BFD_RELOC_NANOMIPS_GPREL18_S3:
+    case BFD_RELOC_NANOMIPS_GPREL18:
+    case BFD_RELOC_NANOMIPS_GPREL16_S2:
+    case BFD_RELOC_NANOMIPS_GPREL7_S2:
+    case BFD_RELOC_NANOMIPS_GPREL17_S1:
+    case BFD_RELOC_NANOMIPS_GOT_LO12:
+      if (fixP->fx_done)
+	as_bad_where (fixP->fx_file, fixP->fx_line,
+		      _("unsupported constant in relocation"));
+      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)
+	{
+	  if (8 <= sizeof (valueT))
+	    md_number_to_chars (buf, *valP, 8);
+	  else
+	    {
+	      valueT hiv;
+
+	      if ((*valP & 0x80000000) != 0)
+		hiv = 0xffffffff;
+	      else
+		hiv = 0;
+	      md_number_to_chars (buf + (target_big_endian ? 4 : 0),
+				  *valP, 4);
+	      md_number_to_chars (buf + (target_big_endian ? 0 : 4), hiv, 4);
+	    }
+	}
+      break;
+
+    case BFD_RELOC_RVA:
+    case BFD_RELOC_32:
+    case BFD_RELOC_32_PCREL:
+    case BFD_RELOC_16:
+    case BFD_RELOC_8:
+    case BFD_RELOC_NANOMIPS_UNSIGNED_8:
+    case BFD_RELOC_NANOMIPS_UNSIGNED_16:
+    case BFD_RELOC_NANOMIPS_SIGNED_8:
+    case BFD_RELOC_NANOMIPS_SIGNED_16:
+      /* 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
+	 resolved when it appears but is later defined.  */
+      if (fixP->fx_done)
+	md_number_to_chars (buf, *valP, fixP->fx_size);
+      break;
+
+    case BFD_RELOC_NANOMIPS_ASHIFTR_1:
+      if (fixP->fx_done)
+	*valP = (*valP >> 1);
+      break;
+
+    case BFD_RELOC_NANOMIPS_HI20:
+    case BFD_RELOC_NANOMIPS_GPREL_HI20:
+      if (!fixP->fx_done)
+	break;
+
+      insn = read_reloc_insn (buf, fixP->fx_r_type);
+
+      insn |= ((*valP >> 12) & 0x1ff) << 12
+	      | ((*valP >> 21) & 0x3ff) << 2
+	      | ((*valP >> 31) & 1);
+
+      write_reloc_insn (buf, fixP->fx_r_type, insn);
+      break;
+
+    case BFD_RELOC_NANOMIPS_I32:
+    case BFD_RELOC_NANOMIPS_GPREL_I32:
+      if (fixP->fx_done)
+	write_compressed_insn (buf, *valP & 0xffffffff, 4);
+      break;
+
+    case BFD_RELOC_NANOMIPS_LO12:
+    case BFD_RELOC_NANOMIPS_GPREL_LO12:
+      if (fixP->fx_done)
+	{
+	  insn = read_reloc_insn (buf, fixP->fx_r_type);
+	  insn |= *valP & 0xfff;
+	  write_reloc_insn (buf, fixP->fx_r_type, insn);
+	}
+      break;
+
+    case BFD_RELOC_NANOMIPS_LO4_S2:
+      if (fixP->fx_done)
+	{
+	  if ((*valP & 0xfc3) != 0)
+	    as_bad_where (fixP->fx_file, fixP->fx_line,
+			  _("offset out of range (0x%lx)"),
+			  (long) fixP->fx_offset);
+	  insn = read_reloc_insn (buf, fixP->fx_r_type);
+	  insn |= (*valP >> 2) & 0xf;
+	  write_reloc_insn (buf, fixP->fx_r_type, insn);
+	}
+      break;
+
+    case BFD_RELOC_NANOMIPS_NEG12:
+      if (fixP->fx_done)
+	{
+	  insn = read_reloc_insn (buf, fixP->fx_r_type);
+	  insn |= (-*valP) & 0xfff;
+	  write_reloc_insn (buf, fixP->fx_r_type, insn);
+	}
+      break;
+
+    case BFD_RELOC_NANOMIPS_IMM16:
+      if (fixP->fx_done)
+	{
+	  insn = read_reloc_insn (buf, fixP->fx_r_type);
+	  insn |= *valP & 0xffff;
+	  write_reloc_insn (buf, fixP->fx_r_type, insn);
+	}
+      break;
+
+    case BFD_RELOC_NANOMIPS_PC_I32:
+    case BFD_RELOC_NANOMIPS_PCREL_HI20:
+    case BFD_RELOC_NANOMIPS_GOTPC_I32:
+    case BFD_RELOC_NANOMIPS_GOTPC_HI20:
+      gas_assert (!fixP->fx_done);
+      break;
+
+    case BFD_RELOC_NANOMIPS_4_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_7_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_10_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_11_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_14_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_21_PCREL_S1:
+    case BFD_RELOC_NANOMIPS_25_PCREL_S1:
+      if (!fixP->fx_done)
+	break;
+
+    case BFD_RELOC_VTABLE_INHERIT:
+      fixP->fx_done = 0;
+      if (fixP->fx_addsy
+	  && !S_IS_DEFINED (fixP->fx_addsy)
+	  && !S_IS_WEAK (fixP->fx_addsy))
+	S_SET_WEAK (fixP->fx_addsy);
+      break;
+
+    case BFD_RELOC_VTABLE_ENTRY:
+      fixP->fx_done = 0;
+      break;
+
+    default:
+      abort ();
+    }
+
+  /* Remember value for tc_gen_reloc.  */
+  fixP->fx_addnumber = *valP;
+}
+
+static symbolS *
+get_symbol (void)
+{
+  int c;
+  char *name;
+  symbolS *p;
+
+  c = get_symbol_name (&name);
+  p = symbol_find_or_make (name);
+  (void) restore_line_pointer (c);
+  return p;
+}
+
+/* Create R_NANOMIPS_ALIGN to record the alignment request.  Value of the
+   absolute symbol gives alignment requested.  The relocation is created at
+   the start of padding bytes.  Create R_NANOMIPS_FILL and R_NANOMIPS_MAX
+   to record fill value and maximum alignment respectively.  */
+
+static void
+create_align_relocs (fragS *fragp, int align_to, unsigned int fill_value,
+		     int fill_length, int max_fill)
+{
+  static int symidx = 1;
+  symbolS *sym;
+  char sname[30];
+  fixS *fixp;
+  int where;
+
+  if (fragp == frag_now)
+    where = frag_now_fix ();
+  else
+    where = fragp->fr_fix;
+
+  /* The '\2' ensures that no other symbol will get the
+     same name as this.  */
+  sprintf (sname, "__reloc_align_\2_%d", symidx++);
+  sym = symbol_find (sname);
+  sym = symbol_new (sname, absolute_section, &zero_address_frag, align_to);
+  symbol_table_insert (sym);
+
+  fixp = fix_new (fragp, where, 0, sym, 0, false, BFD_RELOC_NANOMIPS_ALIGN);
+  if (fragp->tc_frag_data == NULL)
+    fragp->tc_frag_data = fixp;
+
+  /* Generate fill reloc.  Default fill value is nop32.  */
+  if (fill_length)
+    {
+      sprintf (sname, "__reloc_fill_\2_%x", fill_value);
+      sym = symbol_find (sname);
+      if (sym == NULL)
+	{
+	  sym = symbol_new (sname, absolute_section, &zero_address_frag, fill_value);
+	  symbol_table_insert (sym);
+	  elf_symbol (symbol_get_bfdsym (sym))->internal_elf_sym.st_size
+	    = fill_length;
+	}
+
+      fix_new (fragp, where, 0, sym, 0, false, BFD_RELOC_NANOMIPS_FILL);
+    }
+
+  /* Generate max reloc.  */
+  if (max_fill != 0)
+    {
+      sprintf (sname, "__reloc_max_\2_%x", max_fill);
+      sym = symbol_find (sname);
+      if (sym == NULL)
+	{
+	  sym = symbol_new (sname, absolute_section, &zero_address_frag, max_fill);
+	  symbol_table_insert (sym);
+	}
+      fix_new (fragp, where, 0, sym, 0, false, BFD_RELOC_NANOMIPS_MAX);
+    }
+}
+
+/* Align the current frag to a given power of two.  If a particular
+   fill byte should be used, FILL points to an integer that contains
+   that byte, otherwise FILL is null.  */
+
+static void
+nanomips_align (int to, int *fill, symbolS *label)
+{
+  nanomips_flush_pending_output ();
+  struct insn_label_list *l;
+
+  if (fill == NULL && subseg_text_p (now_seg))
+    frag_align_code (to, 0);
+  else
+      frag_align (to, fill ? *fill : 0, 0);
+  record_alignment (now_seg, to);
+  if (label != NULL)
+    {
+      gas_assert (S_GET_SEGMENT (label) == now_seg);
+      symbol_set_frag (label, frag_now);
+      S_SET_VALUE (label, (valueT) frag_now_fix ());
+    }
+}
+
+/* Align to a given power of two.  .align 0 turns off the automatic
+   alignment used by the data creating pseudo-ops.  */
+
+static void
+s_align (int x ATTRIBUTE_UNUSED)
+{
+  int temp, fill_value, *fill_ptr;
+  long max_alignment = 15;
+
+  /* Note that the assembler pulls down any immediately preceding label
+     to the aligned address. */
+
+  temp = get_absolute_expression ();
+  if (temp > max_alignment)
+    as_bad (_("alignment too large, %d assumed"), temp = max_alignment);
+  else if (temp < 0)
+    {
+      as_warn (_("alignment negative, 0 assumed"));
+      temp = 0;
+    }
+  if (*input_line_pointer == ',')
+    {
+      ++input_line_pointer;
+      fill_value = get_absolute_expression ();
+      fill_ptr = &fill_value;
+    }
+  else
+    fill_ptr = 0;
+  if (temp)
+    {
+      segment_info_type *si = seg_info (now_seg);
+      struct insn_label_list *l = si->label_list;
+
+      if (nanomips_linkrelax_p
+	  && (bfd_section_flags (now_seg) & SEC_CODE) != 0
+	  && temp > 1)
+	{
+	  int fill_length = 0;
+
+	  if (fill_ptr == NULL && subseg_text_p (now_seg))
+	    frag_grow (MAX_MEM_FOR_RS_ALIGN_CODE(p2align, max));
+	  else
+	    {
+	      fill_length = 1;
+	      frag_grow (1);
+	    }
+
+	  create_align_relocs (frag_now, temp, fill_value, fill_length, 0);
+	}
+
+      /* Auto alignment should be switched on by next section change.  */
+      auto_align = 1;
+      nanomips_align (temp, fill_ptr, l != NULL ? l->label : NULL);
+    }
+  else
+    {
+      auto_align = 0;
+    }
+  demand_empty_rest_of_line ();
+}
+
+static void
+s_change_sec (int sec)
+{
+  segT seg;
+
+  if ((now_seg->flags & SHF_EXECINSTR) != 0
+      && stub_funcless_mode
+      && !nanomips_opts.no_balc_stubs
+      && stubg_now != NULL)
+    {
+      stub_funcless_mode = false;
+      stubgroup_wane ();
+    }
+
+  /* The ELF backend needs to know that we are changing sections, so
+     that .previous works correctly.  We could do something like check
+     for an obj_section_change_hook macro, but that might be confusing
+     as it would not be appropriate to use it in the section changing
+     functions in read.c, since obj-elf.c intercepts those.  FIXME:
+     This should be cleaner, somehow.  */
+  obj_elf_section_change_hook ();
+
+  nanomips_flush_pending_output ();
+
+  switch (sec)
+    {
+    case 't':
+      s_text (0);
+      break;
+    case 'd':
+      s_data (0);
+      break;
+    case 'b':
+      subseg_set (bss_section, (subsegT) get_absolute_expression ());
+      demand_empty_rest_of_line ();
+      break;
+
+    case 'r':
+      seg = subseg_new (RDATA_SECTION_NAME,
+			get_absolute_expression ());
+      bfd_set_section_flags (seg, (SEC_ALLOC | SEC_LOAD
+					      | SEC_READONLY | SEC_RELOC
+					      | SEC_DATA));
+      if (strncmp (TARGET_OS, "elf", 3) != 0)
+	record_alignment (seg, 4);
+      demand_empty_rest_of_line ();
+      break;
+
+    case 's':
+      seg = subseg_new (".sdata", get_absolute_expression ());
+      bfd_set_section_flags (seg,
+			     SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
+      if (strncmp (TARGET_OS, "elf", 3) != 0)
+	record_alignment (seg, 4);
+      demand_empty_rest_of_line ();
+      break;
+
+    case 'B':
+      seg = subseg_new (".sbss", get_absolute_expression ());
+      bfd_set_section_flags (seg, SEC_ALLOC);
+      if (strncmp (TARGET_OS, "elf", 3) != 0)
+	record_alignment (seg, 4);
+      demand_empty_rest_of_line ();
+      break;
+    }
+  auto_align = 1;
+}
+
+void
+s_change_section (int ignore ATTRIBUTE_UNUSED)
+{
+  char *saved_ilp;
+  const char *section_name;
+  char c, next_c = 0;
+  int section_type;
+  int section_flag;
+  int section_entry_size;
+
+  saved_ilp = input_line_pointer;
+  section_name = obj_elf_section_name ();
+  if (section_name == NULL)
+	  return;
+  c = input_line_pointer[0];
+  if (c)
+    next_c = input_line_pointer[1];
+
+  if ((now_seg->flags & SHF_EXECINSTR) != 0
+      && stub_funcless_mode
+      && !nanomips_opts.no_balc_stubs
+      && stubg_now != NULL)
+    {
+      stub_funcless_mode = false;
+      stubgroup_wane ();
+    }
+
+  /* Do we have .section Name<,"flags">?  */
+  if (c != ',' || (c == ',' && next_c == '"'))
+    {
+      input_line_pointer = saved_ilp;
+      obj_elf_section (ignore);
+      return;
+    }
+  input_line_pointer++;
+
+  /* Do we have .section Name<,type><,flag><,entry_size><,alignment>  */
+  if (c == ',')
+    section_type = get_absolute_expression ();
+  else
+    section_type = 0;
+  if (*input_line_pointer++ == ',')
+    section_flag = get_absolute_expression ();
+  else
+    section_flag = 0;
+  if (*input_line_pointer++ == ',')
+    section_entry_size = get_absolute_expression ();
+  else
+    section_entry_size = 0;
+
+  section_name = xstrdup (section_name);
+
+  if ((now_seg->flags & SHF_EXECINSTR) != 0
+      && stub_funcless_mode
+      && !nanomips_opts.no_balc_stubs
+      && stubg_now != NULL)
+    {
+      stub_funcless_mode = false;
+      stubgroup_wane ();
+    }
+  obj_elf_change_section (section_name, section_type, section_flag,
+			  section_entry_size, 0, false);
+}
+
+void
+nanomips_enable_auto_align (void)
+{
+  auto_align = 1;
+}
+
+static void
+s_cons (int log_size)
+{
+  segment_info_type *si = seg_info (now_seg);
+  struct insn_label_list *l = si->label_list;
+  symbolS *label;
+
+  label = l != NULL ? l->label : NULL;
+  nanomips_flush_pending_output ();
+  if (log_size > 0
+      && auto_align)
+    nanomips_align (log_size, 0, label);
+  cons (1 << log_size);
+  nanomips_clear_insn_labels ();
+}
+
+static void
+s_sign_cons (int log_size)
+{
+  sign_cons = true;
+  s_cons (log_size);
+  sign_cons = false;
+}
+
+static void
+s_float_cons (int type)
+{
+  segment_info_type *si = seg_info (now_seg);
+  struct insn_label_list *l = si->label_list;
+  symbolS *label;
+
+  label = l != NULL ? l->label : NULL;
+  nanomips_flush_pending_output ();
+
+  if (auto_align)
+    {
+      if (type == 'd')
+	nanomips_align (3, 0, label);
+      else
+	nanomips_align (2, 0, label);
+    }
+  float_cons (type);
+  nanomips_clear_insn_labels ();
+}
+
+/* This structure is used to hold a stack of .set values.  */
+
+struct nanomips_option_stack
+{
+  struct nanomips_option_stack *next;
+  struct nanomips_set_options options;
+};
+
+static struct nanomips_option_stack *nanomips_opts_stack;
+
+static bool
+parse_code_option (char *name)
+{
+  const struct nanomips_ase *ase;
+  if (strcmp (name, "at") == 0)
+    nanomips_opts.noat = 0;
+  else if (strcmp (name, "noat") == 0)
+    nanomips_opts.noat = 1;
+  else if (strcmp (name, "softfloat") == 0)
+    nanomips_opts.soft_float = 1;
+  else if (strcmp (name, "hardfloat") == 0)
+    nanomips_opts.soft_float = 0;
+  else if (strcmp (name, "singlefloat") == 0)
+    nanomips_opts.single_float = 1;
+  else if (strcmp (name, "doublefloat") == 0)
+    nanomips_opts.single_float = 0;
+  else if (strcmp (name, "nooddspreg") == 0)
+    as_bad (_("nanoMIPS allows odd single-precision FP registers"));
+  else if (strcmp (name, "mips16") == 0 || strcmp (name, "MIPS-16") == 0)
+    as_bad (_("nanoMIPS does not support MIPS16 ASE"));
+  else if (strcmp (name, "micromips") == 0)
+    as_bad (_("micromips is incompatible with nanoMIPS"));
+  else if (name[0] == 'n'
+	   && name[1] == 'o'
+	   && (ase = nanomips_lookup_ase (name + 2)))
+    nanomips_set_ase (ase, &nanomips_opts, false);
+  else if ((ase = nanomips_lookup_ase (name)))
+    nanomips_set_ase (ase, &nanomips_opts, true);
+  else if (strncmp (name, "mips", 4) == 0)
+    as_bad (_("cannot change ISA from nanoMIPS to %s"), name);
+  else if (strncmp (name, "arch=mips", 9) == 0)
+    as_bad (_("cannot change ISA from nanoMIPS to %s"), name + 5);
+  else if (strncmp (name, "arch=", 5) == 0)
+    {
+      /* Permit the user to change the ISA and architecture on the fly.
+         Needless to say, misuse can cause serious problems.  */
+      const struct nanomips_cpu_info *p;
+
+      p = nanomips_parse_cpu ("internal use", name + 5);
+      if (!p)
+	as_bad (_("unknown architecture %s"), name + 5);
+      else
+	{
+	  nanomips_opts.arch = p->cpu;
+	  nanomips_opts.isa = p->isa;
+	  nanomips_opts.init_ase = p->ase;
+	}
+    }
+  else if (strcmp (name, "insn32") == 0)
+    nanomips_opts.insn32 = true;
+  else if (strcmp (name, "noinsn32") == 0)
+    nanomips_opts.insn32 = false;
+  else if (strncmp (name, "mcmodel=", 8) == 0)
+    {
+      if (strcmp (name + 8, "auto") == 0)
+	nanomips_opts.mc_model = MC_AUTO;
+      else if (strcmp (name + 8, "medium") == 0)
+	nanomips_opts.mc_model = MC_MEDIUM;
+      else if (strcmp (name + 8, "large") == 0)
+	nanomips_opts.mc_model = MC_LARGE;
+      else
+	as_bad (_("invalid memory model setting %s"), name);
+    }
+  else if (strncmp (name, "pic", 3) == 0)
+    {
+      int i = atoi (name + 3);
+      if (i == 0)
+	nanomips_opts.pic = NO_PIC;
+      else if (i == 1)
+	nanomips_opts.pic = SVR4_PIC;
+      else if (i == 2)
+	nanomips_opts.pic = SVR4_LARGE_PIC;
+      else
+	as_bad (_(".set pic%d not supported"), i);
+    }
+  else if (strcmp (name, "nopic") == 0)
+    nanomips_opts.pic = NO_PIC;
+  else if (strcmp (name, "pid") == 0)
+    nanomips_opts.pid = true;
+  else if (strcmp (name, "nopic") == 0)
+    nanomips_opts.pid = false;
+  else if (strcmp (name, "pcrel") == 0)
+    nanomips_opts.pcrel = true;
+  else if (strcmp (name, "nopcrel") == 0)
+    nanomips_opts.pcrel = false;
+  else
+    return (strcmp (name, "oddspreg") == 0
+	    || strcmp (name, "nomips16") == 0
+	    || strcmp (name, "noMIPS-16") == 0
+	    || strcmp (name, "nomicromips") == 0
+	    || strcmp (name, "nobopt") == 0
+	    || strcmp (name, "noforbidden-slots") == 0
+	    || strcmp (name, "move") == 0
+	    || strcmp (name, "nomove") == 0
+	    || strcmp (name, "novolatile") == 0
+	    || strcmp (name, "volatile") == 0);
+
+  return true;
+}
+
+/* Handle the .set pseudo-op.  */
+
+static void
+s_nanomipsset (int x ATTRIBUTE_UNUSED)
+{
+  char *name = input_line_pointer, ch;
+  int prev_isa = nanomips_opts.isa;
+
+  file_check_options ();
+
+  while (!is_end_of_stmt (*input_line_pointer))
+    ++input_line_pointer;
+  ch = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  if (strchr (name, ','))
+    {
+      /* Generic ".set" directive; use the generic handler.  */
+      *input_line_pointer = ch;
+      input_line_pointer = name;
+      s_set (0);
+      return;
+    }
+
+  if (strcmp (name, "reorder") == 0)
+    {
+      if (nanomips_opts.noreorder)
+	end_noreorder ();
+    }
+  else if (strcmp (name, "noreorder") == 0)
+    {
+      if (!nanomips_opts.noreorder)
+	start_noreorder ();
+    }
+  else if (strcmp (name, "macro") == 0)
+    nanomips_opts.nomacro = 0;
+  else if (strcmp (name, "nomacro") == 0)
+    nanomips_opts.nomacro = 1;
+  else if (strcmp (name, "arch=default") == 0)
+    {
+      nanomips_opts.isa = file_nanomips_opts.isa;
+      nanomips_opts.arch = file_nanomips_opts.arch;
+      nanomips_opts.init_ase = file_nanomips_opts.init_ase;
+      nanomips_opts.gp = file_nanomips_opts.gp;
+      nanomips_opts.fp = file_nanomips_opts.fp;
+    }
+  else if (strcmp (name, "push") == 0)
+    {
+      struct nanomips_option_stack *s;
+
+      s = (struct nanomips_option_stack *) xmalloc (sizeof *s);
+      s->next = nanomips_opts_stack;
+      s->options = nanomips_opts;
+      nanomips_opts_stack = s;
+    }
+  else if (strcmp (name, "pop") == 0)
+    {
+      struct nanomips_option_stack *s;
+
+      s = nanomips_opts_stack;
+      if (s == NULL)
+	as_bad (_(".set pop with no .set push"));
+      else
+	{
+	  /* If we're changing the reorder mode we need to handle
+	     delay slots correctly.  */
+	  if (s->options.noreorder && !nanomips_opts.noreorder)
+	    start_noreorder ();
+	  else if (!s->options.noreorder && nanomips_opts.noreorder)
+	    end_noreorder ();
+
+	  nanomips_opts = s->options;
+	  nanomips_opts_stack = s->next;
+	  free (s);
+	}
+    }
+  else if (strcmp (name, "nolinkrelax") == 0)
+    toggle_linkrelax_p = (linkrelax && nanomips_linkrelax_p);
+  else if (strcmp (name, "linkrelax") == 0)
+    toggle_linkrelax_p = (linkrelax && !nanomips_linkrelax_p);
+  else if (strcmp (name, "legacyregs") == 0)
+    nanomips_opts.legacyregs = true;
+  else if (strcmp (name, "nolegacyregs") == 0)
+    nanomips_opts.legacyregs = false;
+  else if (!parse_code_option (name))
+    as_warn (_("tried to set unrecognized symbol: %s\n"), name);
+
+  /* The use of .set [arch|cpu]= historically 'fixes' the width of gp and fp
+     registers based on what is supported by the arch/cpu.  */
+  if (nanomips_opts.isa != prev_isa)
+    {
+      switch (nanomips_opts.isa)
+	{
+	case 0:
+	  break;
+	case ISA_NANOMIPS32R6:
+	  nanomips_opts.gp = 32;
+	  nanomips_opts.fp = 64;
+	  break;
+	case ISA_NANOMIPS64R6:
+	  nanomips_opts.gp = 64;
+	  nanomips_opts.fp = 64;
+	  break;
+	default:
+	  as_bad (_("unknown ISA level %s"), name + 4);
+	  break;
+	}
+    }
+  check_isa_supports_ases ();
+  *input_line_pointer = ch;
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .module pseudo-op.  */
+
+static void
+s_module (int ignore ATTRIBUTE_UNUSED)
+{
+  char *name = input_line_pointer, ch;
+
+  while (!is_end_of_stmt (*input_line_pointer))
+    ++input_line_pointer;
+  ch = *input_line_pointer;
+  *input_line_pointer = '\0';
+
+  if (!file_nanomips_opts_checked)
+    {
+      if (!parse_code_option (name))
+	as_bad (_(".module used with unrecognized symbol: %s\n"), name);
+
+      /* Update module level settings from nanomips_opts.  */
+      file_nanomips_opts = nanomips_opts;
+    }
+  else
+    as_bad (_(".module is not permitted after generating code"));
+
+  *input_line_pointer = ch;
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpsetup pseudo-op defined for NewABI PIC code.  The syntax is:
+     .cpsetup $reg1, offset|$reg2, label */
+
+static void
+s_cpsetup (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS ex;
+  file_check_options ();
+
+  /* If we are not generating SVR4 PIC code, .cpsetup is ignored.  */
+  if (nanomips_opts.pic == NO_PIC)
+    {
+      s_ignore (0);
+      return;
+    }
+
+  ex.X_op = O_symbol;
+  ex.X_add_symbol = symbol_find_or_make ("_gp");
+  ex.X_op_symbol = NULL;
+  ex.X_add_number = 0;
+  symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT;
+
+  macro_start ();
+  macro_pcrel_la (nanomips_gp_register, &ex, BFD_RELOC_UNUSED);
+  macro_end (true);
+  ignore_rest_of_line ();
+
+  return;
+}
+
+/* Handle a .dtprelword, .dtpreldword, .tprelword, or .tpreldword
+   pseudo-op; DIRSTR says which. The pseudo-op generates a BYTES-size
+   DTP- or TP-relative relocation of type RTYPE, for use in DWARF
+   debug information.  */
+
+static void
+s_tls_rel_directive (const size_t bytes, const char *dirstr,
+		     bfd_reloc_code_real_type rtype)
+{
+  expressionS ex;
+  char *p;
+
+  expression (&ex);
+
+  if (ex.X_op != O_symbol)
+    {
+      as_bad (_("unsupported use of %s"), dirstr);
+      ignore_rest_of_line ();
+    }
+
+  p = frag_more (bytes);
+  md_number_to_chars (p, 0, bytes);
+  fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, false, rtype);
+  demand_empty_rest_of_line ();
+}
+
+/* Handle .dtprelword.  */
+
+static void
+s_dtprelword (int ignore ATTRIBUTE_UNUSED)
+{
+  s_tls_rel_directive (4, ".dtprelword", BFD_RELOC_NANOMIPS_TLS_DTPREL);
+}
+
+/* Handle .dtpreldword.  */
+
+static void
+s_dtpreldword (int ignore ATTRIBUTE_UNUSED)
+{
+  s_tls_rel_directive (8, ".dtpreldword", BFD_RELOC_NANOMIPS_TLS_DTPREL);
+}
+
+/* Handle the .ehword pseudo-op.  This is used when generating unwinding
+   tables.  It generates a PC32 reloc.  */
+
+static void
+s_ehword (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS ex;
+  char *p;
+
+  nanomips_flush_pending_output ();
+
+  expression (&ex);
+  nanomips_clear_insn_labels ();
+
+  if (ex.X_op != O_symbol || ex.X_add_number != 0)
+    {
+      as_bad (_("unsupported use of .ehword"));
+      ignore_rest_of_line ();
+    }
+
+  p = frag_more (4);
+  md_number_to_chars (p, 0, 4);
+  fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, false,
+	       BFD_RELOC_NANOMIPS_EH);
+
+  demand_empty_rest_of_line ();
+}
+
+/* Handle the .insn pseudo-op, retained for compatibility.  */
+
+static void
+s_insn (int ignore ATTRIBUTE_UNUSED)
+{
+  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 nanomips_frame_reg.  */
+
+int
+tc_get_register (int frame ATTRIBUTE_UNUSED)
+{
+  unsigned int reg;
+
+  SKIP_WHITESPACE ();
+  if (!reg_lookup (&input_line_pointer, RWARN | RTYPE_NUM | RTYPE_GP, &reg))
+    reg = 0;
+
+  return reg;
+}
+
+/* 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.)  */
+
+static int
+nopic_need_relax (symbolS *sym, int before_relaxing)
+{
+  if (sym == 0)
+    return 0;
+
+  if (g_switch_value > 0)
+    {
+      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 (sym);
+      if (symname != (const char *) NULL
+	  && (strcmp (symname, "eprol") == 0
+	      || strcmp (symname, "etext") == 0
+	      || strcmp (symname, "_gp") == 0
+	      || strcmp (symname, "edata") == 0
+	      || strcmp (symname, "_fbss") == 0
+	      || strcmp (symname, "_fdata") == 0
+	      || strcmp (symname, "_ftext") == 0
+	      || strcmp (symname, "end") == 0
+	      || strcmp (symname, "_gp_disp") == 0))
+	change = 1;
+      else if ((! S_IS_DEFINED (sym) || S_IS_COMMON (sym))
+	       && (0
+#ifndef NO_ECOFF_DEBUGGING
+		   || (symbol_get_obj (sym)->ecoff_extern_size != 0
+		       && (symbol_get_obj (sym)->ecoff_extern_size
+			   <= g_switch_value))
+#endif
+		   /* We must defer this decision until after the whole
+		      file has been read, since there might be a .extern
+		      after the first use of this symbol.  */
+		   || (before_relaxing
+#ifndef NO_ECOFF_DEBUGGING
+		       && symbol_get_obj (sym)->ecoff_extern_size == 0
+#endif
+		       && S_GET_VALUE (sym) == 0)
+		   || (S_GET_VALUE (sym) != 0
+		       && S_GET_VALUE (sym) <= g_switch_value)))
+	change = 0;
+      else
+	{
+	  const char *segname;
+	  segname = segment_name (S_GET_SEGMENT (sym));
+	  change = (strcmp (segname, ".sdata") != 0
+		 && strcmp (segname, ".sbss") != 0
+		 && strncmp (segname, ".sdata.", 7) != 0
+		 && strncmp (segname, ".sbss.", 6) != 0
+		 && strncmp (segname, ".gnu.linkonce.sb.", 17) != 0
+		 && strncmp (segname, ".gnu.linkonce.s.", 16) != 0);
+	}
+      return change;
+    }
+  else
+    /* We are not optimizing for the $gp register.  */
+    return 1;
+}
+
+/* Return true if the given symbol should be considered local for SVR4 PIC.  */
+
+static bool
+pic_need_relax (symbolS *sym, asection *segtype)
+{
+  asection *symsec;
+
+  if (nanomips_opts.mc_model == MC_AUTO && linkrelax)
+    return false;
+
+  /* Handle the case of a symbol equated to another symbol.  */
+  while (symbol_equated_reloc_p (sym))
+    {
+      symbolS *n;
+
+      /* It's possible to get a loop here in a badly written program.  */
+      n = symbol_get_value_expression (sym)->X_add_symbol;
+      if (n == sym)
+	break;
+      sym = n;
+    }
+
+  if (symbol_section_p (sym))
+    return true;
+
+  symsec = S_GET_SEGMENT (sym);
+
+  /* This must duplicate the test in adjust_reloc_syms.  */
+  return (!bfd_is_und_section (symsec)
+	  && !bfd_is_abs_section (symsec)
+	  && !bfd_is_com_section (symsec)
+	  && !s_is_linkonce (sym, segtype)
+	  /* A global or weak symbol is treated as external.  */
+	  && !S_IS_WEAK (sym)
+	  && !S_IS_EXTERNAL (sym));
+}
+
+/* Check if relocation in HEAD refers to an address immediately following
+   instruction in an adjacent frag.  16-bit conditional branches cannot
+   reach the immediate next instruction and must be relaxed.  */
+static bool
+nanomips_frag_match (fragS *head, fragS *matchP)
+{
+  bfd_vma val = S_GET_VALUE (head->fr_symbol);
+
+  /* Typically, a match is as easy as checking that the frags are adjacent
+     and the symbol refered to is at the very beginning of the target frag.  */
+  if (matchP == head->fr_next && val == matchP->fr_address)
+    return true;
+
+  /* The tricky part: when doing assembly listings all sorts of fillers are
+     generated between what would be adjacent frags.  So we need to look
+     through the frag list, until we come to a non-empty frag that either
+     matches or exceeds our target.  */
+  if (listing)
+    {
+      head = head->fr_next;
+      while (head && head != matchP && head->fr_fix + head->fr_var == 0)
+	{
+	  /* Unlikely, but just in case we came too far, bail out!  */
+	  if (head->fr_line > matchP->fr_line)
+	    break;
+	  else
+	    head = head->fr_next;
+	}
+
+      return (head == matchP);
+    }
+  return false;
+}
+
+/* Check if a frag is variable due to explicit relocations.  */
+
+static bool
+reloc_variable_frag_p (fragS *fragp, asection *sec)
+{
+  struct reloc_list *rp;
+  /* Trivial for frags that are already marked.  */
+  if (fragp->tc_frag_data)
+    return true;
+
+  rp = reloc_list;
+
+  /* Now look for fixups in the explicit relocation list.
+     Update tc_frag_data.link_var here and now so the iteration doesn't
+     have to be repeated, at least for this frag.  */
+  if (rp != NULL
+      && symbol_get_frag (rp->u.a.offset_sym) == fragp)
+    {
+      fragp->tc_frag_data = 1;
+      return true;
+    }
+  return false;
+}
+
+/* Compute the length of a branch, and adjust the RELAX_MD_TOOFAR16
+   bit accordingly.  */
+
+static int
+relaxed_16bit_branch_length (fragS *fragp, asection *sec, int update)
+{
+  bool toofar;
+
+  if (fragp
+      && fragp->fr_symbol
+      && S_IS_DEFINED (fragp->fr_symbol)
+      && !S_IS_WEAK (fragp->fr_symbol)
+      && sec == S_GET_SEGMENT (fragp->fr_symbol))
+    {
+      addressT addr;
+      offsetT val;
+      int type;
+
+      val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+
+      /* Assume this is a 2-byte branch.  */
+      addr = fragp->fr_address + fragp->fr_fix + 2;
+
+      val -= addr;
+
+      type = RELAX_MD_TYPE (fragp->fr_subtype);
+      if (type == RT_BRANCH_UCND || type == RT_BALC_STUB)
+	toofar = val < -(0x200 << 1) || val >= (0x200 << 1);
+      else if (type == RT_BRANCH_CNDZ)
+	toofar = val < -(0x40 << 1) || val >= (0x40 << 1);
+      else if (type == RT_BRANCH_CND)
+	toofar = (val <= 0
+		  || val > 30
+		  || (val == 2
+		      && RELAX_MD_TOOFAR16 (fragp->fr_subtype)
+		      && nanomips_frag_match (fragp, symbol_get_frag
+					      (fragp->fr_symbol))));
+      else
+	abort ();
+    }
+  else
+    /* If the symbol is not defined or it's in a different segment,
+       we emit a normal 32-bit branch.  */
+    toofar = true;
+
+  if (fragp && update
+      && toofar != RELAX_MD_TOOFAR16 (fragp->fr_subtype))
+    fragp->fr_subtype
+      = (toofar
+	 ? RELAX_MD_MARK_TOOFAR16 (fragp->fr_subtype)
+	 : RELAX_MD_CLEAR_TOOFAR16 (fragp->fr_subtype));
+
+  if (toofar)
+    return 4;
+
+  return 2;
+}
+
+/* Compute the length of a call potentially going through a BALC stub
+   and adjust the RELAX_MD_USESTUB bit accordingly.  */
+
+static int
+relaxed_stub_call_length (fragS *fragp, asection *sec, bool toofar16,
+			  bool update)
+{
+  bool usestub = false;
+  bool keepstub = false;
+  addressT callsite;
+  struct balc_stub *stub = NULL;
+
+  /* Assume this is a 2-byte branch.  */
+  callsite = fragp->fr_address + fragp->fr_fix;
+
+  /* Find start of correct stub-group list, if in different section or
+     at end of current list.  */
+  if ((stubg_now->seg != sec)
+      || (stubg_now->prev != NULL
+	  && stubg_now->prev->fragp != NULL
+	  && callsite < stubg_now->prev->fragp->fr_address))
+    stubg_now = str_hash_find (balc_stubgroup_table, sec->name);
+
+  /* This is for ugly cases of hand-written assembly where the
+     order of .ent and .section directives is reversed.  */
+  if (stubg_now == NULL || stubg_now->fragp == NULL)
+    return (toofar16 ? 4 : 2);
+
+  while (stubg_now->fragp->fr_address + stubg_now->fragp->fr_fix > 0
+	 && stubg_now->fragp->fr_address + stubg_now->fragp->fr_fix < callsite)
+    if (stubg_now->next != NULL && stubg_now->next->fragp != NULL)
+      stubg_now = stubg_now->next;
+    else
+      break;
+
+  gas_assert (stubg_now->seg == sec);
+
+  usestub = balc_find_stub_inrange (S_GET_NAME (fragp->fr_symbol),
+				    callsite, stubg_now, &stub);
+
+  if (usestub && stub->fragp != NULL)
+    keepstub = RELAX_MD_KEEPSTUB (stub->fragp->fr_subtype);
+
+  /* We need this to happen only once per call frag, but as early as
+     possible in the estimation phase, before actual relaxation.  */
+  if (usestub && !toofar16)
+    {
+      usestub = false;
+      if (RELAX_MD_ONCESTUB (fragp->fr_subtype))
+	{
+	  fragp->fr_subtype
+	    = RELAX_MD_CLEAR_ONCESTUB (fragp->fr_subtype);
+	  if (callsite == stub->first_call->callsite)
+	    stublist_pop_call (stub);
+	  else
+	    stublist_trunc_calls (stub, callsite);
+	}
+    }
+  /* Only track calls that cannot fit within 16-bit instruction.  */
+  if (usestub && toofar16
+      && usestub != RELAX_MD_ONCESTUB (fragp->fr_subtype))
+    {
+      stublist_append_call (stub, callsite);
+      fragp->fr_subtype = RELAX_MD_MARK_ONCESTUB (fragp->fr_subtype);
+    }
+
+  if (update && usestub != RELAX_MD_USESTUB (fragp->fr_subtype))
+    fragp->fr_subtype
+      = ((usestub && keepstub)
+	 ? RELAX_MD_MARK_USESTUB (fragp->fr_subtype)
+	 : RELAX_MD_CLEAR_USESTUB (fragp->fr_subtype));
+
+  if ((usestub && keepstub) || !toofar16)
+    return 2;
+  else
+    return 4;
+}
+
+/* Compute the length of a BALC stub and adjust the RELAX_MD_KEEPSTUB
+   bit accordingly. If a stub is not to be instantiated, its length is 0.  */
+static int
+relaxed_stub_length (fragS *fragp, asection *sec, bool update)
+{
+  bool keepstub = false;
+  addressT stubsite;
+  struct balc_stub *stub = NULL;
+
+  /* Assume this is a 2-byte branch.  */
+  stubsite = fragp->fr_address + fragp->fr_fix;
+
+  if (stubg_now->fragp)
+    {
+      /* Section change.  */
+      if (stubg_now->seg != sec || (stubg_now->fragp->fr_address > stubsite))
+	stubg_now = str_hash_find (balc_stubgroup_table, sec->name);
+
+      while (stubg_now->fragp->fr_address < stubsite)
+	if (stubg_now->next && stubg_now->next->fragp
+	    && stubg_now->next->fragp->fr_address <= stubsite)
+	  stubg_now = stubg_now->next;
+	else
+	  break;
+    }
+
+  gas_assert (stubg_now->seg == sec);
+
+  /* Retain this stub only if it has sufficient associated
+     calls and cannot be merged with another elligible stub.  */
+  if (balc_in_stub_group (S_GET_NAME (fragp->fr_symbol),
+			  stubsite, stubg_now, &stub)
+      && stub->numcalls > 0
+      && !balc_merge_stub (S_GET_NAME (fragp->fr_symbol),
+			   stubg_now, &stub)
+      && stub->numcalls >= 3)
+    keepstub = true;
+
+  if (keepstub != RELAX_MD_KEEPSTUB (fragp->fr_subtype))
+    {
+      if (update)
+	fragp->fr_subtype
+	  = keepstub ? RELAX_MD_MARK_KEEPSTUB (fragp->fr_subtype)
+	  : RELAX_MD_CLEAR_KEEPSTUB (fragp->fr_subtype);
+
+      stubg_now->next_offset += (keepstub ? 4 : -4);
+    }
+
+  if (keepstub)
+    return 4;
+  else
+    {
+      /* A stub that is unused right up to the update stage
+         can be summarily pruned from the table.  */
+      if (update && stub && stub->numcalls == 0)
+	stubtable_delete (stubg_now->stubtable,
+				 S_GET_NAME (fragp->fr_symbol));
+      return 0;
+    }
+}
+
+static bool
+nanomips_gpr3_reg_p (unsigned long reg)
+{
+  /* Check for membership in set {16, 17, 18, 19, 4, 5, 6, 7}.  */
+  return ((reg >> 2 == 1 || reg >> 2 == 4));
+}
+
+/* Compute the length of an ADDIU instruction.  */
+static int
+relaxed_addiu_length (fragS *fragp, bool update)
+{
+  char *buf;
+  unsigned long insn;
+  offsetT sval;
+  unsigned long rt, rs;
+  bool toofar16 = true;
+  bool negoff = false;
+
+  sval = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+  buf = fragp->fr_literal + fragp->fr_fix;
+  insn = read_compressed_insn (buf, 4);
+
+  rt = EXTRACT_BITS (insn, NANOMIPSOP_MASK_RT, NANOMIPSOP_SH_RT);
+  rs = EXTRACT_BITS (insn, NANOMIPSOP_MASK_RS, NANOMIPSOP_SH_RS);
+
+  if (rt == rs
+      && sval >= -8
+      && sval <= 7
+      && !RELAX_MD_FIXED (fragp->fr_subtype))
+    {
+      toofar16 = false;
+      negoff = true;
+    }
+  else if (nanomips_gpr3_reg_p (rs)
+	   && nanomips_gpr3_reg_p (rt)
+	   && sval >= 0
+	   && sval <= 28
+	   && sval % 4 == 0
+	   && !RELAX_MD_FIXED (fragp->fr_subtype))
+    toofar16 = false;
+  else if (nanomips_gpr3_reg_p (rt)
+	   && rs == 0
+	   && (int)sval >= -1
+	   && (int)sval <= 126
+	   && !RELAX_MD_FIXED (fragp->fr_subtype))
+    toofar16 = false;
+  else
+    negoff = (sval < 0);
+
+  if (fragp && update
+      && toofar16 != RELAX_MD_TOOFAR16 (fragp->fr_subtype))
+    fragp->fr_subtype
+      = (toofar16
+	 ? RELAX_MD_MARK_TOOFAR16 (fragp->fr_subtype)
+	 : RELAX_MD_CLEAR_TOOFAR16 (fragp->fr_subtype));
+
+  if (fragp && update && negoff != RELAX_MD_NEGOFF (fragp->fr_subtype))
+    fragp->fr_subtype
+      = negoff ? RELAX_MD_MARK_NEGOFF (fragp->fr_subtype)
+      : RELAX_MD_CLEAR_NEGOFF (fragp->fr_subtype);
+
+  if (toofar16)
+    return 4;
+  else
+    return 2;
+}
+
+/* Estimate the size of a frag before relaxing. We are not really relaxing
+   here. The estimated size is encoded in the subtype information.  */
+
+int
+md_estimate_size_before_relax (fragS *fragp, asection *segtype)
+{
+  int change;
+
+  if (RELAX_MD_P (fragp->fr_subtype))
+    {
+      int length = 4;
+
+      if (!nanomips_opts.no_balc_stubs
+	  && stubg_now != NULL
+	  && RELAX_MD_BALC_STUB_P (fragp->fr_subtype))
+	length = relaxed_stub_length (fragp, segtype, false);
+      else if (RELAX_MD_ADDIU_P (fragp->fr_subtype))
+	length = relaxed_addiu_length (fragp, false);
+      else if (RELAX_MD_TYPE (fragp->fr_subtype) != 0)
+	length = relaxed_16bit_branch_length (fragp, segtype, false);
+
+      /* Try to relax 32-bit call through a stub.  */
+      if (!nanomips_opts.no_balc_stubs
+	  && stubg_now != NULL
+	  && RELAX_MD_TYPE (fragp->fr_subtype) == RT_BRANCH_UCND
+	  && RELAX_MD_LINK (fragp->fr_subtype))
+	length = relaxed_stub_call_length (fragp, segtype, length > 2, false);
+
+      fragp->fr_var = length;
+      return length;
+    }
+
+  if (nanomips_opts.pic == NO_PIC)
+    change = nopic_need_relax (fragp->fr_symbol, 0);
+  else if ((nanomips_opts.pic == SVR4_PIC)
+	   || (nanomips_opts.pic == SVR4_LARGE_PIC))
+    change = pic_need_relax (fragp->fr_symbol, segtype);
+  else
+    abort ();
+
+  if (change)
+    {
+      fragp->fr_subtype |= RELAX_USE_SECOND;
+      return -RELAX_FIRST (fragp->fr_subtype);
+    }
+  else
+    return -RELAX_SECOND (fragp->fr_subtype);
+}
+
+/* This is called to see whether a reloc against a defined symbol
+   should be converted into a reloc against a section.  */
+
+int
+nanomips_fix_adjustable (fixS *fixp)
+{
+  asection *tsect;
+
+  if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    return 0;
+
+  if (fixp->fx_addsy == NULL)
+    return 1;
+
+  /* PC relative relocations for need to be symbol rather than section
+     relative to allow linker relaxations to be performed later on.  */
+  if (pcrel_reloc_p (fixp->fx_r_type))
+    return 0; 
+
+  /* Relocations to code sections need to be symbol rather than section
+     relative for nanoMIPS, to allow linker expansions and relaxations
+     without having to adjust addends.  */
+  tsect = S_GET_SEGMENT (fixp->fx_addsy);
+  if (tsect != NULL
+      && (tsect->flags & SEC_CODE) != 0
+      && (linkrelax || symbol_used_p (fixp->fx_addsy)))
+    return 0;
+
+  return 1;
+}
+
+/* Translate internal representation of relocation info to BFD target
+   format.  */
+
+arelent **
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+  static arelent *retval[MAX_RELOC_EXPANSION + 1];
+  arelent *reloc;
+  bfd_reloc_code_real_type code;
+
+  memset (retval, 0, sizeof (retval));
+  reloc = retval[0] = (arelent *) xcalloc (1, sizeof (arelent));
+  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+  reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+  if (fixp->fx_pcrel && fixp->fx_r_type == BFD_RELOC_32_PCREL)
+    /* At this point, fx_addnumber is "symbol offset - pcrel address".
+       Relocations want only the symbol offset.  */
+    reloc->addend = fixp->fx_addnumber + reloc->address;
+  else
+    reloc->addend = fixp->fx_addnumber;
+
+  code = 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,
+		    _("cannot represent %s relocation in this object file"
+		      " format"), bfd_get_reloc_code_name (code));
+      retval[0] = NULL;
+    }
+
+  if (linkrelax && fixp->fx_subsy)
+    {
+      reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+      reloc->addend = 0;
+      retval[1] = reloc;
+
+      reloc = retval[0] = (arelent *) xcalloc (1, sizeof (arelent));
+      reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+      *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+      reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+      reloc->howto = bfd_reloc_type_lookup (stdoutput,
+					    BFD_RELOC_NANOMIPS_NEG);
+    }
+  else if (linkrelax_reloc_p (fixp->fx_r_type))
+    {
+      /* For a place-holder relocation, we want any succeeding PC-relative
+         relocation at the same offset to be generated first, so that its
+         PC-relative address calculation is disassembled correctly.  */
+      fixS *fixp_iter = fixp;
+
+      while (fixp_iter->fx_next != NULL)
+	{
+	  /* Search ahead for PC-relative relocations at same offset.  */
+	  fixp_iter = fixp_iter->fx_next;
+	  if (!linkrelax_reloc_p (fixp_iter->fx_r_type)
+	      && (fixp_iter->fx_frag->fr_address + fixp_iter->fx_where >
+		  fixp->fx_frag->fr_address + fixp->fx_where))
+	    break;
+
+	  if (fixp_iter->fx_pcrel && !fixp_iter->fx_done)
+	    {
+	      /* Relocation found, generate it before the place-holder
+	         reloc and mark it as done.  */
+	      retval[1] = reloc;
+	      reloc = retval[0] = (arelent *) xcalloc (1, sizeof (arelent));
+	      reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+	      *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp_iter->fx_addsy);
+	      reloc->address = (fixp_iter->fx_frag->fr_address
+				+ fixp_iter->fx_where);
+	      reloc->addend = fixp_iter->fx_addnumber;
+	      reloc->howto = bfd_reloc_type_lookup (stdoutput,
+						    fixp_iter->fx_r_type);
+
+	      fixp_iter->fx_done = true;
+	      break;
+	    }
+	}
+    }
+  return retval;
+}
+
+/* Relax a machine dependent frag.  This returns the amount by which
+   the current size of the frag should change.  */
+
+int
+nanomips_relax_frag (asection *sec, fragS *fragp,
+		     long stretch ATTRIBUTE_UNUSED)
+{
+  if (RELAX_MD_P (fragp->fr_subtype))
+    {
+      offsetT old_var = fragp->fr_var;
+      offsetT new_var = 4;
+
+      if (!nanomips_opts.no_balc_stubs
+	  && stubg_now != NULL
+	  && RELAX_MD_BALC_STUB_P (fragp->fr_subtype))
+	new_var = relaxed_stub_length (fragp, sec, true);
+      else if (RELAX_MD_ADDIU_P (fragp->fr_subtype))
+	new_var = relaxed_addiu_length (fragp, true);
+      else if (RELAX_MD_TYPE (fragp->fr_subtype) != 0)
+	new_var = relaxed_16bit_branch_length (fragp, sec, true);
+      else
+	new_var = old_var;
+
+      /* Try to relax 32-bit call through a stub.  */
+      if (!nanomips_opts.no_balc_stubs
+	  && stubg_now != NULL
+	  && RELAX_MD_TYPE (fragp->fr_subtype) == RT_BRANCH_UCND
+	  && RELAX_MD_LINK (fragp->fr_subtype)
+	  && RELAX_MD_TOOFAR16 (fragp->fr_subtype))
+	new_var = relaxed_stub_call_length (fragp, sec, new_var > 2, true);
+
+      fragp->fr_var = new_var;
+      return new_var - old_var;
+    }
+  return 0;
+}
+
+/* Convert a machine dependent frag.  */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
+{
+  if (RELAX_MD_ADDIU_P (fragp->fr_subtype))
+    {
+      char *buf = fragp->fr_literal + fragp->fr_fix;
+      unsigned long insn = read_compressed_insn (buf, 4);
+      offsetT sval;
+      unsigned rt, rs;
+
+      rt = EXTRACT_BITS (insn, NANOMIPSOP_MASK_RT, NANOMIPSOP_SH_RT);
+      rs = EXTRACT_BITS (insn, NANOMIPSOP_MASK_RS, NANOMIPSOP_SH_RS);
+      sval = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+
+      if (!RELAX_MD_TOOFAR16 (fragp->fr_subtype))
+	{
+	  if (RELAX_MD_NEGOFF (fragp->fr_subtype))
+	    /* ADDIU[32] -> ADDIU[RS5] */
+	    insn = (0x9008
+		    | (rt << NANOMIPSOP_SH_MP)
+		    | (sval & 0x8) << 1
+		    | (sval & 0x7));
+	  else if (rs == 0)
+	    /* LI[32] -> LI[16] */
+	    insn = (0xd000
+		    | (rt << NANOMIPSOP_SH_MM)
+		    | (sval & 0x7f));
+	  else
+	    /* ADDIU[32] -> ADDIU[R2] */
+	    insn = (0x9000
+		    | (rt & 0x7) << NANOMIPSOP_SH_MD
+		    | (rs & 0x7) << NANOMIPSOP_SH_MC
+		    | (sval & 0x1f) >> 2);
+	}
+      else if (RELAX_MD_NEGOFF (fragp->fr_subtype))
+	/* ADDIU[32] -> ADDIU[NEG] */
+	insn = (insn & ~0xfff) | 0x80008000 | (-sval & 0xfff);
+      else
+        insn = (insn & ~0xffff) | (sval & 0xffff);
+
+      buf = write_compressed_insn (buf, insn, fragp->fr_var);
+      fragp->fr_fix += fragp->fr_var;
+      gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
+      return;
+    }
+    else if (RELAX_MD_P (fragp->fr_subtype))
+    {
+      char *buf = fragp->fr_literal + fragp->fr_fix;
+      int type = RELAX_MD_TYPE (fragp->fr_subtype);
+      unsigned long insn;
+      expressionS exp;
+      fixS *fixp;
+      symbolS *stubsym = NULL;
+      const bfd_reloc_code_real_type rtype[]
+	= {
+	BFD_RELOC_NANOMIPS_10_PCREL_S1,
+	BFD_RELOC_NANOMIPS_7_PCREL_S1,
+	BFD_RELOC_NANOMIPS_4_PCREL_S1,
+	BFD_RELOC_NANOMIPS_25_PCREL_S1,
+	BFD_RELOC_NANOMIPS_14_PCREL_S1,
+	BFD_RELOC_NANOMIPS_14_PCREL_S1
+      };
+
+      if (RELAX_MD_USESTUB (fragp->fr_subtype)
+	  && ((stubg_now->seg != (asection *) asec)
+	      || (stubg_now->prev
+		  && stubg_now->prev->fragp
+		  && (fragp->fr_address + fragp->fr_fix
+		      < stubg_now->prev->fragp->fr_address))))
+	stubg_now = str_hash_find (balc_stubgroup_table, asec->name);
+
+      exp.X_op = O_symbol;
+      if (RELAX_MD_USESTUB (fragp->fr_subtype)
+	  && balc_get_stub_for_symbol (S_GET_NAME (fragp->fr_symbol),
+				       fragp->fr_address + fragp->fr_fix,
+				       stubg_now, &stubsym))
+	{
+	  /* Resolve call through a stub symbol.  */
+	  exp.X_add_symbol = stubsym;
+	  exp.X_add_number = 0;
+	  fragp->fr_subtype
+	    = RELAX_MD_CLEAR_TOOFAR16 (fragp->fr_subtype);
+	}
+      else
+	{
+	  exp.X_add_symbol = fragp->fr_symbol;
+	  exp.X_add_number = fragp->fr_offset;
+	}
+
+      fragp->fr_fix += fragp->fr_var;
+
+      /* We generate a fixup instead of applying it right now,
+         because if there is linker relaxation, we're going to
+         need the relocations.  */
+      switch (type)
+	{
+	case RT_BRANCH_UCND:
+	case RT_BRANCH_CNDZ:
+	case RT_BRANCH_CND:
+	  fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 2, &exp, true,
+			      rtype[type - RT_BRANCH_UCND
+				    + (RELAX_MD_TOOFAR16
+				       (fragp->fr_subtype) ? 3 : 0)]);
+	  break;
+	case RT_BALC_STUB:
+	  if (!RELAX_MD_KEEPSTUB (fragp->fr_subtype))
+	    return;
+	  fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, true,
+			      rtype[3]);
+	  break;
+	default:
+	  abort ();
+	}
+
+      fixp->fx_file = fragp->fr_file;
+      fixp->fx_line = fragp->fr_line;
+
+      /* These relocations can have an addend that won't fit in
+         2 octets.  */
+      fixp->fx_no_overflow = 1;
+
+      if (RELAX_MD_USESTUB (fragp->fr_subtype) && stubsym != NULL)
+	{
+	  /* Relax 32-bit call to 16-bit call to stub.  */
+	  buf = write_compressed_insn (buf, 0x3800, 2);
+	  return;
+	}
+
+      /* Nothing left to do for 16-bit branches that fit,
+         or for balc stubs */
+      if (!RELAX_MD_TOOFAR16 (fragp->fr_subtype) || type == RT_BALC_STUB)
+	return;
+
+      /* Relax 16-bit branches to 32-bit branches.  */
+      insn = read_compressed_insn (buf, 2);
+
+      if ((insn & 0xfc00) == 0xd800)	/* beqc[16]/bnec[16]  */
+	{
+	  unsigned long rt = (insn & 0x0380) >> 7;
+	  unsigned long rs = (insn & 0x0070) >> 4;
+	  if (rt > rs)
+	    insn = 0x88000000;	/* beqc  */
+	  else
+	    insn = 0xa8000000;	/* bnec  */
+	  rt = nanomips_to_32_reg_d_map[rt];
+	  rs = nanomips_to_32_reg_d_map[rs];
+	  insn |= (rt << 21);
+	  insn |= (rs << 16);
+	}
+      else if ((insn & 0xdc00) == 0x9800)	/* beqzc[16]/bnezc[16]  */
+	{
+	  unsigned long rt = (insn & 0x0380) >> 7;
+	  rt = nanomips_to_32_reg_d_map[rt];
+	  /* beqc: 0x9800 -> 0x88000000
+	     bnec: 0xb800 -> 0xa8000000  */
+	  insn = 0x88000000 | (((insn & 0x2000) >> 13) << 29);
+	  insn |= (rt << 21);
+	}
+      else if ((insn & 0xdc00) == 0x1800)	/* bc[16]/balc[16]  */
+	/* bc:		0x1800 -> 0x28000000
+	   balc:	0x3800 -> 0x2a000000  */
+	insn = 0x28000000 | (((insn & 0x2000) >> 13) << 25);
+      else
+	abort ();
+
+      /* Nothing else to do, just write it out.  */
+      buf = write_compressed_insn (buf, insn, 4);
+      gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
+      return;
+    }
+  {
+    relax_substateT subtype = fragp->fr_subtype;
+    bool second_longer = (subtype & RELAX_SECOND_LONGER) != 0;
+    bool use_second = (subtype & RELAX_USE_SECOND) != 0;
+    int first = RELAX_FIRST (subtype);
+    int second = RELAX_SECOND (subtype);
+    fixS *fixp = (fixS *) fragp->fr_opcode;
+
+    /* Possibly emit a warning if we've chosen the longer option.  */
+    if (use_second == second_longer)
+      {
+	subtype &= ~RELAX_NOMACRO;
+      }
+
+    /* Go through all the fixups for the first sequence.  Disable them
+       (by marking them as done) if we're going to use the second
+       sequence instead.  */
+    while (fixp
+	   && fixp->fx_frag == fragp
+	   && fixp->fx_where < fragp->fr_fix - second)
+      {
+	if (subtype & RELAX_USE_SECOND)
+	  fixp->fx_done = 1;
+	fixp = fixp->fx_next;
+      }
+
+    /* Go through the fixups for the second sequence.  Disable them if
+       we're going to use the first sequence, otherwise adjust their
+       addresses to account for the relaxation.  */
+    while (fixp && fixp->fx_frag == fragp)
+      {
+	if (subtype & RELAX_USE_SECOND)
+	  fixp->fx_where -= first;
+	else
+	  fixp->fx_done = 1;
+	fixp = fixp->fx_next;
+      }
+
+    /* Now modify the frag contents.  */
+    if (subtype & RELAX_USE_SECOND)
+      {
+	char *start;
+
+	start = fragp->fr_literal + fragp->fr_fix - first - second;
+	memmove (start, start + first, second);
+	fragp->fr_fix -= first;
+      }
+    else
+      fragp->fr_fix -= second;
+  }
+}
+
+/* This function is called whenever a label is defined, including fake
+   labels instantiated off the dot special symbol.  */
+
+static void
+nanomips_record_label (symbolS *sym)
+{
+  segment_info_type *si = seg_info (now_seg);
+  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 = si->label_list;
+  si->label_list = l;
+}
+
+/* This function is called as tc_frob_label() whenever a label is defined
+   and adds a DWARF-2 record we only want for true labels.  */
+
+void
+nanomips_define_label (symbolS *sym)
+{
+  nanomips_record_label (sym);
+  dwarf2_emit_label (sym);
+}
+
+/* Some special processing for a nanoMIPS ELF file.  */
+
+void
+nanomips_elf_final_processing (void)
+{
+  /* Set the nanoMIPS ELF flag bits.  */
+  if (nanomips_opts.pic != NO_PIC)
+    elf_elfheader (stdoutput)->e_flags |= EF_NANOMIPS_PIC;
+
+  if (linkrelax == true)
+    elf_elfheader (stdoutput)->e_flags |= EF_NANOMIPS_LINKRELAX;
+
+  /* Set the nanoMIPS ELF ABI flags.  */
+  if (nanomips_abi == P32_ABI)
+    elf_elfheader (stdoutput)->e_flags |= E_NANOMIPS_ABI_P32;
+  else if (nanomips_abi == P64_ABI)
+    elf_elfheader (stdoutput)->e_flags |= E_NANOMIPS_ABI_P64;
+
+  if (nanomips_32bitmode)
+    elf_elfheader (stdoutput)->e_flags |= EF_NANOMIPS_32BITMODE;
+}
+
+static procS cur_proc;
+static int numprocs;
+
+/* Fill in an rs_align_code fragment.  Unlike elsewhere we want to use
+   32-bit NOPs here (if applicable).  */
+
+void
+nanomips_handle_align (fragS *fragp)
+{
+  char *p;
+  int bytes, size, excess;
+  valueT opcode;
+
+  bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+
+  /* tc_frag_data points to the ALIGN relocation for this frag.
+     Update the size of the absolute alignment symbol to match
+     the actual padding inserted at this alignment point. */
+  if (fragp->tc_frag_data != NULL)
+    {
+      fixS *fixp = fragp->tc_frag_data;
+      asymbol *sym;
+
+      while (fixp != NULL && fixp->fx_frag == fragp)
+	{
+	  if (fixp->fx_r_type == BFD_RELOC_NANOMIPS_ALIGN)
+	    {
+	      sym = symbol_get_bfdsym (fixp->fx_addsy);
+	      elf_symbol (sym)->internal_elf_sym.st_size = bytes;
+	      break;
+	    }
+	  fixp = fixp->fx_next;
+	}
+    }
+
+  if (fragp->fr_type != rs_align_code)
+    return;
+
+  p = fragp->fr_literal + fragp->fr_fix;
+  opcode = nanomips_nop32_insn.insn_opcode;
+  size = 4;
+
+  excess = bytes % size;
+
+  /* Handle the leading part if we're not inserting a whole number of
+     instructions, and make it the end of the fixed part of the frag.
+     Try to fit in a short NOP if applicable and possible,
+     and use zeroes otherwise.  */
+  gas_assert (excess < 4);
+  fragp->fr_fix += excess;
+  switch (excess)
+    {
+    case 3:
+      *p++ = '\0';
+      /* Fall through.  */
+    case 2:
+      if (!nanomips_opts.insn32)
+	{
+	  p = write_compressed_insn (p, nanomips_nop16_insn.insn_opcode, 2);
+	  break;
+	}
+      *p++ = '\0';
+      /* Fall through.  */
+    case 1:
+      *p++ = '\0';
+      /* Fall through.  */
+    case 0:
+      break;
+    }
+
+  write_compressed_insn (p, opcode, size);
+  fragp->fr_var = size;
+}
+
+static long
+get_number (void)
+{
+  int negative = 0;
+  long val = 0;
+
+  if (*input_line_pointer == '-')
+    {
+      ++input_line_pointer;
+      negative = 1;
+    }
+  if (!ISDIGIT (*input_line_pointer))
+    as_bad (_("expected simple number"));
+  if (input_line_pointer[0] == '0')
+    {
+      if (input_line_pointer[1] == 'x')
+	{
+	  input_line_pointer += 2;
+	  while (ISXDIGIT (*input_line_pointer))
+	    {
+	      val <<= 4;
+	      val |= hex_value (*input_line_pointer++);
+	    }
+	  return negative ? -val : val;
+	}
+      else
+	{
+	  ++input_line_pointer;
+	  while (ISDIGIT (*input_line_pointer))
+	    {
+	      val <<= 3;
+	      val |= *input_line_pointer++ - '0';
+	    }
+	  return negative ? -val : val;
+	}
+    }
+  if (!ISDIGIT (*input_line_pointer))
+    {
+      printf (_(" *input_line_pointer == '%c' 0x%02x\n"),
+	      *input_line_pointer, *input_line_pointer);
+      as_warn (_("invalid number"));
+      return -1;
+    }
+  while (ISDIGIT (*input_line_pointer))
+    {
+      val *= 10;
+      val += *input_line_pointer++ - '0';
+    }
+  return negative ? -val : val;
+}
+
+/* The .file directive; just like the usual .file directive, but there
+   is an initial number which is the ECOFF file index.  In the non-ECOFF
+   case .file implies DWARF-2.  */
+
+static void
+s_nanomips_file (int x ATTRIBUTE_UNUSED)
+{
+  static int first_file_directive = 0;
+
+  if (ECOFF_DEBUGGING)
+    {
+      get_number ();
+      s_file (0);
+    }
+  else
+    {
+      char *filename;
+
+      filename = dwarf2_directive_filename ();
+
+      /* Versions of GCC up to 3.1 start files with a ".file"
+         directive even for stabs output.  Make sure that this
+         ".file" is handled.  */
+      if (filename != NULL && !first_file_directive)
+	{
+	  new_logical_line (filename, -1);
+	  s_file_string (filename);
+	}
+      first_file_directive = 1;
+    }
+}
+
+/* The .loc directive, implying DWARF-2.  */
+
+static void
+s_nanomips_loc (int x ATTRIBUTE_UNUSED)
+{
+  if (!ECOFF_DEBUGGING)
+    dwarf2_directive_loc (0);
+}
+
+/* The .linkrelax directive.  */
+
+static void
+s_linkrelax (int x ATTRIBUTE_UNUSED)
+{
+  linkrelax = true;
+  nanomips_linkrelax_p = true;
+}
+
+/* The .end directive.  */
+
+static void
+s_nanomips_end (int x ATTRIBUTE_UNUSED)
+{
+  symbolS *p;
+
+  if (!is_end_of_stmt (*input_line_pointer))
+    {
+      p = get_symbol ();
+      demand_empty_rest_of_line ();
+    }
+  else
+    p = NULL;
+
+  if ((bfd_section_flags (now_seg) & SEC_CODE) == 0)
+    as_warn (_(".end not in text section"));
+
+  if (!cur_proc_ptr)
+    {
+      as_warn (_(".end directive without a preceding .ent directive"));
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+  if (p != NULL)
+    {
+      gas_assert (S_GET_NAME (p));
+      if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->func_sym)))
+	as_warn (_(".end symbol does not match .ent symbol"));
+
+      if (debug_type == DEBUG_STABS)
+	stabs_generate_asm_endfunc (S_GET_NAME (p), S_GET_NAME (p));
+    }
+  else
+    as_warn (_(".end directive missing or unknown symbol"));
+
+  /* Create an expression to calculate the size of the function.  */
+  if (p && cur_proc_ptr)
+    {
+      OBJ_SYMFIELD_TYPE *obj = symbol_get_obj (p);
+      expressionS *exp = notes_alloc (sizeof (*exp));
+
+      if (!nanomips_opts.no_balc_stubs && stubg_now != NULL)
+	stubgroup_wane ();
+
+      obj->size = exp;
+      exp->X_op = O_subtract;
+      exp->X_add_symbol = symbol_temp_new_now ();
+      exp->X_op_symbol = p;
+      exp->X_add_number = 0;
+
+      cur_proc_ptr->func_end_sym = exp->X_add_symbol;
+    }
+  cur_proc_ptr = NULL;
+}
+
+/* The .aent and .ent directives.  */
+
+static void
+s_nanomips_ent (int aent)
+{
+  symbolS *symbolP;
+
+  symbolP = get_symbol ();
+  if (*input_line_pointer == ',')
+    ++input_line_pointer;
+  SKIP_WHITESPACE ();
+  if (ISDIGIT (*input_line_pointer) || *input_line_pointer == '-')
+    get_number ();
+
+  if ((bfd_section_flags (now_seg) & SEC_CODE) == 0)
+    as_warn (_(".ent or .aent not in text section"));
+
+  if (!aent && cur_proc_ptr)
+    as_warn (_("missing .end"));
+
+  if (!aent)
+    {
+      cur_proc_ptr = &cur_proc;
+      memset (cur_proc_ptr, '\0', sizeof (procS));
+
+      cur_proc_ptr->func_sym = symbolP;
+
+      ++numprocs;
+
+      if (debug_type == DEBUG_STABS)
+	stabs_generate_asm_func (S_GET_NAME (symbolP), S_GET_NAME (symbolP));
+    }
+
+  symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION;
+
+  if (stub_funcless_mode && !nanomips_opts.no_balc_stubs && stubg_now != NULL)
+    {
+      stub_funcless_mode = false;
+      stubgroup_wane ();
+    }
+
+  stubgroup_new (now_seg);
+  demand_empty_rest_of_line ();
+}
+
+static void
+s_nanomips_frame (int ignore ATTRIBUTE_UNUSED)
+{
+  if (ECOFF_DEBUGGING)
+    s_ignore (ignore);
+  else
+    {
+      long val;
+
+      if (cur_proc_ptr ==  NULL)
+	{
+	  as_warn (_(".frame outside of .ent"));
+	  demand_empty_rest_of_line ();
+	  return;
+	}
+
+      cur_proc_ptr->frame_reg = tc_get_register (1);
+
+      SKIP_WHITESPACE ();
+      if (*input_line_pointer++ != ','
+	  || get_absolute_expression_and_terminator (&val) != ',')
+	{
+	  as_warn (_("bad .frame directive"));
+	  --input_line_pointer;
+	  demand_empty_rest_of_line ();
+	  return;
+	}
+
+      cur_proc_ptr->frame_offset = val;
+      cur_proc_ptr->pc_reg = tc_get_register (0);
+
+      demand_empty_rest_of_line ();
+    }
+}
+
+/* The .fmask and .mask directives.   */
+
+static void
+s_nanomips_mask (int reg_type)
+{
+  if (ECOFF_DEBUGGING)
+    s_ignore (reg_type);
+  else
+    {
+      long mask, off;
+
+      if (cur_proc_ptr == NULL)
+	{
+	  as_warn (_(".mask/.fmask outside of .ent"));
+	  demand_empty_rest_of_line ();
+	  return;
+	}
+
+      if (get_absolute_expression_and_terminator (&mask) != ',')
+	{
+	  as_warn (_("bad .mask/.fmask directive"));
+	  --input_line_pointer;
+	  demand_empty_rest_of_line ();
+	  return;
+	}
+
+      off = get_absolute_expression ();
+
+      if (reg_type == 'F')
+	{
+	  cur_proc_ptr->fpreg_mask = mask;
+	  cur_proc_ptr->fpreg_offset = off;
+	}
+      else
+	{
+	  cur_proc_ptr->reg_mask = mask;
+	  cur_proc_ptr->reg_offset = off;
+	}
+
+      demand_empty_rest_of_line ();
+    }
+}
+
+/* A table describing all the processors gas knows about.  Names are
+   matched in the order listed.
+
+   To ease comparison, please keep this table in the same order as
+   gcc's nanomips_cpu_info_table[].  */
+static const struct nanomips_cpu_info nanomips_cpu_info_table[] = {
+  /* Entries for generic ISAs */
+  { "32r6",	NANOMIPS_CPU_IS_ISA,
+    ASE_xNMS, ISA_NANOMIPS32R6, CPU_NANOMIPS32R6 },
+  { "32r6s",	NANOMIPS_CPU_IS_ISA,
+    0,		ISA_NANOMIPS32R6, CPU_NANOMIPS32R6 },
+  { "64r6",    	NANOMIPS_CPU_IS_ISA,
+    ASE_xNMS, ISA_NANOMIPS64R6, CPU_NANOMIPS64R6 },
+
+  /* End marker */
+  { NULL, 0, 0, 0, 0 }
+};
+
+/* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied
+   CPU name.  */
+
+static bool
+nanomips_matching_cpu_name_p (const char *canonical, const char *given)
+{
+  while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
+    given++, canonical++;
+
+  return (*given == 0 && *canonical == 0);
+}
+
+/* Parse an option that takes the name of a processor as its argument.
+   OPTION is the name of the option and CPU_STRING is the argument.
+   Return the corresponding processor enumeration if the CPU_STRING is
+   recognized, otherwise report an error and return null.
+
+   A similar function exists in GCC.  */
+
+static const struct nanomips_cpu_info *
+nanomips_parse_cpu (const char *option, const char *cpu_string)
+{
+  const struct nanomips_cpu_info *p;
+
+  if (strcasecmp (cpu_string, "from-abi") == 0)
+    {
+      if (ABI_NEEDS_32BIT_REGS (nanomips_abi))
+	return nanomips_cpu_info_from_isa (ISA_NANOMIPS32R6, false);
+
+      if (ABI_NEEDS_64BIT_REGS (nanomips_abi))
+	return nanomips_cpu_info_from_isa (ISA_NANOMIPS64R6, false);
+
+      return nanomips_cpu_info_from_isa (NANOMIPS_DEFAULT_64BIT
+					 ? ISA_NANOMIPS64R6
+					 : ISA_NANOMIPS32R6,
+					 false);
+    }
+
+  /* 'default' has traditionally been a no-op.  Probably not very useful.  */
+  if (strcasecmp (cpu_string, "default") == 0)
+    return 0;
+
+  for (p = nanomips_cpu_info_table; p->name != 0; p++)
+    if (nanomips_matching_cpu_name_p (p->name, cpu_string))
+      return p;
+
+  if (option != NULL)
+    as_bad (_("bad value (%s) for %s"), cpu_string, option);
+  return 0;
+}
+
+/* Return the canonical processor information for ISA (a member of the
+   ISA_NANOMIPS* enumeration).  */
+
+static const struct nanomips_cpu_info *
+nanomips_cpu_info_from_isa (int isa, bool subset)
+{
+  int i;
+
+  for (i = 0; nanomips_cpu_info_table[i].name != NULL; i++)
+    if ((nanomips_cpu_info_table[i].flags & NANOMIPS_CPU_IS_ISA)
+	&& isa == nanomips_cpu_info_table[i].isa)
+      {
+	if (subset && (nanomips_cpu_info_table[i].ase & ASE_xNMS) != 0)
+	  i++;
+
+	return (&nanomips_cpu_info_table[i]);
+      }
+  return NULL;
+}
+
+static const struct nanomips_cpu_info *
+nanomips_cpu_info_from_arch (int arch)
+{
+  int i;
+  for (i = 0; nanomips_cpu_info_table[i].name != NULL; i++)
+    if (arch == nanomips_cpu_info_table[i].cpu)
+      return (&nanomips_cpu_info_table[i]);
+
+  return NULL;
+}
+
+static void
+show (FILE *stream, const char *string, int *col_p, int *first_p)
+{
+  if (*first_p)
+    {
+      fprintf (stream, "%24s", "");
+      *col_p = 24;
+    }
+  else
+    {
+      fprintf (stream, ", ");
+      *col_p += 2;
+    }
+
+  if (*col_p + strlen (string) > 72)
+    {
+      fprintf (stream, "\n%24s", "");
+      *col_p = 24;
+    }
+
+  fprintf (stream, "%s", string);
+  *col_p += strlen (string);
+
+  *first_p = 0;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+  int column, first;
+  size_t i;
+
+  fprintf (stream, _("\
+nanoMIPS options:\n\
+-EB			generate big endian output\n\
+-EL			generate little endian output\n\
+-g, -g2			do not remove unneeded NOPs or swap branches\n\
+-G NUM			allow referencing objects up to NUM bytes\n\
+			implicitly with the gp register [default 8]\n"));
+  fprintf (stream, _("\
+-march=CPU/-mtune=CPU	generate code/schedule for CPU, where CPU is one of:\n"));
+
+  first = 1;
+
+  for (i = 0; nanomips_cpu_info_table[i].name != NULL; i++)
+    show (stream, nanomips_cpu_info_table[i].name, &column, &first);
+  show (stream, "from-abi", &column, &first);
+  fputc ('\n', stream);
+  fprintf (stream, _("\
+-m32			create p32 ABI object file (default)\n\
+-m64			create p64 ABI object file\n"));
+  fprintf (stream, _("\
+-mcmodel=MMODEL		Generate code for a specific memory model,\n\
+			where MMODEL is one of: auto, medium, large\n\
+-m[no-]pic		[dis]allow Position Independent Code generation\n\
+-m[no-]PIC		[dis]allow large model Position Independent Code generation\n\
+-m[no-]pid		[dis]allow Position Independent Data access\n\
+-m[no-]pcrel		[dis]allow PC-relative address calculations\n"));
+
+  fprintf (stream, _("\
+-minsn32		only generate 32-bit nanoMIPS instructions\n\
+-mno-insn32		generate all nanoMIPS instructions\n\
+-mhard-float		allow floating-point instructions\n\
+-msoft-float		do not allow floating-point instructions\n\
+-msingle-float		only allow 32-bit floating-point operations\n\
+-mdouble-float		allow 32-bit and 64-bit floating-point operations\n\
+--[no-]construct-floats	[dis]allow floating point values to be constructed\n"));
+
+  fprintf (stream, _("\
+-mmt			generate MT instructions\n\
+-mno-mt			do not generate MT instructions\n"));
+  fprintf (stream, _("\
+-mvirt			generate Virtualization instructions\n\
+-mno-virt		do not generate Virtualization instructions\n"));
+  fprintf (stream, _("\
+-m[no-]balc-stubs	enable/disable out-of-range call optimization\n\
+			through trampoline stubs\n\
+-m[no-]legacyregs	[dis]allow mumerical register formats\n"));
+  fprintf (stream, _("\
+--linkrelax		allow linker relaxations\n\
+--trap, --no-break	trap exception on div by 0 and mult overflow\n\
+--break, --no-trap	break exception on div by 0 and mult overflow\n"));
+  fputc ('\n', stream);
+}
+
+int
+nanomips_dwarf2_addr_size (void)
+{
+  if (HAVE_64BIT_OBJECTS)
+    return 8;
+  else
+    return 4;
+}
+
+/* Standard calling conventions leave the CFA at SP on entry.  */
+void
+nanomips_cfi_frame_initial_instructions (void)
+{
+  cfi_add_CFA_def_cfa_register (SP);
+}
+
+int
+tc_nanomips_regname_to_dw2regnum (char *regname)
+{
+  unsigned int regnum = -1;
+  unsigned int reg;
+
+  if (reg_lookup (&regname, RTYPE_GP | RTYPE_NUM, &reg))
+    regnum = reg;
+
+  return regnum;
+}
+
+void
+nanomips_md_finish (void)
+{
+  int fpabi = Val_GNU_NANOMIPS_ABI_FP_ANY;
+
+  nanomips_flush_pending_output ();
+  if (cur_proc_ptr)
+    {
+      as_warn (_("missing .end at end of assembly"));
+      if (stubg_now != NULL)
+	stubgroup_wane ();
+    }
+
+  if (stub_funcless_mode && !nanomips_opts.no_balc_stubs && stubg_now != NULL)
+    {
+      stub_funcless_mode = false;
+      stubgroup_wane ();
+    }
+
+  /* Just in case no code was emitted, do the consistency check.  */
+  file_check_options ();
+}
+
+/* Map a data slot size to its nanoMIPS relocation.  */
+static bfd_reloc_code_real_type
+nanomips_bytes_to_reloc (int nbytes, bool signed_val)
+{
+  bfd_reloc_code_real_type rel = BFD_RELOC_NONE;
+  switch (nbytes)
+    {
+    case 1:
+      if (signed_val)
+	rel = BFD_RELOC_NANOMIPS_SIGNED_8;
+      else
+	rel = BFD_RELOC_NANOMIPS_UNSIGNED_8;
+      break;
+    case 2:
+      if (signed_val)
+	rel = BFD_RELOC_NANOMIPS_SIGNED_16;
+      else
+	rel = BFD_RELOC_NANOMIPS_UNSIGNED_16;
+      break;
+    case 4:
+      rel = BFD_RELOC_32;
+      break;
+    case 8:
+      rel = BFD_RELOC_64;
+      break;
+    case 16:
+      break;
+    default:
+      as_bad (_("unsupported BFD relocation size %u"), nbytes);
+      rel = BFD_RELOC_NONE;
+    }
+  return rel;
+}
+
+/* Check if the expression is a simple difference of local labels in
+   code sections.  */
+
+static bool
+simple_diff_expr_p (expressionS *exp)
+{
+  return (exp->X_op == O_subtract
+	  && exp->X_add_symbol
+	  && exp->X_op_symbol
+	  && exp->X_add_number == 0
+	  && symbol_constant_p (exp->X_add_symbol)
+	  && symbol_constant_p (exp->X_op_symbol));
+}
+
+/* Parse a .byte, .word, etc. expression.
+   Values for the status register are specified with %st(label).
+   `label' will be right shifted by 2.  */
+
+bfd_reloc_code_real_type
+nanomips_parse_cons_expression (expressionS *exp, unsigned int nbytes)
+{
+  bfd_reloc_code_real_type rel = BFD_RELOC_NONE;
+  int repeat = 1;
+  expression (exp);
+
+  if (*input_line_pointer == ':')
+    {
+      expressionS count;
+      ++input_line_pointer;
+      expression (&count);
+      if (count.X_op != O_constant || count.X_add_number <= 0)
+	{
+	  as_warn (_("unresolvable or nonpositive repeat count; using 1"));
+	  return BFD_RELOC_NONE;
+	}
+
+      /* The cons function is going to output this expression once.  So we
+         output it count - 1 times.  */
+      repeat = count.X_add_number;
+    }
+
+  /* Sometimes subtract expressions containing data labels are deemed
+     not-constant by general parse code because they fall in distinct
+     frags.  These wouldn't be affected by linker relaxation which only
+     touches code, so we skip relocations for those expressions and hope
+     that this will not break any valid case.  */
+  if (linkrelax
+      && exp->X_op != O_constant
+      && (exp->X_op != O_subtract
+	  || ((S_GET_SEGMENT (exp->X_add_symbol))->flags & SEC_CODE) != 0
+	  || ((S_GET_SEGMENT (exp->X_op_symbol))->flags & SEC_CODE) != 0))
+    {
+      expressionS *iter;
+      bool done = false;
+
+      /* By-pass explicit reloc generation for simple label differences to
+	 allow the general framework to evaluate whether the difference is
+	 fixed or link-time variable.  */
+      if (simple_diff_expr_p (exp))
+	return rel;
+
+      rel = nanomips_bytes_to_reloc (nbytes, sign_cons);
+
+      /* Check if we can actually handle this expression.  */
+      iter = exp;
+      while (iter->X_add_symbol != NULL && !done)
+	{
+	  switch (iter->X_op)
+	    {
+	    case O_subtract:
+	      /* exp := symbol - exp */
+	      if (!symbol_constant_p (iter->X_add_symbol))
+		{
+		  as_bad ("Expression too complex to relocate!");
+		  rel = BFD_RELOC_NONE;
+		}
+	      iter = symbol_get_value_expression (iter->X_op_symbol);
+	      break;
+
+	    case O_right_shift:
+	      /* exp := exp >> const */
+	      if (!symbol_constant_p (iter->X_op_symbol)
+		  || S_GET_SEGMENT (iter->X_op_symbol) != absolute_section
+		  || iter->X_add_number != 0)
+		{
+		  as_bad ("Expression too complex to relocate!");
+		  rel = BFD_RELOC_NONE;
+		}
+	      /* Fall through.  */
+
+	    case O_symbol:
+	      /* exp := symbol [ + offset ] */
+	      iter = symbol_get_value_expression (iter->X_add_symbol);
+	      break;
+
+	    default:
+	      /* If the expression contains even one operator that we can't
+	         map to relocations, then don't bother.  */
+	      rel = BFD_RELOC_NONE;
+	      done = true;
+	      break;
+	    }
+	}
+    }
+  while (repeat-- > 1)
+    emit_expr_with_reloc (exp, nbytes, rel);
+
+  return rel;
+}
+
+void
+nanomips_cons_fix_new (fragS *frag, int where, int nbytes, expressionS *exp,
+		       bfd_reloc_code_real_type r)
+{
+  if (r != BFD_RELOC_NONE)
+    {
+      /* Assuming 64 entries will be enough.  */
+      struct s_rentry
+      {
+	symbolS *sym;
+	offsetT addend;
+	bfd_reloc_code_real_type reloc;
+      } rentry[64];
+      size_t numrelocs = 0;
+      bool comp_p;
+      bfd_reloc_code_real_type sym_reloc = (HAVE_64BIT_SYMBOLS
+					    ? BFD_RELOC_64 : BFD_RELOC_32);
+      expressionS *iter = exp;
+
+      /* Provisionally fill the outer-most entry.  */
+      rentry[numrelocs].sym = NULL;
+      rentry[numrelocs].addend = 0;
+      rentry[numrelocs].reloc = r;
+
+      /* Dealing with composite relocation.  */
+      comp_p = (iter->X_op != O_symbol);
+
+      if (iter->X_op == O_right_shift && r != sym_reloc)
+	/* Finalize the outer entry now.  */
+	numrelocs++;
+      else
+	/* Defer the outer entry for further combination.  */
+	sym_reloc = r;
+
+      while (iter->X_add_symbol != NULL)
+	{
+
+	  if (numrelocs + 1 >= ARRAY_SIZE (rentry))
+	    {
+	      as_bad ("Expression too complex to relocate!");
+	      break;
+	    }
+
+	  switch (iter->X_op)
+	    {
+	    case O_subtract:
+	      rentry[numrelocs].sym = iter->X_add_symbol;
+	      rentry[numrelocs].addend = 0;
+	      rentry[numrelocs++].reloc = sym_reloc;
+	      rentry[numrelocs].sym = iter->X_op_symbol;
+	      rentry[numrelocs].addend = iter->X_add_number;
+	      sym_reloc = BFD_RELOC_NANOMIPS_NEG;
+	      break;
+
+	    case O_right_shift:
+	      {
+		int scount;
+		gas_assert (symbol_constant_p (iter->X_op_symbol));
+		scount = S_GET_VALUE (iter->X_op_symbol);
+
+		/* Iterate over the shift count and track the
+		   required number of relocs.  */
+		while (scount-- > 0)
+		  {
+		    rentry[numrelocs].sym = NULL;
+		    rentry[numrelocs].addend = 0;
+		    rentry[numrelocs++].reloc = BFD_RELOC_NANOMIPS_ASHIFTR_1;
+		  }
+
+		/* We can roll the a shift step in to the expression
+		   if it is just a simple symbol reference. This just
+		   sets things up for the last-level block below.  */
+		if (S_GET_VALUE (iter->X_op_symbol) != 0
+		    && sym_reloc != BFD_RELOC_NANOMIPS_NEG)
+		  {
+		    sym_reloc = BFD_RELOC_NANOMIPS_ASHIFTR_1;
+		    numrelocs--;
+		  }
+	      }
+	      /* Fall through */
+
+	    case O_symbol:
+	      rentry[numrelocs].sym = iter->X_add_symbol;
+	      if (sym_reloc == BFD_RELOC_NANOMIPS_NEG)
+		/* S1 - (S2 + A) => S1 + (-S2 + -A) */
+		rentry[numrelocs].addend = -iter->X_add_number;
+	      else
+		rentry[numrelocs].addend = iter->X_add_number;
+	      break;
+
+	    default:
+	      as_bad ("Expression too complex to relocate!");
+	      break;
+	    }
+
+	  iter = symbol_get_value_expression (rentry[numrelocs].sym);
+
+	  /* No more levels of nesting?  */
+	  if (iter->X_add_symbol == NULL)
+	    rentry[numrelocs++].reloc = sym_reloc;
+	}
+
+      /* Step through the list in reverse and create relocations.  */
+      while (numrelocs > 0)
+	{
+	  fixS *fixP;
+	  --numrelocs;
+	  fixP = fix_new (frag, where, nbytes, rentry[numrelocs].sym,
+			  rentry[numrelocs].addend, 0,
+			  rentry[numrelocs].reloc);
+
+	  if (comp_p)
+	    fixP->fx_tcbit = 1;
+	}
+    }
+  else
+    {
+      reloc_howto_type *reloc_howto;
+      int size = nbytes;
+
+      if (r != TC_PARSE_CONS_RETURN_NONE)
+	{
+	  reloc_howto = bfd_reloc_type_lookup (stdoutput, r);
+	  size = bfd_get_reloc_size (reloc_howto);
+
+	  if (size > nbytes)
+	    {
+	      as_bad (_("%s relocations do not fit in %u bytes\n"),
+		      reloc_howto->name, nbytes);
+	      return;
+	    }
+	}
+      else
+	switch (size)
+	  {
+	  case 1:
+	    r = BFD_RELOC_8;
+	    break;
+	  case 2:
+	    r = BFD_RELOC_16;
+	    break;
+	  case 3:
+	    r = BFD_RELOC_24;
+	    break;
+	  case 4:
+	    r = BFD_RELOC_32;
+	    break;
+	  case 8:
+	    r = BFD_RELOC_64;
+	    break;
+	  default:
+	    as_bad (_("unsupported BFD relocation size %u"), size);
+	    return;
+	  }
+
+      fix_new_exp (frag, where, size, exp, 0, r);
+    }
+}
+
+/* Check if subtraction of 2 symbols can be fully evaluated at assembly.  */
+static bool
+nanomips_allow_local_subtract_symbols (symbolS *left, symbolS *right,
+				       bool pre_relax)
+{
+  /* Check for intervening fixups between symbols within the same frag to
+     determine if relaxation could affect the difference.  */
+  if (symbol_get_frag (left) == symbol_get_frag (right))
+    {
+      fixS *fixP;
+      fragS *fragP;
+      bfd_vma low, high;
+      struct reloc_list *rp;
+
+      fragP = symbol_get_frag (left);
+
+      high = S_GET_VALUE (left);
+      low = S_GET_VALUE (right);
+
+      /* Swap if necessary.  */
+      if (high < low)
+	{
+	  high = high + low;
+	  low = high - low;
+	  high = high - low;
+	}
+
+      /* Start with the first fix-up in this frag.  */
+      fixP = fragP->tc_frag_data;
+
+      /* Now look for fixups within the range.  */
+      while (fixP != NULL
+	     && fixP->fx_where < low
+	     && fixP->fx_where < high)
+	fixP = fixP->fx_next;
+
+      /* Fixups within the range.  */
+      if (fixP != NULL && fixP->fx_where < high)
+	return false;
+
+      /* Too early for checking explicit relocations, assume the worst.  */
+      if (pre_relax)
+	return false;
+
+      rp = reloc_list;
+      while (rp != NULL)
+	{
+	  if (rp->u.a.offset_sym)
+	    {
+	      if (S_GET_VALUE (rp->u.a.offset_sym) >= low
+		  && S_GET_VALUE (rp->u.a.offset_sym) < high)
+		return false;
+	      else if (S_GET_VALUE (rp->u.a.offset_sym) > high)
+		break;
+	    }
+	  rp = rp->next;
+	}
+      /* No fixup within the range.  */
+      return true;
+    }
+
+  /* We have to assume that there may be instructions between the
+     two symbols and that relaxation may increase the distance between
+     them.  */
+  return false;
+}
+
+/* md_allow_local_subtract interface
+   Check if a subtraction expression can be fully evaluated.  */
+bool
+nanomips_allow_local_subtract (expressionS *left, expressionS *right,
+			       segT section)
+{
+  /* If the symbols are not in a code section then they are OK.  */
+  if ((section->flags & SEC_CODE) == 0)
+    return true;
+
+  if ((left->X_add_symbol == right->X_add_symbol))
+    return true;
+
+  if (!linkrelax)
+    return true;
+
+  /* For lable-difference relocations in .debug_info sections, we
+     want to force a relocation, even if the difference is link-time
+     invariant so that the linker has the opportunity to zero-out
+     AT_high_pc expressions when the corresponding sections get
+     garbage-collected.  Spurious non-zero AT_high_pc fields of
+     garbage-collected sections can trip-up GDB source disassembly.  */
+  if (strcmp (now_seg->name, ".debug_info") == 0)
+    return false;
+  else
+    return nanomips_allow_local_subtract_symbols (left->X_add_symbol,
+						  right->X_add_symbol,
+						  true);
+}
+
+/* Create relocations for alignment directives.  */
+void
+nanomips_md_do_align (int n, const char *fill, int fill_length, int max_fill)
+{
+  unsigned int fill_value = 0;
+
+  if (nanomips_linkrelax_p
+      && (bfd_section_flags (now_seg) & SEC_CODE) != 0
+      && n > 1)
+    {
+      fill_value = bfd_get_bits (fill, fill_length * 8, target_big_endian);
+
+      if (fill == NULL && subseg_text_p (now_seg))
+	frag_grow (MAX_MEM_FOR_RS_ALIGN_CODE(p2align, max));
+      else if (fill_length > 1)
+	frag_grow (fill_length);
+      else
+	frag_grow (1);
+
+      create_align_relocs (frag_now, n, fill_value, fill_length, max_fill);
+    }
+}
+
+/* TC_VALIDATE_FIX_SUB hook.  */
+
+bool
+nanomips_validate_fix_sub (fixS *fix)
+{
+  segT add_symbol_segment, sub_symbol_segment;
+
+  /* The difference of two symbols should be resolved by the assembler when
+     linkrelax is not set.  If the linker may relax the section containing
+     the symbols, then composite relocations are generated so that the
+     linker knows how to adjust the difference value.  */
+  if (!linkrelax || fix->fx_addsy == NULL)
+    return 0;
+
+  /* Make sure both symbols are in the same segment, and that segment is
+     "normal" and relaxable.  If the segment is not "normal", then the
+     fix is not valid.  If the segment is not "relaxable", then the fix
+     should have been handled earlier.  */
+  add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
+  if (!SEG_NORMAL (add_symbol_segment)
+      || (add_symbol_segment->flags & SEC_DEBUGGING) != 0)
+    return 0;
+
+  sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
+
+  if (sub_symbol_segment != add_symbol_segment)
+    return 0;
+  else
+    {
+      /* For lable-difference relocations in .debug_info sections, we
+	 want to force a relocation, even if the difference is link-time
+	 invariant so that the linker has the opportunity to zero-out
+	 AT_high_pc expressions when the corresponding sections get
+	 garbage-collected.  Spurious non-zero AT_high_pc fields for
+	 garbage-collected sections can trip-up GDB source disassembly.  */
+      if (frag_in_section_p (fix->fx_frag, ".debug_info"))
+	return 1;
+      else
+	return (!nanomips_allow_local_subtract_symbols (fix->fx_addsy,
+							fix->fx_subsy,
+							false));
+    }
+}
+
+/* TC_EH_FRAME_ESTIMATE_SIZE_BEFORE_RELAX hook.
+   Check if the rs_cfa frag could be affected by linker relaxation and
+   choose a suitable size estimation heuristic. If not, fall back to
+   the generic estimation logic.
+
+   The heuristic is currently hard-coded to 4 bytes.  */
+int
+nanomips_eh_frame_estimate_size_before_relax (fragS *frag)
+{
+  expressionS *exp = symbol_get_value_expression (frag->fr_symbol);
+
+  if (linkrelax
+      && !nanomips_allow_local_subtract_symbols (exp->X_add_symbol,
+						 exp->X_op_symbol,
+						 false))
+    {
+      int ret;
+      gas_assert ((frag->fr_subtype >> 3) > 0);
+      ret = 4;
+      frag->fr_subtype = (frag->fr_subtype & ~7) | ret;
+
+      return ret;
+    }
+  else
+    return eh_frame_estimate_size_before_relax (frag);
+}
+
+/* TC_EH_FRAME_RELAX_FRAG hook  */
+int
+nanomips_eh_frame_relax_frag (fragS *frag)
+{
+  int oldsize, newsize;
+
+  oldsize = frag->fr_subtype & 7;
+  newsize = nanomips_eh_frame_estimate_size_before_relax (frag);
+  return newsize - oldsize;
+}
+
+/* TC_EH_FRAME_CONVERT_FRAG hook
+   Check if the rs_cfa frag could be affected by linker relaxation and
+   generate relocations to fix-up the value after relaxation.
+   Then fall-back to the generic convert_frag.  */
+void
+nanomips_eh_frame_convert_frag (fragS *frag)
+{
+  expressionS *exp = symbol_get_value_expression (frag->fr_symbol);
+
+  if (linkrelax && !nanomips_allow_local_subtract_symbols (exp->X_add_symbol,
+							   exp->X_op_symbol,
+							   false))
+    {
+      /* Generate label-difference relocations for this frag.  */
+      int size = nanomips_eh_frame_estimate_size_before_relax (frag);
+      bfd_reloc_code_real_type r = nanomips_bytes_to_reloc (size, false);
+
+      nanomips_cons_fix_new (frag, frag->fr_fix, size, exp, r);
+    }
+  eh_frame_convert_frag (frag);
+}
+
+/* Hook to run before relaxation.  */
+
+void nanomips_pre_relax_hook (void)
+{
+  struct reloc_list *iter, *prev, *copy, *next;
+
+  /* Make a private copy of the reloc_list and reverse it,
+     all in one iteration.  */
+  iter = reloc_list;
+  prev = copy = NULL;
+
+  while (iter != NULL)
+    {
+      if (iter->u.a.howto->type != R_NANOMIPS_FRAME_REG)
+	{
+	  reloc_list_copy = XNEW (struct reloc_list);
+	  *reloc_list_copy = *iter;
+	  reloc_list_copy->next = prev;
+	  prev = reloc_list_copy;
+	}
+      iter = iter->next;
+    }
+
+  /* Once reversed, reloc_list is mostly sorted, on account of how
+     the compiler emits explicit relocations.  Optimized lookup
+     depends on the order of .relocs in the source matching the
+     order of offset symbols for those relocations.  We either confirm
+     that the order matches or fall-back to a slower lookup.  */
+  iter = reloc_list_copy;
+  if (iter != NULL)
+    next = iter->next;
+
+  while (iter != NULL && next != NULL)
+    {
+      fragS *fiter = symbol_get_frag (iter->u.a.offset_sym);
+      fragS *fnext = symbol_get_frag (next->u.a.offset_sym);
+
+      if (fiter == fnext)
+	{
+	  if (S_GET_VALUE (iter->u.a.offset_sym)
+	      > S_GET_VALUE (next->u.a.offset_sym))
+	    {
+	      reloc_ordered = false;
+	      break;
+	    }
+	}
+      else
+	{
+	  while (fiter && fiter != fnext)
+	    fiter = fiter->fr_next;
+	  if (fiter != fnext)
+	    {
+	      reloc_ordered = false;
+	      break;
+	    }
+	}
+      iter = next;
+      next = iter->next;
+    }
+}
+
+/* Hook to run after relaxation.  */
+
+void nanomips_post_relax_hook (void)
+{
+  reloc_list_iter = reloc_list_copy;
+}
diff --git a/gas/config/tc-nanomips.h b/gas/config/tc-nanomips.h
new file mode 100644
index 00000000000..518bf8a8818
--- /dev/null
+++ b/gas/config/tc-nanomips.h
@@ -0,0 +1,187 @@
+/* tc-nanomips.h -- header file for tc-nanomips.c.
+   Copyright (C) 2018-2026 Free Software Foundation, CIP United Inc.
+   Contributed by MIPS Tech LLC.
+   Written by Faraz Shahbazker <[email protected]>
+
+   This file is part of GAS.
+
+   GAS is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3, or (at your option)
+   any later version.
+
+   GAS is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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, 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
+
+#ifndef TC_NANOMIPS
+#define TC_NANOMIPS
+
+struct frag;
+struct expressionS;
+
+/* Default to little endian.  */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN		0
+#endif
+
+#define TARGET_ARCH bfd_arch_nanomips
+
+#define WORKING_DOT_WORD	1
+#define OLD_FLOAT_READS
+#define RELOC_EXPANSION_POSSIBLE
+#define MAX_RELOC_EXPANSION 2
+#define LOCAL_LABELS_FB 1
+
+#define TC_ADDRESS_BYTES nanomips_address_bytes
+extern int nanomips_address_bytes (void);
+
+#define md_relax_frag(segment, fragp, stretch) \
+  nanomips_relax_frag(segment, fragp, stretch)
+extern int nanomips_relax_frag (asection *, struct frag *, long);
+
+#define md_undefined_symbol(name)	(0)
+#define md_operand(x)
+
+extern void nanomips_handle_align (struct frag *);
+#define HANDLE_ALIGN(sec, fragp)  nanomips_handle_align (fragp)
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE(p2align, max)  (3 + 4)
+
+struct insn_label_list;
+#define TC_SEGMENT_INFO_TYPE struct insn_label_list *
+
+/* The endianness of the target format may change based on command
+   line arguments.  */
+#define TARGET_FORMAT nanomips_target_format()
+extern const char *nanomips_target_format (void);
+
+extern int tc_get_register (int);
+
+#define md_after_parse_args() nanomips_after_parse_args()
+extern void nanomips_after_parse_args (void);
+
+#define tc_frob_label(sym) nanomips_define_label (sym)
+extern void nanomips_define_label (symbolS *);
+
+#define tc_frob_file_before_adjust() nanomips_frob_file_before_adjust ()
+extern void nanomips_frob_file_before_adjust (void);
+
+#define tc_fix_adjustable(fixp) nanomips_fix_adjustable (fixp)
+extern int nanomips_fix_adjustable (struct fix *);
+
+/* Values passed to md_apply_fix don't include symbol values.  */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define TC_FORCE_RELOCATION(FIX) nanomips_force_relocation (FIX)
+extern int nanomips_force_relocation (struct fix *);
+
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEG) \
+  (! SEG_NORMAL (SEG) || nanomips_force_relocation (FIX))
+
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) \
+  (((SEG)->flags & SEC_CODE) != 0 && nanomips_force_relocation (FIX))
+
+#define elf_tc_final_processing nanomips_elf_final_processing
+extern void nanomips_elf_final_processing (void);
+
+extern void nanomips_md_finish (void);
+#define md_finish()	nanomips_md_finish()
+
+extern void nanomips_pop_insert (void);
+#define md_pop_insert()		nanomips_pop_insert()
+
+extern void nanomips_flush_pending_output (void);
+#define md_flush_pending_output nanomips_flush_pending_output
+
+extern void nanomips_enable_auto_align (void);
+#define md_elf_section_change_hook()	nanomips_enable_auto_align()
+
+extern int nanomips_dwarf2_addr_size (void);
+#define DWARF2_ADDR_SIZE(bfd) nanomips_dwarf2_addr_size ()
+#define DWARF2_FDE_RELOC_SIZE nanomips_dwarf2_addr_size ()
+
+#define TARGET_USE_CFIPOP 1
+
+#define tc_cfi_frame_initial_instructions nanomips_cfi_frame_initial_instructions
+extern void nanomips_cfi_frame_initial_instructions (void);
+
+#define tc_regname_to_dw2regnum tc_nanomips_regname_to_dw2regnum
+extern int tc_nanomips_regname_to_dw2regnum (char *regname);
+
+#define DWARF2_DEFAULT_RETURN_COLUMN 31
+#define DWARF2_CIE_DATA_ALIGNMENT (-4)
+
+/* Definitions needed to parse cons in target-specific manner.  */
+#define TC_PARSE_CONS_RETURN_TYPE bfd_reloc_code_real_type
+#define TC_PARSE_CONS_RETURN_NONE BFD_RELOC_NONE
+
+/* Parse a cons expression to determined what kind of relocations
+   may be needed to represent it.  */
+extern bfd_reloc_code_real_type
+nanomips_parse_cons_expression (struct expressionS *, unsigned);
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) \
+  nanomips_parse_cons_expression (EXP, NBYTES)
+
+/* Walk a cons expression tree and create the relocations needed
+   to represent the operation.  */
+extern void nanomips_cons_fix_new (struct frag *, int, int,
+				   struct expressionS *,
+				   const bfd_reloc_code_real_type);
+#define TC_CONS_FIX_NEW(FRAG, WHERE, NBYTES, EXP, RELOC)	\
+  nanomips_cons_fix_new (FRAG, WHERE, NBYTES, EXP, RELOC)
+
+/* Determine what kind of difference expressions must be
+   evaluated by assembler.  */
+extern bool nanomips_allow_local_subtract (expressionS *,
+						  expressionS *, segT);
+#define md_allow_local_subtract(lhs,rhs,sect)	\
+  nanomips_allow_local_subtract (lhs, rhs, sect)
+
+#define TC_FRAG_TYPE struct fix *
+
+extern void nanomips_md_do_align (int, const char *, int, int);
+#define md_do_align(n,f,l,m,j)		nanomips_md_do_align (n,f,l,m)
+
+/* If defined, this macro allows control over whether fixups for a
+   given section will be processed when the linkrelax variable is
+   set. Define it to zero and handle things in md_apply_fix instead.*/
+#define TC_LINKRELAX_FIXUP(SEG) 0
+
+/* This macro is evaluated for any fixup with a fx_subsy that
+   fixup_segment cannot reduce to a number.  If the macro returns
+   false an error will be reported.  */
+#define TC_VALIDATE_FIX_SUB(fix, seg)   nanomips_validate_fix_sub (fix)
+extern bool nanomips_validate_fix_sub (struct fix *);
+
+#define LEX_BR LEX_NAME
+
+#define MD_PCREL_FROM_SECTION(FIX,SEG) 0
+
+int nanomips_eh_frame_estimate_size_before_relax (fragS *);
+#define TC_EH_FRAME_ESTIMATE_SIZE_BEFORE_RELAX(frag) \
+  nanomips_eh_frame_estimate_size_before_relax (frag)
+
+int nanomips_eh_frame_relax_frag (fragS *);
+#define TC_EH_FRAME_RELAX_FRAG(frag) \
+  nanomips_eh_frame_relax_frag (frag)
+
+void nanomips_eh_frame_convert_frag (fragS *);
+#define TC_EH_FRAME_CONVERT_FRAG(frag) \
+  nanomips_eh_frame_convert_frag (frag)
+
+#define md_section_align(SEGMENT, SIZE)     (SIZE)
+
+void nanomips_pre_relax_hook (void);
+#define md_pre_relax_hook nanomips_pre_relax_hook()
+
+void nanomips_post_relax_hook (void);
+#define md_post_relax_hook nanomips_post_relax_hook()
+
+#endif /* TC_NANOMIPS */
diff --git a/gas/configure b/gas/configure
index 3b120d3f441..b8d25684409 100755
--- a/gas/configure
+++ b/gas/configure
@@ -12694,6 +12694,60 @@ _ACEOF
 
 cat >>confdefs.h <<_ACEOF
 #define MIPS_DEFAULT_ABI $mips_default_abi
+_ACEOF
+
+	;;
+      nanomips)
+	# Set nanomips_cpu to the name of the default CPU.
+	case ${target_cpu} in
+	  nanomips | nanomipsel | nanomipseb)
+	    nanomips_cpu=from-abi
+	    ;;
+	  nanomips32*)
+	    nanomips_cpu=32r6
+	    ;;
+	  nanomips64*)
+	    nanomips_cpu=64r6
+	    ;;
+	  *)
+	    as_fn_error "$target_cpu isn't a supported NANOMIPS CPU name" "$LINENO" 5
+	    ;;
+	esac
+	# Decide whether to generate 32-bit or 64-bit code by default.
+	# Used to resolve -march=from-abi when an embedded ABI is selected.
+	case ${target} in
+	  nanomips64*-*-*)
+	    nanomips_default_64bit=1
+	    ;;
+	  *)
+	    nanomips_default_64bit=0
+	    ;;
+	esac
+	# Decide which ABI to target by default.
+	case ${target} in
+	  nanomips64*-*)
+	    nanomips_default_abi=P64_ABI
+	    ;;
+	  nanomips*-*)
+	    nanomips_default_abi=P32_ABI
+	    ;;
+	  *)
+	    nanomips_default_abi=NO_ABI
+	    ;;
+	esac
+
+cat >>confdefs.h <<_ACEOF
+#define NANOMIPS_CPU_STRING_DEFAULT "$nanomips_cpu"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define NANOMIPS_DEFAULT_64BIT $nanomips_default_64bit
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define NANOMIPS_DEFAULT_ABI $nanomips_default_abi
 _ACEOF
 
 	;;
diff --git a/gas/configure.ac b/gas/configure.ac
index 69547f5ffb9..03c0cb17904 100644
--- a/gas/configure.ac
+++ b/gas/configure.ac
@@ -423,6 +423,51 @@ changequote([,])dnl
 	AC_DEFINE_UNQUOTED(MIPS_DEFAULT_ABI, $mips_default_abi,
 			   [Choose a default ABI for MIPS targets. ])
 	;;
+      nanomips)
+	# Set nanomips_cpu to the name of the default CPU.
+	case ${target_cpu} in
+	  nanomips | nanomipsel | nanomipseb)
+	    nanomips_cpu=from-abi
+	    ;;
+	  nanomips32*)
+	    nanomips_cpu=32r6
+	    ;;
+	  nanomips64*)
+	    nanomips_cpu=64r6
+	    ;;
+	  *)
+	    AC_MSG_ERROR($target_cpu isn't a supported MIPS CPU name)
+	    ;;
+	esac
+	# Decide whether to generate 32-bit or 64-bit code by default.
+	# Used to resolve -march=from-abi when an embedded ABI is selected.
+	case ${target} in
+	  nanomips64*-*-*)
+	    nanomips_default_64bit=1
+	    ;;
+	  *)
+	    nanomips_default_64bit=0
+	    ;;
+	esac
+	# Decide which ABI to target by default.
+	case ${target} in
+	  nanomips64*-*)
+	    nanomips_default_abi=P64_ABI
+	    ;;
+	  nanomips*-*)
+	    nanomips_default_abi=P32_ABI
+	    ;;
+	  *)
+	    nanomips_default_abi=NO_ABI
+	    ;;
+	esac
+	AC_DEFINE_UNQUOTED(NANOMIPS_CPU_STRING_DEFAULT, "$nanomips_cpu",
+			   [Default CPU for nanoMIPS targets. ])
+	AC_DEFINE_UNQUOTED(NANOMIPS_DEFAULT_64BIT, $nanomips_default_64bit,
+			   [Generate 64-bit code by default on nanoMIPS targets. ])
+	AC_DEFINE_UNQUOTED(NANOMIPS_DEFAULT_ABI, $nanomips_default_abi,
+			   [Choose a default ABI for nanoMIPS targets. ])
+	;;
     esac
 
     # Do we need the opcodes library?
diff --git a/gas/configure.tgt b/gas/configure.tgt
index 7a5645fd9ae..8e1e7727603 100644
--- a/gas/configure.tgt
+++ b/gas/configure.tgt
@@ -85,6 +85,8 @@ case ${cpu} in
   mips*el)		cpu_type=mips endian=little ;;
   mips*)		cpu_type=mips endian=big ;;
   mt)			cpu_type=mt endian=big ;;
+  nanomips*eb)		cpu_type=nanomips endian=big ;;
+  nanomips*)		cpu_type=nanomips endian=little ;;
   nds32be)		cpu_type=nds32 endian=big ;;
   nds32le)		cpu_type=nds32 endian=little ;;
   or1k* | or1knd*)	cpu_type=or1k endian=big ;;
@@ -345,6 +347,9 @@ case ${generic_target} in
 
   msp430-*-*)				fmt=elf ;;
 
+  nanomips*-*-elf*)			fmt=elf ;;
+  nanomips*-*-linux*)			fmt=elf em=linux;;
+
   nds32-*-elf*)				fmt=elf ;;
   nds32-*-linux*)			fmt=elf em=linux ;;
 
diff --git a/gas/read.c b/gas/read.c
index ae481007c32..a25828839d2 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -2615,7 +2615,7 @@ bss_alloc (symbolS *symbolP, addressT size, unsigned int align)
   subsegT current_subseg = now_subseg;
   segT bss_seg = bss_section;
 
-#if defined (TC_MIPS) || defined (TC_ALPHA)
+#if defined (TC_MIPS) || defined (TC_ALPHA) || defined (TC_NANOMIPS)
   if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour
       || OUTPUT_FLAVOR == bfd_target_elf_flavour)
     {
diff --git a/ld/configure.tgt b/ld/configure.tgt
index 60b62e69c08..644b6631a90 100644
--- a/ld/configure.tgt
+++ b/ld/configure.tgt
@@ -545,6 +545,8 @@ mips*-sde-elf* | mips*-mti-elf* | mips*-img-elf*)
 			targ_emul=elf32btsmip
 			targ_extra_emuls="elf32ltsmip elf32btsmipn32 elf64btsmip elf32ltsmipn32 elf64ltsmip"
 			;;
+nanomips*-*)		targ_emul=vanilla
+			;;
 mips64*el-ps2-elf*)	targ_emul=elf32lr5900n32
 			targ_extra_libpath=elf32lr5900
 			;;
-- 
2.39.5
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.