MIPS16: Switch to 32-bit opcode table interpretation
authorMaciej W. Rozycki <macro@imgtec.com>
Tue, 20 Dec 2016 02:03:40 +0000 (02:03 +0000)
committerMaciej W. Rozycki <macro@imgtec.com>
Tue, 20 Dec 2016 11:56:32 +0000 (11:56 +0000)
Switch to 32-bit MIPS16 opcode table entry interpretation, similar to
how the microMIPS opcode table is handled, for both the `match' and
`mask' fields, removing special casing for JAL and JALX instructions and
their `a' and `i' operand codes throughout, while retaining automatic
processing of extendable opcodes in assembly and disassembly.

In assembly disallow size enforcement suffixes as appropriate: `.t' for
both 32-bit instructions and macros and `.e' for macros only, making
macro handling consistent with the microMIPS instruction set.

In disassembly fully decode EXTEND prefixes prepended to unsupported
instruction encodings (according to the ISA selection) rather than
dumping them as hexadecimal data along with the following instruction,
removing all special casing for the EXTEND prefix and making its
handling rely on its opcode table entry, except where it is considered a
part of an extendable instruction.

include/
* opcode/mips.h (mips_opcode_32bit_p): New inline function.

gas/
* config/tc-mips.c (micromips_insn_length): Use
`mips_opcode_32bit_p'.
(is_size_valid): Adjust description.
(is_size_valid_16): New function.
(validate_mips_insn): Use `mips_opcode_32bit_p' in MIPS16
operand decoding.
(validate_mips16_insn): Remove `a' and `i' operand code special
casing, use `mips_opcode_32bit_p' to determine instruction
width.
(append_insn): Adjust forced MIPS16 instruction size
determination.
(match_mips16_insn): Likewise.  Don't shift the instruction's
opcode with the `a' and `i' operand codes.  Use
`mips_opcode_32bit_p' in operand decoding.
(match_mips16_insns): Check for forced instruction size's
validity.
(mips16_ip): Don't force instruction size in the `noautoextend'
mode.
* testsuite/gas/mips/mips16-jal-e.d: New test.
* testsuite/gas/mips/mips16-jal-t.d: New test.
* testsuite/gas/mips/mips16-macro-e.d: New test.
* testsuite/gas/mips/mips16-macro-t.d: New test.
* testsuite/gas/mips/mips16-jal-t.l: New stderr output.
* testsuite/gas/mips/mips16-macro-e.l: New stderr output.
* testsuite/gas/mips/mips16-macro-t.l: New stderr output.
* testsuite/gas/mips/mips16-jal-e.s: New test source.
* testsuite/gas/mips/mips16-jal-t.s: New test source.
* testsuite/gas/mips/mips16-macro-e.s: New test source.
* testsuite/gas/mips/mips16-macro-t.s: New test source.
* testsuite/gas/mips/mips.exp: Run the new tests.

opcodes/
* mips-dis.c (print_mips16_insn_arg): Always handle `extend' and
`insn' together, with `extend' as the high-order 16 bits.
(match_kind): New enum.
(print_insn_mips16): Rework for 32-bit instruction matching.
Do not dump EXTEND prefixes here.
* mips16-opc.c (mips16_opcodes): Move "extend" entry to the end.
Recode `match' and `mask' fields as 32-bit in absolute "jal" and
"jalx" entries.

binutils/
* testsuite/binutils-all/mips/mips16-extend-noinsn.d: Adjust
test for separate EXTEND prefix disassembly.

21 files changed:
binutils/ChangeLog
binutils/testsuite/binutils-all/mips/mips16-extend-noinsn.d
gas/ChangeLog
gas/config/tc-mips.c
gas/testsuite/gas/mips/mips.exp
gas/testsuite/gas/mips/mips16-jal-e.d [new file with mode: 0644]
gas/testsuite/gas/mips/mips16-jal-e.s [new file with mode: 0644]
gas/testsuite/gas/mips/mips16-jal-t.d [new file with mode: 0644]
gas/testsuite/gas/mips/mips16-jal-t.l [new file with mode: 0644]
gas/testsuite/gas/mips/mips16-jal-t.s [new file with mode: 0644]
gas/testsuite/gas/mips/mips16-macro-e.d [new file with mode: 0644]
gas/testsuite/gas/mips/mips16-macro-e.l [new file with mode: 0644]
gas/testsuite/gas/mips/mips16-macro-e.s [new file with mode: 0644]
gas/testsuite/gas/mips/mips16-macro-t.d [new file with mode: 0644]
gas/testsuite/gas/mips/mips16-macro-t.l [new file with mode: 0644]
gas/testsuite/gas/mips/mips16-macro-t.s [new file with mode: 0644]
include/ChangeLog
include/opcode/mips.h
opcodes/ChangeLog
opcodes/mips-dis.c
opcodes/mips16-opc.c

index fd86ed5f33f274a60263044eed1b9c63faba27e8..6e9de9b5acf363ac4bd8b2395243a5d024a4cbd4 100644 (file)
@@ -1,3 +1,8 @@
+2016-12-20  Maciej W. Rozycki  <macro@imgtec.com>
+
+       * testsuite/binutils-all/mips/mips16-extend-noinsn.d: Adjust
+       test for separate EXTEND prefix disassembly.
+
 2016-12-20  Andrew Waterman  <andrew@sifive.com>
 
        * readelf.c (get_machine_flags): Use
index 5694de6df70d3349d7db5092aadc654f384d4c41..398f5a5294e9c17f8a2f5a7ee462dcc9cab77641 100644 (file)
@@ -17,12 +17,18 @@ Disassembly of section \.text:
 [0-9a-f]+ <[^>]*> f432         extend  0x432
 [0-9a-f]+ <[^>]*> 1c00 0000    jalx    00000000 <foo>
 [0-9a-f]+ <[^>]*> 6500         nop
-[0-9a-f]+ <[^>]*> f123 6621    0xf123 0x6621
-[0-9a-f]+ <[^>]*> f456 e935    0xf456 0xe935
-[0-9a-f]+ <[^>]*> f765 ea60    0xf765 0xea60
-[0-9a-f]+ <[^>]*> f432 ece0    0xf432 0xece0
-[0-9a-f]+ <[^>]*> f5aa e971    0xf5aa 0xe971
-[0-9a-f]+ <[^>]*> f655 ebf1    0xf655 0xebf1
+[0-9a-f]+ <[^>]*> f123         extend  0x123
+[0-9a-f]+ <[^>]*> 6621         0x6621
+[0-9a-f]+ <[^>]*> f456         extend  0x456
+[0-9a-f]+ <[^>]*> e935         0xe935
+[0-9a-f]+ <[^>]*> f765         extend  0x765
+[0-9a-f]+ <[^>]*> ea60         0xea60
+[0-9a-f]+ <[^>]*> f432         extend  0x432
+[0-9a-f]+ <[^>]*> ece0         0xece0
+[0-9a-f]+ <[^>]*> f5aa         extend  0x5aa
+[0-9a-f]+ <[^>]*> e971         0xe971
+[0-9a-f]+ <[^>]*> f655         extend  0x655
+[0-9a-f]+ <[^>]*> ebf1         0xebf1
 [0-9a-f]+ <[^>]*> 6621         0x6621
 [0-9a-f]+ <[^>]*> e935         0xe935
 [0-9a-f]+ <[^>]*> ea60         0xea60
index c1e0e1e70e80bc3e8dc71e1823730b4f2e85162b..4b15c9fc2906b834f678d6e47d473f19022d5325 100644 (file)
@@ -1,3 +1,36 @@
+2016-12-20  Maciej W. Rozycki  <macro@imgtec.com>
+
+       * config/tc-mips.c (micromips_insn_length): Use
+       `mips_opcode_32bit_p'.
+       (is_size_valid): Adjust description.
+       (is_size_valid_16): New function.
+       (validate_mips_insn): Use `mips_opcode_32bit_p' in MIPS16
+       operand decoding.
+       (validate_mips16_insn): Remove `a' and `i' operand code special
+       casing, use `mips_opcode_32bit_p' to determine instruction
+       width.
+       (append_insn): Adjust forced MIPS16 instruction size
+       determination.
+       (match_mips16_insn): Likewise.  Don't shift the instruction's
+       opcode with the `a' and `i' operand codes.  Use
+       `mips_opcode_32bit_p' in operand decoding.
+       (match_mips16_insns): Check for forced instruction size's
+       validity.
+       (mips16_ip): Don't force instruction size in the `noautoextend'
+       mode.
+       * testsuite/gas/mips/mips16-jal-e.d: New test.
+       * testsuite/gas/mips/mips16-jal-t.d: New test.
+       * testsuite/gas/mips/mips16-macro-e.d: New test.
+       * testsuite/gas/mips/mips16-macro-t.d: New test.
+       * testsuite/gas/mips/mips16-jal-t.l: New stderr output.
+       * testsuite/gas/mips/mips16-macro-e.l: New stderr output.
+       * testsuite/gas/mips/mips16-macro-t.l: New stderr output.
+       * testsuite/gas/mips/mips16-jal-e.s: New test source.
+       * testsuite/gas/mips/mips16-jal-t.s: New test source.
+       * testsuite/gas/mips/mips16-macro-e.s: New test source.
+       * testsuite/gas/mips/mips16-macro-t.s: New test source.
+       * testsuite/gas/mips/mips.exp: Run the new tests.
+
 2016-12-20  Maciej W. Rozycki  <macro@imgtec.com>
 
        * testsuite/gas/mips/mips16-macro.l: New list test.
index 1241b9c1e5d343d716eee006a5a399dc33ed45f2..a68e267d5ae99fd7b5be3ce233c875add2b986a3 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.  */
@@ -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,21 @@ 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;
+  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 +3372,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 +3430,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.  */
@@ -7357,9 +7364,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 +7399,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 +8060,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 +8126,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,11 +8179,10 @@ 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 ();
 
@@ -8315,11 +8343,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 +8357,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 +8377,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;
 }
 
@@ -13845,9 +13892,6 @@ mips16_ip (char *str, struct mips_cl_insn *insn)
       return;
     }
 
-  if (mips_opts.noautoextend && !forced_insn_length)
-    forced_insn_length = 2;
-
   *end = 0;
   first = (struct mips_opcode *) hash_find (mips16_op_hash, str);
   *end = c;
index 101b6531d8dbed321f3c7595d2b02774add1cca9..ffc08a0fd713793832bc864b15e0d75bd007cb01 100644 (file)
@@ -854,6 +854,8 @@ if { [istarget mips*-*-vxworks*] } {
     }
     run_dump_test "mips16-macro"
     run_list_test "mips16-macro" "-32 -march=mips1"
+    run_dump_test "mips16-macro-t" "{{as} {-march=mips3}}"
+    run_dump_test "mips16-macro-e" "{{as} {-march=mips3}}"
     # Check MIPS16e extensions
     run_dump_test_arches "mips16e" [mips_arch_list_matching mips32 !micromips \
                                        !mips32r6]
@@ -1318,6 +1320,8 @@ if { [istarget mips*-*-vxworks*] } {
 
     run_dump_test "mips16-branch-unextended-1"
     run_dump_test "mips16-branch-unextended-2"
+    run_dump_test "mips16-jal-t"
+    run_dump_test "mips16-jal-e"
 
     run_dump_test "vxworks1"
     run_dump_test "vxworks1-xgot"
diff --git a/gas/testsuite/gas/mips/mips16-jal-e.d b/gas/testsuite/gas/mips/mips16-jal-e.d
new file mode 100644 (file)
index 0000000..bbccb7c
--- /dev/null
@@ -0,0 +1,12 @@
+#objdump: -d --prefix-addresses --show-raw-insn
+#name: MIPS16 explicit extended JAL instructions
+#as: -32
+
+.*: +file format .*mips.*
+
+Disassembly of section \.text:
+[0-9a-f]+ <[^>]*> 1c00 0000    jalx    00000000 <foo>
+[0-9a-f]+ <[^>]*> 6500         nop
+[0-9a-f]+ <[^>]*> 1800 0000    jal     00000000 <foo>
+[0-9a-f]+ <[^>]*> 6500         nop
+       \.\.\.
diff --git a/gas/testsuite/gas/mips/mips16-jal-e.s b/gas/testsuite/gas/mips/mips16-jal-e.s
new file mode 100644 (file)
index 0000000..9c18f5f
--- /dev/null
@@ -0,0 +1,8 @@
+       .set    mips16
+foo:
+       jalx.e  bar
+       jal.e   baz
+
+# Force some (non-delay-slot) zero bytes, to make 'objdump' print ...
+       .align  4, 0
+       .space  16
diff --git a/gas/testsuite/gas/mips/mips16-jal-t.d b/gas/testsuite/gas/mips/mips16-jal-t.d
new file mode 100644 (file)
index 0000000..287f78d
--- /dev/null
@@ -0,0 +1,3 @@
+#name: MIPS16 explicit unextended JAL instructions
+#as: -32
+#error-output: mips16-jal-t.l
diff --git a/gas/testsuite/gas/mips/mips16-jal-t.l b/gas/testsuite/gas/mips/mips16-jal-t.l
new file mode 100644 (file)
index 0000000..0f67828
--- /dev/null
@@ -0,0 +1,3 @@
+.*: Assembler messages:
+.*:3: Error: unrecognized unextended version of MIPS16 opcode `jalx\.t bar'
+.*:4: Error: invalid operands `jal\.t baz'
diff --git a/gas/testsuite/gas/mips/mips16-jal-t.s b/gas/testsuite/gas/mips/mips16-jal-t.s
new file mode 100644 (file)
index 0000000..33d35ac
--- /dev/null
@@ -0,0 +1,8 @@
+       .set    mips16
+foo:
+       jalx.t  bar
+       jal.t   baz
+
+# Force some (non-delay-slot) zero bytes, to make 'objdump' print ...
+       .align  4, 0
+       .space  16
diff --git a/gas/testsuite/gas/mips/mips16-macro-e.d b/gas/testsuite/gas/mips/mips16-macro-e.d
new file mode 100644 (file)
index 0000000..464bac2
--- /dev/null
@@ -0,0 +1,3 @@
+#as: -32
+#name: MIPS16 explicit extended macros
+#error-output: mips16-macro-e.l
diff --git a/gas/testsuite/gas/mips/mips16-macro-e.l b/gas/testsuite/gas/mips/mips16-macro-e.l
new file mode 100644 (file)
index 0000000..225d9a9
--- /dev/null
@@ -0,0 +1,56 @@
+.*: Assembler messages:
+.*:4: Error: invalid operands `div\.e \$2,\$3,\$4'
+.*:5: Error: invalid operands `divu\.e \$3,\$4,\$5'
+.*:6: Error: invalid operands `ddiv\.e \$4,\$5,\$6'
+.*:7: Error: invalid operands `ddivu\.e \$5,\$6,\$7'
+.*:8: Error: invalid operands `rem\.e \$6,\$7,\$16'
+.*:9: Error: invalid operands `remu\.e \$6,\$7,\$17'
+.*:10: Error: invalid operands `drem\.e \$2,\$3,\$4'
+.*:11: Error: invalid operands `dremu\.e \$3,\$4,\$5'
+.*:12: Error: unrecognized extended version of MIPS16 opcode `mul\.e \$4,\$5,\$6'
+.*:13: Error: unrecognized extended version of MIPS16 opcode `dmul\.e \$5,\$6,\$7'
+.*:14: Error: invalid operands `subu\.e \$2,-32767'
+.*:15: Error: invalid operands `subu\.e \$3,16'
+.*:16: Error: invalid operands `subu\.e \$4,32768'
+.*:17: Error: invalid operands `subu\.e \$3,\$7,-16383'
+.*:18: Error: invalid operands `subu\.e \$4,\$16,4'
+.*:19: Error: invalid operands `subu\.e \$5,\$17,16384'
+.*:20: Error: invalid operands `dsubu\.e \$4,-32767'
+.*:21: Error: invalid operands `dsubu\.e \$6,6'
+.*:22: Error: invalid operands `dsubu\.e \$7,32768'
+.*:23: Error: invalid operands `dsubu\.e \$2,\$4,-16383'
+.*:24: Error: invalid operands `dsubu\.e \$3,\$7,8'
+.*:25: Error: invalid operands `dsubu\.e \$4,\$5,16384'
+.*:26: Error: unrecognized extended version of MIPS16 opcode `beq\.e \$2,\$3,1b'
+.*:27: Error: unrecognized extended version of MIPS16 opcode `bne\.e \$4,\$5,1b'
+.*:28: Error: unrecognized extended version of MIPS16 opcode `blt\.e \$6,\$7,1b'
+.*:29: Error: unrecognized extended version of MIPS16 opcode `bltu\.e \$16,\$17,1b'
+.*:30: Error: unrecognized extended version of MIPS16 opcode `ble\.e \$4,\$7,1b'
+.*:31: Error: unrecognized extended version of MIPS16 opcode `bleu\.e \$5,\$6,1b'
+.*:32: Error: unrecognized extended version of MIPS16 opcode `bge\.e \$4,\$16,1b'
+.*:33: Error: unrecognized extended version of MIPS16 opcode `bgeu\.e \$5,\$17,1b'
+.*:34: Error: unrecognized extended version of MIPS16 opcode `bgt\.e \$4,\$6,1b'
+.*:35: Error: unrecognized extended version of MIPS16 opcode `bgtu\.e \$5,\$7,1b'
+.*:36: Error: unrecognized extended version of MIPS16 opcode `beq\.e \$2,1,1b'
+.*:37: Error: unrecognized extended version of MIPS16 opcode `beq\.e \$3,65535,1b'
+.*:38: Error: unrecognized extended version of MIPS16 opcode `bne\.e \$4,1,1b'
+.*:39: Error: unrecognized extended version of MIPS16 opcode `bne\.e \$5,65535,1b'
+.*:40: Error: unrecognized extended version of MIPS16 opcode `blt\.e \$6,-32768,1b'
+.*:41: Error: unrecognized extended version of MIPS16 opcode `blt\.e \$7,32767,1b'
+.*:42: Error: unrecognized extended version of MIPS16 opcode `bltu\.e \$16,-32768,1b'
+.*:43: Error: unrecognized extended version of MIPS16 opcode `bltu\.e \$17,32767,1b'
+.*:44: Error: unrecognized extended version of MIPS16 opcode `ble\.e \$2,-32769,1b'
+.*:45: Error: unrecognized extended version of MIPS16 opcode `ble\.e \$3,32766,1b'
+.*:46: Error: unrecognized extended version of MIPS16 opcode `bleu\.e \$4,-32769,1b'
+.*:47: Error: unrecognized extended version of MIPS16 opcode `bleu\.e \$5,32766,1b'
+.*:48: Error: unrecognized extended version of MIPS16 opcode `bge\.e \$6,-32768,1b'
+.*:49: Error: unrecognized extended version of MIPS16 opcode `bge\.e \$7,32766,1b'
+.*:50: Error: unrecognized extended version of MIPS16 opcode `bgeu\.e \$16,-32768,1b'
+.*:51: Error: unrecognized extended version of MIPS16 opcode `bgeu\.e \$17,32767,1b'
+.*:52: Error: unrecognized extended version of MIPS16 opcode `bgt\.e \$2,-32769,1b'
+.*:53: Error: unrecognized extended version of MIPS16 opcode `bgt\.e \$3,32766,1b'
+.*:54: Error: unrecognized extended version of MIPS16 opcode `bgtu\.e \$4,-32769,1b'
+.*:55: Error: unrecognized extended version of MIPS16 opcode `bgtu\.e \$5,32766,1b'
+.*:56: Error: unrecognized extended version of MIPS16 opcode `abs\.e \$2'
+.*:57: Error: unrecognized extended version of MIPS16 opcode `abs\.e \$3,\$3'
+.*:58: Error: unrecognized extended version of MIPS16 opcode `abs\.e \$4,\$5'
diff --git a/gas/testsuite/gas/mips/mips16-macro-e.s b/gas/testsuite/gas/mips/mips16-macro-e.s
new file mode 100644 (file)
index 0000000..048ebda
--- /dev/null
@@ -0,0 +1,59 @@
+       .set    mips16
+       .ent    foo
+foo:
+       div.e   $2,$3,$4
+       divu.e  $3,$4,$5
+       ddiv.e  $4,$5,$6
+       ddivu.e $5,$6,$7
+       rem.e   $6,$7,$16
+       remu.e  $6,$7,$17
+       drem.e  $2,$3,$4
+       dremu.e $3,$4,$5
+       mul.e   $4,$5,$6
+       dmul.e  $5,$6,$7
+       subu.e  $2,-32767
+       subu.e  $3,16
+       subu.e  $4,32768
+       subu.e  $3,$7,-16383
+       subu.e  $4,$16,4
+       subu.e  $5,$17,16384
+       dsubu.e $4,-32767
+       dsubu.e $6,6
+       dsubu.e $7,32768
+       dsubu.e $2,$4,-16383
+       dsubu.e $3,$7,8
+       dsubu.e $4,$5,16384
+1:     beq.e   $2,$3,1b
+1:     bne.e   $4,$5,1b
+1:     blt.e   $6,$7,1b
+1:     bltu.e  $16,$17,1b
+1:     ble.e   $4,$7,1b
+1:     bleu.e  $5,$6,1b
+1:     bge.e   $4,$16,1b
+1:     bgeu.e  $5,$17,1b
+1:     bgt.e   $4,$6,1b
+1:     bgtu.e  $5,$7,1b
+1:     beq.e   $2,1,1b
+1:     beq.e   $3,65535,1b
+1:     bne.e   $4,1,1b
+1:     bne.e   $5,65535,1b
+1:     blt.e   $6,-32768,1b
+1:     blt.e   $7,32767,1b
+1:     bltu.e  $16,-32768,1b
+1:     bltu.e  $17,32767,1b
+1:     ble.e   $2,-32769,1b
+1:     ble.e   $3,32766,1b
+1:     bleu.e  $4,-32769,1b
+1:     bleu.e  $5,32766,1b
+1:     bge.e   $6,-32768,1b
+1:     bge.e   $7,32766,1b
+1:     bgeu.e  $16,-32768,1b
+1:     bgeu.e  $17,32767,1b
+1:     bgt.e   $2,-32769,1b
+1:     bgt.e   $3,32766,1b
+1:     bgtu.e  $4,-32769,1b
+1:     bgtu.e  $5,32766,1b
+       abs.e   $2
+       abs.e   $3,$3
+       abs.e   $4,$5
+       .end    foo
diff --git a/gas/testsuite/gas/mips/mips16-macro-t.d b/gas/testsuite/gas/mips/mips16-macro-t.d
new file mode 100644 (file)
index 0000000..104094d
--- /dev/null
@@ -0,0 +1,3 @@
+#as: -32
+#name: MIPS16 explicit unextended macros
+#error-output: mips16-macro-t.l
diff --git a/gas/testsuite/gas/mips/mips16-macro-t.l b/gas/testsuite/gas/mips/mips16-macro-t.l
new file mode 100644 (file)
index 0000000..9850cb3
--- /dev/null
@@ -0,0 +1,56 @@
+.*: Assembler messages:
+.*:4: Error: invalid operands `div\.t \$2,\$3,\$4'
+.*:5: Error: invalid operands `divu\.t \$3,\$4,\$5'
+.*:6: Error: invalid operands `ddiv\.t \$4,\$5,\$6'
+.*:7: Error: invalid operands `ddivu\.t \$5,\$6,\$7'
+.*:8: Error: invalid operands `rem\.t \$6,\$7,\$16'
+.*:9: Error: invalid operands `remu\.t \$6,\$7,\$17'
+.*:10: Error: invalid operands `drem\.t \$2,\$3,\$4'
+.*:11: Error: invalid operands `dremu\.t \$3,\$4,\$5'
+.*:12: Error: unrecognized unextended version of MIPS16 opcode `mul\.t \$4,\$5,\$6'
+.*:13: Error: unrecognized unextended version of MIPS16 opcode `dmul\.t \$5,\$6,\$7'
+.*:14: Error: invalid operands `subu\.t \$2,-32767'
+.*:15: Error: invalid operands `subu\.t \$3,16'
+.*:16: Error: invalid operands `subu\.t \$4,32768'
+.*:17: Error: invalid operands `subu\.t \$3,\$7,-16383'
+.*:18: Error: invalid operands `subu\.t \$4,\$16,4'
+.*:19: Error: invalid operands `subu\.t \$5,\$17,16384'
+.*:20: Error: invalid operands `dsubu\.t \$4,-32767'
+.*:21: Error: invalid operands `dsubu\.t \$6,6'
+.*:22: Error: invalid operands `dsubu\.t \$7,32768'
+.*:23: Error: invalid operands `dsubu\.t \$2,\$4,-16383'
+.*:24: Error: invalid operands `dsubu\.t \$3,\$7,8'
+.*:25: Error: invalid operands `dsubu\.t \$4,\$5,16384'
+.*:26: Error: unrecognized unextended version of MIPS16 opcode `beq\.t \$2,\$3,1b'
+.*:27: Error: unrecognized unextended version of MIPS16 opcode `bne\.t \$4,\$5,1b'
+.*:28: Error: unrecognized unextended version of MIPS16 opcode `blt\.t \$6,\$7,1b'
+.*:29: Error: unrecognized unextended version of MIPS16 opcode `bltu\.t \$16,\$17,1b'
+.*:30: Error: unrecognized unextended version of MIPS16 opcode `ble\.t \$4,\$7,1b'
+.*:31: Error: unrecognized unextended version of MIPS16 opcode `bleu\.t \$5,\$6,1b'
+.*:32: Error: unrecognized unextended version of MIPS16 opcode `bge\.t \$4,\$16,1b'
+.*:33: Error: unrecognized unextended version of MIPS16 opcode `bgeu\.t \$5,\$17,1b'
+.*:34: Error: unrecognized unextended version of MIPS16 opcode `bgt\.t \$4,\$6,1b'
+.*:35: Error: unrecognized unextended version of MIPS16 opcode `bgtu\.t \$5,\$7,1b'
+.*:36: Error: unrecognized unextended version of MIPS16 opcode `beq\.t \$2,1,1b'
+.*:37: Error: unrecognized unextended version of MIPS16 opcode `beq\.t \$3,65535,1b'
+.*:38: Error: unrecognized unextended version of MIPS16 opcode `bne\.t \$4,1,1b'
+.*:39: Error: unrecognized unextended version of MIPS16 opcode `bne\.t \$5,65535,1b'
+.*:40: Error: unrecognized unextended version of MIPS16 opcode `blt\.t \$6,-32768,1b'
+.*:41: Error: unrecognized unextended version of MIPS16 opcode `blt\.t \$7,32767,1b'
+.*:42: Error: unrecognized unextended version of MIPS16 opcode `bltu\.t \$16,-32768,1b'
+.*:43: Error: unrecognized unextended version of MIPS16 opcode `bltu\.t \$17,32767,1b'
+.*:44: Error: unrecognized unextended version of MIPS16 opcode `ble\.t \$2,-32769,1b'
+.*:45: Error: unrecognized unextended version of MIPS16 opcode `ble\.t \$3,32766,1b'
+.*:46: Error: unrecognized unextended version of MIPS16 opcode `bleu\.t \$4,-32769,1b'
+.*:47: Error: unrecognized unextended version of MIPS16 opcode `bleu\.t \$5,32766,1b'
+.*:48: Error: unrecognized unextended version of MIPS16 opcode `bge\.t \$6,-32768,1b'
+.*:49: Error: unrecognized unextended version of MIPS16 opcode `bge\.t \$7,32766,1b'
+.*:50: Error: unrecognized unextended version of MIPS16 opcode `bgeu\.t \$16,-32768,1b'
+.*:51: Error: unrecognized unextended version of MIPS16 opcode `bgeu\.t \$17,32767,1b'
+.*:52: Error: unrecognized unextended version of MIPS16 opcode `bgt\.t \$2,-32769,1b'
+.*:53: Error: unrecognized unextended version of MIPS16 opcode `bgt\.t \$3,32766,1b'
+.*:54: Error: unrecognized unextended version of MIPS16 opcode `bgtu\.t \$4,-32769,1b'
+.*:55: Error: unrecognized unextended version of MIPS16 opcode `bgtu\.t \$5,32766,1b'
+.*:56: Error: unrecognized unextended version of MIPS16 opcode `abs\.t \$2'
+.*:57: Error: unrecognized unextended version of MIPS16 opcode `abs\.t \$3,\$3'
+.*:58: Error: unrecognized unextended version of MIPS16 opcode `abs\.t \$4,\$5'
diff --git a/gas/testsuite/gas/mips/mips16-macro-t.s b/gas/testsuite/gas/mips/mips16-macro-t.s
new file mode 100644 (file)
index 0000000..cc04249
--- /dev/null
@@ -0,0 +1,59 @@
+       .set    mips16
+       .ent    foo
+foo:
+       div.t   $2,$3,$4
+       divu.t  $3,$4,$5
+       ddiv.t  $4,$5,$6
+       ddivu.t $5,$6,$7
+       rem.t   $6,$7,$16
+       remu.t  $6,$7,$17
+       drem.t  $2,$3,$4
+       dremu.t $3,$4,$5
+       mul.t   $4,$5,$6
+       dmul.t  $5,$6,$7
+       subu.t  $2,-32767
+       subu.t  $3,16
+       subu.t  $4,32768
+       subu.t  $3,$7,-16383
+       subu.t  $4,$16,4
+       subu.t  $5,$17,16384
+       dsubu.t $4,-32767
+       dsubu.t $6,6
+       dsubu.t $7,32768
+       dsubu.t $2,$4,-16383
+       dsubu.t $3,$7,8
+       dsubu.t $4,$5,16384
+1:     beq.t   $2,$3,1b
+1:     bne.t   $4,$5,1b
+1:     blt.t   $6,$7,1b
+1:     bltu.t  $16,$17,1b
+1:     ble.t   $4,$7,1b
+1:     bleu.t  $5,$6,1b
+1:     bge.t   $4,$16,1b
+1:     bgeu.t  $5,$17,1b
+1:     bgt.t   $4,$6,1b
+1:     bgtu.t  $5,$7,1b
+1:     beq.t   $2,1,1b
+1:     beq.t   $3,65535,1b
+1:     bne.t   $4,1,1b
+1:     bne.t   $5,65535,1b
+1:     blt.t   $6,-32768,1b
+1:     blt.t   $7,32767,1b
+1:     bltu.t  $16,-32768,1b
+1:     bltu.t  $17,32767,1b
+1:     ble.t   $2,-32769,1b
+1:     ble.t   $3,32766,1b
+1:     bleu.t  $4,-32769,1b
+1:     bleu.t  $5,32766,1b
+1:     bge.t   $6,-32768,1b
+1:     bge.t   $7,32766,1b
+1:     bgeu.t  $16,-32768,1b
+1:     bgeu.t  $17,32767,1b
+1:     bgt.t   $2,-32769,1b
+1:     bgt.t   $3,32766,1b
+1:     bgtu.t  $4,-32769,1b
+1:     bgtu.t  $5,32766,1b
+       abs.t   $2
+       abs.t   $3,$3
+       abs.t   $4,$5
+       .end    foo
index ffefe6550e1414d81cb7ccc0f204572096edac96..aeb3797b98c3c087e5406475d694d2437599f949 100644 (file)
@@ -1,3 +1,7 @@
+2016-12-20  Maciej W. Rozycki  <macro@imgtec.com>
+
+       * opcode/mips.h (mips_opcode_32bit_p): New inline function.
+
 2016-12-20  Andrew Waterman  <andrew@sifive.com>
 
        * elf/riscv.h (EF_RISCV_SOFT_FLOAT): Don't define.
index a199f06b24775b4af886b0c11b4f67cf484b4168..329a69873647dab08d013cb774a2d51775bad123 100644 (file)
@@ -753,6 +753,14 @@ struct mips_opcode
   unsigned long exclusions;
 };
 
+/* Return true if MO is an instruction that requires 32-bit encoding.  */
+
+static inline bfd_boolean
+mips_opcode_32bit_p (const struct mips_opcode *mo)
+{
+  return mo->mask >> 16 != 0;
+}
+
 /* These are the characters which may appear in the args field of an
    instruction.  They appear in the order in which the fields appear
    when the instruction is used.  Commas and parentheses in the args
index 289035418973cc2f6f843afd868c77d201ef7ae4..bffa445cd16ea96b3c3f09405a63ac7fb8ebbc0b 100644 (file)
@@ -1,3 +1,14 @@
+2016-12-20  Maciej W. Rozycki  <macro@imgtec.com>
+
+       * mips-dis.c (print_mips16_insn_arg): Always handle `extend' and
+       `insn' together, with `extend' as the high-order 16 bits.
+       (match_kind): New enum.
+       (print_insn_mips16): Rework for 32-bit instruction matching.
+       Do not dump EXTEND prefixes here.
+       * mips16-opc.c (mips16_opcodes): Move "extend" entry to the end.
+       Recode `match' and `mask' fields as 32-bit in absolute "jal" and
+       "jalx" entries.
+
 2016-12-20  Maciej W. Rozycki  <macro@imgtec.com>
 
        * mips16-opc.c (mips16_opcodes): Set membership to I3 rather
index 8c35759ffda17a7f7540d586cbe3d06068375255..609e0ba489d6ba0b366fe14a861aefb6399a5268 100644 (file)
@@ -1927,13 +1927,11 @@ print_mips16_insn_arg (struct disassemble_info *info,
        }
 
       if (operand->size == 26)
-       /* In this case INSN is the first two bytes of the instruction
-          and EXTEND is the second two bytes.  */
-       uval = ((insn & 0x1f) << 21) | ((insn & 0x3e0) << 11) | extend;
+       uval = ((extend & 0x1f) << 21) | ((extend & 0x3e0) << 11) | insn;
       else
        {
          /* Calculate the full field value.  */
-         uval = mips_extract_operand (operand, insn);
+         uval = mips_extract_operand (operand, (extend << 16) | insn);
          if (use_extend)
            {
              ext_operand = decode_mips16_operand (type, TRUE);
@@ -2015,6 +2013,15 @@ is_mips16_plt_tail (struct disassemble_info *info, bfd_vma addr)
   return FALSE;
 }
 
+/* Whether none, a 32-bit or a 16-bit instruction match has been done.  */
+
+enum match_kind
+{
+  MATCH_NONE,
+  MATCH_FULL,
+  MATCH_SHORT
+};
+
 /* Disassemble mips16 instructions.  */
 
 static int
@@ -2023,13 +2030,13 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
   const fprintf_ftype infprintf = info->fprintf_func;
   int status;
   bfd_byte buffer[4];
-  int length;
-  int insn;
-  bfd_boolean use_extend;
-  int extend = 0;
   const struct mips_opcode *op, *opend;
   struct mips_print_arg_state state;
   void *is = info->stream;
+  bfd_boolean have_second;
+  unsigned int second;
+  unsigned int first;
+  unsigned int full;
 
   info->bytes_per_chunk = 2;
   info->display_endian = info->endian;
@@ -2070,44 +2077,26 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
       return -1;
     }
 
-  length = 2;
-
   if (info->endian == BFD_ENDIAN_BIG)
-    insn = bfd_getb16 (buffer);
+    first = bfd_getb16 (buffer);
   else
-    insn = bfd_getl16 (buffer);
+    first = bfd_getl16 (buffer);
 
-  /* Handle the extend opcode specially.  */
-  use_extend = FALSE;
-  if ((insn & 0xf800) == 0xf000)
+  status = (*info->read_memory_func) (memaddr + 2, buffer, 2, info);
+  if (status == 0)
     {
-      use_extend = TRUE;
-      extend = insn & 0x7ff;
-
-      memaddr += 2;
-
-      status = (*info->read_memory_func) (memaddr, buffer, 2, info);
-      if (status != 0)
-       {
-         infprintf (is, "extend\t0x%x", (unsigned int) extend);
-         (*info->memory_error_func) (status, memaddr, info);
-         return -1;
-       }
-
+      have_second = TRUE;
       if (info->endian == BFD_ENDIAN_BIG)
-       insn = bfd_getb16 (buffer);
+       second = bfd_getb16 (buffer);
       else
-       insn = bfd_getl16 (buffer);
-
-      /* Check for an extend opcode followed by an extend opcode.  */
-      if ((insn & 0xf800) == 0xf000)
-       {
-         infprintf (is, "extend\t0x%x", (unsigned int) extend);
-         info->insn_type = dis_noninsn;
-         return length;
-       }
-
-      length += 2;
+       second = bfd_getl16 (buffer);
+      full = (first << 16) | second;
+    }
+  else
+    {
+      have_second = FALSE;
+      second = 0;
+      full = first;
     }
 
   /* FIXME: Should probably use a hash table on the major opcode here.  */
@@ -2115,37 +2104,35 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
   opend = mips16_opcodes + bfd_mips16_num_opcodes;
   for (op = mips16_opcodes; op < opend; op++)
     {
-      if (op->pinfo != INSN_MACRO
-         && !(no_aliases && (op->pinfo2 & INSN2_ALIAS))
-         && (insn & op->mask) == op->match)
-       {
-         const char *s;
-
-         if (op->args[0] == 'a' || op->args[0] == 'i')
-           {
-             if (use_extend)
-               {
-                 infprintf (is, "extend\t0x%x", (unsigned int) extend);
-                 info->insn_type = dis_noninsn;
-                 return length - 2;
-               }
+      enum match_kind match;
 
-             use_extend = FALSE;
-
-             memaddr += 2;
+      if (op->pinfo == INSN_MACRO
+         || (no_aliases && (op->pinfo2 & INSN2_ALIAS)))
+       match = MATCH_NONE;
+      else if (mips_opcode_32bit_p (op))
+       {
+         if (have_second
+             && (full & op->mask) == op->match)
+           match = MATCH_FULL;
+         else
+           match = MATCH_NONE;
+       }
+      else if ((first & op->mask) == op->match)
+       {
+         match = MATCH_SHORT;
+         second = 0;
+         full = first;
+       }
+      else if ((first & 0xf800) == 0xf000
+              && have_second
+              && (second & op->mask) == op->match)
+       match = MATCH_FULL;
+      else
+       match = MATCH_NONE;
 
-             status = (*info->read_memory_func) (memaddr, buffer, 2,
-                                                 info);
-             if (status == 0)
-               {
-                 use_extend = TRUE;
-                 if (info->endian == BFD_ENDIAN_BIG)
-                   extend = bfd_getb16 (buffer);
-                 else
-                   extend = bfd_getl16 (buffer);
-                 length += 2;
-               }
-           }
+      if (match != MATCH_NONE)
+       {
+         const char *s;
 
          infprintf (is, "%s", op->name);
          if (op->args[0] != '\0')
@@ -2156,7 +2143,7 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
            {
              if (*s == ','
                  && s[1] == 'w'
-                 && GET_OP (insn, RX) == GET_OP (insn, RY))
+                 && GET_OP (full, RX) == GET_OP (full, RY))
                {
                  /* Skip the register and the comma.  */
                  ++s;
@@ -2164,14 +2151,25 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
                }
              if (*s == ','
                  && s[1] == 'v'
-                 && GET_OP (insn, RZ) == GET_OP (insn, RX))
+                 && GET_OP (full, RZ) == GET_OP (full, RX))
                {
                  /* Skip the register and the comma.  */
                  ++s;
                  continue;
                }
-             print_mips16_insn_arg (info, &state, op, *s, memaddr, insn,
-                                    use_extend, extend, s[1] == '(');
+             switch (match)
+               {
+                 case MATCH_FULL:
+                   print_mips16_insn_arg (info, &state, op, *s, memaddr + 2,
+                                          second, TRUE, first, s[1] == '(');
+                   break;
+                 case MATCH_SHORT:
+                   print_mips16_insn_arg (info, &state, op, *s, memaddr,
+                                          first, FALSE, 0, s[1] == '(');
+                   break;
+                 case MATCH_NONE:      /* Stop the compiler complaining.  */
+                   break;
+               }
            }
 
          /* Figure out branch instruction type and delay slot information.  */
@@ -2188,17 +2186,15 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
          else if ((op->pinfo2 & INSN2_COND_BRANCH) != 0)
            info->insn_type = dis_condbranch;
 
-         return length;
+         return match == MATCH_FULL ? 4 : 2;
        }
     }
 #undef GET_OP
 
-  if (use_extend)
-    infprintf (is, "0x%x ", extend | 0xf000);
-  infprintf (is, "0x%x", insn);
+  infprintf (is, "0x%x", first);
   info->insn_type = dis_noninsn;
 
-  return length;
+  return 2;
 }
 
 /* Disassemble microMIPS instructions.  */
index 0c4bb7f53d2f06304686dbc8e064c4d270f73a33..758b78fba62070bf3c47587b210f7cf3750fde68 100644 (file)
@@ -273,13 +273,12 @@ const struct mips_opcode mips16_opcodes[] =
 {"exit",    "L",       0xef09, 0xff1f,         TRAP,                   0,              I1,     0,      0 },
 {"entry",   "",                0xe809, 0xffff,         TRAP,                   0,              I1,     0,      0 },
 {"entry",   "l",       0xe809, 0xf81f,         TRAP,                   0,              I1,     0,      0 },
-{"extend",  "e",       0xf000, 0xf800,         0,                      0,              I1,     0,      0 },
 {"jalr",    "x",       0xe840, 0xf8ff,         RD_1|WR_31|UBD,         0,              I1,     0,      0 },
 {"jalr",    "R,x",     0xe840, 0xf8ff,         RD_2|WR_31|UBD,         0,              I1,     0,      0 },
 {"jal",     "x",       0xe840, 0xf8ff,         RD_1|WR_31|UBD,         0,              I1,     0,      0 },
 {"jal",     "R,x",     0xe840, 0xf8ff,         RD_2|WR_31|UBD,         0,              I1,     0,      0 },
-{"jal",            "a",        0x1800, 0xfc00,         WR_31|UBD,              0,              I1,     0,      0 },
-{"jalx",    "i",       0x1c00, 0xfc00,         WR_31|UBD,              0,              I1,     0,      0 },
+{"jal",            "a",        0x18000000, 0xfc000000, WR_31|UBD,              0,              I1,     0,      0 },
+{"jalx",    "i",       0x1c000000, 0xfc000000, WR_31|UBD,              0,              I1,     0,      0 },
 {"jr",     "x",        0xe800, 0xf8ff,         RD_1|UBD,               0,              I1,     0,      0 },
 {"jr",     "R",        0xe820, 0xffff,         UBD,                    RD_31,          I1,     0,      0 },
 {"j",      "x",        0xe800, 0xf8ff,         RD_1|UBD,               0,              I1,     0,      0 },
@@ -356,6 +355,9 @@ const struct mips_opcode mips16_opcodes[] =
 {"zeb",            "x",        0xe811, 0xf8ff,         MOD_1,                  0,              I32,    0,      0 },
 {"zeh",            "x",        0xe831, 0xf8ff,         MOD_1,                  0,              I32,    0,      0 },
 {"zew",            "x",        0xe851, 0xf8ff,         MOD_1,                  0,              I64,    0,      0 },
+  /* Place EXTEND last so that it catches any prefix that didn't match
+     anything.  */
+{"extend",  "e",       0xf000, 0xf800,         0,                      0,              I1,     0,      0 },
 };
 
 const int bfd_mips16_num_opcodes =