MIPS16: Handle non-extensible instructions correctly
[deliverable/binutils-gdb.git] / gas / config / tc-mips.c
index 224b9d021fcff91a99f40bc8a06bd1f5a87f4a54..562da649332bd3cc477242f19c762f80a4149b4f 100644 (file)
@@ -2111,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.  */
@@ -3217,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;
@@ -3258,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)
@@ -3280,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.
 
@@ -3356,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)
@@ -3414,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.  */
@@ -4711,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;
@@ -7357,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'
@@ -7378,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);
@@ -8039,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;
@@ -8097,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;
@@ -8150,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)
@@ -8315,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
     {
@@ -8327,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;
     }
@@ -8343,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;
 }
 
@@ -8723,7 +8765,6 @@ mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
          break;
 
        case '<':
-       case '>':
        case '4':
        case '5':
        case 'H':
@@ -9785,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
@@ -9848,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
@@ -9997,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]);
@@ -10022,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)
@@ -10068,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;
@@ -10085,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
@@ -10112,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]);
@@ -10128,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,
@@ -10145,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]);
@@ -10161,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;
@@ -10185,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,
@@ -10202,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
@@ -10229,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]);
@@ -10245,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;
@@ -10262,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:
@@ -10458,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
@@ -11792,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)
@@ -12685,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],
@@ -12698,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
@@ -12710,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 ();
@@ -12747,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 ();
@@ -13456,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:
@@ -13505,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]);
@@ -13785,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':
@@ -13802,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);
@@ -18344,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;
@@ -18362,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.03492 seconds and 4 git commands to generate.