diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c index b28c05fbd82..14687290720 100644 --- a/gas/config/tc-aarch64.c +++ b/gas/config/tc-aarch64.c @@ -6521,6 +6521,7 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_Rt_LS64: case AARCH64_OPND_Rt_SYS: case AARCH64_OPND_PAIRREG: + case AARCH64_OPND_PAIRREG_OR_XZR: case AARCH64_OPND_SVE_Rm: po_int_fp_reg_or_fail (REG_TYPE_R_ZR); diff --git a/include/opcode/aarch64.h b/include/opcode/aarch64.h index 554e4e14aec..187a4287898 100644 --- a/include/opcode/aarch64.h +++ b/include/opcode/aarch64.h @@ -455,6 +455,7 @@ enum aarch64_opnd AARCH64_OPND_Rn_SP, /* Integer Rn or SP. */ AARCH64_OPND_Rm_SP, /* Integer Rm or SP. */ AARCH64_OPND_PAIRREG, /* Paired register operand. */ + AARCH64_OPND_PAIRREG_OR_XZR, /* Paired register operand, optionally xzr. */ AARCH64_OPND_Rm_EXT, /* Integer Rm extended. */ AARCH64_OPND_Rm_SFT, /* Integer Rm shifted. */ diff --git a/opcodes/aarch64-dis.c b/opcodes/aarch64-dis.c index ba1f5dd887c..8f9925978dc 100644 --- a/opcodes/aarch64-dis.c +++ b/opcodes/aarch64-dis.c @@ -302,8 +302,11 @@ aarch64_ext_regno_pair (const aarch64_operand *self ATTRIBUTE_UNUSED, aarch64_op aarch64_operand_error *errors ATTRIBUTE_UNUSED) { assert (info->idx == 1 - || info->idx ==3); - info->reg.regno = inst->operands[info->idx - 1].reg.regno + 1; + || info->idx == 3); + + unsigned prev_regno = inst->operands[info->idx - 1].reg.regno; + info->reg.regno = (prev_regno == 0x1f) ? 0x1f + : prev_regno + 1; return true; } diff --git a/opcodes/aarch64-opc.c b/opcodes/aarch64-opc.c index 49b31c65cb0..a5f09370b6f 100644 --- a/opcodes/aarch64-opc.c +++ b/opcodes/aarch64-opc.c @@ -1693,8 +1693,22 @@ operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx, switch (aarch64_operands[type].op_class) { case AARCH64_OPND_CLASS_INT_REG: - /* Check pair reg constraints for cas* instructions. */ - if (type == AARCH64_OPND_PAIRREG) + /* Check for pair of xzr registers. */ + if (type == AARCH64_OPND_PAIRREG_OR_XZR + && opnds[idx - 1].reg.regno == 0x1f) + { + if (opnds[idx].reg.regno != 0x1f) + { + set_syntax_error (mismatch_detail, idx - 1, + _("second reg in pair should be xzr if first is" + " xzr")); + return 0; + } + } + /* Check pair reg constraints for instructions taking a pair of + consecutively-numbered general-purpose registers. */ + else if (type == AARCH64_OPND_PAIRREG + || type == AARCH64_OPND_PAIRREG_OR_XZR) { assert (idx == 1 || idx == 3); if (opnds[idx - 1].reg.regno % 2 != 0) @@ -3771,6 +3785,7 @@ aarch64_print_operand (char *buf, size_t size, bfd_vma pc, case AARCH64_OPND_Rt_LS64: case AARCH64_OPND_Rt_SYS: case AARCH64_OPND_PAIRREG: + case AARCH64_OPND_PAIRREG_OR_XZR: case AARCH64_OPND_SVE_Rm: case AARCH64_OPND_LSE128_Rt: case AARCH64_OPND_LSE128_Rt2: diff --git a/opcodes/aarch64-tbl.h b/opcodes/aarch64-tbl.h index 47c25d59eed..b2a8b347335 100644 --- a/opcodes/aarch64-tbl.h +++ b/opcodes/aarch64-tbl.h @@ -6174,6 +6174,8 @@ const struct aarch64_opcode aarch64_opcode_table[] = "an integer or stack pointer register") \ X(INT_REG, 0, ext_regno_pair, "PAIRREG", 0, F(), \ "the second reg of a pair") \ + X(INT_REG, 0, ext_regno_pair, "PAIRREG_OR_XZR", 0, F(), \ + "the second reg of a pair") \ Y(MODIFIED_REG, reg_extended, "Rm_EXT", 0, F(), \ "an integer register with optional extension") \ Y(MODIFIED_REG, reg_shifted, "Rm_SFT", 0, F(), \