aboutsummaryrefslogtreecommitdiff
path: root/gas
diff options
context:
space:
mode:
authorRichard Sandiford <richard.sandiford@arm.com>2021-12-02 15:00:57 +0000
committerRichard Sandiford <richard.sandiford@arm.com>2021-12-02 15:00:57 +0000
commit63eff947512b36c770c92d45e4b22cb8a18a39be (patch)
tree336e8d9a01ee6665d9ec7b8148155ee7cab992a0 /gas
parent6327658ee73502ffb55dfb6b28a20d1dde15a4dc (diff)
downloadgdb-63eff947512b36c770c92d45e4b22cb8a18a39be.zip
gdb-63eff947512b36c770c92d45e4b22cb8a18a39be.tar.gz
gdb-63eff947512b36c770c92d45e4b22cb8a18a39be.tar.bz2
aarch64: Enforce P/M/E order for MOPS instructions
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. 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.
Diffstat (limited to 'gas')
-rw-r--r--gas/config/tc-aarch64.c38
-rw-r--r--gas/testsuite/gas/aarch64/mops_invalid_2.d72
-rw-r--r--gas/testsuite/gas/aarch64/mops_invalid_2.l27
-rw-r--r--gas/testsuite/gas/aarch64/mops_invalid_2.s75
4 files changed, 202 insertions, 10 deletions
diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index 4aadf5b..ea65da5 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 0000000..f5e7228
--- /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 0000000..4f75753
--- /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 0000000..f78ca54
--- /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