From 7fd5392005624f8b0522d881aa94bbb470d68cd7 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Tue, 20 Dec 2016 02:03:40 +0000 Subject: MIPS16: Switch to 32-bit opcode table interpretation 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. --- opcodes/ChangeLog | 11 ++++ opcodes/mips-dis.c | 152 +++++++++++++++++++++++++-------------------------- opcodes/mips16-opc.c | 8 ++- 3 files changed, 90 insertions(+), 81 deletions(-) (limited to 'opcodes') diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog index 2890354..bffa445 100644 --- a/opcodes/ChangeLog +++ b/opcodes/ChangeLog @@ -1,5 +1,16 @@ 2016-12-20 Maciej W. Rozycki + * 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 + * mips16-opc.c (mips16_opcodes): Set membership to I3 rather than I1 for the "ddiv", "ddivu", "drem", "dremu" and "dsubu" INSN_MACRO entries. diff --git a/opcodes/mips-dis.c b/opcodes/mips-dis.c index 8c35759..609e0ba 100644 --- a/opcodes/mips-dis.c +++ b/opcodes/mips-dis.c @@ -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. */ diff --git a/opcodes/mips16-opc.c b/opcodes/mips16-opc.c index 0c4bb7f..758b78f 100644 --- a/opcodes/mips16-opc.c +++ b/opcodes/mips16-opc.c @@ -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 = -- cgit v1.1