diff options
Diffstat (limited to 'opcodes/mips-dis.c')
-rw-r--r-- | opcodes/mips-dis.c | 216 |
1 files changed, 201 insertions, 15 deletions
diff --git a/opcodes/mips-dis.c b/opcodes/mips-dis.c index b797e5d..1eb1d45 100644 --- a/opcodes/mips-dis.c +++ b/opcodes/mips-dis.c @@ -572,6 +572,14 @@ const struct mips_arch_choice mips_arch_choices[] = mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, + { "mips32r6", 1, bfd_mach_mipsisa32r6, CPU_MIPS32R6, + ISA_MIPS32R6, + (ASE_EVA | ASE_MSA | ASE_VIRT | ASE_XPA | ASE_MCU | ASE_MT | ASE_DSP + | ASE_DSPR2), + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, + /* For stock MIPS64, disassemble all applicable MIPS-specified ASEs. */ { "mips64", 1, bfd_mach_mipsisa64, CPU_MIPS64, ISA_MIPS64, ASE_MIPS3D | ASE_MDMX, @@ -603,6 +611,14 @@ const struct mips_arch_choice mips_arch_choices[] = mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, + { "mips64r6", 1, bfd_mach_mipsisa64r6, CPU_MIPS64R6, + ISA_MIPS64R6, + (ASE_EVA | ASE_MSA | ASE_MSA64 | ASE_XPA | ASE_VIRT | ASE_VIRT64 + | ASE_MCU | ASE_MT | ASE_DSP | ASE_DSPR2), + mips_cp0_names_mips3264r2, + mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2), + mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 }, + { "sb1", 1, bfd_mach_mips_sb1, CPU_SB1, ISA_MIPS64 | INSN_SB1, ASE_MIPS3D, mips_cp0_names_sb1, @@ -832,7 +848,8 @@ parse_mips_dis_option (const char *option, unsigned int len) mips_ase |= ASE_MSA; if ((mips_isa & INSN_ISA_MASK) == ISA_MIPS64R2 || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R3 - || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R5) + || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R5 + || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R6) mips_ase |= ASE_MSA64; return; } @@ -842,7 +859,8 @@ parse_mips_dis_option (const char *option, unsigned int len) mips_ase |= ASE_VIRT; if (mips_isa & ISA_MIPS64R2 || mips_isa & ISA_MIPS64R3 - || mips_isa & ISA_MIPS64R5) + || mips_isa & ISA_MIPS64R5 + || mips_isa & ISA_MIPS64R6) mips_ase |= ASE_VIRT64; return; } @@ -1084,6 +1102,8 @@ struct mips_print_arg_state { OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG. */ enum mips_reg_operand_type last_reg_type; unsigned int last_regno; + unsigned int dest_regno; + unsigned int seen_dest; }; /* Initialize STATE for the start of an instruction. */ @@ -1113,6 +1133,23 @@ print_vu0_channel (struct disassemble_info *info, abort (); } +/* Record information about a register operand. */ + +static void +mips_seen_register (struct mips_print_arg_state *state, + unsigned int regno, + enum mips_reg_operand_type reg_type) +{ + state->last_reg_type = reg_type; + state->last_regno = regno; + + if (!state->seen_dest) + { + state->seen_dest = 1; + state->dest_regno = regno; + } +} + /* Print operand OPERAND of OPCODE, using STATE to track inter-operand state. UVAL is the encoding of the operand (shifted into bit 0) and BASE_PC is the base address for OP_PCREL operands. */ @@ -1179,8 +1216,7 @@ print_insn_arg (struct disassemble_info *info, uval = mips_decode_reg_operand (reg_op, uval); print_reg (info, opcode, reg_op->reg_type, uval); - state->last_reg_type = reg_op->reg_type; - state->last_regno = uval; + mips_seen_register (state, uval, reg_op->reg_type); } break; @@ -1246,6 +1282,15 @@ print_insn_arg (struct disassemble_info *info, } break; + case OP_SAME_RS_RT: + case OP_CHECK_PREV: + case OP_NON_ZERO_REG: + { + print_reg (info, opcode, OP_REG_GP, uval & 31); + mips_seen_register (state, uval, OP_REG_GP); + } + break; + case OP_LWM_SWM_LIST: if (operand->size == 2) { @@ -1368,8 +1413,8 @@ print_insn_arg (struct disassemble_info *info, break; case OP_REPEAT_DEST_REG: - /* Should always match OP_REPEAT_PREV_REG first. */ - abort (); + print_reg (info, opcode, state->last_reg_type, state->dest_regno); + break; case OP_PC: infprintf (is, "$pc"); @@ -1392,15 +1437,130 @@ print_insn_arg (struct disassemble_info *info, } } +/* Validate the arguments for INSN, which is described by OPCODE. + Use DECODE_OPERAND to get the encoding of each operand. */ + +static bfd_boolean +validate_insn_args (const struct mips_opcode *opcode, + const struct mips_operand *(*decode_operand) (const char *), + unsigned int insn) +{ + struct mips_print_arg_state state; + const struct mips_operand *operand; + const char *s; + unsigned int uval; + + init_print_arg_state (&state); + for (s = opcode->args; *s; ++s) + { + switch (*s) + { + case ',': + case '(': + case ')': + break; + + case '#': + ++s; + break; + + default: + operand = decode_operand (s); + + if (operand) + { + uval = mips_extract_operand (operand, insn); + switch (operand->type) + { + case OP_REG: + case OP_OPTIONAL_REG: + { + const struct mips_reg_operand *reg_op; + + reg_op = (const struct mips_reg_operand *) operand; + uval = mips_decode_reg_operand (reg_op, uval); + mips_seen_register (&state, uval, reg_op->reg_type); + } + break; + + case OP_SAME_RS_RT: + { + unsigned int reg1, reg2; + + reg1 = uval & 31; + reg2 = uval >> 5; + + if (reg1 != reg2 || reg1 == 0) + return FALSE; + } + break; + + case OP_CHECK_PREV: + { + const struct mips_check_prev_operand *prev_op; + + prev_op = (const struct mips_check_prev_operand *) operand; + + if (!prev_op->zero_ok && uval == 0) + return FALSE; + + if (((prev_op->less_than_ok && uval < state.last_regno) + || (prev_op->greater_than_ok && uval > state.last_regno) + || (prev_op->equal_ok && uval == state.last_regno))) + break; + + return FALSE; + } + + case OP_NON_ZERO_REG: + { + if (uval == 0) + return FALSE; + } + break; + + case OP_INT: + case OP_MAPPED_INT: + case OP_MSB: + case OP_REG_PAIR: + case OP_PCREL: + case OP_PERF_REG: + case OP_ADDIUSP_INT: + case OP_CLO_CLZ_DEST: + case OP_LWM_SWM_LIST: + case OP_ENTRY_EXIT_LIST: + case OP_MDMX_IMM_REG: + case OP_REPEAT_PREV_REG: + case OP_REPEAT_DEST_REG: + case OP_PC: + case OP_VU0_SUFFIX: + case OP_VU0_MATCH_SUFFIX: + case OP_IMM_INDEX: + case OP_REG_INDEX: + break; + + case OP_SAVE_RESTORE_LIST: + /* Should be handled by the caller due to extend behavior. */ + abort (); + } + } + if (*s == 'm' || *s == '+' || *s == '-') + ++s; + } + } + return TRUE; +} + /* Print the arguments for INSN, which is described by OPCODE. Use DECODE_OPERAND to get the encoding of each operand. Use BASE_PC - as the base of OP_PCREL operands. */ + as the base of OP_PCREL operands, adjusting by LENGTH if the OP_PCREL + operand is for a branch or jump. */ static void print_insn_args (struct disassemble_info *info, const struct mips_opcode *opcode, const struct mips_operand *(*decode_operand) (const char *), - unsigned int insn, bfd_vma base_pc) + unsigned int insn, bfd_vma insn_pc, unsigned int length) { const fprintf_ftype infprintf = info->fprintf_func; void *is = info->stream; @@ -1462,9 +1622,27 @@ print_insn_args (struct disassemble_info *info, infprintf (is, "$%d,%d", reg, sel); } else - print_insn_arg (info, &state, opcode, operand, base_pc, - mips_extract_operand (operand, insn)); - if (*s == 'm' || *s == '+') + { + bfd_vma base_pc = insn_pc; + + /* Adjust the PC relative base so that branch/jump insns use + the following PC as the base but genuinely PC relative + operands use the current PC. */ + if (operand->type == OP_PCREL) + { + const struct mips_pcrel_operand *pcrel_op; + + pcrel_op = (const struct mips_pcrel_operand *) operand; + /* The include_isa_bit flag is sufficient to distinguish + branch/jump from other PC relative operands. */ + if (pcrel_op->include_isa_bit) + base_pc += length; + } + + print_insn_arg (info, &state, opcode, operand, base_pc, + mips_extract_operand (operand, insn)); + } + if (*s == 'm' || *s == '+' || *s == '-') ++s; break; } @@ -1530,9 +1708,11 @@ print_insn_mips (bfd_vma memaddr, && !(no_aliases && (op->pinfo2 & INSN2_ALIAS)) && (word & op->mask) == op->match) { - /* We always allow to disassemble the jalx instruction. */ + /* We always disassemble the jalx instruction, except for MIPS r6. */ if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor) - && strcmp (op->name, "jalx")) + && (strcmp (op->name, "jalx") + || (mips_isa & INSN_ISA_MASK) == ISA_MIPS32R6 + || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R6)) continue; /* Figure out instruction type and branch delay information. */ @@ -1557,6 +1737,9 @@ print_insn_mips (bfd_vma memaddr, | INSN_LOAD_MEMORY)) != 0) info->insn_type = dis_dref; + if (!validate_insn_args (op, decode_mips_operand, word)) + continue; + infprintf (is, "%s", op->name); if (op->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) { @@ -1571,7 +1754,7 @@ print_insn_mips (bfd_vma memaddr, { infprintf (is, "\t"); print_insn_args (info, op, decode_mips_operand, word, - memaddr + 4); + memaddr, 4); } return INSNLEN; @@ -2077,13 +2260,16 @@ print_insn_micromips (bfd_vma memaddr, struct disassemble_info *info) && ((length == 2 && (op->mask & 0xffff0000) == 0) || (length == 4 && (op->mask & 0xffff0000) != 0))) { + if (!validate_insn_args (op, decode_micromips_operand, insn)) + continue; + infprintf (is, "%s", op->name); if (op->args[0]) { infprintf (is, "\t"); print_insn_args (info, op, decode_micromips_operand, insn, - memaddr + length + 1); + memaddr + 1, length); } /* Figure out instruction type and branch delay information. */ |