From 63eff947512b36c770c92d45e4b22cb8a18a39be Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Thu, 2 Dec 2021 15:00:57 +0000 Subject: [PATCH] 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. --- 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 48d2fa8ff64..51d8532b8f2 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.30.2