Add support for MIPS R6.
[deliverable/binutils-gdb.git] / opcodes / mips-dis.c
index b797e5dad51e9447864028028aae5bd13acdb9e2..1eb1d45b1a3f9324ba0f23532202d76126924b08 100644 (file)
@@ -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.  */
This page took 0.028978 seconds and 4 git commands to generate.