[PATCH 08/10] aarch64: Enforce P/M/E order for MOPS instructions

Richard Sandiford richard.sandiford@arm.com
Tue Nov 30 13:20:59 GMT 2021


The MOPS instructions should be used as a triple, such as:

       cpyfp [x0]!, [x1]!, x2!
       cpyfm [x0]!, [x1]!, x2!
       cpyfe [x0]!, [x1]!, x2!

The registers should also be the same for each writeback operand.
This patch adds a warning for code that doesn't follow this rule,
along similar lines to the warning that we already emit for
invalid uses of MOVPRFX.

Tested on aarch64-linux-gnu.  OK to install?

Richard


include/
	* opcode/aarch64.h (C_SCAN_MOPS_P, C_SCAN_MOPS_M, C_SCAN_MOPS_E)
	(C_SCAN_MOPS_PME): New macros.
	(AARCH64_OPDE_A_SHOULD_FOLLOW_B): New aarch64_operand_error_kind.
	(AARCH64_OPDE_EXPECTED_A_AFTER_B): Likewise.
	(aarch64_operand_error): Make each data value a union between
	an int and a string.

opcodes/
	* aarch64-tbl.h (MOPS_CPY_OP1_OP2_INSN): Add scan flags.
	(MOPS_SET_OP1_OP2_INSN): Likewise.
	* aarch64-opc.c (set_out_of_range_error): Update after change to
	aarch64_operand_error.
	(set_unaligned_error, set_reg_list_error): Likewise.
	(init_insn_sequence): Use a 3-instruction sequence for
	MOPS P instructions.
	(verify_mops_pme_sequence): New function.
	(verify_constraints): Call it.
	* aarch64-dis.c (print_verifier_notes): Handle
	AARCH64_OPDE_A_SHOULD_FOLLOW_B and AARCH64_OPDE_EXPECTED_A_AFTER_B.

gas/
	* config/tc-aarch64.c (operand_mismatch_kind_names): Add entries
	for AARCH64_OPDE_A_SHOULD_FOLLOW_B and AARCH64_OPDE_EXPECTED_A_AFTER_B.
	(operand_error_higher_severity_p): Check that
	AARCH64_OPDE_A_SHOULD_FOLLOW_B and AARCH64_OPDE_EXPECTED_A_AFTER_B
	come between AARCH64_OPDE_RECOVERABLE and AARCH64_OPDE_SYNTAX_ERROR;
	their relative order is not significant.
	(record_operand_error_with_data): Update after change to
	aarch64_operand_error.
	(output_operand_error_record): Likewise.  Handle
	AARCH64_OPDE_A_SHOULD_FOLLOW_B and AARCH64_OPDE_EXPECTED_A_AFTER_B.
	* testsuite/gas/aarch64/mops_invalid_2.s,
	testsuite/gas/aarch64/mops_invalid_2.d,
	testsuite/gas/aarch64/mops_invalid_2.l: New test.
---
 gas/config/tc-aarch64.c                    | 38 ++++++---
 gas/testsuite/gas/aarch64/mops_invalid_2.d | 72 ++++++++++++++++
 gas/testsuite/gas/aarch64/mops_invalid_2.l | 27 ++++++
 gas/testsuite/gas/aarch64/mops_invalid_2.s | 75 +++++++++++++++++
 include/opcode/aarch64.h                   | 32 ++++++--
 opcodes/aarch64-dis.c                      | 27 +++++-
 opcodes/aarch64-opc.c                      | 95 ++++++++++++++++++++--
 opcodes/aarch64-tbl.h                      | 20 +++--
 8 files changed, 354 insertions(+), 32 deletions(-)
 create mode 100644 gas/testsuite/gas/aarch64/mops_invalid_2.d
 create mode 100644 gas/testsuite/gas/aarch64/mops_invalid_2.l
 create mode 100644 gas/testsuite/gas/aarch64/mops_invalid_2.s

diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index 4aadf5bf99f..ea65da580de 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -5019,6 +5019,8 @@ const char* operand_mismatch_kind_names[] =
 {
   "AARCH64_OPDE_NIL",
   "AARCH64_OPDE_RECOVERABLE",
+  "AARCH64_OPDE_A_SHOULD_FOLLOW_B",
+  "AARCH64_OPDE_EXPECTED_A_AFTER_B",
   "AARCH64_OPDE_SYNTAX_ERROR",
   "AARCH64_OPDE_FATAL_SYNTAX_ERROR",
   "AARCH64_OPDE_INVALID_VARIANT",
@@ -5040,7 +5042,10 @@ operand_error_higher_severity_p (enum aarch64_operand_error_kind lhs,
 				 enum aarch64_operand_error_kind rhs)
 {
   gas_assert (AARCH64_OPDE_RECOVERABLE > AARCH64_OPDE_NIL);
-  gas_assert (AARCH64_OPDE_SYNTAX_ERROR > AARCH64_OPDE_RECOVERABLE);
+  gas_assert (AARCH64_OPDE_A_SHOULD_FOLLOW_B > AARCH64_OPDE_RECOVERABLE);
+  gas_assert (AARCH64_OPDE_EXPECTED_A_AFTER_B > AARCH64_OPDE_RECOVERABLE);
+  gas_assert (AARCH64_OPDE_SYNTAX_ERROR > AARCH64_OPDE_A_SHOULD_FOLLOW_B);
+  gas_assert (AARCH64_OPDE_SYNTAX_ERROR > AARCH64_OPDE_EXPECTED_A_AFTER_B);
   gas_assert (AARCH64_OPDE_FATAL_SYNTAX_ERROR > AARCH64_OPDE_SYNTAX_ERROR);
   gas_assert (AARCH64_OPDE_INVALID_VARIANT > AARCH64_OPDE_FATAL_SYNTAX_ERROR);
   gas_assert (AARCH64_OPDE_OUT_OF_RANGE > AARCH64_OPDE_INVALID_VARIANT);
@@ -5233,9 +5238,9 @@ record_operand_error_with_data (const aarch64_opcode *opcode, int idx,
   info.index = idx;
   info.kind = kind;
   info.error = error;
-  info.data[0] = extra_data[0];
-  info.data[1] = extra_data[1];
-  info.data[2] = extra_data[2];
+  info.data[0].i = extra_data[0];
+  info.data[1].i = extra_data[1];
+  info.data[2].i = extra_data[2];
   info.non_fatal = false;
   record_operand_error_info (opcode, &info);
 }
@@ -5410,6 +5415,19 @@ output_operand_error_record (const operand_error_record *record, char *str)
     case AARCH64_OPDE_NIL:
       gas_assert (0);
       break;
+
+    case AARCH64_OPDE_A_SHOULD_FOLLOW_B:
+      handler (_("this `%s' should have an immediately preceding `%s'"
+		 " -- `%s'"),
+	       detail->data[0].s, detail->data[1].s, str);
+      break;
+
+    case AARCH64_OPDE_EXPECTED_A_AFTER_B:
+      handler (_("the preceding `%s' should be followed by `%s` rather"
+		 " than `%s` -- `%s'"),
+	       detail->data[1].s, detail->data[0].s, opcode->name, str);
+      break;
+
     case AARCH64_OPDE_SYNTAX_ERROR:
     case AARCH64_OPDE_RECOVERABLE:
     case AARCH64_OPDE_FATAL_SYNTAX_ERROR:
@@ -5541,31 +5559,31 @@ output_operand_error_record (const operand_error_record *record, char *str)
       break;
 
     case AARCH64_OPDE_OUT_OF_RANGE:
-      if (detail->data[0] != detail->data[1])
+      if (detail->data[0].i != detail->data[1].i)
 	handler (_("%s out of range %d to %d at operand %d -- `%s'"),
 		 detail->error ? detail->error : _("immediate value"),
-		 detail->data[0], detail->data[1], idx + 1, str);
+		 detail->data[0].i, detail->data[1].i, idx + 1, str);
       else
 	handler (_("%s must be %d at operand %d -- `%s'"),
 		 detail->error ? detail->error : _("immediate value"),
-		 detail->data[0], idx + 1, str);
+		 detail->data[0].i, idx + 1, str);
       break;
 
     case AARCH64_OPDE_REG_LIST:
-      if (detail->data[0] == 1)
+      if (detail->data[0].i == 1)
 	handler (_("invalid number of registers in the list; "
 		   "only 1 register is expected at operand %d -- `%s'"),
 		 idx + 1, str);
       else
 	handler (_("invalid number of registers in the list; "
 		   "%d registers are expected at operand %d -- `%s'"),
-	       detail->data[0], idx + 1, str);
+	       detail->data[0].i, idx + 1, str);
       break;
 
     case AARCH64_OPDE_UNALIGNED:
       handler (_("immediate value must be a multiple of "
 		 "%d at operand %d -- `%s'"),
-	       detail->data[0], idx + 1, str);
+	       detail->data[0].i, idx + 1, str);
       break;
 
     default:
diff --git a/gas/testsuite/gas/aarch64/mops_invalid_2.d b/gas/testsuite/gas/aarch64/mops_invalid_2.d
new file mode 100644
index 00000000000..f5e7228f6e7
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/mops_invalid_2.d
@@ -0,0 +1,72 @@
+# warning_output: mops_invalid_2.l
+# objdump: -dr -M notes
+
+.*
+
+
+Disassembly of section \.text:
+
+0+ <\.text>:
+[^:]*:	1901d440 	cpyfpwtn	\[x0\]!, \[x1\]!, x2!
+[^:]*:	1981d440 	cpyfewtn	\[x0\]!, \[x1\]!, x2!  // note: expected `cpyfmwtn' after previous `cpyfpwtn'
+[^:]*:	1941d440 	cpyfmwtn	\[x0\]!, \[x1\]!, x2!  // note: this `cpyfmwtn' should have an immediately preceding `cpyfpwtn'
+[^:]*:	1901d440 	cpyfpwtn	\[x0\]!, \[x1\]!, x2!
+[^:]*:	1941f440 	cpyfmtn	\[x0\]!, \[x1\]!, x2!  // note: expected `cpyfmwtn' after previous `cpyfpwtn'
+[^:]*:	1981f440 	cpyfetn	\[x0\]!, \[x1\]!, x2!
+[^:]*:	1d010440 	cpyp	\[x0\]!, \[x1\]!, x2!
+[^:]*:	19c24420 	setm	\[x0\]!, x1!, x2  // note: expected `cpym' after previous `cpyp'
+[^:]*:	19c28420 	sete	\[x0\]!, x1!, x2
+[^:]*:	19011440 	cpyfpwt	\[x0\]!, \[x1\]!, x2!
+[^:]*:	19411443 	cpyfmwt	\[x3\]!, \[x1\]!, x2!  // note: destination register differs from preceding instruction at operand 1
+[^:]*:	19811444 	cpyfewt	\[x4\]!, \[x1\]!, x2!  // note: destination register differs from preceding instruction at operand 1
+[^:]*:	19011440 	cpyfpwt	\[x0\]!, \[x1\]!, x2!
+[^:]*:	19431440 	cpyfmwt	\[x0\]!, \[x3\]!, x2!  // note: source register differs from preceding instruction at operand 2
+[^:]*:	19841440 	cpyfewt	\[x0\]!, \[x4\]!, x2!  // note: source register differs from preceding instruction at operand 2
+[^:]*:	19011440 	cpyfpwt	\[x0\]!, \[x1\]!, x2!
+[^:]*:	19411460 	cpyfmwt	\[x0\]!, \[x1\]!, x3!  // note: size register differs from preceding instruction at operand 3
+[^:]*:	19811480 	cpyfewt	\[x0\]!, \[x1\]!, x4!  // note: size register differs from preceding instruction at operand 3
+[^:]*:	1901d440 	cpyfpwtn	\[x0\]!, \[x1\]!, x2!
+[^:]*:	8b020020 	add	x0, x1, x2  // note: expected `cpyfmwtn' after previous `cpyfpwtn'
+[^:]*:	1901e440 	cpyfprtn	\[x0\]!, \[x1\]!, x2!
+[^:]*:	1941e440 	cpyfmrtn	\[x0\]!, \[x1\]!, x2!
+
+Disassembly of section \.text2:
+
+0+ <\.text2>:
+[^:]*:	1901d440 	cpyfpwtn	\[x0\]!, \[x1\]!, x2!  // note: instruction opens new dependency sequence without ending previous one
+
+Disassembly of section \.text3:
+
+0+ <\.text3>:
+[^:]*:	1941d440 	cpyfmwtn	\[x0\]!, \[x1\]!, x2!  // note: this `cpyfmwtn' should have an immediately preceding `cpyfpwtn'
+
+Disassembly of section \.text4:
+
+0+ <\.text4>:
+[^:]*:	1981d440 	cpyfewtn	\[x0\]!, \[x1\]!, x2!  // note: this `cpyfewtn' should have an immediately preceding `cpyfmwtn'
+[^:]*:	19014440 	cpyfpwn	\[x0\]!, \[x1\]!, x2!
+
+Disassembly of section \.text5:
+
+0+ <\.text5>:
+[^:]*:	91000020 	add	x0, x1, #0x0  // note: expected `cpyfmwn' after previous `cpyfpwn'
+[^:]*:	19c20420 	setp	\[x0\]!, x1!, x2
+[^:]*:	19c28420 	sete	\[x0\]!, x1!, x2  // note: expected `setm' after previous `setp'
+[^:]*:	19c24420 	setm	\[x0\]!, x1!, x2  // note: this `setm' should have an immediately preceding `setp'
+[^:]*:	19c20420 	setp	\[x0\]!, x1!, x2
+[^:]*:	19c24423 	setm	\[x3\]!, x1!, x2  // note: destination register differs from preceding instruction at operand 1
+[^:]*:	19c28424 	sete	\[x4\]!, x1!, x2  // note: destination register differs from preceding instruction at operand 1
+[^:]*:	19c20420 	setp	\[x0\]!, x1!, x2
+[^:]*:	19c24460 	setm	\[x0\]!, x3!, x2  // note: size register differs from preceding instruction at operand 2
+[^:]*:	19c28480 	sete	\[x0\]!, x4!, x2  // note: size register differs from preceding instruction at operand 2
+[^:]*:	19c20420 	setp	\[x0\]!, x1!, x2
+[^:]*:	19c44420 	setm	\[x0\]!, x1!, x4
+[^:]*:	19c38420 	sete	\[x0\]!, x1!, x3
+[^:]*:	0420bc20 	movprfx	z0, z1
+[^:]*:	19c24420 	setm	\[x0\]!, x1!, x2  // note: SVE instruction expected after `movprfx'
+[^:]*:	19c20420 	setp	\[x0\]!, x1!, x2
+[^:]*:	0420bc20 	movprfx	z0, z1  // note: instruction opens new dependency sequence without ending previous one
+[^:]*:	65808080 	fadd	z0\.s, p0/m, z0\.s, z4\.s
+[^:]*:	19c20420 	setp	\[x0\]!, x1!, x2
+[^:]*:	0420bc20 	movprfx	z0, z1  // note: instruction opens new dependency sequence without ending previous one
+[^:]*:	65808082 	fadd	z2\.s, p0/m, z2\.s, z4\.s  // note: output register of preceding `movprfx' not used in current instruction at operand 1
diff --git a/gas/testsuite/gas/aarch64/mops_invalid_2.l b/gas/testsuite/gas/aarch64/mops_invalid_2.l
new file mode 100644
index 00000000000..4f757530fb5
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/mops_invalid_2.l
@@ -0,0 +1,27 @@
+[^:]*: Assembler messages:
+[^:]*:4: Warning: the preceding `cpyfpwtn' should be followed by `cpyfmwtn` rather than `cpyfewtn` -- `cpyfewtn \[x0\]!,\[x1\]!,x2!'
+[^:]*:5: Warning: this `cpyfmwtn' should have an immediately preceding `cpyfpwtn' -- `cpyfmwtn \[x0\]!,\[x1\]!,x2!'
+[^:]*:8: Warning: the preceding `cpyfpwtn' should be followed by `cpyfmwtn` rather than `cpyfmtn` -- `cpyfmtn \[x0\]!,\[x1\]!,x2!'
+[^:]*:12: Warning: the preceding `cpyp' should be followed by `cpym` rather than `setm` -- `setm \[x0\]!,x1!,x2'
+[^:]*:16: Warning: destination register differs from preceding instruction at operand 1 -- `cpyfmwt \[x3\]!,\[x1\]!,x2!'
+[^:]*:17: Warning: destination register differs from preceding instruction at operand 1 -- `cpyfewt \[x4\]!,\[x1\]!,x2!'
+[^:]*:20: Warning: source register differs from preceding instruction at operand 2 -- `cpyfmwt \[x0\]!,\[x3\]!,x2!'
+[^:]*:21: Warning: source register differs from preceding instruction at operand 2 -- `cpyfewt \[x0\]!,\[x4\]!,x2!'
+[^:]*:24: Warning: size register differs from preceding instruction at operand 3 -- `cpyfmwt \[x0\]!,\[x1\]!,x3!'
+[^:]*:25: Warning: size register differs from preceding instruction at operand 3 -- `cpyfewt \[x0\]!,\[x1\]!,x4!'
+[^:]*:28: Warning: the preceding `cpyfpwtn' should be followed by `cpyfmwtn` rather than `add` -- `add x0,x1,x2'
+[^:]*:39: Warning: this `cpyfmwtn' should have an immediately preceding `cpyfpwtn' -- `cpyfmwtn \[x0\]!,\[x1\]!,x2!'
+[^:]*:43: Warning: this `cpyfewtn' should have an immediately preceding `cpyfmwtn' -- `cpyfewtn \[x0\]!,\[x1\]!,x2!'
+[^:]*:51: Warning: the preceding `setp' should be followed by `setm` rather than `sete` -- `sete \[x0\]!,x1!,x2'
+[^:]*:52: Warning: this `setm' should have an immediately preceding `setp' -- `setm \[x0\]!,x1!,x2'
+[^:]*:55: Warning: destination register differs from preceding instruction at operand 1 -- `setm \[x3\]!,x1!,x2'
+[^:]*:56: Warning: destination register differs from preceding instruction at operand 1 -- `sete \[x4\]!,x1!,x2'
+[^:]*:59: Warning: size register differs from preceding instruction at operand 2 -- `setm \[x0\]!,x3!,x2'
+[^:]*:60: Warning: size register differs from preceding instruction at operand 2 -- `sete \[x0\]!,x4!,x2'
+[^:]*:67: Warning: SVE instruction expected after `movprfx' -- `setm \[x0\]!,x1!,x2'
+[^:]*:70: Warning: instruction opens new dependency sequence without ending previous one -- `movprfx z0,z1'
+[^:]*:74: Warning: instruction opens new dependency sequence without ending previous one -- `movprfx z0,z1'
+[^:]*:75: Warning: output register of preceding `movprfx' not used in current instruction at operand 1 -- `fadd z2\.s,p0/m,z2\.s,z4\.s'
+[^:]*:31: Warning: previous `cpyfprtn' sequence has not been closed
+[^:]*:35: Warning: previous `cpyfpwtn' sequence has not been closed
+[^:]*:44: Warning: previous `cpyfpwn' sequence has not been closed
diff --git a/gas/testsuite/gas/aarch64/mops_invalid_2.s b/gas/testsuite/gas/aarch64/mops_invalid_2.s
new file mode 100644
index 00000000000..f78ca54e84f
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/mops_invalid_2.s
@@ -0,0 +1,75 @@
+	.arch	armv8.8-a+sve
+
+	cpyfpwtn [x0]!, [x1]!, x2!
+	cpyfewtn [x0]!, [x1]!, x2!
+	cpyfmwtn [x0]!, [x1]!, x2!
+
+	cpyfpwtn [x0]!, [x1]!, x2!
+	cpyfmtn [x0]!, [x1]!, x2!
+	cpyfetn [x0]!, [x1]!, x2!
+
+	cpyp [x0]!, [x1]!, x2!
+	setm [x0]!, x1!, x2
+	sete [x0]!, x1!, x2
+
+	cpyfpwt [x0]!, [x1]!, x2!
+	cpyfmwt [x3]!, [x1]!, x2!
+	cpyfewt [x4]!, [x1]!, x2!
+
+	cpyfpwt [x0]!, [x1]!, x2!
+	cpyfmwt [x0]!, [x3]!, x2!
+	cpyfewt [x0]!, [x4]!, x2!
+
+	cpyfpwt [x0]!, [x1]!, x2!
+	cpyfmwt [x0]!, [x1]!, x3!
+	cpyfewt [x0]!, [x1]!, x4!
+
+	cpyfpwtn [x0]!, [x1]!, x2!
+	add x0, x1, x2
+
+	cpyfprtn [x0]!, [x1]!, x2!
+	cpyfmrtn [x0]!, [x1]!, x2!
+
+	.section .text2, "ax", @progbits
+
+	cpyfpwtn [x0]!, [x1]!, x2!
+
+	.section .text3, "ax", @progbits
+
+	cpyfmwtn [x0]!, [x1]!, x2!
+
+	.section .text4, "ax", @progbits
+
+	cpyfewtn [x0]!, [x1]!, x2!
+	cpyfpwn [x0]!, [x1]!, x2!
+
+	.section .text5, "ax", @progbits
+
+	add x0, x1, #0
+
+	setp [x0]!, x1!, x2
+	sete [x0]!, x1!, x2
+	setm [x0]!, x1!, x2
+
+	setp [x0]!, x1!, x2
+	setm [x3]!, x1!, x2
+	sete [x4]!, x1!, x2
+
+	setp [x0]!, x1!, x2
+	setm [x0]!, x3!, x2
+	sete [x0]!, x4!, x2
+
+	setp [x0]!, x1!, x2
+	setm [x0]!, x1!, x4 // OK
+	sete [x0]!, x1!, x3 // OK
+
+	movprfx z0, z1
+	setm [x0]!, x1!, x2
+
+	setp [x0]!, x1!, x2
+	movprfx z0, z1
+	fadd z0.s, p0/m, z0.s, z4.s
+
+	setp [x0]!, x1!, x2
+	movprfx z0, z1
+	fadd z2.s, p0/m, z2.s, z4.s
diff --git a/include/opcode/aarch64.h b/include/opcode/aarch64.h
index 183d28150c3..21ba0bf0074 100644
--- a/include/opcode/aarch64.h
+++ b/include/opcode/aarch64.h
@@ -927,7 +927,11 @@ extern const aarch64_opcode aarch64_opcode_table[];
 /* This instruction's operation width is determined by the operand with the
    largest element size.  */
 #define C_MAX_ELEM (1U << 1)
-/* Next bit is 2.  */
+#define C_SCAN_MOPS_P (1U << 2)
+#define C_SCAN_MOPS_M (2U << 2)
+#define C_SCAN_MOPS_E (3U << 2)
+#define C_SCAN_MOPS_PME (3U << 2)
+/* Next bit is 4.  */
 
 static inline bool
 alias_opcode_p (const aarch64_opcode *opcode)
@@ -1221,6 +1225,17 @@ struct aarch64_inst
      Less severe error found during the parsing, very possibly because that
      GAS has picked up a wrong instruction template for the parsing.
 
+   AARCH64_OPDE_A_SHOULD_FOLLOW_B
+     The instruction forms (or is expected to form) part of a sequence,
+     but the preceding instruction in the sequence wasn't the expected one.
+     The message refers to two strings: the name of the current instruction,
+     followed by the name of the expected preceding instruction.
+
+   AARCH64_OPDE_EXPECTED_A_AFTER_B
+     Same as AARCH64_OPDE_A_SHOULD_FOLLOW_B, but shifting the focus
+     so that the current instruction is assumed to be the incorrect one:
+     "since the previous instruction was B, the current one should be A".
+
    AARCH64_OPDE_SYNTAX_ERROR
      General syntax error; it can be either a user error, or simply because
      that GAS is trying a wrong instruction template.
@@ -1255,11 +1270,8 @@ struct aarch64_inst
      Error of the highest severity and used for any severe issue that does not
      fall into any of the above categories.
 
-   The enumerators are only interesting to GAS.  They are declared here (in
-   libopcodes) because that some errors are detected (and then notified to GAS)
-   by libopcodes (rather than by GAS solely).
-
-   The first three errors are only deteced by GAS while the
+   AARCH64_OPDE_RECOVERABLE, AARCH64_OPDE_SYNTAX_ERROR and
+   AARCH64_OPDE_FATAL_SYNTAX_ERROR are only deteced by GAS while the
    AARCH64_OPDE_INVALID_VARIANT error can only be spotted by libopcodes as
    only libopcodes has the information about the valid variants of each
    instruction.
@@ -1273,6 +1285,8 @@ enum aarch64_operand_error_kind
 {
   AARCH64_OPDE_NIL,
   AARCH64_OPDE_RECOVERABLE,
+  AARCH64_OPDE_A_SHOULD_FOLLOW_B,
+  AARCH64_OPDE_EXPECTED_A_AFTER_B,
   AARCH64_OPDE_SYNTAX_ERROR,
   AARCH64_OPDE_FATAL_SYNTAX_ERROR,
   AARCH64_OPDE_INVALID_VARIANT,
@@ -1290,7 +1304,11 @@ struct aarch64_operand_error
   enum aarch64_operand_error_kind kind;
   int index;
   const char *error;
-  int data[3];	/* Some data for extra information.  */
+  /* Some data for extra information.  */
+  union {
+    int i;
+    const char *s;
+  } data[3];
   bool non_fatal;
 };
 
diff --git a/opcodes/aarch64-dis.c b/opcodes/aarch64-dis.c
index 8e6123db02e..fdb87b4526f 100644
--- a/opcodes/aarch64-dis.c
+++ b/opcodes/aarch64-dis.c
@@ -3388,10 +3388,29 @@ print_verifier_notes (aarch64_operand_error *detail,
   assert (detail->non_fatal);
   assert (detail->error);
 
-  /* If there are multiple verifier messages, concat them up to 1k.  */
-  (*info->fprintf_func) (info->stream, "  // note: %s", detail->error);
-  if (detail->index >= 0)
-     (*info->fprintf_func) (info->stream, " at operand %d", detail->index + 1);
+  (*info->fprintf_func) (info->stream, "  // note: ");
+  switch (detail->kind)
+    {
+    case AARCH64_OPDE_A_SHOULD_FOLLOW_B:
+      (*info->fprintf_func) (info->stream,
+			     _("this `%s' should have an immediately"
+			       " preceding `%s'"),
+			     detail->data[0].s, detail->data[1].s);
+      break;
+
+    case AARCH64_OPDE_EXPECTED_A_AFTER_B:
+      (*info->fprintf_func) (info->stream,
+			     _("expected `%s' after previous `%s'"),
+			     detail->data[0].s, detail->data[1].s);
+      break;
+
+    default:
+      (*info->fprintf_func) (info->stream, "%s", detail->error);
+      if (detail->index >= 0)
+	(*info->fprintf_func) (info->stream, " at operand %d",
+			       detail->index + 1);
+      break;
+    }
 }
 
 /* Print the instruction according to *INST.  */
diff --git a/opcodes/aarch64-opc.c b/opcodes/aarch64-opc.c
index cfd47812684..49dfe98769a 100644
--- a/opcodes/aarch64-opc.c
+++ b/opcodes/aarch64-opc.c
@@ -1351,8 +1351,8 @@ set_out_of_range_error (aarch64_operand_error *mismatch_detail,
   if (mismatch_detail == NULL)
     return;
   set_error (mismatch_detail, AARCH64_OPDE_OUT_OF_RANGE, idx, error);
-  mismatch_detail->data[0] = lower_bound;
-  mismatch_detail->data[1] = upper_bound;
+  mismatch_detail->data[0].i = lower_bound;
+  mismatch_detail->data[1].i = upper_bound;
 }
 
 static inline void
@@ -1424,7 +1424,7 @@ set_unaligned_error (aarch64_operand_error *mismatch_detail, int idx,
   if (mismatch_detail == NULL)
     return;
   set_error (mismatch_detail, AARCH64_OPDE_UNALIGNED, idx, NULL);
-  mismatch_detail->data[0] = alignment;
+  mismatch_detail->data[0].i = alignment;
 }
 
 static inline void
@@ -1434,7 +1434,7 @@ set_reg_list_error (aarch64_operand_error *mismatch_detail, int idx,
   if (mismatch_detail == NULL)
     return;
   set_error (mismatch_detail, AARCH64_OPDE_REG_LIST, idx, NULL);
-  mismatch_detail->data[0] = expected_num;
+  mismatch_detail->data[0].i = expected_num;
 }
 
 static inline void
@@ -5480,6 +5480,8 @@ init_insn_sequence (const struct aarch64_inst *inst,
      best.  */
   if (inst && inst->opcode->constraints & C_SCAN_MOVPRFX)
     num_req_entries = 1;
+  if (inst && (inst->opcode->constraints & C_SCAN_MOPS_PME) == C_SCAN_MOPS_P)
+    num_req_entries = 2;
 
   insn_sequence->num_added_insns = 0;
   insn_sequence->num_allocated_insns = num_req_entries;
@@ -5491,6 +5493,80 @@ init_insn_sequence (const struct aarch64_inst *inst,
     }
 }
 
+/* Subroutine of verify_constraints.  Check whether the instruction
+   is part of a MOPS P/M/E sequence and, if so, whether sequencing
+   expectations are met.  Return true if the check passes, otherwise
+   describe the problem in MISMATCH_DETAIL.
+
+   IS_NEW_SECTION is true if INST is assumed to start a new section.
+   The other arguments are as for verify_constraints.  */
+
+static bool
+verify_mops_pme_sequence (const struct aarch64_inst *inst,
+			  bool is_new_section,
+			  aarch64_operand_error *mismatch_detail,
+			  aarch64_instr_sequence *insn_sequence)
+{
+  const struct aarch64_opcode *opcode;
+  const struct aarch64_inst *prev_insn;
+  int i;
+
+  opcode = inst->opcode;
+  if (insn_sequence->instr)
+    prev_insn = insn_sequence->instr + (insn_sequence->num_added_insns - 1);
+  else
+    prev_insn = NULL;
+
+  if (prev_insn
+      && (prev_insn->opcode->constraints & C_SCAN_MOPS_PME)
+      && prev_insn->opcode != opcode - 1)
+    {
+      mismatch_detail->kind = AARCH64_OPDE_EXPECTED_A_AFTER_B;
+      mismatch_detail->index = -1;
+      mismatch_detail->data[0].s = prev_insn->opcode[1].name;
+      mismatch_detail->data[1].s = prev_insn->opcode->name;
+      mismatch_detail->non_fatal = true;
+      return false;
+    }
+
+  if (opcode->constraints & C_SCAN_MOPS_PME)
+    {
+      if (is_new_section || !prev_insn || prev_insn->opcode != opcode - 1)
+	{
+	  mismatch_detail->kind = AARCH64_OPDE_A_SHOULD_FOLLOW_B;
+	  mismatch_detail->index = -1;
+	  mismatch_detail->data[0].s = opcode->name;
+	  mismatch_detail->data[1].s = opcode[-1].name;
+	  mismatch_detail->non_fatal = true;
+	  return false;
+	}
+
+      for (i = 0; i < 3; ++i)
+	/* There's no specific requirement for the data register to be
+	   the same between consecutive SET* instructions.  */
+	if ((opcode->operands[i] == AARCH64_OPND_MOPS_ADDR_Rd
+	     || opcode->operands[i] == AARCH64_OPND_MOPS_ADDR_Rs
+	     || opcode->operands[i] == AARCH64_OPND_MOPS_WB_Rn)
+	    && prev_insn->operands[i].reg.regno != inst->operands[i].reg.regno)
+	  {
+	    mismatch_detail->kind = AARCH64_OPDE_SYNTAX_ERROR;
+	    if (opcode->operands[i] == AARCH64_OPND_MOPS_ADDR_Rd)
+	      mismatch_detail->error = _("destination register differs from "
+					 "preceding instruction");
+	    else if (opcode->operands[i] == AARCH64_OPND_MOPS_ADDR_Rs)
+	      mismatch_detail->error = _("source register differs from "
+					 "preceding instruction");
+	    else
+	      mismatch_detail->error = _("size register differs from "
+					 "preceding instruction");
+	    mismatch_detail->index = i;
+	    mismatch_detail->non_fatal = true;
+	    return false;
+	  }
+    }
+
+  return true;
+}
 
 /*  This function verifies that the instruction INST adheres to its specified
     constraints.  If it does then ERR_OK is returned, if not then ERR_VFI is
@@ -5540,13 +5616,22 @@ verify_constraints (const struct aarch64_inst *inst,
       return res;
     }
 
+  bool is_new_section = (!encoding && pc == 0);
+  if (!verify_mops_pme_sequence (inst, is_new_section, mismatch_detail,
+				 insn_sequence))
+    {
+      res = ERR_VFI;
+      if ((opcode->constraints & C_SCAN_MOPS_PME) != C_SCAN_MOPS_M)
+	init_insn_sequence (NULL, insn_sequence);
+    }
+
   /* Verify constraints on an existing sequence.  */
   if (insn_sequence->instr)
     {
       const struct aarch64_opcode* inst_opcode = insn_sequence->instr->opcode;
       /* If we're decoding and we hit PC=0 with an open sequence then we haven't
 	 closed a previous one that we should have.  */
-      if (!encoding && pc == 0)
+      if (is_new_section && res == ERR_OK)
 	{
 	  mismatch_detail->kind = AARCH64_OPDE_SYNTAX_ERROR;
 	  mismatch_detail->error = _("previous `movprfx' sequence not closed");
diff --git a/opcodes/aarch64-tbl.h b/opcodes/aarch64-tbl.h
index 504f6ac914c..67a24d21e02 100644
--- a/opcodes/aarch64-tbl.h
+++ b/opcodes/aarch64-tbl.h
@@ -2687,10 +2687,15 @@ static const aarch64_feature_set aarch64_feature_mops_memtag =
 	     OP3 (MOPS_ADDR_Rd, MOPS_ADDR_Rs, MOPS_WB_Rn), QL_I3SAMEX, \
 	     FLAGS, CONSTRAINTS, VERIFIER (three_different_regs))
 
+/* These instructions must remain consecutive, since we rely on the order
+   when detecting invalid sequences.  */
 #define MOPS_CPY_OP1_OP2_INSN(NAME, SUFFIX, OPCODE, MASK) \
-  MOPS_CPY_OP1_OP2_PME_INSN (NAME "p" SUFFIX, OPCODE, MASK, F_SCAN, 0), \
-  MOPS_CPY_OP1_OP2_PME_INSN (NAME "m" SUFFIX, OPCODE | 0x400000, MASK, 0, 0), \
-  MOPS_CPY_OP1_OP2_PME_INSN (NAME "e" SUFFIX, OPCODE | 0x800000, MASK, 0, 0)
+  MOPS_CPY_OP1_OP2_PME_INSN (NAME "p" SUFFIX, OPCODE, MASK, F_SCAN, \
+			     C_SCAN_MOPS_P), \
+  MOPS_CPY_OP1_OP2_PME_INSN (NAME "m" SUFFIX, OPCODE | 0x400000, MASK, \
+			     0, C_SCAN_MOPS_M), \
+  MOPS_CPY_OP1_OP2_PME_INSN (NAME "e" SUFFIX, OPCODE | 0x800000, MASK, \
+			     0, C_SCAN_MOPS_E)
 
 #define MOPS_CPY_OP1_INSN(NAME, SUFFIX, OPCODE, MASK) \
   MOPS_CPY_OP1_OP2_INSN (NAME, SUFFIX, OPCODE, MASK), \
@@ -2709,12 +2714,15 @@ static const aarch64_feature_set aarch64_feature_mops_memtag =
        OP3 (MOPS_ADDR_Rd, MOPS_WB_Rn, Rm), QL_I3SAMEX, FLAGS, \
        CONSTRAINTS, VERIFIER (three_different_regs))
 
+/* These instructions must remain consecutive, since we rely on the order
+   when detecting invalid sequences.  */
 #define MOPS_SET_OP1_OP2_INSN(NAME, SUFFIX, OPCODE, MASK, ISA) \
-  MOPS_SET_OP1_OP2_PME_INSN (NAME "p" SUFFIX, OPCODE, MASK, 0, 0, ISA), \
+  MOPS_SET_OP1_OP2_PME_INSN (NAME "p" SUFFIX, OPCODE, MASK, \
+			     F_SCAN, C_SCAN_MOPS_P, ISA), \
   MOPS_SET_OP1_OP2_PME_INSN (NAME "m" SUFFIX, OPCODE | 0x4000, MASK, \
-			     0, 0, ISA), \
+			     0, C_SCAN_MOPS_M, ISA), \
   MOPS_SET_OP1_OP2_PME_INSN (NAME "e" SUFFIX, OPCODE | 0x8000, MASK, \
-			     0, 0, ISA)
+			     0, C_SCAN_MOPS_E, ISA)
 
 #define MOPS_SET_INSN(NAME, OPCODE, MASK, ISA) \
   MOPS_SET_OP1_OP2_INSN (NAME, "", OPCODE, MASK, ISA), \
-- 
2.25.1



More information about the Binutils mailing list