summaryrefslogtreecommitdiff
path: root/opcodes/aarch64-opc.c
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 /opcodes/aarch64-opc.c
parent6327658ee73502ffb55dfb6b28a20d1dde15a4dc (diff)
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 'opcodes/aarch64-opc.c')
-rw-r--r--opcodes/aarch64-opc.c95
1 files changed, 90 insertions, 5 deletions
diff --git a/opcodes/aarch64-opc.c b/opcodes/aarch64-opc.c
index cfd4781268..49dfe98769 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");