gdb: add target_ops::supports_displaced_step
[deliverable/binutils-gdb.git] / opcodes / mips-dis.c
index ec3b57b85281868bbff4a12abb42ba1f1a612f50..755bbe294bdbf3ff1974b63eae56820f7c9593d2 100644 (file)
@@ -1,5 +1,5 @@
 /* Print mips instructions for GDB, the GNU debugger, or for objdump.
-   Copyright (C) 1989-2016 Free Software Foundation, Inc.
+   Copyright (C) 1989-2020 Free Software Foundation, Inc.
    Contributed by Nobuyuki Hikichi(hikichi@sra.co.jp).
 
    This file is part of the GNU opcodes library.
    MA 02110-1301, USA.  */
 
 #include "sysdep.h"
-#include "dis-asm.h"
+#include "disassemble.h"
 #include "libiberty.h"
 #include "opcode/mips.h"
 #include "opintl.h"
+#include "elf-bfd.h"
+#include "elf/mips.h"
+#include "elfxx-mips.h"
 
 /* FIXME: These are needed to figure out if the code is mips16 or
    not. The low bit of the address is often a good indicator.  No
@@ -32,8 +35,6 @@
 
 #if !defined(EMBEDDED_ENV)
 #define SYMTAB_AVAILABLE 1
-#include "elf-bfd.h"
-#include "elf/mips.h"
 #endif
 
 /* Mips instructions are at maximum this many bytes long.  */
@@ -563,7 +564,7 @@ const struct mips_arch_choice mips_arch_choices[] =
   { "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 | ASE_DSPR3),
+     | ASE_DSPR2 | ASE_DSPR3 | ASE_CRC | ASE_GINV),
     mips_cp0_names_mips3264r2,
     mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
     mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
@@ -602,7 +603,15 @@ const struct mips_arch_choice mips_arch_choices[] =
   { "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 | ASE_DSPR3),
+     | ASE_MCU | ASE_MT | ASE_DSP | ASE_DSPR2 | ASE_DSPR3 | ASE_CRC
+     | ASE_CRC64 | ASE_GINV),
+    mips_cp0_names_mips3264r2,
+    mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
+    mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
+
+  { "interaptiv-mr2",  1, bfd_mach_mips_interaptiv_mr2, CPU_INTERAPTIV_MR2,
+    ISA_MIPS32R3,
+    ASE_MT | ASE_EVA | ASE_DSP | ASE_DSPR2 | ASE_MIPS16E2 | ASE_MIPS16E2_MT,
     mips_cp0_names_mips3264r2,
     mips_cp0sel_names_mips3264r2, ARRAY_SIZE (mips_cp0sel_names_mips3264r2),
     mips_cp1_names_mips3264, mips_hwr_names_mips3264r2 },
@@ -618,12 +627,29 @@ const struct mips_arch_choice mips_arch_choices[] =
     NULL, 0, mips_cp1_names_numeric, mips_hwr_names_numeric },
 
   { "loongson2f",   1, bfd_mach_mips_loongson_2f, CPU_LOONGSON_2F,
-    ISA_MIPS3 | INSN_LOONGSON_2F, 0, mips_cp0_names_numeric,
+    ISA_MIPS3 | INSN_LOONGSON_2F, ASE_LOONGSON_MMI, mips_cp0_names_numeric,
     NULL, 0, mips_cp1_names_numeric, mips_hwr_names_numeric },
 
-  { "loongson3a",   1, bfd_mach_mips_loongson_3a, CPU_LOONGSON_3A,
-    ISA_MIPS64R2 | INSN_LOONGSON_3A, 0, mips_cp0_names_numeric,
-    NULL, 0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
+  /* The loongson3a is an alias of gs464 for compatibility */
+  { "loongson3a",   1, bfd_mach_mips_gs464, CPU_GS464,
+    ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264,
+    mips_hwr_names_numeric },
+
+  { "gs464",   1, bfd_mach_mips_gs464, CPU_GS464,
+    ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT,
+    mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264,
+    mips_hwr_names_numeric },
+
+  { "gs464e",   1, bfd_mach_mips_gs464e, CPU_GS464E,
+    ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT
+    | ASE_LOONGSON_EXT2, mips_cp0_names_numeric, NULL, 0, mips_cp1_names_mips3264,
+    mips_hwr_names_numeric },
+
+  { "gs264e",   1, bfd_mach_mips_gs464e, CPU_GS264E,
+    ISA_MIPS64R2, ASE_LOONGSON_MMI | ASE_LOONGSON_CAM | ASE_LOONGSON_EXT
+    | ASE_LOONGSON_EXT2 | ASE_MSA | ASE_MSA64, mips_cp0_names_numeric, NULL,
+    0, mips_cp1_names_mips3264, mips_hwr_names_numeric },
 
   { "octeon",   1, bfd_mach_mips_octeon, CPU_OCTEON,
     ISA_MIPS64R2 | INSN_OCTEON, 0, mips_cp0_names_numeric, NULL, 0,
@@ -658,7 +684,8 @@ const struct mips_arch_choice mips_arch_choices[] =
 
   /* This entry, mips16, is here only for ISA/processor selection; do
      not print its name.  */
-  { "",                1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS3, 0,
+  { "",                1, bfd_mach_mips16, CPU_MIPS16, ISA_MIPS64,
+    ASE_MIPS16E2 | ASE_MIPS16E2_MT,
     mips_cp0_names_numeric, NULL, 0, mips_cp1_names_numeric,
     mips_hwr_names_numeric },
 };
@@ -764,6 +791,60 @@ is_micromips (Elf_Internal_Ehdr *header)
   return 0;
 }
 
+/* Convert ASE flags from .MIPS.abiflags to internal values.  */
+
+static unsigned long
+mips_convert_abiflags_ases (unsigned long afl_ases)
+{
+  unsigned long opcode_ases = 0;
+
+  if (afl_ases & AFL_ASE_DSP)
+    opcode_ases |= ASE_DSP;
+  if (afl_ases & AFL_ASE_DSPR2)
+    opcode_ases |= ASE_DSPR2;
+  if (afl_ases & AFL_ASE_EVA)
+    opcode_ases |= ASE_EVA;
+  if (afl_ases & AFL_ASE_MCU)
+    opcode_ases |= ASE_MCU;
+  if (afl_ases & AFL_ASE_MDMX)
+    opcode_ases |= ASE_MDMX;
+  if (afl_ases & AFL_ASE_MIPS3D)
+    opcode_ases |= ASE_MIPS3D;
+  if (afl_ases & AFL_ASE_MT)
+    opcode_ases |= ASE_MT;
+  if (afl_ases & AFL_ASE_SMARTMIPS)
+    opcode_ases |= ASE_SMARTMIPS;
+  if (afl_ases & AFL_ASE_VIRT)
+    opcode_ases |= ASE_VIRT;
+  if (afl_ases & AFL_ASE_MSA)
+    opcode_ases |= ASE_MSA;
+  if (afl_ases & AFL_ASE_XPA)
+    opcode_ases |= ASE_XPA;
+  if (afl_ases & AFL_ASE_DSPR3)
+    opcode_ases |= ASE_DSPR3;
+  if (afl_ases & AFL_ASE_MIPS16E2)
+    opcode_ases |= ASE_MIPS16E2;
+  return opcode_ases;
+}
+
+/* Calculate combination ASE flags from regular ASE flags.  */
+
+static unsigned long
+mips_calculate_combination_ases (int opcode_isa, unsigned long opcode_ases)
+{
+  unsigned long combination_ases = 0;
+
+  if ((opcode_ases & (ASE_XPA | ASE_VIRT)) == (ASE_XPA | ASE_VIRT))
+    combination_ases |= ASE_XPA_VIRT;
+  if ((opcode_ases & (ASE_MIPS16E2 | ASE_MT)) == (ASE_MIPS16E2 | ASE_MT))
+    combination_ases |= ASE_MIPS16E2_MT;
+  if ((opcode_ases & ASE_EVA)
+      && ((opcode_isa & INSN_ISA_MASK) == ISA_MIPS64R6
+         || (opcode_isa & INSN_ISA_MASK) == ISA_MIPS32R6))
+    combination_ases |= ASE_EVA_R6;
+  return combination_ases;
+}
+
 static void
 set_default_mips_dis_options (struct disassemble_info *info)
 {
@@ -785,19 +866,6 @@ set_default_mips_dis_options (struct disassemble_info *info)
   mips_hwr_names = mips_hwr_names_numeric;
   no_aliases = 0;
 
-  /* Update settings according to the ELF file header flags.  */
-  if (info->flavour == bfd_target_elf_flavour && info->section != NULL)
-    {
-      Elf_Internal_Ehdr *header;
-
-      header = elf_elfheader (info->section->owner);
-      /* If an ELF "newabi" binary, use the n32/(n)64 GPR names.  */
-      if (is_newabi (header))
-       mips_gpr_names = mips_gpr_names_newabi;
-      /* If a microMIPS binary, then don't use MIPS16 bindings.  */
-      micromips_ase = is_micromips (header);
-    }
-
   /* Set ISA, architecture, and cp0 register names as best we can.  */
 #if ! SYMTAB_AVAILABLE
   /* This is running out on a target machine, not in a host tool.
@@ -818,24 +886,43 @@ set_default_mips_dis_options (struct disassemble_info *info)
       mips_cp1_names = chosen_arch->cp1_names;
       mips_hwr_names = chosen_arch->hwr_names;
     }
-#endif
-}
-
-static void
-parse_mips_dis_option (const char *option, unsigned int len)
-{
-  unsigned int i, optionlen, vallen;
-  const char *val;
-  const struct mips_abi_choice *chosen_abi;
-  const struct mips_arch_choice *chosen_arch;
 
-  /* Try to match options that are simple flags */
-  if (CONST_STRNEQ (option, "no-aliases"))
+  /* Update settings according to the ELF file header flags.  */
+  if (info->flavour == bfd_target_elf_flavour && info->section != NULL)
     {
-      no_aliases = 1;
-      return;
+      struct bfd *abfd = info->section->owner;
+      Elf_Internal_Ehdr *header = elf_elfheader (abfd);
+      Elf_Internal_ABIFlags_v0 *abiflags = NULL;
+
+      /* We won't ever get here if !HAVE_BFD_MIPS_ELF_GET_ABIFLAGS,
+        because we won't then have a MIPS/ELF BFD, however we need
+        to guard against a link error in a `--enable-targets=...'
+        configuration with a 32-bit host where the MIPS target is
+        a secondary, or with MIPS/ECOFF configurations.  */
+#ifdef HAVE_BFD_MIPS_ELF_GET_ABIFLAGS
+      abiflags = bfd_mips_elf_get_abiflags (abfd);
+#endif
+      /* If an ELF "newabi" binary, use the n32/(n)64 GPR names.  */
+      if (is_newabi (header))
+       mips_gpr_names = mips_gpr_names_newabi;
+      /* If a microMIPS binary, then don't use MIPS16 bindings.  */
+      micromips_ase = is_micromips (header);
+      /* OR in any extra ASE flags set in ELF file structures.  */
+      if (abiflags)
+       mips_ase |= mips_convert_abiflags_ases (abiflags->ases);
+      else if (header->e_flags & EF_MIPS_ARCH_ASE_MDMX)
+       mips_ase |= ASE_MDMX;
     }
+#endif
+  mips_ase |= mips_calculate_combination_ases (mips_isa, mips_ase);
+}
 
+/* Parse an ASE disassembler option and set the corresponding global
+   ASE flag(s).  Return TRUE if successful, FALSE otherwise.  */
+
+static bfd_boolean
+parse_mips_ase_option (const char *option)
+{
   if (CONST_STRNEQ (option, "msa"))
     {
       mips_ase |= ASE_MSA;
@@ -844,7 +931,7 @@ parse_mips_dis_option (const char *option, unsigned int len)
           || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R5
           || (mips_isa & INSN_ISA_MASK) == ISA_MIPS64R6)
          mips_ase |= ASE_MSA64;
-      return;
+      return TRUE;
     }
 
   if (CONST_STRNEQ (option, "virt"))
@@ -855,15 +942,69 @@ parse_mips_dis_option (const char *option, unsigned int len)
          || mips_isa & ISA_MIPS64R5
          || mips_isa & ISA_MIPS64R6)
        mips_ase |= ASE_VIRT64;
-      return;
+      return TRUE;
     }
 
   if (CONST_STRNEQ (option, "xpa"))
     {
       mips_ase |= ASE_XPA;
+      return TRUE;
+    }
+
+  if (CONST_STRNEQ (option, "ginv"))
+    {
+      mips_ase |= ASE_GINV;
+      return TRUE;
+    }
+
+  if (CONST_STRNEQ (option, "loongson-mmi"))
+    {
+      mips_ase |= ASE_LOONGSON_MMI;
+      return TRUE;
+    }
+
+  if (CONST_STRNEQ (option, "loongson-cam"))
+    {
+      mips_ase |= ASE_LOONGSON_CAM;
+      return TRUE;
+    }
+  
+  /* Put here for match ext2 frist */
+  if (CONST_STRNEQ (option, "loongson-ext2"))
+    {
+      mips_ase |= ASE_LOONGSON_EXT2;
+      return TRUE;
+    }
+
+  if (CONST_STRNEQ (option, "loongson-ext"))
+    {
+      mips_ase |= ASE_LOONGSON_EXT;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+parse_mips_dis_option (const char *option, unsigned int len)
+{
+  unsigned int i, optionlen, vallen;
+  const char *val;
+  const struct mips_abi_choice *chosen_abi;
+  const struct mips_arch_choice *chosen_arch;
+
+  /* Try to match options that are simple flags */
+  if (CONST_STRNEQ (option, "no-aliases"))
+    {
+      no_aliases = 1;
       return;
     }
 
+  if (parse_mips_ase_option (option))
+    {
+      mips_ase |= mips_calculate_combination_ases (mips_isa, mips_ase);
+      return;
+    }
 
   /* Look for the = that delimits the end of the option name.  */
   for (i = 0; i < len; i++)
@@ -1143,6 +1284,81 @@ mips_seen_register (struct mips_print_arg_state *state,
     }
 }
 
+/* Print SAVE/RESTORE instruction operands according to the argument
+   register mask AMASK, the number of static registers saved NSREG,
+   the $ra, $s0 and $s1 register specifiers RA, S0 and S1 respectively,
+   and the frame size FRAME_SIZE.  */
+
+static void
+mips_print_save_restore (struct disassemble_info *info, unsigned int amask,
+                        unsigned int nsreg, unsigned int ra,
+                        unsigned int s0, unsigned int s1,
+                        unsigned int frame_size)
+{
+  const fprintf_ftype infprintf = info->fprintf_func;
+  unsigned int nargs, nstatics, smask, i, j;
+  void *is = info->stream;
+  const char *sep;
+
+  if (amask == MIPS_SVRS_ALL_ARGS)
+    {
+      nargs = 4;
+      nstatics = 0;
+    }
+  else if (amask == MIPS_SVRS_ALL_STATICS)
+    {
+      nargs = 0;
+      nstatics = 4;
+    }
+  else
+    {
+      nargs = amask >> 2;
+      nstatics = amask & 3;
+    }
+
+  sep = "";
+  if (nargs > 0)
+    {
+      infprintf (is, "%s", mips_gpr_names[4]);
+      if (nargs > 1)
+       infprintf (is, "-%s", mips_gpr_names[4 + nargs - 1]);
+      sep = ",";
+    }
+
+  infprintf (is, "%s%d", sep, frame_size);
+
+  if (ra)                      /* $ra */
+    infprintf (is, ",%s", mips_gpr_names[31]);
+
+  smask = 0;
+  if (s0)                      /* $s0 */
+    smask |= 1 << 0;
+  if (s1)                      /* $s1 */
+    smask |= 1 << 1;
+  if (nsreg > 0)               /* $s2-$s8 */
+    smask |= ((1 << nsreg) - 1) << 2;
+
+  for (i = 0; i < 9; i++)
+    if (smask & (1 << i))
+      {
+       infprintf (is, ",%s", mips_gpr_names[i == 8 ? 30 : (16 + i)]);
+       /* Skip over string of set bits.  */
+       for (j = i; smask & (2 << j); j++)
+         continue;
+       if (j > i)
+         infprintf (is, "-%s", mips_gpr_names[j == 8 ? 30 : (16 + j)]);
+       i = j + 1;
+      }
+  /* Statics $ax - $a3.  */
+  if (nstatics == 1)
+    infprintf (is, ",%s", mips_gpr_names[7]);
+  else if (nstatics > 0)
+    infprintf (is, ",%s-%s",
+              mips_gpr_names[7 - nstatics + 1],
+              mips_gpr_names[7]);
+}
+
+
 /* 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.  */
@@ -1233,9 +1449,10 @@ print_insn_arg (struct disassemble_info *info,
        pcrel_op = (const struct mips_pcrel_operand *) operand;
        info->target = mips_decode_pcrel_operand (pcrel_op, base_pc, uval);
 
-       /* Preserve the ISA bit for the GDB disassembler,
-          otherwise clear it.  */
-       if (info->flavour != bfd_target_unknown_flavour)
+       /* For jumps and branches clear the ISA bit except for
+          the GDB disassembler.  */
+       if (pcrel_op->include_isa_bit
+           && info->flavour != bfd_target_unknown_flavour)
          info->target &= -2;
 
        (*info->print_address_func) (info->target, info);
@@ -1374,7 +1591,7 @@ print_insn_arg (struct disassemble_info *info,
       break;
 
     case OP_SAVE_RESTORE_LIST:
-      /* Should be handled by the caller due to extend behavior.  */
+      /* Should be handled by the caller due to complex behavior.  */
       abort ();
 
     case OP_MDMX_IMM_REG:
@@ -1413,6 +1630,10 @@ print_insn_arg (struct disassemble_info *info,
       infprintf (is, "$pc");
       break;
 
+    case OP_REG28:
+      print_reg (info, opcode, OP_REG_GP, 28);
+      break;
+
     case OP_VU0_SUFFIX:
     case OP_VU0_MATCH_SUFFIX:
       print_vu0_channel (info, operand, uval);
@@ -1526,15 +1747,13 @@ validate_insn_args (const struct mips_opcode *opcode,
                case OP_REPEAT_PREV_REG:
                case OP_REPEAT_DEST_REG:
                case OP_PC:
+               case OP_REG28:
                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 ();
+                 break;
                }
            }
          if (*s == 'm' || *s == '+' || *s == '-')
@@ -1587,12 +1806,26 @@ print_insn_args (struct disassemble_info *info,
                         opcode->name, opcode->args);
              return;
            }
-         if (operand->type == OP_REG
-             && s[1] == ','
-             && s[2] == 'H'
-             && opcode->name[strlen (opcode->name) - 1] == '0')
+
+         if (operand->type == OP_SAVE_RESTORE_LIST)
            {
-             /* Coprocessor register 0 with sel field (MT ASE).  */
+             /* Handle this case here because of the complex behavior.  */
+             unsigned int amask = (insn >> 15) & 0xf;
+             unsigned int nsreg = (insn >> 23) & 0x7;
+             unsigned int ra = insn & 0x1000;                  /* $ra */
+             unsigned int s0 = insn & 0x800;                   /* $s0 */
+             unsigned int s1 = insn & 0x400;                   /* $s1 */
+             unsigned int frame_size = (((insn >> 15) & 0xf0)
+                                        | ((insn >> 6) & 0x0f)) * 8;
+             mips_print_save_restore (info, amask, nsreg, ra, s0, s1,
+                                      frame_size);
+           }
+         else if (operand->type == OP_REG
+                  && s[1] == ','
+                  && s[2] == 'H'
+                  && opcode->name[strlen (opcode->name) - 1] == '0')
+           {
+             /* Coprocessor register 0 with sel field.  */
              const struct mips_cp0sel_name *n;
              unsigned int reg, sel;
 
@@ -1775,6 +2008,7 @@ print_mips16_insn_arg (struct disassemble_info *info,
   const fprintf_ftype infprintf = info->fprintf_func;
   void *is = info->stream;
   const struct mips_operand *operand, *ext_operand;
+  unsigned short ext_size;
   unsigned int uval;
   bfd_vma baseaddr;
 
@@ -1801,72 +2035,17 @@ print_mips16_insn_arg (struct disassemble_info *info,
 
       if (operand->type == OP_SAVE_RESTORE_LIST)
        {
-         /* Handle this case here because of the complex interation
+         /* Handle this case here because of the complex interaction
             with the EXTEND opcode.  */
-         unsigned int amask, nargs, nstatics, nsreg, smask, frame_size, i, j;
-         const char *sep;
-
-         amask = extend & 0xf;
-         if (amask == MIPS16_ALL_ARGS)
-           {
-             nargs = 4;
-             nstatics = 0;
-           }
-         else if (amask == MIPS16_ALL_STATICS)
-           {
-             nargs = 0;
-             nstatics = 4;
-           }
-         else
-           {
-             nargs = amask >> 2;
-             nstatics = amask & 3;
-           }
-
-         sep = "";
-         if (nargs > 0)
-           {
-             infprintf (is, "%s", mips_gpr_names[4]);
-             if (nargs > 1)
-               infprintf (is, "-%s", mips_gpr_names[4 + nargs - 1]);
-             sep = ",";
-           }
-
-         frame_size = ((extend & 0xf0) | (insn & 0x0f)) * 8;
+         unsigned int amask = extend & 0xf;
+         unsigned int nsreg = (extend >> 8) & 0x7;
+         unsigned int ra = insn & 0x40;                        /* $ra */
+         unsigned int s0 = insn & 0x20;                        /* $s0 */
+         unsigned int s1 = insn & 0x10;                        /* $s1 */
+         unsigned int frame_size = ((extend & 0xf0) | (insn & 0x0f)) * 8;
          if (frame_size == 0 && !use_extend)
            frame_size = 128;
-         infprintf (is, "%s%d", sep, frame_size);
-
-         if (insn & 0x40)              /* $ra */
-           infprintf (is, ",%s", mips_gpr_names[31]);
-
-         nsreg = (extend >> 8) & 0x7;
-         smask = 0;
-         if (insn & 0x20)              /* $s0 */
-           smask |= 1 << 0;
-         if (insn & 0x10)              /* $s1 */
-           smask |= 1 << 1;
-         if (nsreg > 0)                /* $s2-$s8 */
-           smask |= ((1 << nsreg) - 1) << 2;
-
-         for (i = 0; i < 9; i++)
-           if (smask & (1 << i))
-             {
-               infprintf (is, ",%s", mips_gpr_names[i == 8 ? 30 : (16 + i)]);
-               /* Skip over string of set bits.  */
-               for (j = i; smask & (2 << j); j++)
-                 continue;
-               if (j > i)
-                 infprintf (is, "-%s", mips_gpr_names[j == 8 ? 30 : (16 + j)]);
-               i = j + 1;
-             }
-         /* Statics $ax - $a3.  */
-         if (nstatics == 1)
-           infprintf (is, ",%s", mips_gpr_names[7]);
-         else if (nstatics > 0)
-           infprintf (is, ",%s-%s",
-                      mips_gpr_names[7 - nstatics + 1],
-                      mips_gpr_names[7]);
+         mips_print_save_restore (info, amask, nsreg, ra, s0, s1, frame_size);
          break;
        }
 
@@ -1879,31 +2058,30 @@ print_mips16_insn_arg (struct disassemble_info *info,
          info->data_size = 1 << int_op->shift;
        }
 
-      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;
-      else
+      ext_size = 0;
+      if (use_extend)
        {
-         /* Calculate the full field value.  */
-         uval = mips_extract_operand (operand, insn);
-         if (use_extend)
+         ext_operand = decode_mips16_operand (type, TRUE);
+         if (ext_operand != operand
+             || (operand->type == OP_INT && operand->lsb == 0
+                 && mips_opcode_32bit_p (opcode)))
            {
-             ext_operand = decode_mips16_operand (type, TRUE);
-             if (ext_operand != operand)
-               {
-                 operand = ext_operand;
-                 if (operand->size == 16)
-                   uval = (((extend & 0x1f) << 11) | (extend & 0x7e0)
-                           | (uval & 0x1f));
-                 else if (operand->size == 15)
-                   uval |= ((extend & 0xf) << 11) | (extend & 0x7f0);
-                 else
-                   uval = ((((extend >> 6) & 0x1f) | (extend & 0x20))
-                           & ((1U << operand->size) - 1));
-               }
+             ext_size = ext_operand->size;
+             operand = ext_operand;
            }
        }
+      if (operand->size == 26)
+       uval = ((extend & 0x1f) << 21) | ((extend & 0x3e0) << 11) | insn;
+      else if (ext_size == 16 || ext_size == 9)
+       uval = ((extend & 0x1f) << 11) | (extend & 0x7e0) | (insn & 0x1f);
+      else if (ext_size == 15)
+       uval = ((extend & 0xf) << 11) | (extend & 0x7f0) | (insn & 0xf);
+      else if (ext_size == 6)
+       uval = ((extend >> 6) & 0x1f) | (extend & 0x20);
+      else
+       uval = mips_extract_operand (operand, (extend << 16) | insn);
+      if (ext_size == 9)
+       uval &= (1U << ext_size) - 1;
 
       baseaddr = memaddr + 2;
       if (operand->type == OP_PCREL)
@@ -1914,32 +2092,36 @@ print_mips16_insn_arg (struct disassemble_info *info,
          if (!pcrel_op->include_isa_bit && use_extend)
            baseaddr = memaddr - 2;
          else if (!pcrel_op->include_isa_bit)
-            {
-              bfd_byte buffer[2];
-
-              /* If this instruction is in the delay slot of a JR
-                 instruction, the base address is the address of the
-                 JR instruction.  If it is in the delay slot of a JALR
-                 instruction, the base address is the address of the
-                 JALR instruction.  This test is unreliable: we have
-                 no way of knowing whether the previous word is
-                 instruction or data.  */
-              if (info->read_memory_func (memaddr - 4, buffer, 2, info) == 0
-                  && (((info->endian == BFD_ENDIAN_BIG
-                        ? bfd_getb16 (buffer)
-                        : bfd_getl16 (buffer))
-                       & 0xf800) == 0x1800))
-                baseaddr = memaddr - 4;
-              else if (info->read_memory_func (memaddr - 2, buffer, 2,
-                                               info) == 0
-                       && (((info->endian == BFD_ENDIAN_BIG
-                             ? bfd_getb16 (buffer)
-                             : bfd_getl16 (buffer))
-                            & 0xf81f) == 0xe800))
-                baseaddr = memaddr - 2;
-              else
-                baseaddr = memaddr;
-            }
+           {
+             bfd_byte buffer[2];
+
+             /* If this instruction is in the delay slot of a JAL/JALX
+                instruction, the base address is the address of the
+                JAL/JALX instruction.  If it is in the delay slot of
+                a JR/JALR instruction, the base address is the address
+                of the JR/JALR instruction.  This test is unreliable:
+                we have no way of knowing whether the previous word is
+                instruction or data.  */
+             if (info->read_memory_func (memaddr - 4, buffer, 2, info) == 0
+                 && (((info->endian == BFD_ENDIAN_BIG
+                       ? bfd_getb16 (buffer)
+                       : bfd_getl16 (buffer))
+                      & 0xf800) == 0x1800))
+               baseaddr = memaddr - 4;
+             else if (info->read_memory_func (memaddr - 2, buffer, 2,
+                                              info) == 0
+                      && (((info->endian == BFD_ENDIAN_BIG
+                            ? bfd_getb16 (buffer)
+                            : bfd_getl16 (buffer))
+                           & 0xf89f) == 0xe800)
+                      && (((info->endian == BFD_ENDIAN_BIG
+                            ? bfd_getb16 (buffer)
+                            : bfd_getl16 (buffer))
+                           & 0x0060) != 0x0060))
+               baseaddr = memaddr - 2;
+             else
+               baseaddr = memaddr;
+           }
        }
 
       print_insn_arg (info, state, opcode, operand, baseaddr + 1, uval);
@@ -1964,6 +2146,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
@@ -1972,13 +2163,14 @@ 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;
+  bfd_boolean extend_only;
+  unsigned int second;
+  unsigned int first;
+  unsigned int full;
 
   info->bytes_per_chunk = 2;
   info->display_endian = info->endian;
@@ -2019,44 +2211,28 @@ print_insn_mips16 (bfd_vma memaddr, struct disassemble_info *info)
       return -1;
     }
 
-  length = 2;
+  extend_only = FALSE;
 
   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 0x%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 0x%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.  */
@@ -2064,37 +2240,47 @@ 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 0x%x", (unsigned int) extend);
-                 info->insn_type = dis_noninsn;
-                 return length - 2;
-               }
-
-             use_extend = FALSE;
+      enum match_kind match;
 
-             memaddr += 2;
+      if (!opcode_is_member (op, mips_isa, mips_ase, mips_processor))
+       continue;
 
-             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 (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
+              && !extend_only
+              && (second & op->mask) == op->match)
+       {
+         if (op->pinfo2 & INSN2_SHORT_ONLY)
+           {
+             match = MATCH_NONE;
+             extend_only = TRUE;
            }
+         else
+           match = MATCH_FULL;
+       }
+      else
+       match = MATCH_NONE;
+
+      if (match != MATCH_NONE)
+       {
+         const char *s;
 
          infprintf (is, "%s", op->name);
          if (op->args[0] != '\0')
@@ -2105,7 +2291,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;
@@ -2113,14 +2299,55 @@ 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] == '(');
+             if (s[0] == 'N'
+                 && s[1] == ','
+                 && s[2] == 'O'
+                 && op->name[strlen (op->name) - 1] == '0')
+               {
+                 /* Coprocessor register 0 with sel field.  */
+                 const struct mips_cp0sel_name *n;
+                 const struct mips_operand *operand;
+                 unsigned int reg, sel;
+
+                 operand = decode_mips16_operand (*s, TRUE);
+                 reg = mips_extract_operand (operand, (first << 16) | second);
+                 s += 2;
+                 operand = decode_mips16_operand (*s, TRUE);
+                 sel = mips_extract_operand (operand, (first << 16) | second);
+
+                 /* CP0 register including 'sel' code for mftc0, to be
+                    printed textually if known.  If not known, print both
+                    CP0 register name and sel numerically since CP0 register
+                    with sel 0 may have a name unrelated to register being
+                    printed.  */
+                 n = lookup_mips_cp0sel_name (mips_cp0sel_names,
+                                              mips_cp0sel_names_len,
+                                              reg, sel);
+                 if (n != NULL)
+                   infprintf (is, "%s", n->name);
+                 else
+                   infprintf (is, "$%d,%d", reg, sel);
+               }
+             else
+               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.  */
@@ -2137,17 +2364,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.  */
@@ -2374,65 +2599,194 @@ print_insn_little_mips (bfd_vma memaddr, struct disassemble_info *info)
   return _print_insn_mips (memaddr, info, BFD_ENDIAN_LITTLE);
 }
 \f
-void
-print_mips_disassembler_options (FILE *stream)
+/* Indices into option argument vector for options accepting an argument.
+   Use MIPS_OPTION_ARG_NONE for options accepting no argument.  */
+typedef enum
 {
-  unsigned int i;
+  MIPS_OPTION_ARG_NONE = -1,
+  MIPS_OPTION_ARG_ABI,
+  MIPS_OPTION_ARG_ARCH,
+  MIPS_OPTION_ARG_SIZE
+} mips_option_arg_t;
+
+/* Valid MIPS disassembler options.  */
+static struct
+{
+  const char *name;
+  const char *description;
+  mips_option_arg_t arg;
+} mips_options[] =
+{
+  { "no-aliases", N_("Use canonical instruction forms.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "msa",        N_("Recognize MSA instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "virt",       N_("Recognize the virtualization ASE instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "xpa",        N_("Recognize the eXtended Physical Address (XPA) ASE\n\
+                  instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "ginv",       N_("Recognize the Global INValidate (GINV) ASE "
+                    "instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "loongson-mmi",
+                 N_("Recognize the Loongson MultiMedia extensions "
+                    "Instructions (MMI) ASE instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "loongson-cam",
+                 N_("Recognize the Loongson Content Address Memory (CAM) "
+                    " instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "loongson-ext",
+                 N_("Recognize the Loongson EXTensions (EXT) "
+                    " instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "loongson-ext2",
+                 N_("Recognize the Loongson EXTensions R2 (EXT2) "
+                    " instructions.\n"),
+                 MIPS_OPTION_ARG_NONE },
+  { "gpr-names=", N_("Print GPR names according to specified ABI.\n\
+                  Default: based on binary being disassembled.\n"),
+                 MIPS_OPTION_ARG_ABI },
+  { "fpr-names=", N_("Print FPR names according to specified ABI.\n\
+                  Default: numeric.\n"),
+                 MIPS_OPTION_ARG_ABI },
+  { "cp0-names=", N_("Print CP0 register names according to specified "
+                    "architecture.\n\
+                  Default: based on binary being disassembled.\n"),
+                 MIPS_OPTION_ARG_ARCH },
+  { "hwr-names=", N_("Print HWR names according to specified architecture.\n\
+                  Default: based on binary being disassembled.\n"),
+                 MIPS_OPTION_ARG_ARCH },
+  { "reg-names=", N_("Print GPR and FPR names according to specified ABI.\n"),
+                 MIPS_OPTION_ARG_ABI },
+  { "reg-names=", N_("Print CP0 register and HWR names according to "
+                    "specified\n\
+                  architecture."),
+                 MIPS_OPTION_ARG_ARCH }
+};
 
-  fprintf (stream, _("\n\
-The following MIPS specific disassembler options are supported for use\n\
-with the -M switch (multiple options should be separated by commas):\n"));
+/* Build the structure representing valid MIPS disassembler options.
+   This is done dynamically for maintenance ease purpose; a static
+   initializer would be unreadable.  */
 
-  fprintf (stream, _("\n\
-  msa                      Recognize MSA instructions.\n"));
+const disasm_options_and_args_t *
+disassembler_options_mips (void)
+{
+  static disasm_options_and_args_t *opts_and_args;
 
-  fprintf (stream, _("\n\
-  virt                     Recognize the virtualization ASE instructions.\n"));
+  if (opts_and_args == NULL)
+    {
+      size_t num_options = ARRAY_SIZE (mips_options);
+      size_t num_args = MIPS_OPTION_ARG_SIZE;
+      disasm_option_arg_t *args;
+      disasm_options_t *opts;
+      size_t i;
+      size_t j;
+
+      args = XNEWVEC (disasm_option_arg_t, num_args + 1);
+
+      args[MIPS_OPTION_ARG_ABI].name = "ABI";
+      args[MIPS_OPTION_ARG_ABI].values
+       = XNEWVEC (const char *, ARRAY_SIZE (mips_abi_choices) + 1);
+      for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++)
+       args[MIPS_OPTION_ARG_ABI].values[i] = mips_abi_choices[i].name;
+      /* The array we return must be NULL terminated.  */
+      args[MIPS_OPTION_ARG_ABI].values[i] = NULL;
+
+      args[MIPS_OPTION_ARG_ARCH].name = "ARCH";
+      args[MIPS_OPTION_ARG_ARCH].values
+       = XNEWVEC (const char *, ARRAY_SIZE (mips_arch_choices) + 1);
+      for (i = 0, j = 0; i < ARRAY_SIZE (mips_arch_choices); i++)
+       if (*mips_arch_choices[i].name != '\0')
+         args[MIPS_OPTION_ARG_ARCH].values[j++] = mips_arch_choices[i].name;
+      /* The array we return must be NULL terminated.  */
+      args[MIPS_OPTION_ARG_ARCH].values[j] = NULL;
+
+      /* The array we return must be NULL terminated.  */
+      args[MIPS_OPTION_ARG_SIZE].name = NULL;
+      args[MIPS_OPTION_ARG_SIZE].values = NULL;
+
+      opts_and_args = XNEW (disasm_options_and_args_t);
+      opts_and_args->args = args;
+
+      opts = &opts_and_args->options;
+      opts->name = XNEWVEC (const char *, num_options + 1);
+      opts->description = XNEWVEC (const char *, num_options + 1);
+      opts->arg = XNEWVEC (const disasm_option_arg_t *, num_options + 1);
+      for (i = 0; i < num_options; i++)
+       {
+         opts->name[i] = mips_options[i].name;
+         opts->description[i] = _(mips_options[i].description);
+         if (mips_options[i].arg != MIPS_OPTION_ARG_NONE)
+           opts->arg[i] = &args[mips_options[i].arg];
+         else
+           opts->arg[i] = NULL;
+       }
+      /* The array we return must be NULL terminated.  */
+      opts->name[i] = NULL;
+      opts->description[i] = NULL;
+      opts->arg[i] = NULL;
+    }
 
-  fprintf (stream, _("\n\
-  xpa                      Recognize the eXtended Physical Address (XPA)\n\
-                           ASE instructions.\n"));
+  return opts_and_args;
+}
 
-  fprintf (stream, _("\n\
-  gpr-names=ABI            Print GPR names according to specified ABI.\n\
-                           Default: based on binary being disassembled.\n"));
+void
+print_mips_disassembler_options (FILE *stream)
+{
+  const disasm_options_and_args_t *opts_and_args;
+  const disasm_option_arg_t *args;
+  const disasm_options_t *opts;
+  size_t max_len = 0;
+  size_t i;
+  size_t j;
 
-  fprintf (stream, _("\n\
-  fpr-names=ABI            Print FPR names according to specified ABI.\n\
-                           Default: numeric.\n"));
+  opts_and_args = disassembler_options_mips ();
+  opts = &opts_and_args->options;
+  args = opts_and_args->args;
 
   fprintf (stream, _("\n\
-  cp0-names=ARCH           Print CP0 register names according to\n\
-                           specified architecture.\n\
-                           Default: based on binary being disassembled.\n"));
+The following MIPS specific disassembler options are supported for use\n\
+with the -M switch (multiple options should be separated by commas):\n\n"));
 
-  fprintf (stream, _("\n\
-  hwr-names=ARCH           Print HWR names according to specified \n\
-                           architecture.\n\
-                           Default: based on binary being disassembled.\n"));
+  /* Compute the length of the longest option name.  */
+  for (i = 0; opts->name[i] != NULL; i++)
+    {
+      size_t len = strlen (opts->name[i]);
 
-  fprintf (stream, _("\n\
-  reg-names=ABI            Print GPR and FPR names according to\n\
-                           specified ABI.\n"));
+      if (opts->arg[i] != NULL)
+       len += strlen (opts->arg[i]->name);
+      if (max_len < len)
+       max_len = len;
+    }
 
-  fprintf (stream, _("\n\
-  reg-names=ARCH           Print CP0 register and HWR names according to\n\
-                           specified architecture.\n"));
+  for (i = 0, max_len++; opts->name[i] != NULL; i++)
+    {
+      fprintf (stream, "  %s", opts->name[i]);
+      if (opts->arg[i] != NULL)
+       fprintf (stream, "%s", opts->arg[i]->name);
+      if (opts->description[i] != NULL)
+       {
+         size_t len = strlen (opts->name[i]);
 
-  fprintf (stream, _("\n\
-  For the options above, the following values are supported for \"ABI\":\n\
-   "));
-  for (i = 0; i < ARRAY_SIZE (mips_abi_choices); i++)
-    fprintf (stream, " %s", mips_abi_choices[i].name);
-  fprintf (stream, _("\n"));
+         if (opts->arg[i] != NULL)
+           len += strlen (opts->arg[i]->name);
+         fprintf (stream,
+                  "%*c %s", (int) (max_len - len), ' ', opts->description[i]);
+       }
+      fprintf (stream, _("\n"));
+    }
 
-  fprintf (stream, _("\n\
-  For the options above, The following values are supported for \"ARCH\":\n\
-   "));
-  for (i = 0; i < ARRAY_SIZE (mips_arch_choices); i++)
-    if (*mips_arch_choices[i].name != '\0')
-      fprintf (stream, " %s", mips_arch_choices[i].name);
-  fprintf (stream, _("\n"));
+  for (i = 0; args[i].name != NULL; i++)
+    {
+      fprintf (stream, _("\n\
+  For the options above, the following values are supported for \"%s\":\n   "),
+              args[i].name);
+      for (j = 0; args[i].values[j] != NULL; j++)
+       fprintf (stream, " %s", args[i].values[j]);
+      fprintf (stream, _("\n"));
+    }
 
   fprintf (stream, _("\n"));
 }
This page took 0.036413 seconds and 4 git commands to generate.