summaryrefslogtreecommitdiff
path: root/opcodes
diff options
context:
space:
mode:
authorRichard Sandiford <richard.sandiford@arm.com>2023-03-30 11:09:10 +0100
committerRichard Sandiford <richard.sandiford@arm.com>2023-03-30 11:09:10 +0100
commitf5b57feac2389eba64bea45f0115474fbbb13d8e (patch)
treeafbb46bf8da4e5bc451623cf340271fae73c4a38 /opcodes
parentb5c36ad2e03bc9b8a45a8e495b690c1424cf018f (diff)
downloadbinutils-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.c5
-rw-r--r--opcodes/aarch64-opc.c74
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)
{