diff options
author | Richard Sandiford <richard.sandiford@arm.com> | 2023-03-30 11:09:10 +0100 |
---|---|---|
committer | Richard Sandiford <richard.sandiford@arm.com> | 2023-03-30 11:09:10 +0100 |
commit | f5b57feac2389eba64bea45f0115474fbbb13d8e (patch) | |
tree | afbb46bf8da4e5bc451623cf340271fae73c4a38 /opcodes | |
parent | b5c36ad2e03bc9b8a45a8e495b690c1424cf018f (diff) | |
download | binutils-gdb-f5b57feac2389eba64bea45f0115474fbbb13d8e.tar.gz |
aarch64: Add support for strided register lists
SME2 has instructions that accept strided register lists,
such as { z0.s, z4.s, z8.s, z12.s }. The purpose of this
patch is to extend binutils to support such lists.
The parsing code already had (unused) support for strides of 2.
The idea here is instead to accept all strides during parsing
and reject invalid strides during constraint checking.
The SME2 instructions that accept strided operands also have
non-strided forms. The errors about invalid strides therefore
take a bitmask of acceptable strides, which allows multiple
possibilities to be summed up in a single message.
I've tried to update all code that handles register lists.
Diffstat (limited to 'opcodes')
-rw-r--r-- | opcodes/aarch64-dis.c | 5 | ||||
-rw-r--r-- | opcodes/aarch64-opc.c | 74 |
2 files changed, 56 insertions, 23 deletions
diff --git a/opcodes/aarch64-dis.c b/opcodes/aarch64-dis.c index 05e285fac99..e722514053e 100644 --- a/opcodes/aarch64-dis.c +++ b/opcodes/aarch64-dis.c @@ -439,6 +439,7 @@ aarch64_ext_reglist (const aarch64_operand *self, aarch64_opnd_info *info, info->reglist.first_regno = extract_field (self->fields[0], code, 0); /* len */ info->reglist.num_regs = extract_field (FLD_len, code, 0) + 1; + info->reglist.stride = 1; return true; } @@ -482,6 +483,7 @@ aarch64_ext_ldst_reglist (const aarch64_operand *self ATTRIBUTE_UNUSED, if (expected_num != data[value].num_elements || data[value].is_reserved) return false; info->reglist.num_regs = data[value].num_regs; + info->reglist.stride = 1; return true; } @@ -510,6 +512,7 @@ aarch64_ext_ldst_reglist_r (const aarch64_operand *self ATTRIBUTE_UNUSED, if (info->reglist.num_regs == 1 && value == (aarch64_insn) 1) info->reglist.num_regs = 2; + info->reglist.stride = 1; return true; } @@ -573,6 +576,7 @@ aarch64_ext_ldst_elemlist (const aarch64_operand *self ATTRIBUTE_UNUSED, info->reglist.has_index = 1; info->reglist.num_regs = 0; + info->reglist.stride = 1; /* Number of registers is equal to the number of elements in each structure to be loaded/stored. */ info->reglist.num_regs = get_opcode_dependent_value (inst->opcode); @@ -1982,6 +1986,7 @@ aarch64_ext_sve_reglist (const aarch64_operand *self, { info->reglist.first_regno = extract_field (self->fields[0], code, 0); info->reglist.num_regs = get_opcode_dependent_value (inst->opcode); + info->reglist.stride = 1; return true; } diff --git a/opcodes/aarch64-opc.c b/opcodes/aarch64-opc.c index 8a9e51faebd..4e950cf70f8 100644 --- a/opcodes/aarch64-opc.c +++ b/opcodes/aarch64-opc.c @@ -1439,12 +1439,22 @@ set_unaligned_error (aarch64_operand_error *mismatch_detail, int idx, } static inline void -set_reg_list_error (aarch64_operand_error *mismatch_detail, int idx, - int expected_num) +set_reg_list_length_error (aarch64_operand_error *mismatch_detail, int idx, + int expected_num) { if (mismatch_detail == NULL) return; - set_error (mismatch_detail, AARCH64_OPDE_REG_LIST, idx, NULL); + set_error (mismatch_detail, AARCH64_OPDE_REG_LIST_LENGTH, idx, NULL); + mismatch_detail->data[0].i = 1 << expected_num; +} + +static inline void +set_reg_list_stride_error (aarch64_operand_error *mismatch_detail, int idx, + int expected_num) +{ + if (mismatch_detail == NULL) + return; + set_error (mismatch_detail, AARCH64_OPDE_REG_LIST_STRIDE, idx, NULL); mismatch_detail->data[0].i = 1 << expected_num; } @@ -1482,6 +1492,27 @@ check_reglane (const aarch64_opnd_info *opnd, return true; } +/* Check that register list operand OPND has NUM_REGS registers and a + register stride of STRIDE. */ + +static bool +check_reglist (const aarch64_opnd_info *opnd, + aarch64_operand_error *mismatch_detail, int idx, + int num_regs, int stride) +{ + if (opnd->reglist.num_regs != num_regs) + { + set_reg_list_length_error (mismatch_detail, idx, num_regs); + return false; + } + if (opnd->reglist.stride != stride) + { + set_reg_list_stride_error (mismatch_detail, idx, stride); + return false; + } + return true; +} + /* Check that indexed ZA operand OPND has: - a selection register in the range [MIN_WREG, MIN_WREG + 3] @@ -1637,11 +1668,8 @@ operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx, case AARCH64_OPND_CLASS_SVE_REGLIST: num = get_opcode_dependent_value (opcode); - if (opnd->reglist.num_regs != num) - { - set_reg_list_error (mismatch_detail, idx, num); - return 0; - } + if (!check_reglist (opnd, mismatch_detail, idx, num, 1)) + return 0; break; case AARCH64_OPND_CLASS_ZA_ACCESS: @@ -2123,26 +2151,25 @@ operand_general_constraint_met_p (const aarch64_opnd_info *opnds, int idx, assert (num >= 1 && num <= 4); /* Unless LD1/ST1, the number of registers should be equal to that of the structure elements. */ - if (num != 1 && opnd->reglist.num_regs != num) - { - set_reg_list_error (mismatch_detail, idx, num); - return 0; - } + if (num != 1 && !check_reglist (opnd, mismatch_detail, idx, num, 1)) + return 0; break; case AARCH64_OPND_LVt_AL: case AARCH64_OPND_LEt: assert (num >= 1 && num <= 4); /* The number of registers should be equal to that of the structure elements. */ - if (opnd->reglist.num_regs != num) - { - set_reg_list_error (mismatch_detail, idx, num); - return 0; - } + if (!check_reglist (opnd, mismatch_detail, idx, num, 1)) + return 0; break; default: break; } + if (opnd->reglist.stride != 1) + { + set_reg_list_stride_error (mismatch_detail, idx, 1); + return 0; + } break; case AARCH64_OPND_CLASS_IMMEDIATE: @@ -3199,8 +3226,9 @@ print_register_list (char *buf, size_t size, const aarch64_opnd_info *opnd, const char *prefix, struct aarch64_styler *styler) { const int num_regs = opnd->reglist.num_regs; + const int stride = opnd->reglist.stride; const int first_reg = opnd->reglist.first_regno; - const int last_reg = (first_reg + num_regs - 1) & 0x1f; + const int last_reg = (first_reg + (num_regs - 1) * stride) & 0x1f; const char *qlf_name = aarch64_get_qualifier_name (opnd->qualifier); char tb[16]; /* Temporary buffer. */ @@ -3218,16 +3246,16 @@ print_register_list (char *buf, size_t size, const aarch64_opnd_info *opnd, /* The hyphenated form is preferred for disassembly if there are more than two registers in the list, and the register numbers are monotonically increasing in increments of one. */ - if (num_regs > 2 && last_reg > first_reg) + if (stride == 1 && num_regs > 2 && last_reg > first_reg) snprintf (buf, size, "{%s-%s}%s", style_reg (styler, "%s%d.%s", prefix, first_reg, qlf_name), style_reg (styler, "%s%d.%s", prefix, last_reg, qlf_name), tb); else { const int reg0 = first_reg; - const int reg1 = (first_reg + 1) & 0x1f; - const int reg2 = (first_reg + 2) & 0x1f; - const int reg3 = (first_reg + 3) & 0x1f; + const int reg1 = (first_reg + stride) & 0x1f; + const int reg2 = (first_reg + stride * 2) & 0x1f; + const int reg3 = (first_reg + stride * 3) & 0x1f; switch (num_regs) { |