aboutsummaryrefslogtreecommitdiff
path: root/opcodes/mips-dis.c
diff options
context:
space:
mode:
Diffstat (limited to 'opcodes/mips-dis.c')
-rw-r--r--opcodes/mips-dis.c216
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. */