]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - opcodes/arm-dis.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / opcodes / arm-dis.c
index be2a93253bb5673579780f2053161e281e64cb04..f16a5902479acebadff5e912b6b0355bc8381259 100644 (file)
@@ -1,5 +1,5 @@
 /* Instruction printing code for the ARM
-   Copyright (C) 1994-2020 Free Software Foundation, Inc.
+   Copyright (C) 1994-2021 Free Software Foundation, Inc.
    Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
    Modification by James G. Smith (jsmith@cygnus.co.uk)
 
@@ -355,6 +355,16 @@ struct opcode32
   const char *  assembler;     /* How to disassemble this insn.  */
 };
 
+struct cdeopcode32
+{
+  arm_feature_set arch;                /* Architecture defining this insn.  */
+  uint8_t coproc_shift;                /* coproc is this far into op.  */
+  uint16_t coproc_mask;                /* Length of coproc field in op.  */
+  unsigned long value;         /* If arch is 0 then value is a sentinel.  */
+  unsigned long mask;          /* Recognise insn if (op & mask) == value.  */
+  const char *  assembler;     /* How to disassemble this insn.  */
+};
+
 /* MVE opcodes.  */
 
 struct mopcode32
@@ -460,6 +470,75 @@ enum opcode_sentinel_enum
 
 /* Common coprocessor opcodes shared between Arm and Thumb-2.  */
 
+/* print_insn_cde recognizes the following format control codes:
+
+   %%                  %
+
+   %a                  print 'a' iff bit 28 is 1
+   %p                  print bits 8-10 as coprocessor
+   %<bitfield>d                print as decimal
+   %<bitfield>r                print as an ARM register
+   %<bitfield>n                print as an ARM register but r15 is APSR_nzcv
+   %<bitfield>T                print as an ARM register + 1
+   %<bitfield>R                as %r but r13 is UNPREDICTABLE
+   %<bitfield>S                as %r but rX where X > 10 is UNPREDICTABLE
+   %j                  print immediate taken from bits (16..21,7,0..5)
+   %k                  print immediate taken from bits (20..21,7,0..5).
+   %l                  print immediate taken from bits (20..22,7,4..5).  */
+
+/* At the moment there is only one valid position for the coprocessor number,
+   and hence that's encoded in the macro below.  */
+#define CDE_OPCODE(ARCH, VALUE, MASK, ASM) \
+  { ARCH, 8, 7, VALUE, MASK, ASM }
+static const struct cdeopcode32 cde_opcodes[] =
+{
+  /* Custom Datapath Extension instructions.  */
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xee000000, 0xefc00840,
+             "cx1%a\t%p, %12-15n, #%0-5,7,16-21d"),
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xee000040, 0xefc00840,
+             "cx1d%a\t%p, %12-15S, %12-15T, #%0-5,7,16-21d"),
+
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xee400000, 0xefc00840,
+             "cx2%a\t%p, %12-15n, %16-19n, #%0-5,7,20-21d"),
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xee400040, 0xefc00840,
+             "cx2d%a\t%p, %12-15S, %12-15T, %16-19n, #%0-5,7,20-21d"),
+
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xee800000, 0xef800840,
+             "cx3%a\t%p, %0-3n, %16-19n, %12-15n, #%4-5,7,20-22d"),
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xee800040, 0xef800840,
+            "cx3d%a\t%p, %0-3S, %0-3T, %16-19n, %12-15n, #%4-5,7,20-22d"),
+
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xec200000, 0xeeb00840,
+             "vcx1%a\t%p, %12-15,22V, #%0-5,7,16-19d"),
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xec200040, 0xeeb00840,
+             "vcx1%a\t%p, %12-15,22V, #%0-5,7,16-19,24d"),
+
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xec300000, 0xeeb00840,
+             "vcx2%a\t%p, %12-15,22V, %0-3,5V, #%4,7,16-19d"),
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xec300040, 0xeeb00840,
+             "vcx2%a\t%p, %12-15,22V, %0-3,5V, #%4,7,16-19,24d"),
+
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xec800000, 0xee800840,
+             "vcx3%a\t%p, %12-15,22V, %16-19,7V, %0-3,5V, #%4,20-21d"),
+  CDE_OPCODE (ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE),
+             0xec800040, 0xee800840,
+             "vcx3%a\t%p, %12-15,22V, %16-19,7V, %0-3,5V, #%4,20-21,24d"),
+
+  CDE_OPCODE (ARM_FEATURE_CORE_LOW (0), 0, 0, 0)
+
+};
+
 static const struct sopcode32 coprocessor_opcodes[] =
 {
   /* XScale instructions.  */
@@ -1415,17 +1494,17 @@ static const struct opcode32 neon_opcodes[] =
 
   /* Data transfer between ARM and NEON registers.  */
   {ARM_FEATURE_COPROC (FPU_NEON_EXT_V1),
-    0x0e800b10, 0x1ff00f70, "vdup%c.32\t%16-19,7D, %12-15r"},
+    0x0e800b10, 0x0ff00f70, "vdup%c.32\t%16-19,7D, %12-15r"},
   {ARM_FEATURE_COPROC (FPU_NEON_EXT_V1),
-    0x0e800b30, 0x1ff00f70, "vdup%c.16\t%16-19,7D, %12-15r"},
+    0x0e800b30, 0x0ff00f70, "vdup%c.16\t%16-19,7D, %12-15r"},
   {ARM_FEATURE_COPROC (FPU_NEON_EXT_V1),
-    0x0ea00b10, 0x1ff00f70, "vdup%c.32\t%16-19,7Q, %12-15r"},
+    0x0ea00b10, 0x0ff00f70, "vdup%c.32\t%16-19,7Q, %12-15r"},
   {ARM_FEATURE_COPROC (FPU_NEON_EXT_V1),
-    0x0ea00b30, 0x1ff00f70, "vdup%c.16\t%16-19,7Q, %12-15r"},
+    0x0ea00b30, 0x0ff00f70, "vdup%c.16\t%16-19,7Q, %12-15r"},
   {ARM_FEATURE_COPROC (FPU_NEON_EXT_V1),
-    0x0ec00b10, 0x1ff00f70, "vdup%c.8\t%16-19,7D, %12-15r"},
+    0x0ec00b10, 0x0ff00f70, "vdup%c.8\t%16-19,7D, %12-15r"},
   {ARM_FEATURE_COPROC (FPU_NEON_EXT_V1),
-    0x0ee00b10, 0x1ff00f70, "vdup%c.8\t%16-19,7Q, %12-15r"},
+    0x0ee00b10, 0x0ff00f70, "vdup%c.8\t%16-19,7Q, %12-15r"},
 
   /* Move data element to all lanes.  */
   {ARM_FEATURE_COPROC (FPU_NEON_EXT_V1),
@@ -3606,6 +3685,10 @@ static const struct opcode32 arm_opcodes[] =
   {ARM_FEATURE_CORE_HIGH (ARM_EXT2_RAS),
     0xe320f010, 0xffffffff, "esb"},
 
+  /* V8-R instructions.  */
+  {ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8R),
+    0xf57ff04c, 0xffffffff, "dfb"},
+
   /* V8 instructions.  */
   {ARM_FEATURE_CORE_LOW (ARM_EXT_V8),
     0x0320f005, 0x0fffffff, "sevl"},
@@ -4656,6 +4739,10 @@ static const struct opcode32 thumb32_opcodes[] =
   {ARM_FEATURE_CORE_LOW (ARM_EXT_V8),
     0xe8d000ff, 0xfff000ff, "ldaexd%c\t%12-15r, %8-11r, [%16-19R]"},
 
+  /* V8-R instructions.  */
+  {ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8R),
+    0xf3bf8f4c, 0xffffffff, "dfb%c"},
+
   /* CRC32 instructions.  */
   {ARM_FEATURE_CORE_HIGH (ARM_EXT2_CRC),
     0xfac0f080, 0xfff0f0f0, "crc32b\t%8-11R, %16-19R, %0-3R"},
@@ -5115,7 +5202,8 @@ static const arm_regname regnames[] =
   { "reg-names-atpcs", N_("Select register names used in the ATPCS"),
     { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "v7",  "v8",  "IP",  "SP",  "LR",  "PC" }},
   { "reg-names-special-atpcs", N_("Select special register names used in the ATPCS"),
-    { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "WR", "v5", "SB", "SL",  "FP",  "IP",  "SP",  "LR",  "PC" }}
+    { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "WR", "v5", "SB", "SL",  "FP",  "IP",  "SP",  "LR",  "PC" }},
+  { "coproc<N>=(cde|generic)", N_("Enable CDE extensions for coprocessor N space"), { NULL } }
 };
 
 static const char *const iwmmxt_wwnames[] =
@@ -5195,6 +5283,7 @@ static unsigned int regname_selected = 1;
 #define arm_regnames      regnames[regname_selected].reg_names
 
 static bfd_boolean force_thumb = FALSE;
+static uint16_t cde_coprocs = 0;
 
 /* Current IT instruction state.  This contains the same state as the IT
    bits in the CPSR.  */
@@ -6945,14 +7034,14 @@ print_simd_imm8 (struct disassemble_info *info, unsigned long given,
       return;
     }
 
-  // printU determines whether the immediate value should be printed as
-  // unsigned.
+  /* printU determines whether the immediate value should be printed as
+     unsigned.  */
   unsigned printU = 0;
   switch (insn->mve_op)
     {
     default:
       break;
-    // We want this for instructions that don't have a 'signed' type
+    /* We want this for instructions that don't have a 'signed' type.  */
     case MVE_VBIC_IMM:
     case MVE_VORR_IMM:
     case MVE_VMVN_IMM:
@@ -8786,6 +8875,140 @@ print_arm_address (bfd_vma pc, struct disassemble_info *info, long given)
   return (signed long) offset;
 }
 
+
+/* Print one cde instruction on INFO->STREAM.
+   Return TRUE if the instuction matched, FALSE if this is not a
+   recognised cde instruction.  */
+static bfd_boolean
+print_insn_cde (struct disassemble_info *info, long given, bfd_boolean thumb)
+{
+  const struct cdeopcode32 *insn;
+  void *stream = info->stream;
+  fprintf_ftype func = info->fprintf_func;
+
+  if (thumb)
+  {
+    /* Manually extract the coprocessor code from a known point.
+       This position is the same across all CDE instructions.  */
+    for (insn = cde_opcodes; insn->assembler; insn++)
+    {
+      uint16_t coproc = (given >> insn->coproc_shift) & insn->coproc_mask;
+      uint16_t coproc_mask = 1 << coproc;
+      if (! (coproc_mask & cde_coprocs))
+       continue;
+
+      if ((given & insn->mask) == insn->value)
+      {
+       bfd_boolean is_unpredictable = FALSE;
+       const char *c;
+
+       for (c = insn->assembler; *c; c++)
+       {
+         if (*c == '%')
+         {
+           switch (*++c)
+           {
+             case '%':
+               func (stream, "%%");
+               break;
+
+             case '0': case '1': case '2': case '3': case '4':
+             case '5': case '6': case '7': case '8': case '9':
+             {
+               int width;
+               unsigned long value;
+
+               c = arm_decode_bitfield (c, given, &value, &width);
+
+               switch (*c)
+               {
+                 case 'S':
+                   if (value > 10)
+                     is_unpredictable = TRUE;
+                   /* Fall through.  */
+                 case 'R':
+                   if (value == 13)
+                     is_unpredictable = TRUE;
+                   /* Fall through.  */
+                 case 'r':
+                   func (stream, "%s", arm_regnames[value]);
+                   break;
+
+                 case 'n':
+                   if (value == 15)
+                     func (stream, "%s", "APSR_nzcv");
+                   else
+                     func (stream, "%s", arm_regnames[value]);
+                   break;
+
+                 case 'T':
+                   func (stream, "%s", arm_regnames[value + 1]);
+                   break;
+
+                 case 'd':
+                   func (stream, "%ld", value);
+                   break;
+
+                 case 'V':
+                   if (given & (1 << 6))
+                     func (stream, "q%ld", value >> 1);
+                   else if (given & (1 << 24))
+                     func (stream, "d%ld", value);
+                   else
+                     {
+                       /* Encoding for S register is different than for D and
+                          Q registers.  S registers are encoded using the top
+                          single bit in position 22 as the lowest bit of the
+                          register number, while for Q and D it represents the
+                          highest bit of the register number.  */
+                       uint8_t top_bit = (value >> 4) & 1;
+                       uint8_t tmp = (value << 1) & 0x1e;
+                       uint8_t res = tmp | top_bit;
+                       func (stream, "s%u", res);
+                     }
+                   break;
+
+               default:
+                 abort ();
+               }
+             }
+           break;
+
+           case 'p':
+             {
+               uint8_t proc_number = (given >> 8) & 0x7;
+               func (stream, "p%u", proc_number);
+               break;
+             }
+
+           case 'a':
+             {
+               uint8_t a_offset = 28;
+               if (given & (1 << a_offset))
+                 func (stream, "a");
+               break;
+             }
+         default:
+           abort ();
+         }
+       }
+       else
+         func (stream, "%c", *c);
+      }
+
+      if (is_unpredictable)
+       func (stream, UNPREDICTABLE_INSTRUCTION);
+
+      return TRUE;
+      }
+    }
+    return FALSE;
+  }
+  else
+    return FALSE;
+}
+
+
 /* Print one neon instruction on INFO->STREAM.
    Return TRUE if the instuction matched, FALSE if this is not a
    recognised neon instruction.  */
@@ -8817,13 +9040,51 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
               || (given & 0xff000000) == 0xfc000000)
        ;
       /* vdup is also a valid neon instruction.  */
-      else if ((given & 0xff910f5f) != 0xee800b10)
+      else if ((given & 0xff900f5f) != 0xee800b10)
        return FALSE;
     }
 
   for (insn = neon_opcodes; insn->assembler; insn++)
     {
-      if ((given & insn->mask) == insn->value)
+      unsigned long cond_mask = insn->mask;
+      unsigned long cond_value = insn->value;
+      int cond;
+
+      if (thumb)
+        {
+          if ((cond_mask & 0xf0000000) == 0) {
+              /* For the entries in neon_opcodes, an opcode mask/value with
+                 the high 4 bits equal to 0 indicates a conditional
+                 instruction. For thumb however, we need to include those
+                 bits in the instruction matching.  */
+              cond_mask |= 0xf0000000;
+              /* Furthermore, the thumb encoding of a conditional instruction
+                 will have the high 4 bits equal to 0xe.  */
+              cond_value |= 0xe0000000;
+          }
+          if (ifthen_state)
+            cond = IFTHEN_COND;
+          else
+            cond = COND_UNCOND;
+        }
+      else
+        {
+          if ((given & 0xf0000000) == 0xf0000000)
+            {
+              /* If the instruction is unconditional, update the mask to only
+                 match against unconditional opcode values.  */
+              cond_mask |= 0xf0000000;
+              cond = COND_UNCOND;
+            }
+          else
+            {
+              cond = (given >> 28) & 0xf;
+              if (cond == 0xe)
+                cond = COND_UNCOND;
+            }
+        }
+
+      if ((given & cond_mask) == cond_value)
        {
          signed long value_in_comment = 0;
          bfd_boolean is_unpredictable = FALSE;
@@ -8845,8 +9106,7 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
 
                      /* Fall through.  */
                    case 'c':
-                     if (thumb && ifthen_state)
-                       func (stream, "%s", arm_conditional[IFTHEN_COND]);
+                     func (stream, "%s", arm_conditional[cond]);
                      break;
 
                    case 'A':
@@ -10587,6 +10847,9 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given)
   if (is_mve && print_insn_mve (info, given))
     return;
 
+  if (print_insn_cde (info, given, TRUE))
+    return;
+
   if (print_insn_generic_coprocessor (pc, info, given, TRUE))
     return;
 
@@ -11356,6 +11619,36 @@ parse_arm_disassembler_options (const char *options)
        force_thumb = 1;
       else if (CONST_STRNEQ (opt, "no-force-thumb"))
        force_thumb = 0;
+      else if (CONST_STRNEQ (opt, "coproc"))
+       {
+         const char *procptr = opt + sizeof ("coproc") - 1;
+         char *endptr;
+         uint8_t coproc_number = strtol (procptr, &endptr, 10);
+         if (endptr != procptr + 1 || coproc_number > 7)
+           {
+             opcodes_error_handler (_("cde coprocessor not between 0-7: %s"),
+                                    opt);
+             continue;
+           }
+         if (*endptr != '=')
+           {
+             opcodes_error_handler (_("coproc must have an argument: %s"),
+                                    opt);
+             continue;
+           }
+         endptr += 1;
+         if (CONST_STRNEQ (endptr, "generic"))
+           cde_coprocs &= ~(1 << coproc_number);
+         else if (CONST_STRNEQ (endptr, "cde")
+                  || CONST_STRNEQ (endptr, "CDE"))
+           cde_coprocs |= (1 << coproc_number);
+         else
+           {
+             opcodes_error_handler (
+                 _("coprocN argument takes options \"generic\","
+                   " \"cde\", or \"CDE\": %s"), opt);
+           }
+       }
       else
        /* xgettext: c-format */
        opcodes_error_handler (_("unrecognised disassembler option: %s"), opt);