[37/57,Arm,OBJDUMP] Add framework for MVE instructions

Message ID 195c38ed-37e2-3e5e-16db-8e8782e22b5c@arm.com
State New
Headers show
Series
  • : Add support for Armv8.1-M Mainline MVE instructions
Related show

Commit Message

Andre Vieira (lists) May 1, 2019, 5:38 p.m.
Hi,

This patch adds a framework to OBJDUMP to support MVE instructions.

Since MVE and NEON share some encoding space, but may need a slightly 
different decoding, we decided to split up instruction decoding based on 
whether '-marmv8.1-m.main' has been passed to OBJDUMP.  The new function 
'is_mve_architecture' will check this and direct 'print_insn_thumb32' to 
either use 'print_insn_neon' or 'print_insn_mve'.

This framework also includes helper functions to determine encoding 
conflicts, undefined encodings and unpredictable encodings.  These are 
further implemented for each relevant instruction in subsequent patches.

By encoding conflicts we refer to encodings where two different 
instructions have overlapping base opcodes, but can be distinguished 
based on specific operand values.  In the ISA specification such 
'conflicts' can be identified by looking for clauses that redirect you 
to 'Related Encodings' or specific instructions.  The VMLALDAV 
instruction is an example of the latter, where if RdaHi (bits 20-22) has 
value 7, then the encoding is actually to be decoded as the VMLADAV 
instruction.

This patch also sets force_thumb when passing '-marmv8.1-m.main' to 
objdump. This makes it unnecessary to use '-M force-thumb' when 
disassembling for non-elf targets.

opcodes/ChangeLog:

2019-05-01  Andre Vieira  <andre.simoesdiasvieira@arm.com>
             Michael Collison <michael.collison@arm.com>

	* arm-dis.c (enum mve_instructions): New enum.
	(enum mve_unpredictable): Likewise.
	(enum mve_undefined): Likewise.
	(struct mopcode32): New struct.
	(is_mve_okay_in_it): New function.
	(is_mve_architecture): Likewise.
	(arm_decode_field): Likewise.
	(arm_decode_field_multiple): Likewise.
	(is_mve_encoding_conflict): Likewise.
	(is_mve_undefined): Likewise.
	(is_mve_unpredictable): Likewise.
	(print_mve_undefined): Likewise.
	(print_mve_unpredictable): Likewise.
	(print_insn_coprocessor_1): Use arm_decode_field_multiple.
	(print_insn_mve): New function.
	(print_insn_thumb32): Handle MVE architecture.
         (select_arm_features): Force thumb for Armv8.1-m Mainline.

Patch

diff --git a/opcodes/arm-dis.c b/opcodes/arm-dis.c
index 15d4d7248ea66f4b61278c708b796b3a5a23570b..6452d7c514bea751430a4906fa0ad4c8c8a136e8 100644
--- a/opcodes/arm-dis.c
+++ b/opcodes/arm-dis.c
@@ -68,6 +68,23 @@  struct arm_private_data
   bfd_vma last_mapping_addr;
 };
 
+enum mve_instructions
+{
+  MVE_NONE
+};
+
+enum mve_unpredictable
+{
+  UNPRED_IT_BLOCK,		/* Unpredictable because mve insn in it block.
+				 */
+  UNPRED_NONE			/* No unpredictable behavior.  */
+};
+
+enum mve_undefined
+{
+  UNDEF_NONE			/* no undefined behavior.  */
+};
+
 struct opcode32
 {
   arm_feature_set arch;		/* Architecture defining this insn.  */
@@ -76,6 +93,18 @@  struct opcode32
   const char *  assembler;	/* How to disassemble this insn.  */
 };
 
+/* MVE opcodes.  */
+
+struct mopcode32
+{
+  arm_feature_set arch;		/* Architecture defining this insn.  */
+  enum mve_instructions mve_op;  /* Specific mve instruction for faster
+				    decoding.  */
+  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.  */
+};
+
 enum isa {
   ANY,
   T32,
@@ -1756,6 +1785,18 @@  static const struct opcode32 neon_opcodes[] =
   {ARM_FEATURE_CORE_LOW (0), 0 ,0, 0}
 };
 
+/* mve opcode table.  */
+
+/* print_insn_mve recognizes the following format control codes:
+
+   %%			%
+
+   */
+
+static const struct mopcode32 mve_opcodes[] =
+{
+};
+
 /* Opcode tables: ARM, 16-bit Thumb, 32-bit Thumb.  All three are partially
    ordered: they must be searched linearly from the top to obtain a correct
    match.  */
@@ -3449,6 +3490,105 @@  arm_decode_shift (long given, fprintf_ftype func, void *stream,
     }
 }
 
+/* Return TRUE if the MATCHED_INSN can be inside an IT block.  */
+
+static bfd_boolean
+is_mve_okay_in_it (enum mve_instructions matched_insn)
+{
+  return FALSE;
+}
+
+static bfd_boolean
+is_mve_architecture (struct disassemble_info *info)
+{
+  struct arm_private_data *private_data = info->private_data;
+  arm_feature_set allowed_arches = private_data->features;
+
+  arm_feature_set arm_ext_v8_1m_main
+    = ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_1M_MAIN);
+
+  if (ARM_CPU_HAS_FEATURE (arm_ext_v8_1m_main, allowed_arches)
+      && !ARM_CPU_IS_ANY (allowed_arches))
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/* Decode a bitfield from opcode GIVEN, with starting bitfield = START
+   and ending bitfield = END.  END must be greater than START.  */
+
+static unsigned long
+arm_decode_field (unsigned long given, unsigned int start, unsigned int end)
+{
+  int bits = end - start;
+
+  if (bits < 0)
+    abort ();
+
+  return ((given >> start) & ((2ul << bits) - 1));
+}
+
+/* Decode a bitfield from opcode GIVEN, with multiple bitfields:
+   START:END and START2:END2.  END/END2 must be greater than
+   START/START2.  */
+
+static unsigned long
+arm_decode_field_multiple (unsigned long given, unsigned int start,
+			   unsigned int end, unsigned int start2,
+			   unsigned int end2)
+{
+  int bits = end - start;
+  int bits2 = end2 - start2;
+  unsigned long value = 0;
+  int width = 0;
+
+  if (bits2 < 0)
+    abort ();
+
+  value = arm_decode_field (given, start, end);
+  width += bits + 1;
+
+  value |= ((given >> start2) & ((2ul << bits2) - 1)) << width;
+  return value;
+}
+
+/* Return TRUE if the GIVEN encoding should not be decoded as MATCHED_INSN.
+   This helps us decode instructions that change mnemonic depending on specific
+   operand values/encodings.  */
+
+static bfd_boolean
+is_mve_encoding_conflict (unsigned long given,
+			  enum mve_instructions matched_insn)
+{
+  return FALSE;
+}
+
+/* Return FALSE if GIVEN is not an undefined encoding for MATCHED_INSN.
+   Otherwise, return TRUE and set UNDEFINED_CODE to give a reason as to why
+   this encoding is undefined.  */
+
+static bfd_boolean
+is_mve_undefined (unsigned long given, enum mve_instructions matched_insn,
+		  enum mve_undefined *undefined_code)
+{
+  *undefined_code = UNDEF_NONE;
+
+  return FALSE;
+}
+
+/* Return FALSE if GIVEN is not an unpredictable encoding for MATCHED_INSN.
+   Otherwise, return TRUE and set UNPREDICTABLE_CODE to give a reason as to
+   why this encoding is unpredictable.  */
+
+static bfd_boolean
+is_mve_unpredictable (unsigned long given, enum mve_instructions matched_insn,
+		      enum mve_unpredictable *unpredictable_code)
+{
+  *unpredictable_code = UNPRED_NONE;
+
+  return FALSE;
+}
+
 #define W_BIT 21
 #define I_BIT 22
 #define U_BIT 23
@@ -3459,6 +3599,43 @@  arm_decode_shift (long given, fprintf_ftype func, void *stream,
 #define NEGATIVE_BIT_SET   ((given & (1 << U_BIT)) == 0)
 #define PRE_BIT_SET         (given & (1 << P_BIT))
 
+static void
+print_mve_undefined (struct disassemble_info *info,
+		     enum mve_undefined undefined_code)
+{
+  void *stream = info->stream;
+  fprintf_ftype func = info->fprintf_func;
+
+  func (stream, "\t\tundefined instruction: ");
+
+  switch (undefined_code)
+    {
+    case UNDEF_NONE:
+      break;
+    }
+
+}
+
+static void
+print_mve_unpredictable (struct disassemble_info *info,
+			 enum mve_unpredictable unpredict_code)
+{
+  void *stream = info->stream;
+  fprintf_ftype func = info->fprintf_func;
+
+  func (stream, "%s: ", UNPREDICTABLE_INSTRUCTION);
+
+  switch (unpredict_code)
+    {
+    case UNPRED_IT_BLOCK:
+      func (stream, "mve instruction in it block");
+      break;
+
+    case UNPRED_NONE:
+      break;
+    }
+}
+
 /* Print one coprocessor instruction on INFO->STREAM.
    Return TRUE if the instuction matched, FALSE if this is not a
    recognised coprocessor instruction.  */
@@ -3737,7 +3914,8 @@  print_insn_coprocessor_1 (const struct sopcode32 *opcodes,
 
 		case 'J':
 		  {
-		    int regno = ((given >> 19) & 0x8) | ((given >> 13) & 0x7);
+		    unsigned long regno
+		      = arm_decode_field_multiple (given, 13, 15, 22, 22);
 
 		    switch (regno)
 		      {
@@ -3760,7 +3938,7 @@  print_insn_coprocessor_1 (const struct sopcode32 *opcodes,
 			func (stream, "FPCXTS");
 			break;
 		      default:
-			func (stream, "<invalid reg %d>", regno);
+			func (stream, "<invalid reg %lu>", regno);
 			break;
 		      }
 		  }
@@ -4774,6 +4952,75 @@  print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
   return FALSE;
 }
 
+/* Print one mve instruction on INFO->STREAM.
+   Return TRUE if the instuction matched, FALSE if this is not a
+   recognised mve instruction.  */
+
+static bfd_boolean
+print_insn_mve (struct disassemble_info *info, long given)
+{
+  const struct mopcode32 *insn;
+  void *stream = info->stream;
+  fprintf_ftype func = info->fprintf_func;
+
+  for (insn = mve_opcodes; insn->assembler; insn++)
+    {
+      if (((given & insn->mask) == insn->value)
+	  && !is_mve_encoding_conflict (given, insn->mve_op))
+	{
+	  signed long value_in_comment = 0;
+	  bfd_boolean is_unpredictable = FALSE;
+	  bfd_boolean is_undefined = FALSE;
+	  const char *c;
+	  enum mve_unpredictable unpredictable_cond = UNPRED_NONE;
+	  enum mve_undefined undefined_cond = UNDEF_NONE;
+
+	  /* Most vector mve instruction are illegal in a it block.
+	     There are a few exceptions; check for them.  */
+	  if (ifthen_state && !is_mve_okay_in_it (insn->mve_op))
+	    {
+	      is_unpredictable = TRUE;
+	      unpredictable_cond = UNPRED_IT_BLOCK;
+	    }
+	  else if (is_mve_unpredictable (given, insn->mve_op,
+					 &unpredictable_cond))
+	    is_unpredictable = TRUE;
+
+	  if (is_mve_undefined (given, insn->mve_op, &undefined_cond))
+	    is_undefined = TRUE;
+
+	  for (c = insn->assembler; *c; c++)
+	    {
+	      if (*c == '%')
+		{
+		  switch (*++c)
+		    {
+		    case '%':
+		      func (stream, "%%");
+		      break;
+
+		    }
+		}
+	      else
+		func (stream, "%c", *c);
+	    }
+
+	  if (value_in_comment > 32 || value_in_comment < -16)
+	    func (stream, "\t; 0x%lx", value_in_comment);
+
+	  if (is_unpredictable)
+	    print_mve_unpredictable (info, unpredictable_cond);
+
+	  if (is_undefined)
+	    print_mve_undefined (info, undefined_cond);
+
+	  return TRUE;
+	}
+    }
+  return FALSE;
+}
+
+
 /* Return the name of a v7A special register.  */
 
 static const char *
@@ -5687,11 +5934,15 @@  print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given)
   const struct opcode32 *insn;
   void *stream = info->stream;
   fprintf_ftype func = info->fprintf_func;
+  bfd_boolean is_mve = is_mve_architecture (info);
 
   if (print_insn_coprocessor (pc, info, given, TRUE))
     return;
 
-  if (print_insn_neon (info, given, TRUE))
+  if ((is_mve == FALSE) && print_insn_neon (info, given, TRUE))
+    return;
+
+  if (is_mve && print_insn_mve (info, given))
     return;
 
   if (print_insn_generic_coprocessor (pc, info, given, TRUE))
@@ -6739,7 +6990,10 @@  select_arm_features (unsigned long mach,
     case bfd_mach_arm_8R:	 ARM_SET_FEATURES (ARM_ARCH_V8R); break;
     case bfd_mach_arm_8M_BASE:	 ARM_SET_FEATURES (ARM_ARCH_V8M_BASE); break;
     case bfd_mach_arm_8M_MAIN:	 ARM_SET_FEATURES (ARM_ARCH_V8M_MAIN); break;
-    case bfd_mach_arm_8_1M_MAIN: ARM_SET_FEATURES (ARM_ARCH_V8_1M_MAIN); break;
+    case bfd_mach_arm_8_1M_MAIN:
+      ARM_SET_FEATURES (ARM_ARCH_V8_1M_MAIN);
+      force_thumb = 1;
+      break;
       /* If the machine type is unknown allow all architecture types and all
 	 extensions.  */
     case bfd_mach_arm_unknown:	 ARM_SET_FEATURES (ARM_FEATURE_ALL); break;