MIPS16: Handle non-extensible instructions correctly
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 5768f186b07afc1d8a824c17de48e52457672ba1..562da649332bd3cc477242f19c762f80a4149b4f 100644 (file)
@@ -1153,13 +1153,14 @@ static int mips_relax_branch;
    code found in the opcode file for this relocation, the register
    selected as the assembler temporary, whether in the 32-bit
    instruction mode, 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
+   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,           \
+                              uncond, compact, link, nods,     \
                               relax32, toofar16, toofar32)     \
   (0x40000000                                                  \
    | ((type) & 0xff)                                           \
@@ -1168,9 +1169,10 @@ static int mips_relax_branch;
    | ((uncond) ? 0x4000 : 0)                                   \
    | ((compact) ? 0x8000 : 0)                                  \
    | ((link) ? 0x10000 : 0)                                    \
-   | ((relax32) ? 0x20000 : 0)                                 \
-   | ((toofar16) ? 0x40000 : 0)                                        \
-   | ((toofar32) ? 0x80000 : 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)
@@ -1178,14 +1180,15 @@ static int mips_relax_branch;
 #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_RELAX32(i) (((i) & 0x20000) != 0)
+#define RELAX_MICROMIPS_NODS(i) (((i) & 0x20000) != 0)
+#define RELAX_MICROMIPS_RELAX32(i) (((i) & 0x40000) != 0)
 
-#define RELAX_MICROMIPS_TOOFAR16(i) (((i) & 0x40000) != 0)
-#define RELAX_MICROMIPS_MARK_TOOFAR16(i) ((i) | 0x40000)
-#define RELAX_MICROMIPS_CLEAR_TOOFAR16(i) ((i) & ~0x40000)
-#define RELAX_MICROMIPS_TOOFAR32(i) (((i) & 0x80000) != 0)
-#define RELAX_MICROMIPS_MARK_TOOFAR32(i) ((i) | 0x80000)
-#define RELAX_MICROMIPS_CLEAR_TOOFAR32(i) ((i) & ~0x80000)
+#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)
@@ -2108,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.  */
@@ -3214,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;
@@ -3255,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)
@@ -3277,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.
 
@@ -3353,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)
@@ -3411,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.  */
@@ -4708,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;
@@ -6830,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;
@@ -6854,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'
@@ -7042,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
@@ -7293,21 +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,
+      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,
+                                               uncond, compact, al, nods,
                                                relax32, 0, 0),
                        address_expr->X_add_symbol,
                        address_expr->X_add_number);
@@ -7315,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'
@@ -7336,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);
@@ -7512,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;
@@ -7956,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;
@@ -8014,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;
@@ -8067,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)
@@ -8232,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
     {
@@ -8244,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;
     }
@@ -8260,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;
 }
 
@@ -8274,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;
 }
 
@@ -8634,7 +8765,6 @@ mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
          break;
 
        case '<':
-       case '>':
        case '4':
        case '5':
        case 'H':
@@ -9696,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
@@ -9759,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
@@ -9908,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]);
@@ -9933,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)
@@ -9979,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;
@@ -9996,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
@@ -10023,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]);
@@ -10039,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,
@@ -10056,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]);
@@ -10072,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;
@@ -10096,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,
@@ -10113,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
@@ -10140,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]);
@@ -10156,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;
@@ -10173,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:
@@ -10369,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
@@ -11703,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)
@@ -12596,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],
@@ -12609,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
@@ -12621,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 ();
@@ -12658,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 ();
@@ -13367,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:
@@ -13416,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]);
@@ -13696,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':
@@ -13713,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);
@@ -17185,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
@@ -17194,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)
@@ -17233,19 +17418,15 @@ relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
     {
       bfd_boolean compact_known = fragp != NULL;
       bfd_boolean compact = FALSE;
-      bfd_boolean insn32 = TRUE;
       bfd_boolean uncond;
-      int short_insn_size;
 
       if (fragp)
        {
          compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
          uncond = RELAX_MICROMIPS_UNCOND (fragp->fr_subtype);
-         insn32 = RELAX_MICROMIPS_INSN32 (fragp->fr_subtype);
        }
       else
        uncond = update < 0;
-      short_insn_size = insn32 ? 4 : 2;
 
       /* If label is out of range, we turn branch <br>:
 
@@ -17275,6 +17456,13 @@ relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
       if (mips_pic != NO_PIC)
        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
@@ -17283,6 +17471,12 @@ relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
       if (!uncond)
        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;
 }
@@ -17847,6 +18041,7 @@ 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;
@@ -17898,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.  */
@@ -17925,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;
            }
@@ -17937,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))
        {
@@ -18007,7 +18219,8 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 
       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;
@@ -18019,7 +18232,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 
          buf = write_compressed_insn (buf, insn, 4);
 
-         if (compact)
+         if (compact || nods)
            {
              /* nop  */
              if (insn32)
@@ -18068,7 +18281,7 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
 
              buf = write_compressed_insn (buf, insn, 4);
 
-             if (compact)
+             if (compact || nods)
                /* nop  */
                buf = write_compressed_insn (buf, 0x00000000, 4);
            }
@@ -18076,12 +18289,20 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
            {
              /* jr/jrc/jalr/jalrs $at  */
              unsigned long jalr = short_ds ? 0x45e0 : 0x45c0;  /* jalr/s  */
-             unsigned long jr = compact ? 0x45a0 : 0x4580;     /* jr/c  */
+             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);
+               }
            }
        }
 
@@ -18192,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;
@@ -18210,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.041273 seconds and 4 git commands to generate.