]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-mips.c
-Wimplicit-fallthrough warning fixes
[thirdparty/binutils-gdb.git] / gas / config / tc-mips.c
index bfc639e154e9fc1313f985c73fcb2fa30185abb1..283ed80d3c5b0e400834e3f5dea664e3293162e3 100644 (file)
@@ -349,7 +349,7 @@ static int mips_32bitmode = 0;
 
 /* Likewise 64-bit registers.  */
 #define ABI_NEEDS_64BIT_REGS(ABI)      \
-  ((ABI) == N32_ABI                    \
+  ((ABI) == N32_ABI                    \
    || (ABI) == N64_ABI                 \
    || (ABI) == O64_ABI)
 
@@ -1017,7 +1017,7 @@ static int mips_relax_branch;
 
 /* Branch without likely bit.  If label is out of range, we turn:
 
-       beq reg1, reg2, label
+       beq reg1, reg2, label
        delay slot
 
    into
@@ -1151,37 +1151,44 @@ static int mips_relax_branch;
 
    The information we store for this type of relaxation is the argument
    code found in the opcode file for this relocation, the register
-   selected as the assembler temporary, whether the branch is
-   unconditional, whether it is compact, whether it stores the link
-   address implicitly in $ra, whether relaxation of out-of-range 32-bit
-   branches to a sequence of instructions is enabled, and whether the
-   displacement of a branch is too large to fit as an immediate argument
-   of a 16-bit and a 32-bit branch, respectively.  */
-#define RELAX_MICROMIPS_ENCODE(type, at, uncond, compact, link,        \
+   selected as the assembler temporary, whether in the 32-bit
+   instruction mode, whether the branch is unconditional, whether it is
+   compact, whether there is no delay-slot instruction available to fill
+   in, whether it stores the link address implicitly in $ra, whether
+   relaxation of out-of-range 32-bit branches to a sequence of
+   instructions is enabled, and whether the displacement of a branch is
+   too large to fit as an immediate argument of a 16-bit and a 32-bit
+   branch, respectively.  */
+#define RELAX_MICROMIPS_ENCODE(type, at, insn32,               \
+                              uncond, compact, link, nods,     \
                               relax32, toofar16, toofar32)     \
   (0x40000000                                                  \
    | ((type) & 0xff)                                           \
    | (((at) & 0x1f) << 8)                                      \
-   | ((uncond) ? 0x2000 : 0)                                   \
-   | ((compact) ? 0x4000 : 0)                                  \
-   | ((link) ? 0x8000 : 0)                                     \
-   | ((relax32) ? 0x10000 : 0)                                 \
-   | ((toofar16) ? 0x20000 : 0)                                        \
-   | ((toofar32) ? 0x40000 : 0))
+   | ((insn32) ? 0x2000 : 0)                                   \
+   | ((uncond) ? 0x4000 : 0)                                   \
+   | ((compact) ? 0x8000 : 0)                                  \
+   | ((link) ? 0x10000 : 0)                                    \
+   | ((nods) ? 0x20000 : 0)                                    \
+   | ((relax32) ? 0x40000 : 0)                                 \
+   | ((toofar16) ? 0x80000 : 0)                                        \
+   | ((toofar32) ? 0x100000 : 0))
 #define RELAX_MICROMIPS_P(i) (((i) & 0xc0000000) == 0x40000000)
 #define RELAX_MICROMIPS_TYPE(i) ((i) & 0xff)
 #define RELAX_MICROMIPS_AT(i) (((i) >> 8) & 0x1f)
-#define RELAX_MICROMIPS_UNCOND(i) (((i) & 0x2000) != 0)
-#define RELAX_MICROMIPS_COMPACT(i) (((i) & 0x4000) != 0)
-#define RELAX_MICROMIPS_LINK(i) (((i) & 0x8000) != 0)
-#define RELAX_MICROMIPS_RELAX32(i) (((i) & 0x10000) != 0)
-
-#define RELAX_MICROMIPS_TOOFAR16(i) (((i) & 0x20000) != 0)
-#define RELAX_MICROMIPS_MARK_TOOFAR16(i) ((i) | 0x20000)
-#define RELAX_MICROMIPS_CLEAR_TOOFAR16(i) ((i) & ~0x20000)
-#define RELAX_MICROMIPS_TOOFAR32(i) (((i) & 0x40000) != 0)
-#define RELAX_MICROMIPS_MARK_TOOFAR32(i) ((i) | 0x40000)
-#define RELAX_MICROMIPS_CLEAR_TOOFAR32(i) ((i) & ~0x40000)
+#define RELAX_MICROMIPS_INSN32(i) (((i) & 0x2000) != 0)
+#define RELAX_MICROMIPS_UNCOND(i) (((i) & 0x4000) != 0)
+#define RELAX_MICROMIPS_COMPACT(i) (((i) & 0x8000) != 0)
+#define RELAX_MICROMIPS_LINK(i) (((i) & 0x10000) != 0)
+#define RELAX_MICROMIPS_NODS(i) (((i) & 0x20000) != 0)
+#define RELAX_MICROMIPS_RELAX32(i) (((i) & 0x40000) != 0)
+
+#define RELAX_MICROMIPS_TOOFAR16(i) (((i) & 0x80000) != 0)
+#define RELAX_MICROMIPS_MARK_TOOFAR16(i) ((i) | 0x80000)
+#define RELAX_MICROMIPS_CLEAR_TOOFAR16(i) ((i) & ~0x80000)
+#define RELAX_MICROMIPS_TOOFAR32(i) (((i) & 0x100000) != 0)
+#define RELAX_MICROMIPS_MARK_TOOFAR32(i) ((i) | 0x100000)
+#define RELAX_MICROMIPS_CLEAR_TOOFAR32(i) ((i) & ~0x100000)
 
 /* Sign-extend 16-bit value X.  */
 #define SEXT_16BIT(X) ((((X) + 0x8000) & 0xffff) - 0x8000)
@@ -1410,6 +1417,8 @@ enum options
     OPTION_NO_SMARTMIPS,
     OPTION_DSPR2,
     OPTION_NO_DSPR2,
+    OPTION_DSPR3,
+    OPTION_NO_DSPR3,
     OPTION_EVA,
     OPTION_NO_EVA,
     OPTION_XPA,
@@ -1522,6 +1531,8 @@ struct option md_longopts[] =
   {"mno-smartmips", no_argument, NULL, OPTION_NO_SMARTMIPS},
   {"mdspr2", no_argument, NULL, OPTION_DSPR2},
   {"mno-dspr2", no_argument, NULL, OPTION_NO_DSPR2},
+  {"mdspr3", no_argument, NULL, OPTION_DSPR3},
+  {"mno-dspr3", no_argument, NULL, OPTION_NO_DSPR3},
   {"meva", no_argument, NULL, OPTION_EVA},
   {"mno-eva", no_argument, NULL, OPTION_NO_EVA},
   {"mmicromips", no_argument, NULL, OPTION_MICROMIPS},
@@ -1663,6 +1674,11 @@ static const struct mips_ase mips_ases[] = {
     2, 2, 2, 2,
     -1 },
 
+  { "dspr3", ASE_DSP | ASE_DSPR2 | ASE_DSPR3, 0,
+    OPTION_DSPR3, OPTION_NO_DSPR3,
+    6, 6, -1, -1,
+    -1 },
+
   { "eva", ASE_EVA, 0,
     OPTION_EVA, OPTION_NO_EVA,
      2,  2,  2,  2,
@@ -1716,7 +1732,7 @@ 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_DSP | ASE_DSPR2 | ASE_DSPR3
 };
 \f
 /* Pseudo-op table.
@@ -4051,6 +4067,7 @@ mips16_reloc_p (bfd_reloc_code_real_type reloc)
     case BFD_RELOC_MIPS16_HI16_S:
     case BFD_RELOC_MIPS16_HI16:
     case BFD_RELOC_MIPS16_LO16:
+    case BFD_RELOC_MIPS16_16_PCREL_S1:
       return TRUE;
 
     default:
@@ -4099,6 +4116,18 @@ jmp_reloc_p (bfd_reloc_code_real_type reloc)
   return reloc == BFD_RELOC_MIPS_JMP || reloc == BFD_RELOC_MICROMIPS_JMP;
 }
 
+static inline bfd_boolean
+b_reloc_p (bfd_reloc_code_real_type reloc)
+{
+  return (reloc == BFD_RELOC_MIPS_26_PCREL_S2
+         || reloc == BFD_RELOC_MIPS_21_PCREL_S2
+         || reloc == BFD_RELOC_16_PCREL_S2
+         || reloc == BFD_RELOC_MIPS16_16_PCREL_S1
+         || reloc == BFD_RELOC_MICROMIPS_16_PCREL_S1
+         || reloc == BFD_RELOC_MICROMIPS_10_PCREL_S1
+         || reloc == BFD_RELOC_MICROMIPS_7_PCREL_S1);
+}
+
 static inline bfd_boolean
 got16_reloc_p (bfd_reloc_code_real_type reloc)
 {
@@ -4142,6 +4171,7 @@ limited_pcrel_reloc_p (bfd_reloc_code_real_type reloc)
   switch (reloc)
     {
     case BFD_RELOC_16_PCREL_S2:
+    case BFD_RELOC_MIPS16_16_PCREL_S1:
     case BFD_RELOC_MICROMIPS_7_PCREL_S1:
     case BFD_RELOC_MICROMIPS_10_PCREL_S1:
     case BFD_RELOC_MICROMIPS_16_PCREL_S1:
@@ -6264,8 +6294,8 @@ nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
   return 0;
 }
 
-#define BASE_REG_EQ(INSN1, INSN2)      \
-  ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
+#define BASE_REG_EQ(INSN1, INSN2)      \
+  ((((INSN1) >> OP_SH_RS) & OP_MASK_RS)        \
       == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
 
 /* Return the minimum alignment for this store instruction.  */
@@ -6787,7 +6817,7 @@ get_append_method (struct mips_cl_insn *ip, expressionS *address_expr,
   if (mips_relax.sequence == 2)
     return APPEND_ADD;
 
-  /* We must not dabble with instructions in a ".set norerorder" block.  */
+  /* We must not dabble with instructions in a ".set noreorder" block.  */
   if (mips_opts.noreorder)
     return APPEND_ADD;
 
@@ -6803,23 +6833,33 @@ get_append_method (struct mips_cl_insn *ip, expressionS *address_expr,
          && gpr_read_mask (ip) != 0)
        return APPEND_ADD_COMPACT;
 
+      if (mips_opts.micromips
+         && ((ip->insn_opcode & 0xffe0) == 0x4580
+             || (!forced_insn_length
+                 && ((ip->insn_opcode & 0xfc00) == 0xcc00
+                     || (ip->insn_opcode & 0xdc00) == 0x8c00))
+             || (ip->insn_opcode & 0xdfe00000) == 0x94000000
+             || (ip->insn_opcode & 0xdc1f0000) == 0x94000000))
+       return APPEND_ADD_COMPACT;
+
       return APPEND_ADD_WITH_NOP;
     }
 
   return APPEND_ADD;
 }
 
-/* IP is a MIPS16 instruction whose opcode we have just changed.
-   Point IP->insn_mo to the new opcode's definition.  */
+/* IP is an instruction whose opcode we have just changed, END points
+   to the end of the opcode table processed.  Point IP->insn_mo to the
+   new opcode's definition.  */
 
 static void
-find_altered_mips16_opcode (struct mips_cl_insn *ip)
+find_altered_opcode (struct mips_cl_insn *ip, const struct mips_opcode *end)
 {
-  const struct mips_opcode *mo, *end;
+  const struct mips_opcode *mo;
 
-  end = &mips16_opcodes[bfd_mips16_num_opcodes];
   for (mo = ip->insn_mo; mo < end; mo++)
-    if ((ip->insn_opcode & mo->mask) == mo->match)
+    if (mo->pinfo != INSN_MACRO
+       && (ip->insn_opcode & mo->mask) == mo->match)
       {
        ip->insn_mo = mo;
        return;
@@ -6827,6 +6867,24 @@ find_altered_mips16_opcode (struct mips_cl_insn *ip)
   abort ();
 }
 
+/* IP is a MIPS16 instruction whose opcode we have just changed.
+   Point IP->insn_mo to the new opcode's definition.  */
+
+static void
+find_altered_mips16_opcode (struct mips_cl_insn *ip)
+{
+  find_altered_opcode (ip, &mips16_opcodes[bfd_mips16_num_opcodes]);
+}
+
+/* IP is a microMIPS instruction whose opcode we have just changed.
+   Point IP->insn_mo to the new opcode's definition.  */
+
+static void
+find_altered_micromips_opcode (struct mips_cl_insn *ip)
+{
+  find_altered_opcode (ip, &micromips_opcodes[bfd_micromips_num_opcodes]);
+}
+
 /* For microMIPS macros, we need to generate a local number label
    as the target of branches.  */
 #define MICROMIPS_LABEL_CHAR           '\037'
@@ -6963,6 +7021,7 @@ calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
       return TRUE;
 
     case BFD_RELOC_HI16_S:
+    case BFD_RELOC_HI16_S_PCREL:
     case BFD_RELOC_MICROMIPS_HI16_S:
     case BFD_RELOC_MIPS16_HI16_S:
       *result = ((operand + 0x8000) >> 16) & 0xffff;
@@ -6975,6 +7034,7 @@ calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
       return TRUE;
 
     case BFD_RELOC_LO16:
+    case BFD_RELOC_LO16_PCREL:
     case BFD_RELOC_MICROMIPS_LO16:
     case BFD_RELOC_MIPS16_LO16:
       *result = operand & 0xffff;
@@ -7013,8 +7073,14 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
   prev_pinfo2 = history[0].insn_mo->pinfo2;
   pinfo = ip->insn_mo->pinfo;
 
+  /* Don't raise alarm about `nods' frags as they'll fill in the right
+     kind of nop in relaxation if required.  */
   if (mips_opts.micromips
       && !expansionp
+      && !(history[0].frag
+          && history[0].frag->fr_type == rs_machine_dependent
+          && RELAX_MICROMIPS_P (history[0].frag->fr_subtype)
+          && RELAX_MICROMIPS_NODS (history[0].frag->fr_subtype))
       && (((prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
           && micromips_insn_length (ip->insn_mo) != 2)
          || ((prev_pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
@@ -7035,7 +7101,9 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
          {
            int shift;
 
-           shift = mips_opts.micromips ? 1 : 2;
+           /* Shift is 2, unusually, for microMIPS JALX.  */
+           shift = (mips_opts.micromips
+                    && strcmp (ip->insn_mo->name, "jalx") != 0) ? 1 : 2;
            if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
              as_bad (_("jump to misaligned address (0x%lx)"),
                      (unsigned long) address_expr->X_add_number);
@@ -7262,20 +7330,26 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
              16-bit/32-bit instructions.  */
           && !forced_insn_length)
     {
-      bfd_boolean relax16 = *reloc_type > BFD_RELOC_UNUSED;
+      bfd_boolean relax16 = (method != APPEND_ADD_COMPACT
+                            && *reloc_type > BFD_RELOC_UNUSED);
       int type = relax16 ? *reloc_type - BFD_RELOC_UNUSED : 0;
       int uncond = uncond_branch_p (ip) ? -1 : 0;
-      int compact = compact_branch_p (ip);
+      int compact = compact_branch_p (ip) || method == APPEND_ADD_COMPACT;
+      int nods = method == APPEND_ADD_WITH_NOP;
       int al = pinfo & INSN_WRITE_GPR_31;
-      int length32;
+      int length32 = nods ? 8 : 4;
 
       gas_assert (address_expr != NULL);
       gas_assert (!mips_relax.sequence);
 
       relaxed_branch = TRUE;
-      length32 = relaxed_micromips_32bit_branch_length (NULL, NULL, uncond);
-      add_relaxed_insn (ip, relax32 ? length32 : 4, relax16 ? 2 : 4,
-                       RELAX_MICROMIPS_ENCODE (type, AT, uncond, compact, al,
+      if (nods)
+       method = APPEND_ADD;
+      if (relax32)
+       length32 = relaxed_micromips_32bit_branch_length (NULL, NULL, uncond);
+      add_relaxed_insn (ip, length32, relax16 ? 2 : 4,
+                       RELAX_MICROMIPS_ENCODE (type, AT, mips_opts.insn32,
+                                               uncond, compact, al, nods,
                                                relax32, 0, 0),
                        address_expr->X_add_symbol,
                        address_expr->X_add_number);
@@ -7283,15 +7357,31 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
     }
   else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
     {
+      symbolS *symbol;
+      offsetT offset;
+
       /* We need to set up a variant frag.  */
       gas_assert (address_expr != NULL);
+      /* Pass any `O_symbol' expression unchanged as an `expr_section'
+         symbol created by `make_expr_symbol' may not get a necessary
+         external relocation produced.  */
+      if (address_expr->X_op == O_symbol)
+       {
+         symbol = address_expr->X_add_symbol;
+         offset = address_expr->X_add_number;
+       }
+      else
+       {
+         symbol = make_expr_symbol (address_expr);
+         offset = 0;
+       }
       add_relaxed_insn (ip, 4, 0,
                        RELAX_MIPS16_ENCODE
                        (*reloc_type - BFD_RELOC_UNUSED,
                         forced_insn_length == 2, forced_insn_length == 4,
                         delayed_branch_p (&history[0]),
                         history[0].mips16_absolute_jump_p),
-                       make_expr_symbol (address_expr), 0);
+                       symbol, offset);
     }
   else if (mips_opts.mips16 && insn_length (ip) == 2)
     {
@@ -7438,7 +7528,6 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
            ip->fixp[i]->fx_tcbit = 1;
          }
     }
-  install_insn (ip);
 
   /* Update the register mask information.  */
   mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
@@ -7465,9 +7554,50 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
 
     case APPEND_ADD_COMPACT:
       /* Convert MIPS16 jr/jalr into a "compact" jump.  */
-      gas_assert (mips_opts.mips16);
-      ip->insn_opcode |= 0x0080;
-      find_altered_mips16_opcode (ip);
+      if (mips_opts.mips16)
+       {
+         ip->insn_opcode |= 0x0080;
+         find_altered_mips16_opcode (ip);
+       }
+      /* Convert microMIPS instructions.  */
+      else if (mips_opts.micromips)
+       {
+         /* jr16->jrc */
+         if ((ip->insn_opcode & 0xffe0) == 0x4580)
+           ip->insn_opcode |= 0x0020;
+         /* b16->bc */
+         else if ((ip->insn_opcode & 0xfc00) == 0xcc00)
+           ip->insn_opcode = 0x40e00000;
+         /* beqz16->beqzc, bnez16->bnezc */
+         else if ((ip->insn_opcode & 0xdc00) == 0x8c00)
+           {
+             unsigned long regno;
+
+             regno = ip->insn_opcode >> MICROMIPSOP_SH_MD;
+             regno &= MICROMIPSOP_MASK_MD;
+             regno = micromips_to_32_reg_d_map[regno];
+             ip->insn_opcode = (((ip->insn_opcode << 9) & 0x00400000)
+                                | (regno << MICROMIPSOP_SH_RS)
+                                | 0x40a00000) ^ 0x00400000;
+           }
+         /* beqz->beqzc, bnez->bnezc */
+         else if ((ip->insn_opcode & 0xdfe00000) == 0x94000000)
+           ip->insn_opcode = ((ip->insn_opcode & 0x001f0000)
+                              | ((ip->insn_opcode >> 7) & 0x00400000)
+                              | 0x40a00000) ^ 0x00400000;
+         /* beq $0->beqzc, bne $0->bnezc */
+         else if ((ip->insn_opcode & 0xdc1f0000) == 0x94000000)
+           ip->insn_opcode = (((ip->insn_opcode >>
+                                (MICROMIPSOP_SH_RT - MICROMIPSOP_SH_RS))
+                               & (MICROMIPSOP_MASK_RS << MICROMIPSOP_SH_RS))
+                              | ((ip->insn_opcode >> 7) & 0x00400000)
+                              | 0x40a00000) ^ 0x00400000;
+         else
+           abort ();
+         find_altered_micromips_opcode (ip);
+       }
+      else
+       abort ();
       install_insn (ip);
       insert_into_history (0, 1, ip);
       break;
@@ -7475,13 +7605,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
     case APPEND_SWAP:
       {
        struct mips_cl_insn delay = history[0];
-       if (mips_opts.mips16)
-         {
-           know (delay.frag == ip->frag);
-           move_insn (ip, delay.frag, delay.where);
-           move_insn (&delay, ip->frag, ip->where + insn_length (ip));
-         }
-       else if (relaxed_branch || delay.frag != ip->frag)
+
+       if (relaxed_branch || delay.frag != ip->frag)
          {
            /* Add the delay slot instruction to the end of the
               current frag and shrink the fixed part of the
@@ -7494,9 +7619,10 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
          }
        else
          {
-           move_insn (&delay, ip->frag,
-                      ip->where - branch_disp + insn_length (ip));
-           move_insn (ip, history[0].frag, history[0].where);
+           /* If this is not a relaxed branch and we are in the
+              same frag, then just swap the instructions.  */
+           move_insn (ip, delay.frag, delay.where);
+           move_insn (&delay, ip->frag, ip->where + insn_length (ip));
          }
        history[0] = *ip;
        delay.fixed_p = 1;
@@ -8231,19 +8357,25 @@ macro_start (void)
   memset (&mips_macro_warning.insns, 0, sizeof (mips_macro_warning.insns));
   mips_macro_warning.delay_slot_p = (mips_opts.noreorder
                                     && delayed_branch_p (&history[0]));
-  switch (history[0].insn_mo->pinfo2
-         & (INSN2_BRANCH_DELAY_32BIT | INSN2_BRANCH_DELAY_16BIT))
-    {
-    case INSN2_BRANCH_DELAY_32BIT:
-      mips_macro_warning.delay_slot_length = 4;
-      break;
-    case INSN2_BRANCH_DELAY_16BIT:
-      mips_macro_warning.delay_slot_length = 2;
-      break;
-    default:
-      mips_macro_warning.delay_slot_length = 0;
-      break;
-    }
+  if (history[0].frag
+      && history[0].frag->fr_type == rs_machine_dependent
+      && RELAX_MICROMIPS_P (history[0].frag->fr_subtype)
+      && RELAX_MICROMIPS_NODS (history[0].frag->fr_subtype))
+    mips_macro_warning.delay_slot_length = 0;
+  else
+    switch (history[0].insn_mo->pinfo2
+           & (INSN2_BRANCH_DELAY_32BIT | INSN2_BRANCH_DELAY_16BIT))
+      {
+      case INSN2_BRANCH_DELAY_32BIT:
+       mips_macro_warning.delay_slot_length = 4;
+       break;
+      case INSN2_BRANCH_DELAY_16BIT:
+       mips_macro_warning.delay_slot_length = 2;
+       break;
+      default:
+       mips_macro_warning.delay_slot_length = 0;
+       break;
+      }
   mips_macro_warning.first_frag = NULL;
 }
 
@@ -9716,6 +9848,7 @@ macro (struct mips_cl_insn *ip, char *str)
     {
     case M_DABS:
       dbl = 1;
+      /* Fall through.  */
     case M_ABS:
       /*    bgez    $a0,1f
            move    v0,$a0
@@ -9865,6 +9998,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BGEL:
       likely = 1;
+      /* Fall through.  */
     case M_BGE:
       if (op[1] == 0)
        macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, op[0]);
@@ -9890,6 +10024,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BGTL_I:
       likely = 1;
+      /* Fall through.  */
     case M_BGT_I:
       /* Check for > max integer.  */
       if (imm_expr.X_add_number >= GPR_SMAX)
@@ -9936,6 +10071,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BGEUL:
       likely = 1;
+      /* Fall through.  */
     case M_BGEU:
       if (op[1] == 0)
        goto do_true;
@@ -9953,6 +10089,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BGTUL_I:
       likely = 1;
+      /* Fall through.  */
     case M_BGTU_I:
       if (op[0] == 0
          || (GPR_SIZE == 32
@@ -9980,6 +10117,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BGTL:
       likely = 1;
+      /* Fall through.  */
     case M_BGT:
       if (op[1] == 0)
        macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, op[0]);
@@ -9996,6 +10134,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BGTUL:
       likely = 1;
+      /* Fall through.  */
     case M_BGTU:
       if (op[1] == 0)
        macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
@@ -10013,6 +10152,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BLEL:
       likely = 1;
+      /* Fall through.  */
     case M_BLE:
       if (op[1] == 0)
        macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, op[0]);
@@ -10029,6 +10169,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BLEL_I:
       likely = 1;
+      /* Fall through.  */
     case M_BLE_I:
       if (imm_expr.X_add_number >= GPR_SMAX)
        goto do_true;
@@ -10053,6 +10194,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BLEUL:
       likely = 1;
+      /* Fall through.  */
     case M_BLEU:
       if (op[1] == 0)
        macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
@@ -10070,6 +10212,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BLEUL_I:
       likely = 1;
+      /* Fall through.  */
     case M_BLEU_I:
       if (op[0] == 0
          || (GPR_SIZE == 32
@@ -10097,6 +10240,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BLTL:
       likely = 1;
+      /* Fall through.  */
     case M_BLT:
       if (op[1] == 0)
        macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, op[0]);
@@ -10113,6 +10257,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_BLTUL:
       likely = 1;
+      /* Fall through.  */
     case M_BLTU:
       if (op[1] == 0)
        goto do_false;
@@ -10130,11 +10275,13 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_DDIV_3:
       dbl = 1;
+      /* Fall through.  */
     case M_DIV_3:
       s = "mflo";
       goto do_div3;
     case M_DREM_3:
       dbl = 1;
+      /* Fall through.  */
     case M_REM_3:
       s = "mfhi";
     do_div3:
@@ -10326,11 +10473,13 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_DLCA_AB:
       dbl = 1;
+      /* Fall through.  */
     case M_LCA_AB:
       call = 1;
       goto do_la;
     case M_DLA_AB:
       dbl = 1;
+      /* Fall through.  */
     case M_LA_AB:
     do_la:
       /* Load the address of a symbol into a register.  If breg is not
@@ -11035,7 +11184,7 @@ macro (struct mips_cl_insn *ip, char *str)
                  if (mips_opts.noreorder)
                    macro_build (NULL, "nop", "");
                  expr1.X_add_number = mips_cprestore_offset;
-                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
                                                mips_gp_register,
                                                mips_frame_reg,
                                                HAVE_64BIT_ADDRESSES);
@@ -11179,7 +11328,7 @@ macro (struct mips_cl_insn *ip, char *str)
                  if (mips_opts.noreorder)
                    macro_build (NULL, "nop", "");
                  expr1.X_add_number = mips_cprestore_offset;
-                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+                 macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
                                                mips_gp_register,
                                                mips_frame_reg,
                                                HAVE_64BIT_ADDRESSES);
@@ -12136,8 +12285,8 @@ macro (struct mips_cl_insn *ip, char *str)
                  && offset_expr.X_add_number == 0);
       s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
       if (strcmp (s, ".lit8") == 0)
-       {
-         op[2] = mips_gp_register;
+       {
+         op[2] = mips_gp_register;
          offset_reloc[0] = BFD_RELOC_MIPS_LITERAL;
          offset_reloc[1] = BFD_RELOC_UNUSED;
          offset_reloc[2] = BFD_RELOC_UNUSED;
@@ -12159,7 +12308,7 @@ macro (struct mips_cl_insn *ip, char *str)
          offset_reloc[0] = BFD_RELOC_LO16;
          offset_reloc[1] = BFD_RELOC_UNUSED;
          offset_reloc[2] = BFD_RELOC_UNUSED;
-       }
+       }
       align = 8;
       /* Fall through */
 
@@ -12553,6 +12702,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_DMUL:
       dbl = 1;
+      /* Fall through.  */
     case M_MUL:
       if (mips_opts.arch == CPU_R5900)
        macro_build (NULL, dbl ? "dmultu" : "multu", "d,s,t", op[0], op[1],
@@ -12566,6 +12716,7 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_DMUL_I:
       dbl = 1;
+      /* Fall through.  */
     case M_MUL_I:
       /* The MIPS assembler some times generates shifts and adds.  I'm
         not trying to be that fancy. GCC should do this for us
@@ -12578,12 +12729,14 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_DMULO_I:
       dbl = 1;
+      /* Fall through.  */
     case M_MULO_I:
       imm = 1;
       goto do_mulo;
 
     case M_DMULO:
       dbl = 1;
+      /* Fall through.  */
     case M_MULO:
     do_mulo:
       start_noreorder ();
@@ -12615,12 +12768,14 @@ macro (struct mips_cl_insn *ip, char *str)
 
     case M_DMULOU_I:
       dbl = 1;
+      /* Fall through.  */
     case M_MULOU_I:
       imm = 1;
       goto do_mulou;
 
     case M_DMULOU:
       dbl = 1;
+      /* Fall through.  */
     case M_MULOU:
     do_mulou:
       start_noreorder ();
@@ -13324,11 +13479,13 @@ mips16_macro (struct mips_cl_insn *ip)
 
     case M_DDIV_3:
       dbl = 1;
+      /* Fall through.  */
     case M_DIV_3:
       s = "mflo";
       goto do_div3;
     case M_DREM_3:
       dbl = 1;
+      /* Fall through.  */
     case M_REM_3:
       s = "mfhi";
     do_div3:
@@ -13373,6 +13530,7 @@ mips16_macro (struct mips_cl_insn *ip)
 
     case M_DMUL:
       dbl = 1;
+      /* Fall through.  */
     case M_MUL:
       macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", op[1], op[2]);
       macro_build (NULL, "mflo", "x", op[0]);
@@ -14588,6 +14746,7 @@ md_pcrel_from (fixS *fixP)
 
     case BFD_RELOC_MICROMIPS_16_PCREL_S1:
     case BFD_RELOC_MICROMIPS_JMP:
+    case BFD_RELOC_MIPS16_16_PCREL_S1:
     case BFD_RELOC_16_PCREL_S2:
     case BFD_RELOC_MIPS_21_PCREL_S2:
     case BFD_RELOC_MIPS_26_PCREL_S2:
@@ -14595,6 +14754,11 @@ md_pcrel_from (fixS *fixP)
       /* Return the address of the delay slot.  */
       return addr + 4;
 
+    case BFD_RELOC_MIPS_18_PCREL_S3:
+      /* Return the aligned address of the doubleword containing
+         the instruction.  */
+      return addr & ~7;
+
     default:
       return addr;
     }
@@ -14756,8 +14920,19 @@ mips_force_relocation (fixS *fixp)
       || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1)
     return 1;
 
+  /* We want to keep BFD_RELOC_16_PCREL_S2 BFD_RELOC_MIPS_21_PCREL_S2
+     and BFD_RELOC_MIPS_26_PCREL_S2 relocations against MIPS16 and
+     microMIPS symbols so that we can do cross-mode branch diagnostics
+     and BAL to JALX conversion by the linker.  */
+  if ((fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+       || fixp->fx_r_type == BFD_RELOC_MIPS_21_PCREL_S2
+       || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2)
+      && fixp->fx_addsy
+      && ELF_ST_IS_COMPRESSED (S_GET_OTHER (fixp->fx_addsy)))
+    return 1;
+
   /* We want all PC-relative relocations to be kept for R6 relaxation.  */
-  if (ISA_IS_R6 (mips_opts.isa)
+  if (ISA_IS_R6 (file_mips_opts.isa)
       && (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
          || fixp->fx_r_type == BFD_RELOC_MIPS_21_PCREL_S2
          || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
@@ -14770,6 +14945,22 @@ mips_force_relocation (fixS *fixp)
   return 0;
 }
 
+/* Implement TC_FORCE_RELOCATION_ABS.  */
+
+bfd_boolean
+mips_force_relocation_abs (fixS *fixp)
+{
+  if (generic_force_reloc (fixp))
+    return TRUE;
+
+  /* These relocations do not have enough bits in the in-place addend
+     to hold an arbitrary absolute section's offset.  */
+  if (HAVE_IN_PLACE_ADDENDS && limited_pcrel_reloc_p (fixp->fx_r_type))
+    return TRUE;
+
+  return FALSE;
+}
+
 /* Read the instruction associated with RELOC from BUF.  */
 
 static unsigned int
@@ -14794,6 +14985,195 @@ write_reloc_insn (char *buf, bfd_reloc_code_real_type reloc,
     write_insn (buf, insn);
 }
 
+/* Return TRUE if the instruction pointed to by FIXP is an invalid jump
+   to a symbol in another ISA mode, which cannot be converted to JALX.  */
+
+static bfd_boolean
+fix_bad_cross_mode_jump_p (fixS *fixP)
+{
+  unsigned long opcode;
+  int other;
+  char *buf;
+
+  if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+    return FALSE;
+
+  other = S_GET_OTHER (fixP->fx_addsy);
+  buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+  opcode = read_reloc_insn (buf, fixP->fx_r_type) >> 26;
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_MIPS_JMP:
+      return opcode != 0x1d && opcode != 0x03 && ELF_ST_IS_COMPRESSED (other);
+    case BFD_RELOC_MICROMIPS_JMP:
+      return opcode != 0x3c && opcode != 0x3d && !ELF_ST_IS_MICROMIPS (other);
+    default:
+      return FALSE;
+    }
+}
+
+/* Return TRUE if the instruction pointed to by FIXP is an invalid JALX
+   jump to a symbol in the same ISA mode.  */
+
+static bfd_boolean
+fix_bad_same_mode_jalx_p (fixS *fixP)
+{
+  unsigned long opcode;
+  int other;
+  char *buf;
+
+  if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+    return FALSE;
+
+  other = S_GET_OTHER (fixP->fx_addsy);
+  buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+  opcode = read_reloc_insn (buf, fixP->fx_r_type) >> 26;
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_MIPS_JMP:
+      return opcode == 0x1d && !ELF_ST_IS_COMPRESSED (other);
+    case BFD_RELOC_MIPS16_JMP:
+      return opcode == 0x07 && ELF_ST_IS_COMPRESSED (other);
+    case BFD_RELOC_MICROMIPS_JMP:
+      return opcode == 0x3c && ELF_ST_IS_COMPRESSED (other);
+    default:
+      return FALSE;
+    }
+}
+
+/* Return TRUE if the instruction pointed to by FIXP is an invalid jump
+   to a symbol whose value plus addend is not aligned according to the
+   ultimate (after linker relaxation) jump instruction's immediate field
+   requirement, either to (1 << SHIFT), or, for jumps from microMIPS to
+   regular MIPS code, to (1 << 2).  */
+
+static bfd_boolean
+fix_bad_misaligned_jump_p (fixS *fixP, int shift)
+{
+  bfd_boolean micro_to_mips_p;
+  valueT val;
+  int other;
+
+  if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+    return FALSE;
+
+  other = S_GET_OTHER (fixP->fx_addsy);
+  val = S_GET_VALUE (fixP->fx_addsy) | ELF_ST_IS_COMPRESSED (other);
+  val += fixP->fx_offset;
+  micro_to_mips_p = (fixP->fx_r_type == BFD_RELOC_MICROMIPS_JMP
+                    && !ELF_ST_IS_MICROMIPS (other));
+  return ((val & ((1 << (micro_to_mips_p ? 2 : shift)) - 1))
+         != ELF_ST_IS_COMPRESSED (other));
+}
+
+/* Return TRUE if the instruction pointed to by FIXP is an invalid branch
+   to a symbol whose annotation indicates another ISA mode.  For absolute
+   symbols check the ISA bit instead.
+
+   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.  */
+
+static bfd_boolean
+fix_bad_cross_mode_branch_p (fixS *fixP)
+{
+  bfd_boolean absolute_p;
+  unsigned long opcode;
+  asection *symsec;
+  valueT val;
+  int other;
+  char *buf;
+
+  if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+    return FALSE;
+
+  symsec = S_GET_SEGMENT (fixP->fx_addsy);
+  absolute_p = bfd_is_abs_section (symsec);
+
+  val = S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset;
+  other = S_GET_OTHER (fixP->fx_addsy);
+
+  buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+  opcode = read_reloc_insn (buf, fixP->fx_r_type) >> 16;
+  switch (fixP->fx_r_type)
+    {
+    case BFD_RELOC_16_PCREL_S2:
+      return ((absolute_p ? val & 1 : ELF_ST_IS_COMPRESSED (other))
+             && opcode != 0x0411);
+    case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+      return ((absolute_p ? !(val & 1) : !ELF_ST_IS_MICROMIPS (other))
+             && opcode != 0x4060);
+    case BFD_RELOC_MIPS_21_PCREL_S2:
+    case BFD_RELOC_MIPS_26_PCREL_S2:
+      return absolute_p ? val & 1 : ELF_ST_IS_COMPRESSED (other);
+    case BFD_RELOC_MIPS16_16_PCREL_S1:
+      return absolute_p ? !(val & 1) : !ELF_ST_IS_MIPS16 (other);
+    case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+    case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+      return absolute_p ? !(val & 1) : !ELF_ST_IS_MICROMIPS (other);
+    default:
+      abort ();
+    }
+}
+
+/* Return TRUE if the symbol plus addend associated with a regular MIPS
+   branch instruction pointed to by FIXP is not aligned according to the
+   branch instruction's immediate field requirement.  We need the addend
+   to preserve the ISA bit and also the sum must not have bit 2 set.  We
+   must explicitly OR in the ISA bit from symbol annotation as the bit
+   won't be set in the symbol's value then.  */
+
+static bfd_boolean
+fix_bad_misaligned_branch_p (fixS *fixP)
+{
+  bfd_boolean absolute_p;
+  asection *symsec;
+  valueT isa_bit;
+  valueT val;
+  valueT off;
+  int other;
+
+  if (!fixP->fx_addsy || S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+    return FALSE;
+
+  symsec = S_GET_SEGMENT (fixP->fx_addsy);
+  absolute_p = bfd_is_abs_section (symsec);
+
+  val = S_GET_VALUE (fixP->fx_addsy);
+  other = S_GET_OTHER (fixP->fx_addsy);
+  off = fixP->fx_offset;
+
+  isa_bit = absolute_p ? (val + off) & 1 : ELF_ST_IS_COMPRESSED (other);
+  val |= ELF_ST_IS_COMPRESSED (other);
+  val += off;
+  return (val & 0x3) != isa_bit;
+}
+
+/* Make the necessary checks on a regular MIPS branch pointed to by FIXP
+   and its calculated value VAL.  */
+
+static void
+fix_validate_branch (fixS *fixP, valueT val)
+{
+  if (fixP->fx_done && (val & 0x3) != 0)
+    as_bad_where (fixP->fx_file, fixP->fx_line,
+                 _("branch to misaligned address (0x%lx)"),
+                 (long) (val + md_pcrel_from (fixP)));
+  else if (fix_bad_cross_mode_branch_p (fixP))
+    as_bad_where (fixP->fx_file, fixP->fx_line,
+                 _("branch to a symbol in another ISA mode"));
+  else if (fix_bad_misaligned_branch_p (fixP))
+    as_bad_where (fixP->fx_file, fixP->fx_line,
+                 _("branch to misaligned address (0x%lx)"),
+                 (long) (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset));
+  else if (HAVE_IN_PLACE_ADDENDS && (fixP->fx_offset & 0x3) != 0)
+    as_bad_where (fixP->fx_file, fixP->fx_line,
+                 _("cannot encode misaligned addend "
+                   "in the relocatable field (0x%lx)"),
+                 (long) fixP->fx_offset);
+}
+
 /* Apply a fixup to the object file.  */
 
 void
@@ -14807,6 +15187,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     switch (fixP->fx_r_type)
       {
       case BFD_RELOC_16_PCREL_S2:
+      case BFD_RELOC_MIPS16_16_PCREL_S1:
       case BFD_RELOC_MICROMIPS_7_PCREL_S1:
       case BFD_RELOC_MICROMIPS_10_PCREL_S1:
       case BFD_RELOC_MICROMIPS_16_PCREL_S1:
@@ -14893,16 +15274,48 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_MIPS16_TLS_GOTTPREL:
     case BFD_RELOC_MIPS16_TLS_TPREL_HI16:
     case BFD_RELOC_MIPS16_TLS_TPREL_LO16:
-      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 */
+      if (fixP->fx_addsy)
+       S_SET_THREAD_LOCAL (fixP->fx_addsy);
+      else
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("TLS relocation against a constant"));
+      break;
 
     case BFD_RELOC_MIPS_JMP:
+    case BFD_RELOC_MIPS16_JMP:
+    case BFD_RELOC_MICROMIPS_JMP:
+      {
+       int shift;
+
+       gas_assert (!fixP->fx_done);
+
+       /* Shift is 2, unusually, for microMIPS JALX.  */
+       if (fixP->fx_r_type == BFD_RELOC_MICROMIPS_JMP
+           && (read_compressed_insn (buf, 4) >> 26) != 0x3c)
+         shift = 1;
+       else
+         shift = 2;
+
+       if (fix_bad_cross_mode_jump_p (fixP))
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("jump to a symbol in another ISA mode"));
+       else if (fix_bad_same_mode_jalx_p (fixP))
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("JALX to a symbol in the same ISA mode"));
+       else if (fix_bad_misaligned_jump_p (fixP, shift))
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("jump to misaligned address (0x%lx)"),
+                       (long) (S_GET_VALUE (fixP->fx_addsy)
+                               + fixP->fx_offset));
+       else if (HAVE_IN_PLACE_ADDENDS
+                && (fixP->fx_offset & ((1 << shift) - 1)) != 0)
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("cannot encode misaligned addend "
+                         "in the relocatable field (0x%lx)"),
+                       (long) fixP->fx_offset);
+      }
+      /* Fall through.  */
+
     case BFD_RELOC_MIPS_SHIFT5:
     case BFD_RELOC_MIPS_SHIFT6:
     case BFD_RELOC_MIPS_GOT_DISP:
@@ -14930,14 +15343,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     case BFD_RELOC_MIPS_GOT_LO16:
     case BFD_RELOC_MIPS_CALL_HI16:
     case BFD_RELOC_MIPS_CALL_LO16:
+    case BFD_RELOC_HI16_S_PCREL:
+    case BFD_RELOC_LO16_PCREL:
     case BFD_RELOC_MIPS16_GPREL:
     case BFD_RELOC_MIPS16_GOT16:
     case BFD_RELOC_MIPS16_CALL16:
     case BFD_RELOC_MIPS16_HI16:
     case BFD_RELOC_MIPS16_HI16_S:
     case BFD_RELOC_MIPS16_LO16:
-    case BFD_RELOC_MIPS16_JMP:
-    case BFD_RELOC_MICROMIPS_JMP:
     case BFD_RELOC_MICROMIPS_GOT_DISP:
     case BFD_RELOC_MICROMIPS_GOT_PAGE:
     case BFD_RELOC_MICROMIPS_GOT_OFST:
@@ -15011,16 +15424,39 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       break;
 
     case BFD_RELOC_MIPS_21_PCREL_S2:
-    case BFD_RELOC_MIPS_26_PCREL_S2:
-      if ((*valP & 0x3) != 0)
+      fix_validate_branch (fixP, *valP);
+      if (!fixP->fx_done)
+       break;
+
+      if (*valP + 0x400000 <= 0x7fffff)
+       {
+         insn = read_insn (buf);
+         insn |= (*valP >> 2) & 0x1fffff;
+         write_insn (buf, insn);
+       }
+      else
        as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("branch to misaligned address (%lx)"), (long) *valP);
+                     _("branch out of range"));
+      break;
 
-      gas_assert (!fixP->fx_done);
+    case BFD_RELOC_MIPS_26_PCREL_S2:
+      fix_validate_branch (fixP, *valP);
+      if (!fixP->fx_done)
+       break;
+
+      if (*valP + 0x8000000 <= 0xfffffff)
+       {
+         insn = read_insn (buf);
+         insn |= (*valP >> 2) & 0x3ffffff;
+         write_insn (buf, insn);
+       }
+      else
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch out of range"));
       break;
 
     case BFD_RELOC_MIPS_18_PCREL_S3:
-      if ((S_GET_VALUE (fixP->fx_addsy) & 0x7) != 0)
+      if (fixP->fx_addsy && (S_GET_VALUE (fixP->fx_addsy) & 0x7) != 0)
        as_bad_where (fixP->fx_file, fixP->fx_line,
                      _("PC-relative access using misaligned symbol (%lx)"),
                      (long) S_GET_VALUE (fixP->fx_addsy));
@@ -15028,28 +15464,41 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        as_bad_where (fixP->fx_file, fixP->fx_line,
                      _("PC-relative access using misaligned offset (%lx)"),
                      (long) fixP->fx_offset);
+      if (!fixP->fx_done)
+       break;
 
-      gas_assert (!fixP->fx_done);
+      if (*valP + 0x100000 <= 0x1fffff)
+       {
+         insn = read_insn (buf);
+         insn |= (*valP >> 3) & 0x3ffff;
+         write_insn (buf, insn);
+       }
+      else
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("PC-relative access out of range"));
       break;
 
     case BFD_RELOC_MIPS_19_PCREL_S2:
       if ((*valP & 0x3) != 0)
        as_bad_where (fixP->fx_file, fixP->fx_line,
                      _("PC-relative access to misaligned address (%lx)"),
-                     (long) (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset));
-
-      gas_assert (!fixP->fx_done);
-      break;
+                     (long) *valP);
+      if (!fixP->fx_done)
+       break;
 
-    case BFD_RELOC_HI16_S_PCREL:
-    case BFD_RELOC_LO16_PCREL:
-      gas_assert (!fixP->fx_done);
+      if (*valP + 0x100000 <= 0x1fffff)
+       {
+         insn = read_insn (buf);
+         insn |= (*valP >> 2) & 0x7ffff;
+         write_insn (buf, insn);
+       }
+      else
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("PC-relative access out of range"));
       break;
 
     case BFD_RELOC_16_PCREL_S2:
-      if ((*valP & 0x3) != 0)
-       as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("branch to misaligned address (%lx)"), (long) *valP);
+      fix_validate_branch (fixP, *valP);
 
       /* We need to save the bits in the instruction since fixup_segment()
         might be deleting the relocation entry (i.e., a branch within
@@ -15097,18 +15546,26 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        }
       break;
 
+    case BFD_RELOC_MIPS16_16_PCREL_S1:
     case BFD_RELOC_MICROMIPS_7_PCREL_S1:
     case BFD_RELOC_MICROMIPS_10_PCREL_S1:
     case BFD_RELOC_MICROMIPS_16_PCREL_S1:
-      /* We adjust the offset back to even.  */
-      if ((*valP & 0x1) != 0)
-       --(*valP);
-
-      if (! fixP->fx_done)
-       break;
-
-      /* Should never visit here, because we keep the relocation.  */
-      abort ();
+      gas_assert (!fixP->fx_done);
+      if (fix_bad_cross_mode_branch_p (fixP))
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch to a symbol in another ISA mode"));
+      else if (fixP->fx_addsy
+              && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+              && !bfd_is_abs_section (S_GET_SEGMENT (fixP->fx_addsy))
+              && (fixP->fx_offset & 0x1) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("branch to misaligned address (0x%lx)"),
+                     (long) (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset));
+      else if (HAVE_IN_PLACE_ADDENDS && (fixP->fx_offset & 0x1) != 0)
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("cannot encode misaligned addend "
+                       "in the relocatable field (0x%lx)"),
+                     (long) fixP->fx_offset);
       break;
 
     case BFD_RELOC_VTABLE_INHERIT:
@@ -15513,10 +15970,29 @@ struct mips_option_stack
 
 static struct mips_option_stack *mips_opts_stack;
 
-static bfd_boolean
+/* Return status for .set/.module option handling.  */
+
+enum code_option_type
+{
+  /* Unrecognized option.  */
+  OPTION_TYPE_BAD = -1,
+
+  /* Ordinary option.  */
+  OPTION_TYPE_NORMAL,
+
+  /* ISA changing option.  */
+  OPTION_TYPE_ISA
+};
+
+/* Handle common .set/.module options.  Return status indicating option
+   type.  */
+
+static enum code_option_type
 parse_code_option (char * name)
 {
+  bfd_boolean isa_set = FALSE;
   const struct mips_ase *ase;
+
   if (strncmp (name, "at=", 3) == 0)
     {
       char *s = name + 3;
@@ -15589,6 +16065,7 @@ parse_code_option (char * name)
            {
              mips_opts.arch = p->cpu;
              mips_opts.isa = p->isa;
+             isa_set = TRUE;
            }
        }
       else if (strncmp (name, "mips", 4) == 0)
@@ -15602,6 +16079,7 @@ parse_code_option (char * name)
            {
              mips_opts.arch = p->cpu;
              mips_opts.isa = p->isa;
+             isa_set = TRUE;
            }
        }
       else
@@ -15620,8 +16098,9 @@ parse_code_option (char * name)
   else if (strcmp (name, "nosym32") == 0)
     mips_opts.sym32 = FALSE;
   else
-    return FALSE;
-  return TRUE;
+    return OPTION_TYPE_BAD;
+
+  return isa_set ? OPTION_TYPE_ISA : OPTION_TYPE_NORMAL;
 }
 
 /* Handle the .set pseudo-op.  */
@@ -15629,8 +16108,8 @@ parse_code_option (char * name)
 static void
 s_mipsset (int x ATTRIBUTE_UNUSED)
 {
+  enum code_option_type type = OPTION_TYPE_NORMAL;
   char *name = input_line_pointer, ch;
-  int prev_isa = mips_opts.isa;
 
   file_mips_check_options ();
 
@@ -15707,12 +16186,16 @@ s_mipsset (int x ATTRIBUTE_UNUSED)
          free (s);
        }
     }
-  else if (!parse_code_option (name))
-    as_warn (_("tried to set unrecognized symbol: %s\n"), name);
+  else
+    {
+      type = parse_code_option (name);
+      if (type == OPTION_TYPE_BAD)
+       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 (mips_opts.isa != prev_isa)
+  if (type == OPTION_TYPE_ISA)
     {
       switch (mips_opts.isa)
        {
@@ -15779,7 +16262,7 @@ s_module (int ignore ATTRIBUTE_UNUSED)
 
   if (!file_mips_opts_checked)
     {
-      if (!parse_code_option (name))
+      if (parse_code_option (name) == OPTION_TYPE_BAD)
        as_bad (_(".module used with unrecognized symbol: %s\n"), name);
 
       /* Update module level settings from mips_opts.  */
@@ -16414,7 +16897,7 @@ s_nan (int ignore ATTRIBUTE_UNUSED)
    directive, such as in:
 
    foo:
-       .stabs ...
+       .stabs ...
        .set mips16
 
    so the current mode wins.  */
@@ -16634,12 +17117,17 @@ mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
   if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype))
     return 1;
 
+  symsec = S_GET_SEGMENT (fragp->fr_symbol);
   type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
   operand = mips16_immed_operand (type, FALSE);
+  if (S_FORCE_RELOC (fragp->fr_symbol, TRUE)
+      || (operand->root.type == OP_PCREL
+         ? sec != symsec
+         : !bfd_is_abs_section (symsec)))
+    return 1;
 
   sym_frag = symbol_get_frag (fragp->fr_symbol);
-  val = S_GET_VALUE (fragp->fr_symbol);
-  symsec = S_GET_SEGMENT (fragp->fr_symbol);
+  val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
 
   if (operand->root.type == OP_PCREL)
     {
@@ -16647,47 +17135,16 @@ mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
       addressT addr;
       offsetT maxtiny;
 
-      /* We won't have the section when we are called from
-         mips_relax_frag.  However, we will always have been called
-         from md_estimate_size_before_relax first.  If this is a
-         branch to a different section, we mark it as such.  If SEC is
-         NULL, and the frag is not marked, then it must be a branch to
-         the same section.  */
-      pcrel_op = (const struct mips_pcrel_operand *) operand;
-      if (sec == NULL)
-       {
-         if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype))
-           return 1;
-       }
-      else
-       {
-         /* Must have been called from md_estimate_size_before_relax.  */
-         if (symsec != sec)
-           {
-             fragp->fr_subtype =
-               RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
-
-             /* FIXME: We should support this, and let the linker
-                 catch branches and loads that are out of range.  */
-             as_bad_where (fragp->fr_file, fragp->fr_line,
-                           _("unsupported PC relative reference to different section"));
+      if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype))
+       return 1;
 
-             return 1;
-           }
-         if (fragp != sym_frag && sym_frag->fr_address == 0)
-           /* Assume non-extended on the first relaxation pass.
-              The address we have calculated will be bogus if this is
-              a forward branch to another frag, as the forward frag
-              will have fr_address == 0.  */
-           return 0;
-       }
+      pcrel_op = (const struct mips_pcrel_operand *) operand;
 
-      /* In this case, we know for sure that the symbol fragment is in
-        the same section.  If the relax_marker of the symbol fragment
-        differs from the relax_marker of this fragment, we have not
-        yet adjusted the symbol fragment fr_address.  We want to add
-        in STRETCH in order to get a better estimate of the address.
-        This particularly matters because of the shift bits.  */
+      /* If the relax_marker of the symbol fragment differs from the
+        relax_marker of this fragment, we have not yet adjusted the
+        symbol fragment fr_address.  We want to add in STRETCH in
+        order to get a better estimate of the address.  This
+        particularly matters because of the shift bits.  */
       if (stretch != 0
          && sym_frag->relax_marker != fragp->relax_marker)
        {
@@ -16747,9 +17204,8 @@ mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
       /* If any of the shifted bits are set, we must use an extended
          opcode.  If the address depends on the size of this
          instruction, this can lead to a loop, so we arrange to always
-         use an extended opcode.  We only check this when we are in
-         the main relaxation loop, when SEC is NULL.  */
-      if ((val & ((1 << operand->shift) - 1)) != 0 && sec == NULL)
+         use an extended opcode.  */
+      if ((val & ((1 << operand->shift) - 1)) != 0)
        {
          fragp->fr_subtype =
            RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
@@ -16770,16 +17226,13 @@ mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
         extended with the next value above maxtiny.  */
       maxtiny = mips_int_operand_max (operand);
       if (val == maxtiny + (1 << operand->shift)
-         && ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype)
-         && sec == NULL)
+         && ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
        {
          fragp->fr_subtype =
            RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
          return 1;
        }
     }
-  else if (symsec != absolute_section && sec != NULL)
-    as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported relocation"));
 
   return !mips16_immed_in_range_p (operand, BFD_RELOC_UNUSED, val);
 }
@@ -16847,6 +17300,21 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update)
   return length;
 }
 
+/* Get a FRAG's branch instruction delay slot size, either from the
+   short-delay-slot bit of a branch-and-link instruction if AL is TRUE,
+   or SHORT_INSN_SIZE otherwise.  */
+
+static int
+frag_branch_delay_slot_size (fragS *fragp, bfd_boolean al, int short_insn_size)
+{
+  char *buf = fragp->fr_literal + fragp->fr_fix;
+
+  if (al)
+    return (read_compressed_insn (buf, 4) & 0x02000000) ? 2 : 4;
+  else
+    return short_insn_size;
+}
+
 /* Compute the length of a branch sequence, and adjust the
    RELAX_MICROMIPS_TOOFAR32 bit accordingly.  If FRAGP is NULL, the
    worst-case length is computed, with UPDATE being used to indicate
@@ -16856,9 +17324,21 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update)
 static int
 relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
 {
+  bfd_boolean insn32 = TRUE;
+  bfd_boolean nods = TRUE;
+  bfd_boolean al = TRUE;
+  int short_insn_size;
   bfd_boolean toofar;
   int length;
 
+  if (fragp)
+    {
+      insn32 = RELAX_MICROMIPS_INSN32 (fragp->fr_subtype);
+      nods = RELAX_MICROMIPS_NODS (fragp->fr_subtype);
+      al = RELAX_MICROMIPS_LINK (fragp->fr_subtype);
+    }
+  short_insn_size = insn32 ? 4 : 2;
+
   if (fragp
       && S_IS_DEFINED (fragp->fr_symbol)
       && !S_IS_WEAK (fragp->fr_symbol)
@@ -16897,10 +17377,11 @@ relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
       bfd_boolean compact = FALSE;
       bfd_boolean uncond;
 
-      if (compact_known)
-       compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
       if (fragp)
-       uncond = RELAX_MICROMIPS_UNCOND (fragp->fr_subtype);
+       {
+         compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
+         uncond = RELAX_MICROMIPS_UNCOND (fragp->fr_subtype);
+       }
       else
        uncond = update < 0;
 
@@ -16912,11 +17393,12 @@ relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
          into:
 
                j       label                   # 4 bytes
-               nop                             # 2 bytes if compact && !PIC
+               nop                             # 2/4 bytes if
+                                               #  compact && (!PIC || insn32)
            0:
        */
-      if (mips_pic == NO_PIC && (!compact_known || compact))
-       length += 2;
+      if ((mips_pic == NO_PIC || insn32) && (!compact_known || compact))
+       length += short_insn_size;
 
       /* If assembling PIC code, we further turn:
 
@@ -16926,18 +17408,31 @@ relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
 
                        lw/ld   at, %got(label)(gp)     # 4 bytes
                        d/addiu at, %lo(label)          # 4 bytes
-                       jr/c    at                      # 2 bytes
+                       jr/c    at                      # 2/4 bytes
        */
       if (mips_pic != NO_PIC)
-       length += 6;
+       length += 4 + short_insn_size;
+
+      /* Add an extra nop if the jump has no compact form and we need
+         to fill the delay slot.  */
+      if ((mips_pic == NO_PIC || al) && nods)
+       length += (fragp
+                  ? frag_branch_delay_slot_size (fragp, al, short_insn_size)
+                  : short_insn_size);
 
       /* If branch <br> is conditional, we prepend negated branch <brneg>:
 
                        <brneg> 0f                      # 4 bytes
-                       nop                             # 2 bytes if !compact
+                       nop                             # 2/4 bytes if !compact
        */
       if (!uncond)
-       length += (compact_known && compact) ? 4 : 6;
+       length += (compact_known && compact) ? 4 : 4 + short_insn_size;
+    }
+  else if (nods)
+    {
+      /* Add an extra nop to fill the delay slot.  */
+      gas_assert (fragp);
+      length += frag_branch_delay_slot_size (fragp, al, short_insn_size);
     }
 
   return length;
@@ -17091,16 +17586,18 @@ mips_fix_adjustable (fixS *fixp)
       && (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE) != 0)
     return 0;
 
-  /* There is no place to store an in-place offset for JALR relocations.
-     Likewise an in-range offset of limited PC-relative relocations may
+  /* There is no place to store an in-place offset for JALR relocations.  */
+  if (jalr_reloc_p (fixp->fx_r_type) && HAVE_IN_PLACE_ADDENDS)
+    return 0;
+
+  /* Likewise an in-range offset of limited PC-relative relocations may
      overflow the in-place relocatable field if recalculated against the
      start address of the symbol's containing section.
 
      Also, PC relative relocations for MIPS R6 need to be symbol rather than
      section relative to allow linker relaxations to be performed later on.  */
-  if ((HAVE_IN_PLACE_ADDENDS || ISA_IS_R6 (mips_opts.isa))
-      && (limited_pcrel_reloc_p (fixp->fx_r_type)
-         || jalr_reloc_p (fixp->fx_r_type)))
+  if (limited_pcrel_reloc_p (fixp->fx_r_type)
+      && (HAVE_IN_PLACE_ADDENDS || ISA_IS_R6 (file_mips_opts.isa)))
     return 0;
 
   /* R_MIPS16_26 relocations against non-MIPS16 functions might resolve
@@ -17130,13 +17627,17 @@ mips_fix_adjustable (fixS *fixp)
      There is a further restriction:
 
        5. We cannot reduce jump relocations (R_MIPS_26, R_MIPS16_26 or
-         R_MICROMIPS_26_S1) against MIPS16 or microMIPS symbols on
-         targets with in-place addends; the relocation field cannot
-         encode the low bit.
+         R_MICROMIPS_26_S1) or branch relocations (R_MIPS_PC26_S2,
+         R_MIPS_PC21_S2, R_MIPS_PC16, R_MIPS16_PC16_S1,
+         R_MICROMIPS_PC16_S1, R_MICROMIPS_PC10_S1 or R_MICROMIPS_PC7_S1)
+         against MIPS16 or microMIPS symbols because we need to keep the
+         MIPS16 or microMIPS symbol for the purpose of mode mismatch
+         detection and JAL or BAL to JALX instruction conversion in the
+         linker.
 
      For simplicity, we deal with (3)-(4) by not reducing _any_ relocation
-     against a MIPS16 symbol.  We deal with (5) by by not reducing any
-     such relocations on REL targets.
+     against a MIPS16 symbol.  We deal with (5) by additionally leaving
+     alone any jump and branch relocations against a microMIPS symbol.
 
      We deal with (1)-(2) by saying that, if there's a R_MIPS16_26
      relocation against some symbol R, no relocation against R may be
@@ -17147,10 +17648,10 @@ mips_fix_adjustable (fixS *fixp)
      that we have for MIPS16 symbols.  */
   if (fixp->fx_subsy == NULL
       && (ELF_ST_IS_MIPS16 (S_GET_OTHER (fixp->fx_addsy))
-         || *symbol_get_tc (fixp->fx_addsy)
-         || (HAVE_IN_PLACE_ADDENDS
-             && ELF_ST_IS_MICROMIPS (S_GET_OTHER (fixp->fx_addsy))
-             && jmp_reloc_p (fixp->fx_r_type))))
+         || (ELF_ST_IS_MICROMIPS (S_GET_OTHER (fixp->fx_addsy))
+             && (jmp_reloc_p (fixp->fx_r_type)
+                 || b_reloc_p (fixp->fx_r_type)))
+         || *symbol_get_tc (fixp->fx_addsy)))
     return 0;
 
   return 1;
@@ -17175,6 +17676,7 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
   if (fixp->fx_pcrel)
     {
       gas_assert (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+                 || fixp->fx_r_type == BFD_RELOC_MIPS16_16_PCREL_S1
                  || fixp->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
                  || fixp->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
                  || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
@@ -17188,7 +17690,24 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
 
       /* At this point, fx_addnumber is "symbol offset - pcrel address".
         Relocations want only the symbol offset.  */
-      reloc->addend = fixp->fx_addnumber + reloc->address;
+      switch (fixp->fx_r_type)
+       {
+       case BFD_RELOC_MIPS_18_PCREL_S3:
+         reloc->addend = fixp->fx_addnumber + (reloc->address & ~7);
+         break;
+       default:
+         reloc->addend = fixp->fx_addnumber + reloc->address;
+         break;
+       }
+    }
+  else if (HAVE_IN_PLACE_ADDENDS
+          && fixp->fx_r_type == BFD_RELOC_MICROMIPS_JMP
+          && (read_compressed_insn (fixp->fx_frag->fr_literal
+                                    + fixp->fx_where, 4) >> 26) == 0x3c)
+    {
+      /* Shift is 2, unusually, for microMIPS JALX.  Adjust the in-place
+         addend accordingly.  */
+      reloc->addend = fixp->fx_addnumber >> 1;
     }
   else
     reloc->addend = fixp->fx_addnumber;
@@ -17248,7 +17767,7 @@ mips_relax_frag (asection *sec, fragS *fragp, long stretch)
   if (! RELAX_MIPS16_P (fragp->fr_subtype))
     return 0;
 
-  if (mips16_extended_frag (fragp, NULL, stretch))
+  if (mips16_extended_frag (fragp, sec, stretch))
     {
       if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
        return 0;
@@ -17478,6 +17997,8 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
     {
       char *buf = fragp->fr_literal + fragp->fr_fix;
       bfd_boolean compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
+      bfd_boolean insn32 = RELAX_MICROMIPS_INSN32 (fragp->fr_subtype);
+      bfd_boolean nods = RELAX_MICROMIPS_NODS (fragp->fr_subtype);
       bfd_boolean al = RELAX_MICROMIPS_LINK (fragp->fr_subtype);
       int type = RELAX_MICROMIPS_TYPE (fragp->fr_subtype);
       bfd_boolean short_ds;
@@ -17529,7 +18050,22 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          fixp->fx_line = fragp->fr_line;
 
          if (type == 0)
-           return;
+           {
+             insn = read_compressed_insn (buf, 4);
+             buf += 4;
+
+             if (nods)
+               {
+                 /* Check the short-delay-slot bit.  */
+                 if (!al || (insn & 0x02000000) != 0)
+                   buf = write_compressed_insn (buf, 0x0c00, 2);
+                 else
+                   buf = write_compressed_insn (buf, 0x00000000, 4);
+               }
+
+             gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
+             return;
+           }
        }
 
       /* Relax 16-bit branches to 32-bit branches.  */
@@ -17556,6 +18092,8 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              || !RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
            {
              buf = write_compressed_insn (buf, insn, 4);
+             if (nods)
+               buf = write_compressed_insn (buf, 0x0c00, 2);
              gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
              return;
            }
@@ -17568,7 +18106,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
                     _("relaxed out-of-range branch into a jump"));
 
       /* Set the short-delay-slot bit.  */
-      short_ds = al && (insn & 0x02000000) != 0;
+      short_ds = !al || (insn & 0x02000000) != 0;
 
       if (!RELAX_MICROMIPS_UNCOND (fragp->fr_subtype))
        {
@@ -17625,14 +18163,21 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 
          /* Branch over the jump.  */
          buf = write_compressed_insn (buf, insn, 4);
+
          if (!compact)
-           /* nop */
-           buf = write_compressed_insn (buf, 0x0c00, 2);
+           {
+             /* nop  */
+             if (insn32)
+               buf = write_compressed_insn (buf, 0x00000000, 4);
+             else
+               buf = write_compressed_insn (buf, 0x0c00, 2);
+           }
        }
 
       if (mips_pic == NO_PIC)
        {
-         unsigned long jal = short_ds ? 0x74000000 : 0xf4000000; /* jal/s  */
+         unsigned long jal = (short_ds || nods
+                              ? 0x74000000 : 0xf4000000);      /* jal/s  */
 
          /* j/jal/jals <sym>  R_MICROMIPS_26_S1  */
          insn = al ? jal : 0xd4000000;
@@ -17643,15 +18188,19 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          fixp->fx_line = fragp->fr_line;
 
          buf = write_compressed_insn (buf, insn, 4);
-         if (compact)
-           /* nop */
-           buf = write_compressed_insn (buf, 0x0c00, 2);
+
+         if (compact || nods)
+           {
+             /* nop  */
+             if (insn32)
+               buf = write_compressed_insn (buf, 0x00000000, 4);
+             else
+               buf = write_compressed_insn (buf, 0x0c00, 2);
+           }
        }
       else
        {
          unsigned long at = RELAX_MICROMIPS_AT (fragp->fr_subtype);
-         unsigned long jalr = short_ds ? 0x45e0 : 0x45c0;      /* jalr/s  */
-         unsigned long jr = compact ? 0x45a0 : 0x4580;         /* jr/c  */
 
          /* lw/ld $at, <sym>($gp)  R_MICROMIPS_GOT16  */
          insn = HAVE_64BIT_ADDRESSES ? 0xdc1c0000 : 0xfc1c0000;
@@ -17681,11 +18230,37 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 
          buf = write_compressed_insn (buf, insn, 4);
 
-         /* jr/jrc/jalr/jalrs $at  */
-         insn = al ? jalr : jr;
-         insn |= at << MICROMIPSOP_SH_MJ;
+         if (insn32)
+           {
+             /* jr/jalr $at  */
+             insn = 0x00000f3c | (al ? RA : ZERO) << MICROMIPSOP_SH_RT;
+             insn |= at << MICROMIPSOP_SH_RS;
+
+             buf = write_compressed_insn (buf, insn, 4);
+
+             if (compact || nods)
+               /* nop  */
+               buf = write_compressed_insn (buf, 0x00000000, 4);
+           }
+         else
+           {
+             /* jr/jrc/jalr/jalrs $at  */
+             unsigned long jalr = short_ds ? 0x45e0 : 0x45c0;  /* jalr/s  */
+             unsigned long jr = compact || nods ? 0x45a0 : 0x4580; /* jr/c  */
+
+             insn = al ? jalr : jr;
+             insn |= at << MICROMIPSOP_SH_MJ;
 
-         buf = write_compressed_insn (buf, insn, 2);
+             buf = write_compressed_insn (buf, insn, 2);
+             if (al && nods)
+               {
+                 /* nop  */
+                 if (short_ds)
+                   buf = write_compressed_insn (buf, 0x0c00, 2);
+                 else
+                   buf = write_compressed_insn (buf, 0x00000000, 4);
+               }
+           }
        }
 
       gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
@@ -17699,14 +18274,23 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
       offsetT val;
       char *buf;
       unsigned int user_length, length;
+      bfd_boolean need_reloc;
       unsigned long insn;
       bfd_boolean ext;
+      segT symsec;
 
       type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
       operand = mips16_immed_operand (type, FALSE);
 
       ext = RELAX_MIPS16_EXTENDED (fragp->fr_subtype);
-      val = resolve_symbol_value (fragp->fr_symbol);
+      val = resolve_symbol_value (fragp->fr_symbol) + fragp->fr_offset;
+
+      symsec = S_GET_SEGMENT (fragp->fr_symbol);
+      need_reloc = (S_FORCE_RELOC (fragp->fr_symbol, TRUE)
+                   || (operand->root.type == OP_PCREL
+                       ? asec != symsec
+                       : !bfd_is_abs_section (symsec)));
+
       if (operand->root.type == OP_PCREL)
        {
          const struct mips_pcrel_operand *pcrel_op;
@@ -17719,6 +18303,16 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
              complicated; see mips16_extended_frag.  */
          if (pcrel_op->include_isa_bit)
            {
+             if (!need_reloc)
+               {
+                 if (!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)
+                   as_bad_where (fragp->fr_file, fragp->fr_line,
+                                 _("branch to misaligned address (0x%lx)"),
+                                 (long) val);
+               }
              addr += 2;
              if (ext)
                addr += 2;
@@ -17759,8 +18353,45 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
       else
        user_length = 0;
 
-      mips16_immed (fragp->fr_file, fragp->fr_line, type,
-                   BFD_RELOC_UNUSED, val, user_length, &insn);
+      if (need_reloc)
+       {
+         bfd_reloc_code_real_type reloc = BFD_RELOC_NONE;
+         expressionS exp;
+         fixS *fixp;
+
+         switch (type)
+           {
+           case 'p':
+           case 'q':
+             reloc = BFD_RELOC_MIPS16_16_PCREL_S1;
+             break;
+           default:
+             as_bad_where (fragp->fr_file, fragp->fr_line,
+                           _("unsupported relocation"));
+             break;
+           }
+         if (reloc != BFD_RELOC_NONE)
+           {
+             gas_assert (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, 2, &exp,
+                                 TRUE, reloc);
+
+             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;
+           }
+       }
+      else
+       mips16_immed (fragp->fr_file, fragp->fr_line, type,
+                     BFD_RELOC_UNUSED, val, user_length, &insn);
 
       length = (ext ? 4 : 2);
       gas_assert (mips16_opcode_length (insn) == length);
@@ -17928,6 +18559,8 @@ mips_convert_ase_flags (int ase)
     ext_ases |= AFL_ASE_DSP;
   if (ase & ASE_DSPR2)
     ext_ases |= AFL_ASE_DSPR2;
+  if (ase & ASE_DSPR3)
+    ext_ases |= AFL_ASE_DSPR3;
   if (ase & ASE_EVA)
     ext_ases |= AFL_ASE_EVA;
   if (ase & ASE_MCU)
@@ -18677,7 +19310,7 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
   { "m5100",          0, ASE_MCU,              ISA_MIPS32R5, CPU_MIPS32R5 },
   { "m5101",          0, ASE_MCU,              ISA_MIPS32R5, CPU_MIPS32R5 },
   /* P5600 with EVA and Virtualization ASEs, other ASEs are optional.  */
-  { "p5600",          0, ASE_VIRT | ASE_EVA | ASE_XPA,         ISA_MIPS32R5, CPU_MIPS32R5 },
+  { "p5600",          0, ASE_VIRT | ASE_EVA | ASE_XPA, ISA_MIPS32R5, CPU_MIPS32R5 },
 
   /* MIPS 64 */
   { "5kc",            0, 0,                    ISA_MIPS64,   CPU_MIPS64 },
@@ -18708,8 +19341,9 @@ static const struct mips_cpu_info mips_cpu_info_table[] =
      MIPS64R2 rather than MIPS64.  */
   { "xlp",           0, 0,                     ISA_MIPS64R2, CPU_XLR },
 
-  /* i6400.  */
+  /* MIPS 64 Release 6 */
   { "i6400",         0, ASE_MSA,               ISA_MIPS64R6, CPU_MIPS64R6},
+  { "p6600",         0, ASE_VIRT | ASE_MSA,    ISA_MIPS64R6, CPU_MIPS64R6},
 
   /* End marker */
   { NULL, 0, 0, 0, 0 }
@@ -18937,6 +19571,9 @@ MIPS options:\n\
 -mdspr2                        generate DSP R2 instructions\n\
 -mno-dspr2             do not generate DSP R2 instructions\n"));
   fprintf (stream, _("\
+-mdspr3                        generate DSP R3 instructions\n\
+-mno-dspr3             do not generate DSP R3 instructions\n"));
+  fprintf (stream, _("\
 -mmt                   generate MT instructions\n\
 -mno-mt                        do not generate MT instructions\n"));
   fprintf (stream, _("\