gas/
authorRichard Sandiford <rdsandiford@googlemail.com>
Mon, 19 Aug 2013 19:42:50 +0000 (19:42 +0000)
committerRichard Sandiford <rdsandiford@googlemail.com>
Mon, 19 Aug 2013 19:42:50 +0000 (19:42 +0000)
* config/tc-mips.c (mips_arg_info): Replace allow_nonconst and
lax_max with lax_match.
(match_int_operand): Update accordingly.  Don't report an error
for !lax_match-only cases.
(match_insn): Replace more_alts with lax_match and use it to
initialize the mips_arg_info field.  Add a complete_p parameter.
Handle implicit VU0 suffixes here.
(match_invalid_for_isa, match_insns, match_mips16_insns): New
functions.
(mips_ip, mips16_ip): Use them.

gas/ChangeLog
gas/config/tc-mips.c

index 397a538..0a2f7b7 100644 (file)
@@ -1,5 +1,18 @@
 2013-08-19  Richard Sandiford  <rdsandiford@googlemail.com>
 
+       * config/tc-mips.c (mips_arg_info): Replace allow_nonconst and
+       lax_max with lax_match.
+       (match_int_operand): Update accordingly.  Don't report an error
+       for !lax_match-only cases.
+       (match_insn): Replace more_alts with lax_match and use it to
+       initialize the mips_arg_info field.  Add a complete_p parameter.
+       Handle implicit VU0 suffixes here.
+       (match_invalid_for_isa, match_insns, match_mips16_insns): New
+       functions.
+       (mips_ip, mips16_ip): Use them.
+
+2013-08-19  Richard Sandiford  <rdsandiford@googlemail.com>
+
        * config/tc-mips.c (match_expression):  Report uses of registers here.
        Add a "must be an immediate expression" error.  Handle elided offsets
        here rather than...
index 99369b9..4aa2493 100644 (file)
@@ -4271,14 +4271,11 @@ struct mips_arg_info
      where it gives the lsb position.  */
   unsigned int last_op_int;
 
-  /* If true, the OP_INT match routine should treat plain symbolic operands
-     as if a relocation operator like %lo(...) had been used.  This is only
-     ever true if the operand can be relocated.  */
-  bfd_boolean allow_nonconst;
-
-  /* When true, the OP_INT match routine should allow unsigned N-bit
-     arguments to be used where a signed N-bit operand is expected.  */
-  bfd_boolean lax_max;
+  /* If true, match routines should assume that no later instruction
+     alternative matches and should therefore be as accomodating as
+     possible.  Match routines should not report errors if something
+     is only invalid for !LAX_MATCH.  */
+  bfd_boolean lax_match;
 
   /* True if a reference to the current AT register was seen.  */
   bfd_boolean seen_at;
@@ -4559,8 +4556,6 @@ match_int_operand (struct mips_arg_info *arg,
   factor = 1 << operand->shift;
   min_val = mips_int_operand_min (operand);
   max_val = mips_int_operand_max (operand);
-  if (arg->lax_max)
-    max_val = ((1 << operand_base->size) - 1) << operand->shift;
 
   if (operand_base->lsb == 0
       && operand_base->size == 16
@@ -4580,13 +4575,10 @@ match_int_operand (struct mips_arg_info *arg,
 
       if (offset_expr.X_op != O_constant)
        {
-         /* If non-constant operands are allowed then leave them for
-            the caller to process, otherwise fail the match.  */
-         if (!arg->allow_nonconst)
-           {
-             match_not_constant (arg);
-             return FALSE;
-           }
+         /* Accept non-constant operands if no later alternative matches,
+            leaving it for the caller to process.  */
+         if (!arg->lax_match)
+           return FALSE;
          offset_reloc[0] = BFD_RELOC_LO16;
          return TRUE;
        }
@@ -4595,6 +4587,16 @@ match_int_operand (struct mips_arg_info *arg,
         ourselves.  */
       sval = offset_expr.X_add_number;
       offset_expr.X_op = O_absent;
+
+      /* For compatibility with older assemblers, we accept
+        0x8000-0xffff as signed 16-bit numbers when only
+        signed numbers are allowed.  */
+      if (sval > max_val)
+       {
+         max_val = ((1 << operand_base->size) - 1) << operand->shift;
+         if (!arg->lax_match && sval <= max_val)
+           return FALSE;
+       }
     }
   else
     {
@@ -7047,7 +7049,7 @@ normalize_address_expr (expressionS *ex)
 static bfd_boolean
 match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
            struct mips_operand_token *tokens, unsigned int opcode_extra,
-           bfd_boolean more_alts)
+           bfd_boolean lax_match, bfd_boolean complete_p)
 {
   const char *args;
   struct mips_arg_info arg;
@@ -7062,13 +7064,18 @@ match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
   offset_reloc[2] = BFD_RELOC_UNUSED;
 
   create_insn (insn, opcode);
-  insn->insn_opcode |= opcode_extra;
+  /* When no opcode suffix is specified, assume ".xyzw". */
+  if ((opcode->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) != 0 && opcode_extra == 0)
+    insn->insn_opcode |= 0xf << mips_vu0_channel_mask.lsb;
+  else
+    insn->insn_opcode |= opcode_extra;
   memset (&arg, 0, sizeof (arg));
   arg.insn = insn;
   arg.token = tokens;
   arg.argnum = 1;
   arg.last_regno = ILLEGAL_REG;
   arg.dest_regno = ILLEGAL_REG;
+  arg.lax_match = lax_match;
   for (args = opcode->args;; ++args)
     {
       if (arg.token->type == OT_END)
@@ -7107,6 +7114,8 @@ match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
            return FALSE;
 
          /* Successful match.  */
+         if (!complete_p)
+           return TRUE;
          clear_insn_error ();
          if (arg.dest_regno == arg.last_regno
              && strncmp (insn->insn_mo->name, "jalr", 4) == 0)
@@ -7148,7 +7157,6 @@ match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
       /* Handle special macro operands.  Work out the properties of
         other operands.  */
       arg.opnum += 1;
-      arg.lax_max = FALSE;
       switch (*args)
        {
        case '+':
@@ -7219,32 +7227,6 @@ match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
            return FALSE;
          continue;
 
-         /* ??? This is the traditional behavior, but is flaky if
-            there are alternative versions of the same instruction
-            for different subarchitectures.  The next alternative
-            might not be suitable.  */
-       case 'j':
-         /* For compatibility with older assemblers, we accept
-            0x8000-0xffff as signed 16-bit numbers when only
-            signed numbers are allowed.  */
-         arg.lax_max = !more_alts;
-       case 'i':
-         /* Only accept non-constant operands if this is the
-            final alternative.  Later alternatives might include
-            a macro implementation.  */
-         arg.allow_nonconst = !more_alts;
-         break;
-
-       case 'u':
-         /* There are no macro implementations for out-of-range values.  */
-         arg.allow_nonconst = TRUE;
-         break;
-
-       case 'o':
-         /* There should always be a macro implementation.  */
-         arg.allow_nonconst = FALSE;
-         break;
-
        case 'p':
          *offset_reloc = BFD_RELOC_16_PCREL_S2;
          break;
@@ -7479,6 +7461,141 @@ match_mips16_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
     }
 }
 
+/* Record that the current instruction is invalid for the current ISA.  */
+
+static void
+match_invalid_for_isa (void)
+{
+  set_insn_error_ss
+    (0, _("Opcode not supported on this processor: %s (%s)"),
+     mips_cpu_info_from_arch (mips_opts.arch)->name,
+     mips_cpu_info_from_isa (mips_opts.isa)->name);
+}
+
+/* Try to match TOKENS against a series of opcode entries, starting at FIRST.
+   Return true if a definite match or failure was found, storing any match
+   in INSN.  OPCODE_EXTRA is a value that should be ORed into the opcode
+   (to handle things like VU0 suffixes).  LAX_MATCH is true if we have already
+   tried and failed to match under normal conditions and now want to try a
+   more relaxed match.  */
+
+static bfd_boolean
+match_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
+            const struct mips_opcode *past, struct mips_operand_token *tokens,
+            int opcode_extra, bfd_boolean lax_match)
+{
+  const struct mips_opcode *opcode;
+  const struct mips_opcode *invalid_delay_slot;
+  bfd_boolean seen_valid_for_isa, seen_valid_for_size;
+
+  /* Search for a match, ignoring alternatives that don't satisfy the
+     current ISA or forced_length.  */
+  invalid_delay_slot = 0;
+  seen_valid_for_isa = FALSE;
+  seen_valid_for_size = FALSE;
+  opcode = first;
+  do
+    {
+      gas_assert (strcmp (opcode->name, first->name) == 0);
+      if (is_opcode_valid (opcode))
+       {
+         seen_valid_for_isa = TRUE;
+         if (is_size_valid (opcode))
+           {
+             bfd_boolean delay_slot_ok;
+
+             seen_valid_for_size = TRUE;
+             delay_slot_ok = is_delay_slot_valid (opcode);
+             if (match_insn (insn, opcode, tokens, opcode_extra,
+                             lax_match, delay_slot_ok))
+               {
+                 if (!delay_slot_ok)
+                   {
+                     if (!invalid_delay_slot)
+                       invalid_delay_slot = opcode;
+                   }
+                 else
+                   return TRUE;
+               }
+           }
+       }
+      ++opcode;
+    }
+  while (opcode < past && strcmp (opcode->name, first->name) == 0);
+
+  /* If the only matches we found had the wrong length for the delay slot,
+     pick the first such match.  We'll issue an appropriate warning later.  */
+  if (invalid_delay_slot)
+    {
+      if (match_insn (insn, invalid_delay_slot, tokens, opcode_extra,
+                     lax_match, TRUE))
+       return TRUE;
+      abort ();
+    }
+
+  /* Handle the case where we didn't try to match an instruction because
+     all the alternatives were incompatible with the current ISA.  */
+  if (!seen_valid_for_isa)
+    {
+      match_invalid_for_isa ();
+      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 (mips_opts.insn32)
+       set_insn_error (0, _("Opcode not supported in the `insn32' mode"));
+      else
+       set_insn_error_i
+         (0, _("Unrecognized %d-bit version of microMIPS opcode"),
+          8 * forced_insn_length);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/* Like match_insns, but for MIPS16.  */
+
+static bfd_boolean
+match_mips16_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
+                   struct mips_operand_token *tokens)
+{
+  const struct mips_opcode *opcode;
+  bfd_boolean seen_valid_for_isa;
+
+  /* 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;
+  opcode = first;
+  do
+    {
+      gas_assert (strcmp (opcode->name, first->name) == 0);
+      if (is_opcode_valid_16 (opcode))
+       {
+         seen_valid_for_isa = TRUE;
+         if (match_mips16_insn (insn, opcode, tokens))
+           return TRUE;
+       }
+      ++opcode;
+    }
+  while (opcode < &mips16_opcodes[bfd_mips16_num_opcodes]
+        && strcmp (opcode->name, first->name) == 0);
+
+  /* Handle the case where we didn't try to match an instruction because
+     all the alternatives were incompatible with the current ISA.  */
+  if (!seen_valid_for_isa)
+    {
+      match_invalid_for_isa ();
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 /* Set up global variables for the start of a new macro.  */
 
 static void
@@ -12952,14 +13069,10 @@ mips_lookup_insn (struct hash_control *hash, const char *start,
    to offset_expr.  */
 
 static void
-mips_ip (char *str, struct mips_cl_insn *ip)
+mips_ip (char *str, struct mips_cl_insn *insn)
 {
-  bfd_boolean wrong_delay_slot_insns = FALSE;
-  bfd_boolean need_delay_slot_ok = TRUE;
-  struct mips_opcode *firstinsn = NULL;
-  const struct mips_opcode *past;
+  const struct mips_opcode *first, *past;
   struct hash_control *hash;
-  struct mips_opcode *first, *insn;
   char format;
   size_t end;
   struct mips_operand_token *tokens;
@@ -12976,26 +13089,22 @@ mips_ip (char *str, struct mips_cl_insn *ip)
       past = &mips_opcodes[NUMOPCODES];
     }
   forced_insn_length = 0;
-  insn = NULL;
   opcode_extra = 0;
 
   /* We first try to match an instruction up to a space or to the end.  */
   for (end = 0; str[end] != '\0' && !ISSPACE (str[end]); end++)
     continue;
 
-  first = insn = mips_lookup_insn (hash, str, end, &opcode_extra);
-  if (insn == NULL)
+  first = mips_lookup_insn (hash, str, end, &opcode_extra);
+  if (first == NULL)
     {
       set_insn_error (0, _("Unrecognized opcode"));
       return;
     }
-  /* When no opcode suffix is specified, assume ".xyzw". */
-  if ((insn->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) != 0 && opcode_extra == 0)
-    opcode_extra = 0xf << mips_vu0_channel_mask.lsb;
 
-  if (strcmp (insn->name, "li.s") == 0)
+  if (strcmp (first->name, "li.s") == 0)
     format = 'f';
-  else if (strcmp (insn->name, "li.d") == 0)
+  else if (strcmp (first->name, "li.d") == 0)
     format = 'd';
   else
     format = 0;
@@ -13003,84 +13112,10 @@ mips_ip (char *str, struct mips_cl_insn *ip)
   if (!tokens)
     return;
 
-  /* For microMIPS instructions placed in a fixed-length branch delay slot
-     we make up to two passes over the relevant fragment of the opcode
-     table.  First we try instructions that meet the delay slot's length
-     requirement.  If none matched, then we retry with the remaining ones
-     and if one matches, then we use it and then issue an appropriate
-     warning later on.  */
-  for (;;)
-    {
-      bfd_boolean delay_slot_ok;
-      bfd_boolean size_ok;
-      bfd_boolean ok;
-      bfd_boolean more_alts;
-
-      gas_assert (strcmp (insn->name, first->name) == 0);
-
-      ok = is_opcode_valid (insn);
-      size_ok = is_size_valid (insn);
-      delay_slot_ok = is_delay_slot_valid (insn);
-      if (!delay_slot_ok && !wrong_delay_slot_insns)
-       {
-         firstinsn = insn;
-         wrong_delay_slot_insns = TRUE;
-       }
-      more_alts = (insn + 1 < past
-                  && strcmp (insn[0].name, insn[1].name) == 0);
-      if (!ok || !size_ok || delay_slot_ok != need_delay_slot_ok)
-       {
-         if (more_alts)
-           {
-             ++insn;
-             continue;
-           }
-         if (wrong_delay_slot_insns && need_delay_slot_ok)
-           {
-             gas_assert (firstinsn);
-             need_delay_slot_ok = FALSE;
-             past = insn + 1;
-             insn = firstinsn;
-             continue;
-           }
-
-         if (!ok)
-           set_insn_error_ss
-             (0, _("Opcode not supported on this processor: %s (%s)"),
-              mips_cpu_info_from_arch (mips_opts.arch)->name,
-              mips_cpu_info_from_isa (mips_opts.isa)->name);
-         else if (mips_opts.insn32)
-           set_insn_error
-             (0, _("Opcode not supported in the `insn32' mode"));
-         else
-           set_insn_error_i
-             (0, _("Unrecognized %d-bit version of microMIPS opcode"),
-              8 * forced_insn_length);
-         break;
-       }
-
-      if (match_insn (ip, insn, tokens, opcode_extra,
-                     more_alts || (wrong_delay_slot_insns
-                                   && need_delay_slot_ok)))
-       break;
+  if (!match_insns (insn, first, past, tokens, opcode_extra, FALSE)
+      && !match_insns (insn, first, past, tokens, opcode_extra, TRUE))
+    set_insn_error (0, _("Illegal operands"));
 
-      /* Args don't match.  */
-      set_insn_error (0, _("Illegal operands"));
-      if (more_alts)
-       {
-         ++insn;
-         continue;
-       }
-      if (wrong_delay_slot_insns && need_delay_slot_ok)
-       {
-         gas_assert (firstinsn);
-         need_delay_slot_ok = FALSE;
-         past = insn + 1;
-         insn = firstinsn;
-         continue;
-       }
-      break;
-    }
   obstack_free (&mips_operand_tokens, tokens);
 }
 
@@ -13089,10 +13124,10 @@ mips_ip (char *str, struct mips_cl_insn *ip)
    bytes if the user explicitly requested a small or extended instruction.  */
 
 static void
-mips16_ip (char *str, struct mips_cl_insn *ip)
+mips16_ip (char *str, struct mips_cl_insn *insn)
 {
   char *end, *s, c;
-  struct mips_opcode *insn, *first;
+  struct mips_opcode *first;
   struct mips_operand_token *tokens;
 
   forced_insn_length = 0;
@@ -13133,10 +13168,10 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
     forced_insn_length = 2;
 
   *end = 0;
-  first = insn = (struct mips_opcode *) hash_find (mips16_op_hash, str);
+  first = (struct mips_opcode *) hash_find (mips16_op_hash, str);
   *end = c;
 
-  if (!insn)
+  if (!first)
     {
       set_insn_error (0, _("Unrecognized opcode"));
       return;
@@ -13146,45 +13181,9 @@ mips16_ip (char *str, struct mips_cl_insn *ip)
   if (!tokens)
     return;
 
-  for (;;)
-    {
-      bfd_boolean ok;
-      bfd_boolean more_alts;
-
-      gas_assert (strcmp (insn->name, first->name) == 0);
+  if (!match_mips16_insns (insn, first, tokens))
+    set_insn_error (0, _("Illegal operands"));
 
-      ok = is_opcode_valid_16 (insn);
-      more_alts = (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes]
-                  && strcmp (insn[0].name, insn[1].name) == 0);
-      if (! ok)
-       {
-         if (more_alts)
-           {
-             ++insn;
-             continue;
-           }
-         else
-           {
-             set_insn_error_ss
-               (0, _("Opcode not supported on this processor: %s (%s)"),
-                mips_cpu_info_from_arch (mips_opts.arch)->name,
-                mips_cpu_info_from_isa (mips_opts.isa)->name);
-             break;
-           }
-       }
-
-      if (match_mips16_insn (ip, insn, tokens))
-       break;
-
-      /* Args don't match.  */
-      set_insn_error (0, _("Illegal operands"));
-      if (more_alts)
-       {
-         ++insn;
-         continue;
-       }
-      break;
-    }
   obstack_free (&mips_operand_tokens, tokens);
 }