MIPS16: Handle non-extensible instructions correctly
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 57c2fe994bb0c214b25944418b65f9a836a60094..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
 
    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)                                      \
                               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_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)
 
 /* 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)
 {
 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.  */
 }
 
 /* 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;
 
   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;
     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
 }
 
 /* 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)
 
 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 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.
 
 /* 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)
 
       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)
        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)
 {
 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.  */
 }
 
 /* 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
   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;
      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;
 
          && 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;
 }
 
       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
 
 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++)
   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;
       {
        ip->insn_mo = mo;
        return;
@@ -6850,6 +6876,24 @@ find_altered_mips16_opcode (struct mips_cl_insn *ip)
   abort ();
 }
 
   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'
 /* 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;
 
   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
   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
       && (((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)
     {
              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 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 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;
 
       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);
                                                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)
     {
     }
   else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
     {
+      bfd_boolean require_unextended;
+      bfd_boolean require_extended;
       symbolS *symbol;
       offsetT offset;
 
       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'
       /* 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,
       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);
                         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.  */
 
     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;
       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;
   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;
 
   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;
   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,
                                       &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)
            {
              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;
                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;
        case 'a':
        case 'i':
          *offset_reloc = BFD_RELOC_MIPS16_JMP;
-         insn->insn_opcode <<= 16;
          break;
        }
 
          break;
        }
 
-      operand = decode_mips16_operand (c, FALSE);
+      operand = decode_mips16_operand (c, mips_opcode_32bit_p (opcode));
       if (!operand)
        abort ();
 
       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)
        {
          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;
 {
   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;
 
   /* 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
     {
   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 (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;
     }
        }
       ++opcode;
     }
@@ -8255,6 +8372,19 @@ match_mips16_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
       return TRUE;
     }
 
       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;
 }
 
   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]));
   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;
 }
 
   mips_macro_warning.first_frag = NULL;
 }
 
@@ -8629,7 +8765,6 @@ mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
          break;
 
        case '<':
          break;
 
        case '<':
-       case '>':
        case '4':
        case '5':
        case 'H':
        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.
  * 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
  * 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;
     {
     case M_DABS:
       dbl = 1;
+      /* Fall through.  */
     case M_ABS:
       /*    bgez    $a0,1f
            move    v0,$a0
     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;
 
     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]);
     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;
 
     case M_BGTL_I:
       likely = 1;
+      /* Fall through.  */
     case M_BGT_I:
       /* Check for > max integer.  */
       if (imm_expr.X_add_number >= GPR_SMAX)
     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;
 
     case M_BGEUL:
       likely = 1;
+      /* Fall through.  */
     case M_BGEU:
       if (op[1] == 0)
        goto do_true;
     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;
 
     case M_BGTUL_I:
       likely = 1;
+      /* Fall through.  */
     case M_BGTU_I:
       if (op[0] == 0
          || (GPR_SIZE == 32
     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;
 
     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]);
     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;
 
     case M_BGTUL:
       likely = 1;
+      /* Fall through.  */
     case M_BGTU:
       if (op[1] == 0)
        macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
     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;
 
     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]);
     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;
 
     case M_BLEL_I:
       likely = 1;
+      /* Fall through.  */
     case M_BLE_I:
       if (imm_expr.X_add_number >= GPR_SMAX)
        goto do_true;
     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;
 
     case M_BLEUL:
       likely = 1;
+      /* Fall through.  */
     case M_BLEU:
       if (op[1] == 0)
        macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
     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;
 
     case M_BLEUL_I:
       likely = 1;
+      /* Fall through.  */
     case M_BLEU_I:
       if (op[0] == 0
          || (GPR_SIZE == 32
     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;
 
     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]);
     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;
 
     case M_BLTUL:
       likely = 1;
+      /* Fall through.  */
     case M_BLTU:
       if (op[1] == 0)
        goto do_false;
     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;
 
     case M_DDIV_3:
       dbl = 1;
+      /* Fall through.  */
     case M_DIV_3:
       s = "mflo";
       goto do_div3;
     case M_DREM_3:
       dbl = 1;
     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:
     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;
 
     case M_DLCA_AB:
       dbl = 1;
+      /* Fall through.  */
     case M_LCA_AB:
       call = 1;
       goto do_la;
     case M_DLA_AB:
       dbl = 1;
     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
     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
       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)
             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;
 
     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],
     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;
 
     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
     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;
 
     case M_DMULO_I:
       dbl = 1;
+      /* Fall through.  */
     case M_MULO_I:
       imm = 1;
       goto do_mulo;
 
     case M_DMULO:
       dbl = 1;
     case M_MULO_I:
       imm = 1;
       goto do_mulo;
 
     case M_DMULO:
       dbl = 1;
+      /* Fall through.  */
     case M_MULO:
     do_mulo:
       start_noreorder ();
     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;
 
     case M_DMULOU_I:
       dbl = 1;
+      /* Fall through.  */
     case M_MULOU_I:
       imm = 1;
       goto do_mulou;
 
     case M_DMULOU:
       dbl = 1;
     case M_MULOU_I:
       imm = 1;
       goto do_mulou;
 
     case M_DMULOU:
       dbl = 1;
+      /* Fall through.  */
     case M_MULOU:
     do_mulou:
       start_noreorder ();
     case M_MULOU:
     do_mulou:
       start_noreorder ();
@@ -13362,11 +13520,13 @@ mips16_macro (struct mips_cl_insn *ip)
 
     case M_DDIV_3:
       dbl = 1;
 
     case M_DDIV_3:
       dbl = 1;
+      /* Fall through.  */
     case M_DIV_3:
       s = "mflo";
       goto do_div3;
     case M_DREM_3:
       dbl = 1;
     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:
     case M_REM_3:
       s = "mfhi";
     do_div3:
@@ -13411,6 +13571,7 @@ mips16_macro (struct mips_cl_insn *ip)
 
     case M_DMUL:
       dbl = 1;
 
     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]);
     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;
   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;
 
   for (s = str; ISLOWER (*s); ++s)
     ;
   end = s;
   c = *end;
+
+  l = 0;
   switch (c)
     {
     case '\0':
   switch (c)
     {
     case '\0':
@@ -13708,26 +13870,27 @@ mips16_ip (char *str, struct mips_cl_insn *insn)
       break;
 
     case '.':
       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;
     }
       /* 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);
 
   *end = 0;
   first = (struct mips_opcode *) hash_find (mips16_op_hash, str);
@@ -14800,13 +14963,11 @@ mips_force_relocation (fixS *fixp)
       || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1)
     return 1;
 
       || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1)
     return 1;
 
-  /* We want to keep R_MIPS_PC26_S2, R_MIPS_PC21_S2, 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 do cross-mode branch
-     diagnostics.  */
-  if ((fixp->fx_r_type == R_MIPS_PC26_S2
-       || fixp->fx_r_type == R_MIPS_PC21_S2
-       || fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+  /* 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
        || fixp->fx_r_type == BFD_RELOC_MIPS_21_PCREL_S2
        || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2)
       && fixp->fx_addsy
@@ -14950,7 +15111,12 @@ fix_bad_misaligned_jump_p (fixS *fixP, int shift)
 
 /* Return TRUE if the instruction pointed to by FIXP is an invalid branch
    to a symbol whose annotation indicates another ISA mode.  For absolute
 
 /* 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.  */
+   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)
 
 static bfd_boolean
 fix_bad_cross_mode_branch_p (fixS *fixP)
@@ -14976,6 +15142,11 @@ fix_bad_cross_mode_branch_p (fixS *fixP)
   switch (fixP->fx_r_type)
     {
     case BFD_RELOC_16_PCREL_S2:
   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_MIPS_21_PCREL_S2:
     case BFD_RELOC_MIPS_26_PCREL_S2:
       return absolute_p ? val & 1 : ELF_ST_IS_COMPRESSED (other);
@@ -14983,7 +15154,6 @@ fix_bad_cross_mode_branch_p (fixS *fixP)
       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_MIPS16 (other);
     case BFD_RELOC_MICROMIPS_7_PCREL_S1:
     case BFD_RELOC_MICROMIPS_10_PCREL_S1:
-    case BFD_RELOC_MICROMIPS_16_PCREL_S1:
       return absolute_p ? !(val & 1) : !ELF_ST_IS_MICROMIPS (other);
     default:
       abort ();
       return absolute_p ? !(val & 1) : !ELF_ST_IS_MICROMIPS (other);
     default:
       abort ();
@@ -17173,6 +17343,21 @@ relaxed_branch_length (fragS *fragp, asection *sec, int update)
   return length;
 }
 
   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
 /* 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
@@ -17182,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)
 {
 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;
 
   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)
   if (fragp
       && S_IS_DEFINED (fragp->fr_symbol)
       && !S_IS_WEAK (fragp->fr_symbol)
@@ -17223,10 +17420,11 @@ relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
       bfd_boolean compact = FALSE;
       bfd_boolean uncond;
 
       bfd_boolean compact = FALSE;
       bfd_boolean uncond;
 
-      if (compact_known)
-       compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
       if (fragp)
       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;
 
       else
        uncond = update < 0;
 
@@ -17238,11 +17436,12 @@ relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
          into:
 
                j       label                   # 4 bytes
          into:
 
                j       label                   # 4 bytes
-               nop                             # 2 bytes if compact && !PIC
+               nop                             # 2/4 bytes if
+                                               #  compact && (!PIC || insn32)
            0:
        */
            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:
 
 
       /* If assembling PIC code, we further turn:
 
@@ -17252,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
 
                        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)
        */
       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
 
       /* 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)
        */
       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;
     }
 
   return length;
@@ -17463,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
          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
 
      For simplicity, we deal with (3)-(4) by not reducing _any_ relocation
      against a MIPS16 symbol.  We deal with (5) by additionally leaving
@@ -17827,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);
     {
       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;
       bfd_boolean al = RELAX_MICROMIPS_LINK (fragp->fr_subtype);
       int type = RELAX_MICROMIPS_TYPE (fragp->fr_subtype);
       bfd_boolean short_ds;
@@ -17878,7 +18093,22 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
          fixp->fx_line = fragp->fr_line;
 
          if (type == 0)
          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.  */
        }
 
       /* Relax 16-bit branches to 32-bit branches.  */
@@ -17905,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);
              || !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;
            }
              gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
              return;
            }
@@ -17917,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.  */
                     _("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))
        {
 
       if (!RELAX_MICROMIPS_UNCOND (fragp->fr_subtype))
        {
@@ -17974,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);
 
          /* Branch over the jump.  */
          buf = write_compressed_insn (buf, insn, 4);
+
          if (!compact)
          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)
        {
        }
 
       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;
 
          /* j/jal/jals <sym>  R_MICROMIPS_26_S1  */
          insn = al ? jal : 0xd4000000;
@@ -17992,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);
          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);
        }
       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;
 
          /* lw/ld $at, <sym>($gp)  R_MICROMIPS_GOT16  */
          insn = HAVE_64BIT_ADDRESSES ? 0xdc1c0000 : 0xfc1c0000;
@@ -18030,11 +18273,37 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 
          buf = write_compressed_insn (buf, insn, 4);
 
 
          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);
        }
 
       gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
@@ -18144,10 +18413,10 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
                            _("unsupported relocation"));
              break;
            }
                            _("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;
              exp.X_op = O_symbol;
              exp.X_add_symbol = fragp->fr_symbol;
              exp.X_add_number = fragp->fr_offset;
@@ -18162,6 +18431,9 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
                 in 2 octets.  */
              fixp->fx_no_overflow = 1;
            }
                 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,
        }
       else
        mips16_immed (fragp->fr_file, fragp->fr_line, type,
This page took 0.043119 seconds and 4 git commands to generate.