diff options
author | Richard Sandiford <richard.sandiford@arm.com> | 2021-12-02 15:00:57 +0000 |
---|---|---|
committer | Richard Sandiford <richard.sandiford@arm.com> | 2021-12-02 15:00:57 +0000 |
commit | 63eff947512b36c770c92d45e4b22cb8a18a39be (patch) | |
tree | 336e8d9a01ee6665d9ec7b8148155ee7cab992a0 /opcodes/aarch64-opc.c | |
parent | 6327658ee73502ffb55dfb6b28a20d1dde15a4dc (diff) | |
download | fsf-binutils-gdb-63eff947512b36c770c92d45e4b22cb8a18a39be.zip fsf-binutils-gdb-63eff947512b36c770c92d45e4b22cb8a18a39be.tar.gz fsf-binutils-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 'opcodes/aarch64-opc.c')
-rw-r--r-- | opcodes/aarch64-opc.c | 95 |
1 files changed, 90 insertions, 5 deletions
diff --git a/opcodes/aarch64-opc.c b/opcodes/aarch64-opc.c index cfd4781..49dfe98 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"); |