MIPS16: Handle non-extensible instructions correctly
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index b7997c703c55900a16412e41d275a6f241803a90..562da649332bd3cc477242f19c762f80a4149b4f 100644 (file)
@@ -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)
@@ -2104,7 +2111,7 @@ mips_lookup_ase (const char *name)
 static inline unsigned int
 micromips_insn_length (const struct mips_opcode *mo)
 {
-  return (mo->mask >> 16) == 0 ? 2 : 4;
+  return mips_opcode_32bit_p (mo) ? 4 : 2;
 }
 
 /* Return the length of MIPS16 instruction OPCODE.  */
@@ -3210,7 +3217,7 @@ is_opcode_valid (const struct mips_opcode *mo)
   int fp_s, fp_d;
   unsigned int i;
 
-  if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+  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;
@@ -3251,7 +3258,8 @@ is_opcode_valid_16 (const struct mips_opcode *mo)
 }
 
 /* Return TRUE if the size of the microMIPS opcode MO matches one
-   explicitly requested.  Always TRUE in the standard MIPS mode.  */
+   explicitly requested.  Always TRUE in the standard MIPS mode.
+   Use is_size_valid_16 for MIPS16 opcodes.  */
 
 static bfd_boolean
 is_size_valid (const struct mips_opcode *mo)
@@ -3273,6 +3281,23 @@ is_size_valid (const struct mips_opcode *mo)
   return forced_insn_length == micromips_insn_length (mo);
 }
 
+/* Return TRUE if the size of the MIPS16 opcode MO matches one
+   explicitly requested.  */
+
+static bfd_boolean
+is_size_valid_16 (const struct mips_opcode *mo)
+{
+  if (!forced_insn_length)
+    return TRUE;
+  if (mo->pinfo == INSN_MACRO)
+    return FALSE;
+  if (forced_insn_length == 2 && mips_opcode_32bit_p (mo))
+    return FALSE;
+  if (forced_insn_length == 4 && (mo->pinfo2 & INSN2_SHORT_ONLY))
+    return FALSE;
+  return TRUE;
+}
+
 /* Return TRUE if the microMIPS opcode MO is valid for the delay slot
    of the preceding instruction.  Always TRUE in the standard MIPS mode.
 
@@ -3349,7 +3374,7 @@ validate_mips_insn (const struct mips_opcode *opcode,
 
       default:
        if (!decode_operand)
-         operand = decode_mips16_operand (*s, FALSE);
+         operand = decode_mips16_operand (*s, mips_opcode_32bit_p (opcode));
        else
          operand = decode_operand (s);
        if (!operand && opcode->pinfo != INSN_MACRO)
@@ -3407,18 +3432,9 @@ static int
 validate_mips16_insn (const struct mips_opcode *opcode,
                      struct mips_operand_array *operands)
 {
-  if (opcode->args[0] == 'a' || opcode->args[0] == 'i')
-    {
-      /* In this case OPCODE defines the first 16 bits in a 32-bit jump
-        instruction.  Use TMP to describe the full instruction.  */
-      struct mips_opcode tmp;
+  unsigned long insn_bits = mips_opcode_32bit_p (opcode) ? 0xffffffff : 0xffff;
 
-      tmp = *opcode;
-      tmp.match <<= 16;
-      tmp.mask <<= 16;
-      return validate_mips_insn (&tmp, 0xffffffff, 0, operands);
-    }
-  return validate_mips_insn (opcode, 0xffff, 0, operands);
+  return validate_mips_insn (opcode, insn_bits, 0, operands);
 }
 
 /* The microMIPS version of validate_mips_insn.  */
@@ -4704,7 +4720,7 @@ struct mips_arg_info
   unsigned int last_op_int;
 
   /* If true, match routines should assume that no later instruction
-     alternative matches and should therefore be as accomodating as
+     alternative matches and should therefore be as accommodating as
      possible.  Match routines should not report errors if something
      is only invalid for !LAX_MATCH.  */
   bfd_boolean lax_match;
@@ -6826,23 +6842,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;
@@ -6850,6 +6876,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'
@@ -7038,8 +7082,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
@@ -7289,20 +7339,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);
@@ -7310,9 +7366,23 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
     }
   else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
     {
+      bfd_boolean require_unextended;
+      bfd_boolean require_extended;
       symbolS *symbol;
       offsetT offset;
 
+      if (forced_insn_length != 0)
+       {
+         require_unextended = forced_insn_length == 2;
+         require_extended = forced_insn_length == 4;
+       }
+      else
+       {
+         require_unextended = (mips_opts.noautoextend
+                               && !mips_opcode_32bit_p (ip->insn_mo));
+         require_extended = 0;
+       }
+
       /* We need to set up a variant frag.  */
       gas_assert (address_expr != NULL);
       /* Pass any `O_symbol' expression unchanged as an `expr_section'
@@ -7331,7 +7401,7 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
       add_relaxed_insn (ip, 4, 0,
                        RELAX_MIPS16_ENCODE
                        (*reloc_type - BFD_RELOC_UNUSED,
-                        forced_insn_length == 2, forced_insn_length == 4,
+                        require_unextended, require_extended,
                         delayed_branch_p (&history[0]),
                         history[0].mips16_absolute_jump_p),
                        symbol, offset);
@@ -7507,9 +7577,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;
@@ -7951,9 +8062,17 @@ match_mips16_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
   const char *args;
   const struct mips_operand *operand;
   const struct mips_operand *ext_operand;
+  int required_insn_length;
   struct mips_arg_info arg;
   int relax_char;
 
+  if (forced_insn_length)
+    required_insn_length = forced_insn_length;
+  else if (mips_opts.noautoextend && !mips_opcode_32bit_p (opcode))
+    required_insn_length = 2;
+  else
+    required_insn_length = 0;
+
   create_insn (insn, opcode);
   imm_expr.X_op = O_absent;
   offset_expr.X_op = O_absent;
@@ -8009,13 +8128,13 @@ match_mips16_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
                                       &value))
            {
              mips16_immed (NULL, 0, relax_char, *offset_reloc, value,
-                           forced_insn_length, &insn->insn_opcode);
+                           required_insn_length, &insn->insn_opcode);
              offset_expr.X_op = O_absent;
              *offset_reloc = BFD_RELOC_UNUSED;
            }
          else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED)
            {
-             if (forced_insn_length == 2)
+             if (required_insn_length == 2)
                set_insn_error (0, _("invalid unextended operand value"));
              forced_insn_length = 4;
              insn->insn_opcode |= MIPS16_EXTEND;
@@ -8062,22 +8181,14 @@ match_mips16_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
        case 'a':
        case 'i':
          *offset_reloc = BFD_RELOC_MIPS16_JMP;
-         insn->insn_opcode <<= 16;
          break;
        }
 
-      operand = decode_mips16_operand (c, FALSE);
+      operand = decode_mips16_operand (c, mips_opcode_32bit_p (opcode));
       if (!operand)
        abort ();
 
-      /* '6' is a special case.  It is used for BREAK and SDBBP,
-        whose operands are only meaningful to the software that decodes
-        them.  This means that there is no architectural reason why
-        they cannot be prefixed by EXTEND, but in practice,
-        exception handlers will only look at the instruction
-        itself.  We therefore allow '6' to be extended when
-        disassembling but not when assembling.  */
-      if (operand->type != OP_PCREL && c != '6')
+      if (operand->type != OP_PCREL)
        {
          ext_operand = decode_mips16_operand (c, TRUE);
          if (operand != ext_operand)
@@ -8227,11 +8338,13 @@ match_mips16_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
 {
   const struct mips_opcode *opcode;
   bfd_boolean seen_valid_for_isa;
+  bfd_boolean seen_valid_for_size;
 
   /* Search for a match, ignoring alternatives that don't satisfy the
      current ISA.  There are no separate entries for extended forms so
      we deal with forced_length later.  */
   seen_valid_for_isa = FALSE;
+  seen_valid_for_size = FALSE;
   opcode = first;
   do
     {
@@ -8239,8 +8352,12 @@ match_mips16_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
       if (is_opcode_valid_16 (opcode))
        {
          seen_valid_for_isa = TRUE;
-         if (match_mips16_insn (insn, opcode, tokens))
-           return TRUE;
+         if (is_size_valid_16 (opcode))
+           {
+             seen_valid_for_size = TRUE;
+             if (match_mips16_insn (insn, opcode, tokens))
+               return TRUE;
+           }
        }
       ++opcode;
     }
@@ -8255,6 +8372,19 @@ match_mips16_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
       return TRUE;
     }
 
+  /* Handle the case where we didn't try to match an instruction because
+     all the alternatives were of the wrong size.  */
+  if (!seen_valid_for_size)
+    {
+      if (forced_insn_length == 2)
+       set_insn_error
+         (0, _("unrecognized unextended version of MIPS16 opcode"));
+      else
+       set_insn_error
+         (0, _("unrecognized extended version of MIPS16 opcode"));
+      return TRUE;
+    }
+
   return FALSE;
 }
 
@@ -8269,19 +8399,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;
 }
 
@@ -8629,7 +8765,6 @@ mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
          break;
 
        case '<':
-       case '>':
        case '4':
        case '5':
        case 'H':
@@ -9691,7 +9826,7 @@ small_offset_p (unsigned int range, unsigned int align, unsigned int offbits)
  * optimizing code generation.
  *   One interesting optimization is when several store macros appear
  * consecutively that would load AT with the upper half of the same address.
- * The ensuing load upper instructions are ommited. This implies some kind
+ * The ensuing load upper instructions are omitted. This implies some kind
  * of global optimization. We currently only optimize within a single macro.
  *   For many of the load and store macros if the address is specified as a
  * constant expression in the first 64k of memory (ie ld $2,0x4000c) we
@@ -9754,6 +9889,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
@@ -9903,6 +10039,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]);
@@ -9928,6 +10065,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)
@@ -9974,6 +10112,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;
@@ -9991,6 +10130,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
@@ -10018,6 +10158,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]);
@@ -10034,6 +10175,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,
@@ -10051,6 +10193,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]);
@@ -10067,6 +10210,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;
@@ -10091,6 +10235,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,
@@ -10108,6 +10253,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
@@ -10135,6 +10281,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]);
@@ -10151,6 +10298,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;
@@ -10168,11 +10316,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:
@@ -10364,11 +10514,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
@@ -11698,7 +11850,7 @@ macro (struct mips_cl_insn *ip, char *str)
       else if (offbits != 16)
        {
          /* The offset field is too narrow to be used for a low-part
-            relocation, so load the whole address into the auxillary
+            relocation, so load the whole address into the auxiliary
             register.  */
          load_address (tempreg, &offset_expr, &used_at);
          if (breg != 0)
@@ -12591,6 +12743,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],
@@ -12604,6 +12757,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
@@ -12616,12 +12770,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 ();
@@ -12653,12 +12809,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 ();
@@ -13362,11 +13520,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:
@@ -13411,6 +13571,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]);
@@ -13691,13 +13852,14 @@ mips16_ip (char *str, struct mips_cl_insn *insn)
   char *end, *s, c;
   struct mips_opcode *first;
   struct mips_operand_token *tokens;
-
-  forced_insn_length = 0;
+  unsigned int l;
 
   for (s = str; ISLOWER (*s); ++s)
     ;
   end = s;
   c = *end;
+
+  l = 0;
   switch (c)
     {
     case '\0':
@@ -13708,26 +13870,27 @@ mips16_ip (char *str, struct mips_cl_insn *insn)
       break;
 
     case '.':
-      if (s[1] == 't' && s[2] == ' ')
+      s++;
+      if (*s == 't')
        {
-         forced_insn_length = 2;
-         s += 3;
-         break;
+         l = 2;
+         s++;
        }
-      else if (s[1] == 'e' && s[2] == ' ')
+      else if (*s == 'e')
        {
-         forced_insn_length = 4;
-         s += 3;
-         break;
+         l = 4;
+         s++;
        }
+      if (*s == '\0')
+       break;
+      else if (*s++ == ' ')
+       break;
       /* Fall through.  */
     default:
       set_insn_error (0, _("unrecognized opcode"));
       return;
     }
-
-  if (mips_opts.noautoextend && !forced_insn_length)
-    forced_insn_length = 2;
+  forced_insn_length = l;
 
   *end = 0;
   first = (struct mips_opcode *) hash_find (mips16_op_hash, str);
@@ -14800,6 +14963,17 @@ 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 (file_mips_opts.isa)
       && (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
@@ -14814,6 +14988,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
@@ -14838,6 +15028,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
@@ -14946,6 +15325,40 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       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:
@@ -14981,8 +15394,6 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     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:
@@ -15056,9 +15467,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       break;
 
     case BFD_RELOC_MIPS_21_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);
       if (!fixP->fx_done)
        break;
 
@@ -15074,9 +15483,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       break;
 
     case BFD_RELOC_MIPS_26_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);
       if (!fixP->fx_done)
        break;
 
@@ -15134,9 +15541,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       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
@@ -15188,15 +15593,22 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     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:
@@ -16931,6 +17343,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
@@ -16940,9 +17367,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)
@@ -16981,10 +17420,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;
 
@@ -16996,11 +17436,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:
 
@@ -17010,18 +17451,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;
@@ -17221,7 +17675,8 @@ mips_fix_adjustable (fixS *fixp)
          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 to JALX instruction conversion in the linker.
+         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 additionally leaving
@@ -17585,6 +18040,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;
@@ -17636,7 +18093,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.  */
@@ -17663,6 +18135,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;
            }
@@ -17675,7 +18149,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))
        {
@@ -17732,14 +18206,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;
@@ -17750,15 +18231,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;
@@ -17788,11 +18273,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);
 
-         buf = write_compressed_insn (buf, insn, 2);
+             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);
+             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);
@@ -17806,6 +18317,7 @@ 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;
@@ -17815,6 +18327,13 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 
       ext = RELAX_MIPS16_EXTENDED (fragp->fr_subtype);
       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;
@@ -17827,6 +18346,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;
@@ -17867,11 +18396,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
       else
        user_length = 0;
 
-      symsec = S_GET_SEGMENT (fragp->fr_symbol);
-      if (S_FORCE_RELOC (fragp->fr_symbol, TRUE)
-         || (operand->root.type == OP_PCREL
-             ? asec != symsec
-             : !bfd_is_abs_section (symsec)))
+      if (need_reloc)
        {
          bfd_reloc_code_real_type reloc = BFD_RELOC_NONE;
          expressionS exp;
@@ -17888,10 +18413,10 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
                            _("unsupported relocation"));
              break;
            }
-         if (reloc != BFD_RELOC_NONE)
+         if (reloc == BFD_RELOC_NONE)
+           ;
+         else if (ext)
            {
-             gas_assert (ext);
-
              exp.X_op = O_symbol;
              exp.X_add_symbol = fragp->fr_symbol;
              exp.X_add_number = fragp->fr_offset;
@@ -17906,6 +18431,9 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
                 in 2 octets.  */
              fixp->fx_no_overflow = 1;
            }
+         else
+           as_bad_where (fragp->fr_file, fragp->fr_line,
+                         _("invalid unextended operand value"));
        }
       else
        mips16_immed (fragp->fr_file, fragp->fr_line, type,
This page took 0.045005 seconds and 4 git commands to generate.