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

"Tsing" <[email protected]> Fri, 24 Jul 2026 18:30:53 +0800
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) 2017-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) 2017-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