[PATCH, BINUTILS, AARCH64, 1/9] Add -march=armv8.5-a and related internal feature...
[external/binutils.git] / gas / config / tc-mips.c
index 9fde462..c9fc6c6 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-mips.c -- assemble code for a MIPS chip.
-   Copyright (C) 1993-2017 Free Software Foundation, Inc.
+   Copyright (C) 1993-2018 Free Software Foundation, Inc.
    Contributed by the OSF and Ralph Campbell.
    Written by Keith Knowles and Ralph Campbell, working independently.
    Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
@@ -422,7 +422,9 @@ static int mips_32bitmode = 0;
     || (ISA) == ISA_MIPS64R5           \
     || (ISA) == ISA_MIPS64R6           \
     || (CPU) == CPU_R5900)             \
-   && (CPU) != CPU_LOONGSON_3A)
+   && ((CPU) != CPU_GS464              \
+    || (CPU) != CPU_GS464E             \
+    || (CPU) != CPU_GS264E))
 
 /* Return true if ISA supports move to/from high part of a 64-bit
    floating-point register. */
@@ -1130,38 +1132,40 @@ static bfd_boolean mips_ignore_branch_isa;
    store whether this is known to be a branch to a different section,
    whether we have tried to relax this frag yet, and whether we have
    ever extended a PC relative fragment because of a shift count.  */
-#define RELAX_MIPS16_ENCODE(type, pic, sym32, nomacro,         \
+#define RELAX_MIPS16_ENCODE(type, e2, pic, sym32, nomacro,     \
                            small, ext,                         \
                            dslot, jal_dslot)                   \
   (0x80000000                                                  \
    | ((type) & 0xff)                                           \
-   | ((pic) ? 0x100 : 0)                                       \
-   | ((sym32) ? 0x200 : 0)                                     \
-   | ((nomacro) ? 0x400 : 0)                                   \
-   | ((small) ? 0x800 : 0)                                     \
-   | ((ext) ? 0x1000 : 0)                                      \
-   | ((dslot) ? 0x2000 : 0)                                    \
-   | ((jal_dslot) ? 0x4000 : 0))
+   | ((e2) ? 0x100 : 0)                                                \
+   | ((pic) ? 0x200 : 0)                                       \
+   | ((sym32) ? 0x400 : 0)                                     \
+   | ((nomacro) ? 0x800 : 0)                                   \
+   | ((small) ? 0x1000 : 0)                                    \
+   | ((ext) ? 0x2000 : 0)                                      \
+   | ((dslot) ? 0x4000 : 0)                                    \
+   | ((jal_dslot) ? 0x8000 : 0))
 
 #define RELAX_MIPS16_P(i) (((i) & 0xc0000000) == 0x80000000)
 #define RELAX_MIPS16_TYPE(i) ((i) & 0xff)
-#define RELAX_MIPS16_PIC(i) (((i) & 0x100) != 0)
-#define RELAX_MIPS16_SYM32(i) (((i) & 0x200) != 0)
-#define RELAX_MIPS16_NOMACRO(i) (((i) & 0x400) != 0)
-#define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x800) != 0)
-#define RELAX_MIPS16_USER_EXT(i) (((i) & 0x1000) != 0)
-#define RELAX_MIPS16_DSLOT(i) (((i) & 0x2000) != 0)
-#define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x4000) != 0)
-
-#define RELAX_MIPS16_EXTENDED(i) (((i) & 0x8000) != 0)
-#define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x8000)
-#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) & ~0x8000)
-#define RELAX_MIPS16_ALWAYS_EXTENDED(i) (((i) & 0x10000) != 0)
-#define RELAX_MIPS16_MARK_ALWAYS_EXTENDED(i) ((i) | 0x10000)
-#define RELAX_MIPS16_CLEAR_ALWAYS_EXTENDED(i) ((i) & ~0x10000)
-#define RELAX_MIPS16_MACRO(i) (((i) & 0x20000) != 0)
-#define RELAX_MIPS16_MARK_MACRO(i) ((i) | 0x20000)
-#define RELAX_MIPS16_CLEAR_MACRO(i) ((i) & ~0x20000)
+#define RELAX_MIPS16_E2(i) (((i) & 0x100) != 0)
+#define RELAX_MIPS16_PIC(i) (((i) & 0x200) != 0)
+#define RELAX_MIPS16_SYM32(i) (((i) & 0x400) != 0)
+#define RELAX_MIPS16_NOMACRO(i) (((i) & 0x800) != 0)
+#define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x1000) != 0)
+#define RELAX_MIPS16_USER_EXT(i) (((i) & 0x2000) != 0)
+#define RELAX_MIPS16_DSLOT(i) (((i) & 0x4000) != 0)
+#define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x8000) != 0)
+
+#define RELAX_MIPS16_EXTENDED(i) (((i) & 0x10000) != 0)
+#define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x10000)
+#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) & ~0x10000)
+#define RELAX_MIPS16_ALWAYS_EXTENDED(i) (((i) & 0x20000) != 0)
+#define RELAX_MIPS16_MARK_ALWAYS_EXTENDED(i) ((i) | 0x20000)
+#define RELAX_MIPS16_CLEAR_ALWAYS_EXTENDED(i) ((i) & ~0x20000)
+#define RELAX_MIPS16_MACRO(i) (((i) & 0x40000) != 0)
+#define RELAX_MIPS16_MARK_MACRO(i) ((i) | 0x40000)
+#define RELAX_MIPS16_CLEAR_MACRO(i) ((i) & ~0x40000)
 
 /* For microMIPS code, we use relaxation similar to one we use for
    MIPS16 code.  Some instructions that take immediate values support
@@ -1341,6 +1345,7 @@ static void macro (struct mips_cl_insn *ip, char *str);
 static void mips16_macro (struct mips_cl_insn * ip);
 static void mips_ip (char *str, struct mips_cl_insn * ip);
 static void mips16_ip (char *str, struct mips_cl_insn * ip);
+static unsigned long mips16_immed_extend (offsetT, unsigned int);
 static void mips16_immed
   (const char *, unsigned int, int, bfd_reloc_code_real_type, offsetT,
    unsigned int, unsigned long *);
@@ -1455,7 +1460,10 @@ enum options
     OPTION_NO_MICROMIPS,
     OPTION_MCU,
     OPTION_NO_MCU,
-    OPTION_COMPAT_ARCH_BASE,
+    OPTION_MIPS16E2,
+    OPTION_NO_MIPS16E2,
+    OPTION_CRC,
+    OPTION_NO_CRC,
     OPTION_M4650,
     OPTION_NO_M4650,
     OPTION_M4010,
@@ -1521,6 +1529,16 @@ enum options
     OPTION_NAN,
     OPTION_ODD_SPREG,
     OPTION_NO_ODD_SPREG,
+    OPTION_GINV,
+    OPTION_NO_GINV,
+    OPTION_LOONGSON_MMI,
+    OPTION_NO_LOONGSON_MMI,
+    OPTION_LOONGSON_CAM,
+    OPTION_NO_LOONGSON_CAM,
+    OPTION_LOONGSON_EXT,
+    OPTION_NO_LOONGSON_EXT,
+    OPTION_LOONGSON_EXT2,
+    OPTION_NO_LOONGSON_EXT2,
     OPTION_END_OF_ENUM
   };
 
@@ -1575,6 +1593,20 @@ struct option md_longopts[] =
   {"mno-msa", no_argument, NULL, OPTION_NO_MSA},
   {"mxpa", no_argument, NULL, OPTION_XPA},
   {"mno-xpa", no_argument, NULL, OPTION_NO_XPA},
+  {"mmips16e2", no_argument, NULL, OPTION_MIPS16E2},
+  {"mno-mips16e2", no_argument, NULL, OPTION_NO_MIPS16E2},
+  {"mcrc", no_argument, NULL, OPTION_CRC},
+  {"mno-crc", no_argument, NULL, OPTION_NO_CRC},
+  {"mginv", no_argument, NULL, OPTION_GINV},
+  {"mno-ginv", no_argument, NULL, OPTION_NO_GINV},
+  {"mloongson-mmi", no_argument, NULL, OPTION_LOONGSON_MMI},
+  {"mno-loongson-mmi", no_argument, NULL, OPTION_NO_LOONGSON_MMI},
+  {"mloongson-cam", no_argument, NULL, OPTION_LOONGSON_CAM},
+  {"mno-loongson-cam", no_argument, NULL, OPTION_NO_LOONGSON_CAM},
+  {"mloongson-ext", no_argument, NULL, OPTION_LOONGSON_EXT},
+  {"mno-loongson-ext", no_argument, NULL, OPTION_NO_LOONGSON_EXT},
+  {"mloongson-ext2", no_argument, NULL, OPTION_LOONGSON_EXT2},
+  {"mno-loongson-ext2", no_argument, NULL, OPTION_NO_LOONGSON_EXT2},
 
   /* Old-style architecture options.  Don't add more of these.  */
   {"m4650", no_argument, NULL, OPTION_M4650},
@@ -1755,7 +1787,42 @@ static const struct mips_ase mips_ases[] = {
 
   { "xpa", ASE_XPA, 0,
     OPTION_XPA, OPTION_NO_XPA,
-     2,  2, -1, -1,
+    2, 2, 2, 2,
+    -1 },
+
+  { "mips16e2", ASE_MIPS16E2, 0,
+    OPTION_MIPS16E2, OPTION_NO_MIPS16E2,
+    2,  2, -1, -1,
+    6 },
+
+  { "crc", ASE_CRC, ASE_CRC64,
+    OPTION_CRC, OPTION_NO_CRC,
+    6,  6, -1, -1,
+    -1 },
+
+  { "ginv", ASE_GINV, 0,
+    OPTION_GINV, OPTION_NO_GINV,
+    6,  6, 6, 6,
+    -1 },
+
+  { "loongson-mmi", ASE_LOONGSON_MMI, 0,
+    OPTION_LOONGSON_MMI, OPTION_NO_LOONGSON_MMI,
+    0, 0, -1, -1,
+    -1 },
+
+  { "loongson-cam", ASE_LOONGSON_CAM, 0,
+    OPTION_LOONGSON_CAM, OPTION_NO_LOONGSON_CAM,
+    0, 0, -1, -1,
+    -1 },
+
+  { "loongson-ext", ASE_LOONGSON_EXT, 0,
+    OPTION_LOONGSON_EXT, OPTION_NO_LOONGSON_EXT,
+    0, 0, -1, -1,
+    -1 },
+
+  { "loongson-ext2", ASE_LOONGSON_EXT | ASE_LOONGSON_EXT2, 0,
+    OPTION_LOONGSON_EXT2, OPTION_NO_LOONGSON_EXT2,
+    0, 0, -1, -1,
     -1 },
 };
 
@@ -1764,7 +1831,8 @@ static const struct mips_ase mips_ases[] = {
 
 /* Groups of ASE_* flags that represent different revisions of an ASE.  */
 static const unsigned int mips_ase_groups[] = {
-  ASE_DSP | ASE_DSPR2 | ASE_DSPR3
+  ASE_DSP | ASE_DSPR2 | ASE_DSPR3, 
+  ASE_LOONGSON_EXT | ASE_LOONGSON_EXT2 
 };
 \f
 /* Pseudo-op table.
@@ -2118,8 +2186,28 @@ mips_set_ase (const struct mips_ase *ase, struct mips_set_options *opts,
 
   mask = mips_ase_mask (ase->flags);
   opts->ase &= ~mask;
+
+  /* Clear combination ASE flags, which need to be recalculated based on
+     updated regular ASE settings.  */
+  opts->ase &= ~(ASE_MIPS16E2_MT | ASE_XPA_VIRT);
+
   if (enabled_p)
     opts->ase |= ase->flags;
+
+  /* The Virtualization ASE has eXtended Physical Addressing (XPA)
+     instructions which are only valid when both ASEs are enabled.
+     This sets the ASE_XPA_VIRT flag when both ASEs are present.  */
+  if ((opts->ase & (ASE_XPA | ASE_VIRT)) == (ASE_XPA | ASE_VIRT))
+    {
+      opts->ase |= ASE_XPA_VIRT;
+      mask |= ASE_XPA_VIRT;
+    }
+  if ((opts->ase & (ASE_MIPS16E2 | ASE_MT)) == (ASE_MIPS16E2 | ASE_MT))
+    {
+      opts->ase |= ASE_MIPS16E2_MT;
+      mask |= ASE_MIPS16E2_MT;
+    }
+
   return mask;
 }
 
@@ -2226,7 +2314,12 @@ static inline void
 insn_insert_operand (struct mips_cl_insn *insn,
                     const struct mips_operand *operand, unsigned int uval)
 {
-  insn->insn_opcode = mips_insert_operand (operand, insn->insn_opcode, uval);
+  if (mips_opts.mips16
+      && operand->type == OP_INT && operand->lsb == 0
+      && mips_opcode_32bit_p (insn->insn_mo))
+    insn->insn_opcode |= mips16_immed_extend (uval, operand->size);
+  else
+    insn->insn_opcode = mips_insert_operand (operand, insn->insn_opcode, uval);
 }
 
 /* Extract the value of OPERAND from INSN.  */
@@ -3286,7 +3379,16 @@ is_opcode_valid (const struct mips_opcode *mo)
 static bfd_boolean
 is_opcode_valid_16 (const struct mips_opcode *mo)
 {
-  return opcode_is_member (mo, mips_opts.isa, 0, mips_opts.arch);
+  int isa = mips_opts.isa;
+  int ase = mips_opts.ase;
+  unsigned int i;
+
+  if (ISA_HAS_64BIT_REGS (isa))
+    for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
+      if ((ase & mips_ases[i].flags) == mips_ases[i].flags)
+       ase |= mips_ases[i].flags64;
+
+  return opcode_is_member (mo, isa, ase, mips_opts.arch);
 }
 
 /* Return TRUE if the size of the microMIPS opcode MO matches one
@@ -3417,7 +3519,11 @@ validate_mips_insn (const struct mips_opcode *opcode,
          }
        gas_assert (opno < MAX_OPERANDS);
        operands->operand[opno] = operand;
-       if (operand && operand->type != OP_VU0_MATCH_SUFFIX)
+       if (!decode_operand && operand
+           && operand->type == OP_INT && operand->lsb == 0
+           && mips_opcode_32bit_p (opcode))
+         used_bits |= mips16_immed_extend (-1, operand->size);
+       else if (operand && operand->type != OP_VU0_MATCH_SUFFIX)
          {
            used_bits = mips_insert_operand (operand, used_bits, -1);
            if (operand->type == OP_MDMX_IMM_REG)
@@ -3426,6 +3532,10 @@ validate_mips_insn (const struct mips_opcode *opcode,
              used_bits &= ~(1 << (operand->lsb + 5));
            if (operand->type == OP_ENTRY_EXIT_LIST)
              used_bits &= ~(mask & 0x700);
+           /* interAptiv MR2 SAVE/RESTORE instructions have a discontiguous
+              operand field that cannot be fully described with LSB/SIZE.  */
+           if (operand->type == OP_SAVE_RESTORE_LIST && operand->lsb == 6)
+             used_bits &= ~0x6000;
          }
        /* Skip prefix characters.  */
        if (decode_operand && (*s == '+' || *s == 'm' || *s == '-'))
@@ -4501,6 +4611,9 @@ operand_reg_mask (const struct mips_cl_insn *insn,
     case OP_IMM_INDEX:
       abort ();
 
+    case OP_REG28:
+      return 1 << 28;
+
     case OP_REG:
     case OP_OPTIONAL_REG:
       {
@@ -4840,7 +4953,7 @@ match_expression (struct mips_arg_info *arg, expressionS *value,
 }
 
 /* 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
+   the tokens and return true on success, storing the constant value
    in *VALUE.  */
 
 static bfd_boolean
@@ -5082,7 +5195,10 @@ match_int_operand (struct mips_arg_info *arg,
          /* Accept non-constant operands if no later alternative matches,
             leaving it for the caller to process.  */
          if (!arg->lax_match)
-           return FALSE;
+           {
+             match_not_constant (arg);
+             return FALSE;
+           }
          offset_reloc[0] = BFD_RELOC_LO16;
          return TRUE;
        }
@@ -5099,7 +5215,10 @@ match_int_operand (struct mips_arg_info *arg,
        {
          max_val = ((1 << operand_base->size) - 1) << operand->shift;
          if (!arg->lax_match && sval <= max_val)
-           return FALSE;
+           {
+             match_out_of_range (arg);
+             return FALSE;
+           }
        }
     }
   else
@@ -5509,6 +5628,39 @@ match_entry_exit_operand (struct mips_arg_info *arg,
   return TRUE;
 }
 
+/* Encode regular MIPS SAVE/RESTORE instruction operands according to
+   the argument register mask AMASK, the number of static registers
+   saved NSREG, the $ra, $s0 and $s1 register specifiers RA, S0 and S1
+   respectively, and the frame size FRAME_SIZE.  */
+
+static unsigned int
+mips_encode_save_restore (unsigned int amask, unsigned int nsreg,
+                         unsigned int ra, unsigned int s0, unsigned int s1,
+                         unsigned int frame_size)
+{
+  return ((nsreg << 23) | ((frame_size & 0xf0) << 15) | (amask << 15)
+         | (ra << 12) | (s0 << 11) | (s1 << 10) | ((frame_size & 0xf) << 6));
+}
+
+/* Encode MIPS16 SAVE/RESTORE instruction operands according to the
+   argument register mask AMASK, the number of static registers saved
+   NSREG, the $ra, $s0 and $s1 register specifiers RA, S0 and S1
+   respectively, and the frame size FRAME_SIZE.  */
+
+static unsigned int
+mips16_encode_save_restore (unsigned int amask, unsigned int nsreg,
+                           unsigned int ra, unsigned int s0, unsigned int s1,
+                           unsigned int frame_size)
+{
+  unsigned int args;
+
+  args = (ra << 6) | (s0 << 5) | (s1 << 4) | (frame_size & 0xf);
+  if (nsreg || amask || frame_size == 0 || frame_size > 16)
+    args |= (MIPS16_EXTEND | (nsreg << 24) | (amask << 16)
+            | ((frame_size & 0xf0) << 16));
+  return args;
+}
+
 /* OP_SAVE_RESTORE_LIST matcher.  */
 
 static bfd_boolean
@@ -5516,6 +5668,7 @@ match_save_restore_list_operand (struct mips_arg_info *arg)
 {
   unsigned int opcode, args, statics, sregs;
   unsigned int num_frame_sizes, num_args, num_statics, num_sregs;
+  unsigned int arg_mask, ra, s0, s1;
   offsetT frame_size;
 
   opcode = arg->insn->insn_opcode;
@@ -5524,6 +5677,9 @@ match_save_restore_list_operand (struct mips_arg_info *arg)
   args = 0;
   statics = 0;
   sregs = 0;
+  ra = 0;
+  s0 = 0;
+  s1 = 0;
   do
     {
       unsigned int regno1, regno2;
@@ -5559,7 +5715,7 @@ match_save_restore_list_operand (struct mips_arg_info *arg)
                sregs |= 1 << 8;
              else if (regno1 == 31)
                /* Add $ra to insn.  */
-               opcode |= 0x40;
+               ra = 1;
              else
                return FALSE;
              regno1 += 1;
@@ -5575,10 +5731,10 @@ match_save_restore_list_operand (struct mips_arg_info *arg)
     return FALSE;
   else if (args == 0xf)
     /* All $a0-$a3 are args.  */
-    opcode |= MIPS16_ALL_ARGS << 16;
+    arg_mask = MIPS_SVRS_ALL_ARGS;
   else if (statics == 0xf)
     /* All $a0-$a3 are statics.  */
-    opcode |= MIPS16_ALL_STATICS << 16;
+    arg_mask = MIPS_SVRS_ALL_STATICS;
   else
     {
       /* Count arg registers.  */
@@ -5602,14 +5758,14 @@ match_save_restore_list_operand (struct mips_arg_info *arg)
        return FALSE;
 
       /* Encode args/statics.  */
-      opcode |= ((num_args << 2) | num_statics) << 16;
+      arg_mask = (num_args << 2) | num_statics;
     }
 
   /* Encode $s0/$s1.  */
   if (sregs & (1 << 0))                /* $s0 */
-    opcode |= 0x20;
+    s0 = 1;
   if (sregs & (1 << 1))                /* $s1 */
-    opcode |= 0x10;
+    s1 = 1;
   sregs >>= 2;
 
   /* Encode $s2-$s8. */
@@ -5621,7 +5777,6 @@ match_save_restore_list_operand (struct mips_arg_info *arg)
     }
   if (sregs != 0)
     return FALSE;
-  opcode |= num_sregs << 24;
 
   /* Encode frame size.  */
   if (num_frame_sizes == 0)
@@ -5639,16 +5794,18 @@ match_save_restore_list_operand (struct mips_arg_info *arg)
       set_insn_error (arg->argnum, _("invalid frame size"));
       return FALSE;
     }
-  if (frame_size != 128 || (opcode >> 16) != 0)
-    {
-      frame_size /= 8;
-      opcode |= (((frame_size & 0xf0) << 16)
-                | (frame_size & 0x0f));
-    }
+  frame_size /= 8;
 
   /* Finally build the instruction.  */
-  if ((opcode >> 16) != 0 || frame_size == 0)
-    opcode |= MIPS16_EXTEND;
+  if (mips_opts.mips16)
+    opcode |= mips16_encode_save_restore (arg_mask, num_sregs, ra, s0, s1,
+                                         frame_size);
+  else if (!mips_opts.micromips)
+    opcode |= mips_encode_save_restore (arg_mask, num_sregs, ra, s0, s1,
+                                       frame_size);
+  else
+    abort ();
+
   arg->insn->insn_opcode = opcode;
   return TRUE;
 }
@@ -5789,6 +5946,23 @@ match_pc_operand (struct mips_arg_info *arg)
   return FALSE;
 }
 
+/* OP_REG28 matcher.  */
+
+static bfd_boolean
+match_reg28_operand (struct mips_arg_info *arg)
+{
+  unsigned int regno;
+
+  if (arg->token->type == OT_REG
+      && match_regno (arg, OP_REG_GP, arg->token->u.regno, &regno)
+      && regno == GP)
+    {
+      ++arg->token;
+      return TRUE;
+    }
+  return FALSE;
+}
+
 /* OP_NON_ZERO_REG matcher.  */
 
 static bfd_boolean
@@ -5819,9 +5993,9 @@ match_tied_reg_operand (struct mips_arg_info *arg, unsigned int other_regno)
   return match_reg (arg, OP_REG_GP, &regno) && regno == other_regno;
 }
 
-/* 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.
+/* Try to match a floating-point constant from ARG 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:
 
@@ -6015,8 +6189,8 @@ match_vu0_suffix_operand (struct mips_arg_info *arg,
   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.  */
+/* Try to match a token from ARG against OPERAND.  Consume the token
+   and return true on success, otherwise return false.  */
 
 static bfd_boolean
 match_operand (struct mips_arg_info *arg,
@@ -6073,6 +6247,9 @@ match_operand (struct mips_arg_info *arg,
     case OP_PC:
       return match_pc_operand (arg);
 
+    case OP_REG28:
+      return match_reg28_operand (arg);
+
     case OP_VU0_SUFFIX:
       return match_vu0_suffix_operand (arg, operand, FALSE);
 
@@ -6845,7 +7022,7 @@ can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
                   break;
                 }
             }
-          if (rv == FALSE)
+          if (!rv)
             {
               /* Insert nop after branch to fix short loop. */
               return FALSE;
@@ -7446,6 +7623,7 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
       add_relaxed_insn (ip, 12, 0,
                        RELAX_MIPS16_ENCODE
                        (*reloc_type - BFD_RELOC_UNUSED,
+                        mips_opts.ase & ASE_MIPS16E2,
                         mips_pic != NO_PIC,
                         HAVE_32BIT_SYMBOLS,
                         mips_opts.warn_about_macros,
@@ -8188,7 +8366,7 @@ match_mips16_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
            {
              if (required_insn_length == 2)
                set_insn_error (0, _("invalid unextended operand value"));
-             else
+             else if (!mips_opcode_32bit_p (opcode))
                {
                  forced_insn_length = 4;
                  insn->insn_opcode |= MIPS16_EXTEND;
@@ -8222,6 +8400,8 @@ match_mips16_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
        case 'A':
        case 'B':
        case 'E':
+       case 'V':
+       case 'u':
          relax_char = c;
          break;
 
@@ -8710,7 +8890,26 @@ macro_build (expressionS *ep, const char *name, const char *fmt, ...)
                      || *r == BFD_RELOC_MIPS_HIGHER
                      || *r == BFD_RELOC_HI16_S
                      || *r == BFD_RELOC_LO16
-                     || *r == BFD_RELOC_MIPS_GOT_OFST);
+                     || *r == BFD_RELOC_MIPS_GOT_OFST
+                     || (mips_opts.micromips
+                         && (*r == BFD_RELOC_16
+                             || *r == BFD_RELOC_MIPS_GOT16
+                             || *r == BFD_RELOC_MIPS_CALL16
+                             || *r == BFD_RELOC_MIPS_GOT_HI16
+                             || *r == BFD_RELOC_MIPS_GOT_LO16
+                             || *r == BFD_RELOC_MIPS_CALL_HI16
+                             || *r == BFD_RELOC_MIPS_CALL_LO16
+                             || *r == BFD_RELOC_MIPS_SUB
+                             || *r == BFD_RELOC_MIPS_GOT_PAGE
+                             || *r == BFD_RELOC_MIPS_HIGHEST
+                             || *r == BFD_RELOC_MIPS_GOT_DISP
+                             || *r == BFD_RELOC_MIPS_TLS_GD
+                             || *r == BFD_RELOC_MIPS_TLS_LDM
+                             || *r == BFD_RELOC_MIPS_TLS_DTPREL_HI16
+                             || *r == BFD_RELOC_MIPS_TLS_DTPREL_LO16
+                             || *r == BFD_RELOC_MIPS_TLS_GOTTPREL
+                             || *r == BFD_RELOC_MIPS_TLS_TPREL_HI16
+                             || *r == BFD_RELOC_MIPS_TLS_TPREL_LO16)));
          break;
 
        case 'o':
@@ -13832,7 +14031,7 @@ mips_lookup_insn (struct hash_control *hash, const char *start,
        suffix = 0;
       if (suffix)
        {
-         memcpy (name + opend - 2, name + opend, length - opend + 1);
+         memmove (name + opend - 2, name + opend, length - opend + 1);
          insn = (struct mips_opcode *) hash_find (hash, name);
          if (insn)
            {
@@ -13917,7 +14116,7 @@ mips16_ip (char *str, struct mips_cl_insn *insn)
   struct mips_operand_token *tokens;
   unsigned int l;
 
-  for (s = str; ISLOWER (*s); ++s)
+  for (s = str; *s != '\0' && *s != '.' && *s != ' '; ++s)
     ;
   end = s;
   c = *end;
@@ -13948,8 +14147,6 @@ mips16_ip (char *str, struct mips_cl_insn *insn)
        break;
       else if (*s++ == ' ')
        break;
-      /* Fall through.  */
-    default:
       set_insn_error (0, _("unrecognized opcode"));
       return;
     }
@@ -13982,7 +14179,10 @@ static unsigned long
 mips16_immed_extend (offsetT val, unsigned int nbits)
 {
   int extval;
-  if (nbits == 16)
+
+  extval = 0;
+  val &= (1U << nbits) - 1;
+  if (nbits == 16 || nbits == 9)
     {
       extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
       val &= 0x1f;
@@ -13992,7 +14192,7 @@ mips16_immed_extend (offsetT val, unsigned int nbits)
       extval = ((val >> 11) & 0xf) | (val & 0x7f0);
       val &= 0xf;
     }
-  else
+  else if (nbits == 6)
     {
       extval = ((val & 0x1f) << 6) | (val & 0x20);
       val = 0;
@@ -15189,7 +15389,7 @@ fix_bad_misaligned_jump_p (fixS *fixP, int shift)
    We accept BFD_RELOC_16_PCREL_S2 relocations against MIPS16 and microMIPS
    symbols or BFD_RELOC_MICROMIPS_16_PCREL_S1 relocations against regular
    MIPS symbols and associated with BAL instructions as these instructions
-   may be be converted to JALX by the linker.  */
+   may be converted to JALX by the linker.  */
 
 static bfd_boolean
 fix_bad_cross_mode_branch_p (fixS *fixP)
@@ -17024,6 +17224,7 @@ s_nan (int ignore ATTRIBUTE_UNUSED)
 static void
 s_mips_stab (int type)
 {
+  file_mips_check_options ();
   mips_mark_labels ();
   s_stab (type);
 }
@@ -17697,7 +17898,7 @@ md_estimate_size_before_relax (fragS *fragp, asection *segtype)
       /* We don't want to modify the EXTENDED bit here; it might get us
         into infinite loops.  We change it only in mips_relax_frag().  */
       if (RELAX_MIPS16_MACRO (fragp->fr_subtype))
-       return 12;
+       return RELAX_MIPS16_E2 (fragp->fr_subtype) ? 8 : 12;
       else
        return RELAX_MIPS16_EXTENDED (fragp->fr_subtype) ? 4 : 2;
     }
@@ -17955,7 +18156,7 @@ mips_relax_frag (asection *sec, fragS *fragp, long stretch)
       if (RELAX_MIPS16_MACRO (fragp->fr_subtype))
        {
          fragp->fr_subtype = RELAX_MIPS16_CLEAR_MACRO (fragp->fr_subtype);
-         return -10;
+         return RELAX_MIPS16_E2 (fragp->fr_subtype) ? -6 : -10;
        }
       else if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
        {
@@ -17971,7 +18172,7 @@ mips_relax_frag (asection *sec, fragS *fragp, long stretch)
        {
          fragp->fr_subtype = RELAX_MIPS16_CLEAR_MACRO (fragp->fr_subtype);
          fragp->fr_subtype = RELAX_MIPS16_MARK_EXTENDED (fragp->fr_subtype);
-         return -8;
+         return RELAX_MIPS16_E2 (fragp->fr_subtype) ? -4 : -8;
        }
       else if (!RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
        {
@@ -17989,12 +18190,12 @@ mips_relax_frag (asection *sec, fragS *fragp, long stretch)
        {
          fragp->fr_subtype = RELAX_MIPS16_CLEAR_EXTENDED (fragp->fr_subtype);
          fragp->fr_subtype = RELAX_MIPS16_MARK_MACRO (fragp->fr_subtype);
-         return 8;
+         return RELAX_MIPS16_E2 (fragp->fr_subtype) ? 4 : 8;
        }
       else
        {
          fragp->fr_subtype = RELAX_MIPS16_MARK_MACRO (fragp->fr_subtype);
-         return 10;
+         return RELAX_MIPS16_E2 (fragp->fr_subtype) ? 6 : 10;
        }
     }
 
@@ -18010,7 +18211,6 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
     {
       char *buf;
       unsigned long insn;
-      expressionS exp;
       fixS *fixp;
 
       buf = fragp->fr_literal + fragp->fr_fix;
@@ -18021,12 +18221,9 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          /* We generate a fixup instead of applying it right now
             because, if there are linker relaxations, we're going to
             need the relocations.  */
-         exp.X_op = O_symbol;
-         exp.X_add_symbol = fragp->fr_symbol;
-         exp.X_add_number = fragp->fr_offset;
-
-         fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, TRUE,
-                             BFD_RELOC_16_PCREL_S2);
+         fixp = fix_new (fragp, buf - fragp->fr_literal, 4,
+                         fragp->fr_symbol, fragp->fr_offset,
+                         TRUE, BFD_RELOC_16_PCREL_S2);
          fixp->fx_file = fragp->fr_file;
          fixp->fx_line = fragp->fr_line;
 
@@ -18142,12 +18339,10 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              /* j or jal.  */
              insn = (RELAX_BRANCH_LINK (fragp->fr_subtype)
                      ? 0x0c000000 : 0x08000000);
-             exp.X_op = O_symbol;
-             exp.X_add_symbol = fragp->fr_symbol;
-             exp.X_add_number = fragp->fr_offset;
 
-             fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
-                                 FALSE, BFD_RELOC_MIPS_JMP);
+             fixp = fix_new (fragp, buf - fragp->fr_literal, 4,
+                             fragp->fr_symbol, fragp->fr_offset,
+                             FALSE, BFD_RELOC_MIPS_JMP);
              fixp->fx_file = fragp->fr_file;
              fixp->fx_line = fragp->fr_line;
 
@@ -18160,18 +18355,10 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              /* lw/ld $at, <sym>($gp)  R_MIPS_GOT16 */
              insn = HAVE_64BIT_ADDRESSES ? 0xdf800000 : 0x8f800000;
              insn |= at << OP_SH_RT;
-             exp.X_op = O_symbol;
-             exp.X_add_symbol = fragp->fr_symbol;
-             exp.X_add_number = fragp->fr_offset;
-
-             if (fragp->fr_offset)
-               {
-                 exp.X_add_symbol = make_expr_symbol (&exp);
-                 exp.X_add_number = 0;
-               }
 
-             fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
-                                 FALSE, BFD_RELOC_MIPS_GOT16);
+             fixp = fix_new (fragp, buf - fragp->fr_literal, 4,
+                             fragp->fr_symbol, fragp->fr_offset,
+                             FALSE, BFD_RELOC_MIPS_GOT16);
              fixp->fx_file = fragp->fr_file;
              fixp->fx_line = fragp->fr_line;
 
@@ -18185,8 +18372,9 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              insn = HAVE_64BIT_ADDRESSES ? 0x64000000 : 0x24000000;
              insn |= at << OP_SH_RS | at << OP_SH_RT;
 
-             fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
-                                 FALSE, BFD_RELOC_LO16);
+             fixp = fix_new (fragp, buf - fragp->fr_literal, 4,
+                             fragp->fr_symbol, fragp->fr_offset,
+                             FALSE, BFD_RELOC_LO16);
              fixp->fx_file = fragp->fr_file;
              fixp->fx_line = fragp->fr_line;
 
@@ -18220,13 +18408,8 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
       int type = RELAX_MICROMIPS_TYPE (fragp->fr_subtype);
       bfd_boolean short_ds;
       unsigned long insn;
-      expressionS exp;
       fixS *fixp;
 
-      exp.X_op = O_symbol;
-      exp.X_add_symbol = fragp->fr_symbol;
-      exp.X_add_number = fragp->fr_offset;
-
       fragp->fr_fix += fragp->fr_var;
 
       /* Handle 16-bit branches that fit or are forced to fit.  */
@@ -18235,14 +18418,21 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          /* We generate a fixup instead of applying it right now,
             because if there is linker relaxation, we're going to
             need the relocations.  */
-         if (type == 'D')
-           fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 2, &exp, TRUE,
-                               BFD_RELOC_MICROMIPS_10_PCREL_S1);
-         else if (type == 'E')
-           fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 2, &exp, TRUE,
-                               BFD_RELOC_MICROMIPS_7_PCREL_S1);
-         else
-           abort ();
+         switch (type)
+           {
+           case 'D':
+             fixp = fix_new (fragp, buf - fragp->fr_literal, 2,
+                             fragp->fr_symbol, fragp->fr_offset,
+                             TRUE, BFD_RELOC_MICROMIPS_10_PCREL_S1);
+             break;
+           case 'E':
+             fixp = fix_new (fragp, buf - fragp->fr_literal, 2,
+                             fragp->fr_symbol, fragp->fr_offset,
+                             TRUE, BFD_RELOC_MICROMIPS_7_PCREL_S1);
+             break;
+           default:
+             abort ();
+           }
 
          fixp->fx_file = fragp->fr_file;
          fixp->fx_line = fragp->fr_line;
@@ -18261,8 +18451,9 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          /* We generate a fixup instead of applying it right now,
             because if there is linker relaxation, we're going to
             need the relocations.  */
-         fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, TRUE,
-                             BFD_RELOC_MICROMIPS_16_PCREL_S1);
+         fixp = fix_new (fragp, buf - fragp->fr_literal, 4,
+                         fragp->fr_symbol, fragp->fr_offset,
+                         TRUE, BFD_RELOC_MICROMIPS_16_PCREL_S1);
          fixp->fx_file = fragp->fr_file;
          fixp->fx_line = fragp->fr_line;
 
@@ -18399,8 +18590,9 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          /* j/jal/jals <sym>  R_MICROMIPS_26_S1  */
          insn = al ? jal : 0xd4000000;
 
-         fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
-                             BFD_RELOC_MICROMIPS_JMP);
+         fixp = fix_new (fragp, buf - fragp->fr_literal, 4,
+                         fragp->fr_symbol, fragp->fr_offset,
+                         FALSE, BFD_RELOC_MICROMIPS_JMP);
          fixp->fx_file = fragp->fr_file;
          fixp->fx_line = fragp->fr_line;
 
@@ -18423,14 +18615,9 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          insn = HAVE_64BIT_ADDRESSES ? 0xdc1c0000 : 0xfc1c0000;
          insn |= at << MICROMIPSOP_SH_RT;
 
-         if (exp.X_add_number)
-           {
-             exp.X_add_symbol = make_expr_symbol (&exp);
-             exp.X_add_number = 0;
-           }
-
-         fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
-                             BFD_RELOC_MICROMIPS_GOT16);
+         fixp = fix_new (fragp, buf - fragp->fr_literal, 4,
+                         fragp->fr_symbol, fragp->fr_offset,
+                         FALSE, BFD_RELOC_MICROMIPS_GOT16);
          fixp->fx_file = fragp->fr_file;
          fixp->fx_line = fragp->fr_line;
 
@@ -18440,8 +18627,9 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          insn = HAVE_64BIT_ADDRESSES ? 0x5c000000 : 0x30000000;
          insn |= at << MICROMIPSOP_SH_RT | at << MICROMIPSOP_SH_RS;
 
-         fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
-                             BFD_RELOC_MICROMIPS_LO16);
+         fixp = fix_new (fragp, buf - fragp->fr_literal, 4,
+                         fragp->fr_symbol, fragp->fr_offset,
+                         FALSE, BFD_RELOC_MICROMIPS_LO16);
          fixp->fx_file = fragp->fr_file;
          fixp->fx_line = fragp->fr_line;
 
@@ -18518,7 +18706,8 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 
          if (pcrel_op->include_isa_bit && !need_reloc)
            {
-             if (!ELF_ST_IS_MIPS16 (S_GET_OTHER (fragp->fr_symbol)))
+             if (!mips_ignore_branch_isa
+                 && !ELF_ST_IS_MIPS16 (S_GET_OTHER (fragp->fr_symbol)))
                as_bad_where (fragp->fr_file, fragp->fr_line,
                              _("branch to a symbol in another ISA mode"));
              else if ((fragp->fr_offset & 0x1) != 0)
@@ -18569,10 +18758,13 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          unsigned long reg;
          unsigned long new;
          unsigned long op;
+         bfd_boolean e2;
 
          gas_assert (type == 'A' || type == 'B' || type == 'E');
          gas_assert (RELAX_MIPS16_SYM32 (fragp->fr_subtype));
 
+         e2 = RELAX_MIPS16_E2 (fragp->fr_subtype);
+
          if (need_reloc)
            {
              fixS *fixp;
@@ -18585,7 +18777,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              fixp->fx_file = fragp->fr_file;
              fixp->fx_line = fragp->fr_line;
 
-             fixp = fix_new (fragp, buf - fragp->fr_literal + 8, 4,
+             fixp = fix_new (fragp, buf - fragp->fr_literal + (e2 ? 4 : 8), 4,
                              fragp->fr_symbol, fragp->fr_offset,
                              FALSE, BFD_RELOC_MIPS16_LO16);
              fixp->fx_file = fragp->fr_file;
@@ -18622,15 +18814,18 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              abort ();
            }
 
-         new = 0xf0006800 | (reg << 8);                        /* LI */
+         new = (e2 ? 0xf0006820 : 0xf0006800) | (reg << 8);    /* LUI/LI */
          new |= mips16_immed_extend ((val + 0x8000) >> 16, 16);
          buf = write_compressed_insn (buf, new, 4);
-         new = 0xf4003000 | (reg << 8) | (reg << 5);           /* SLL */
-         buf = write_compressed_insn (buf, new, 4);
+         if (!e2)
+           {
+             new = 0xf4003000 | (reg << 8) | (reg << 5);       /* SLL */
+             buf = write_compressed_insn (buf, new, 4);
+           }
          op |= mips16_immed_extend (val, 16);
          buf = write_compressed_insn (buf, op, 4);
 
-         fragp->fr_fix += 12;
+         fragp->fr_fix += e2 ? 8 : 12;
        }
       else
        {
@@ -18639,7 +18834,6 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          if (need_reloc)
            {
              bfd_reloc_code_real_type reloc = BFD_RELOC_NONE;
-             expressionS exp;
              fixS *fixp;
 
              switch (type)
@@ -18656,13 +18850,9 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
                              _("unsupported relocation"));
              else if (ext)
                {
-                 exp.X_op = O_symbol;
-                 exp.X_add_symbol = fragp->fr_symbol;
-                 exp.X_add_number = fragp->fr_offset;
-
-                 fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
-                                     TRUE, reloc);
-
+                 fixp = fix_new (fragp, buf - fragp->fr_literal, 4,
+                                 fragp->fr_symbol, fragp->fr_offset,
+                                 TRUE, reloc);
                  fixp->fx_file = fragp->fr_file;
                  fixp->fx_line = fragp->fr_line;
                }
@@ -18860,6 +19050,20 @@ mips_convert_ase_flags (int ase)
     ext_ases |= AFL_ASE_MSA;
   if (ase & ASE_XPA)
     ext_ases |= AFL_ASE_XPA;
+  if (ase & ASE_MIPS16E2)
+    ext_ases |= file_ase_mips16 ? AFL_ASE_MIPS16E2 : 0;
+  if (ase & ASE_CRC)
+    ext_ases |= AFL_ASE_CRC;
+  if (ase & ASE_GINV)
+    ext_ases |= AFL_ASE_GINV;
+  if (ase & ASE_LOONGSON_MMI)
+    ext_ases |= AFL_ASE_LOONGSON_MMI;
+  if (ase & ASE_LOONGSON_CAM)
+    ext_ases |= AFL_ASE_LOONGSON_CAM;
+  if (ase & ASE_LOONGSON_EXT)
+    ext_ases |= AFL_ASE_LOONGSON_EXT;
+  if (ase & ASE_LOONGSON_EXT2)
+    ext_ases |= AFL_ASE_LOONGSON_EXT2;
 
   return ext_ases;
 }
@@ -19023,10 +19227,8 @@ mips_elf_final_processing (void)
       else
        elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI32;
     }
-  else if (mips_abi == N32_ABI)
-    elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ABI2;
 
-  /* Nothing to do for N64_ABI.  */
+  /* Nothing to do for N32_ABI or N64_ABI.  */
 
   if (mips_32bitmode)
     elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE;
@@ -19210,7 +19412,7 @@ s_mips_file (int x ATTRIBUTE_UNUSED)
     {
       char *filename;
 
-      filename = dwarf2_directive_file (0);
+      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
@@ -19508,7 +19710,7 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   { "r5900",          0, 0,                    ISA_MIPS3,    CPU_R5900 },
   /* ST Microelectronics Loongson 2E and 2F cores */
   { "loongson2e",     0, 0,                    ISA_MIPS3,    CPU_LOONGSON_2E },
-  { "loongson2f",     0, 0,                    ISA_MIPS3,    CPU_LOONGSON_2F },
+  { "loongson2f",     0, ASE_LOONGSON_MMI,     ISA_MIPS3,    CPU_LOONGSON_2F },
 
   /* MIPS IV */
   { "r8000",          0, 0,                    ISA_MIPS4,    CPU_R8000 },
@@ -19587,6 +19789,9 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   { "1004kf1_1",      0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
   /* interaptiv is the new name for 1004kf */
   { "interaptiv",     0, ASE_DSP | ASE_MT,     ISA_MIPS32R2, CPU_MIPS32R2 },
+  { "interaptiv-mr2", 0,
+    ASE_DSP | ASE_EVA | ASE_MT | ASE_MIPS16E2 | ASE_MIPS16E2_MT,
+    ISA_MIPS32R3, CPU_INTERAPTIV_MR2 },
   /* M5100 family */
   { "m5100",          0, ASE_MCU,              ISA_MIPS32R5, CPU_MIPS32R5 },
   { "m5101",          0, ASE_MCU,              ISA_MIPS32R5, CPU_MIPS32R5 },
@@ -19604,9 +19809,17 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   /* Broadcom SB-1A CPU core */
   { "sb1a",           0, ASE_MIPS3D | ASE_MDMX,        ISA_MIPS64,   CPU_SB1 },
 
-  { "loongson3a",     0, 0,                    ISA_MIPS64R2, CPU_LOONGSON_3A },
-
   /* MIPS 64 Release 2 */
+  /* Loongson CPU core */
+  /* -march=loongson3a is an alias of -march=gs464 for compatibility */
+  { "loongson3a",     0, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT,
+     ISA_MIPS64R2,     CPU_GS464 },
+  { "gs464",          0, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT,
+     ISA_MIPS64R2,     CPU_GS464 },
+  { "gs464e",         0, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT
+     | ASE_LOONGSON_EXT2,      ISA_MIPS64R2,   CPU_GS464E },
+  { "gs264e",         0, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT
+     | ASE_LOONGSON_EXT2 | ASE_MSA | ASE_MSA64,        ISA_MIPS64R2,   CPU_GS264E },
 
   /* Cavium Networks Octeon CPU core */
   { "octeon",        0, 0,                     ISA_MIPS64R2, CPU_OCTEON },
@@ -19840,6 +20053,9 @@ MIPS options:\n\
 -mips16                        generate mips16 instructions\n\
 -no-mips16             do not generate mips16 instructions\n"));
   fprintf (stream, _("\
+-mmips16e2             generate MIPS16e2 instructions\n\
+-mno-mips16e2          do not generate MIPS16e2 instructions\n"));
+  fprintf (stream, _("\
 -mmicromips            generate microMIPS instructions\n\
 -mno-micromips         do not generate microMIPS instructions\n"));
   fprintf (stream, _("\
@@ -19870,6 +20086,24 @@ MIPS options:\n\
 -mvirt                 generate Virtualization instructions\n\
 -mno-virt              do not generate Virtualization instructions\n"));
   fprintf (stream, _("\
+-mcrc                  generate CRC instructions\n\
+-mno-crc               do not generate CRC instructions\n"));
+  fprintf (stream, _("\
+-mginv                 generate Global INValidate (GINV) instructions\n\
+-mno-ginv              do not generate Global INValidate instructions\n"));
+  fprintf (stream, _("\
+-mloongson-mmi         generate Loongson MultiMedia extensions Instructions (MMI) instructions\n\
+-mno-loongson-mmi      do not generate Loongson MultiMedia extensions Instructions\n"));
+  fprintf (stream, _("\
+-mloongson-cam         generate Loongson Content Address Memory (CAM) instructions\n\
+-mno-loongson-cam      do not generate Loongson Content Address Memory Instructions\n"));
+  fprintf (stream, _("\
+-mloongson-ext         generate Loongson EXTensions (EXT) instructions\n\
+-mno-loongson-ext      do not generate Loongson EXTensions Instructions\n"));
+  fprintf (stream, _("\
+-mloongson-ext2                generate Loongson EXTensions R2 (EXT2) instructions\n\
+-mno-loongson-ext2     do not generate Loongson EXTensions R2 Instructions\n"));
+  fprintf (stream, _("\
 -minsn32               only generate 32-bit microMIPS instructions\n\
 -mno-insn32            generate all microMIPS instructions\n"));
   fprintf (stream, _("\
@@ -19882,8 +20116,9 @@ MIPS options:\n\
 -mgp32                 use 32-bit GPRs, regardless of the chosen ISA\n\
 -mfp32                 use 32-bit FPRs, regardless of the chosen ISA\n\
 -msym32                        assume all symbols have 32-bit values\n\
--O0                    remove unneeded NOPs, do not swap branches\n\
--O                     remove unneeded NOPs and swap branches\n\
+-O0                    do not remove unneeded NOPs, do not swap branches\n\
+-O, -O1                        remove unneeded NOPs, do not swap branches\n\
+-O2                    remove unneeded NOPs and swap branches\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"));
   fprintf (stream, _("\
@@ -19926,9 +20161,14 @@ MIPS options:\n\
   fputc ('\n', stream);
 
   fprintf (stream, _("\
--32                    create o32 ABI object file (default)\n\
--n32                   create n32 ABI object file\n\
--64                    create 64 ABI object file\n"));
+-32                    create o32 ABI object file%s\n"),
+          MIPS_DEFAULT_ABI == O32_ABI ? _(" (default)") : "");
+  fprintf (stream, _("\
+-n32                   create n32 ABI object file%s\n"),
+          MIPS_DEFAULT_ABI == N32_ABI ? _(" (default)") : "");
+  fprintf (stream, _("\
+-64                    create 64 ABI object file%s\n"),
+          MIPS_DEFAULT_ABI == N64_ABI ? _(" (default)") : "");
 }
 
 #ifdef TE_IRIX