/* aarch64-dis.c -- AArch64 disassembler.
- Copyright 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
+ Copyright (C) 2009-2016 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of the GNU opcodes library.
N.B. the fields are required to be in such an order than the most signficant
field for VALUE comes the first, e.g. the <index> in
SQDMLAL <Va><d>, <Vb><n>, <Vm>.<Ts>[<index>]
- is encoded in H:L:M in some cases, the the fields H:L:M should be passed in
+ is encoded in H:L:M in some cases, the fields H:L:M should be passed in
the order of H, L, M. */
-static inline aarch64_insn
+aarch64_insn
extract_fields (aarch64_insn code, aarch64_insn mask, ...)
{
uint32_t num;
return value;
}
+/* Extract the value of all fields in SELF->fields from instruction CODE.
+ The least significant bit comes from the final field. */
+
+static aarch64_insn
+extract_all_fields (const aarch64_operand *self, aarch64_insn code)
+{
+ aarch64_insn value;
+ unsigned int i;
+ enum aarch64_field_kind kind;
+
+ value = 0;
+ for (i = 0; i < ARRAY_SIZE (self->fields) && self->fields[i] != FLD_NIL; ++i)
+ {
+ kind = self->fields[i];
+ value <<= fields[kind].width;
+ value |= extract_field (kind, code, 0);
+ }
+ return value;
+}
+
/* Sign-extend bit I of VALUE. */
static inline int32_t
sign_extend (aarch64_insn value, unsigned i)
return qualifier;
}
-/* Given VALUE, return qualifier for a vector register. */
+/* Given VALUE, return qualifier for a vector register. This does not support
+ decoding instructions that accept the 2H vector type. */
+
static inline enum aarch64_opnd_qualifier
get_vreg_qualifier_from_value (aarch64_insn value)
{
enum aarch64_opnd_qualifier qualifier = AARCH64_OPND_QLF_V_8B + value;
+ /* Instructions using vector type 2H should not call this function. Skip over
+ the 2H qualifier. */
+ if (qualifier >= AARCH64_OPND_QLF_V_2H)
+ qualifier += 1;
+
assert (value <= 0x8
&& aarch64_get_qualifier_standard_value (qualifier) == value);
return qualifier;
return 1;
}
+int
+aarch64_ext_regno_pair (const aarch64_operand *self ATTRIBUTE_UNUSED, aarch64_opnd_info *info,
+ const aarch64_insn code ATTRIBUTE_UNUSED,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ assert (info->idx == 1
+ || info->idx ==3);
+ info->reg.regno = inst->operands[info->idx - 1].reg.regno + 1;
+ return 1;
+}
+
/* e.g. IC <ic_op>{, <Xt>}. */
int
aarch64_ext_regrt_sysins (const aarch64_operand *self, aarch64_opnd_info *info,
/* This will make the constraint checking happy and more importantly will
help the disassembler determine whether this operand is optional or
not. */
- info->present = inst->operands[0].sysins_op->has_xt;
+ info->present = aarch64_sys_ins_reg_has_xt (inst->operands[0].sysins_op);
return 1;
}
info->reglist.index = QSsize;
break;
case 0x1:
+ if (QSsize & 0x1)
+ /* UND. */
+ return 0;
info->qualifier = AARCH64_OPND_QLF_S_H;
/* Index encoded in "Q:S:size<1>". */
info->reglist.index = QSsize >> 1;
break;
case 0x2:
+ if ((QSsize >> 1) & 0x1)
+ /* UND. */
+ return 0;
if ((QSsize & 0x1) == 0)
{
info->qualifier = AARCH64_OPND_QLF_S_S;
}
else
{
- info->qualifier = AARCH64_OPND_QLF_S_D;
- /* Index encoded in "Q". */
- info->reglist.index = QSsize >> 3;
if (extract_field (FLD_S, code, 0))
/* UND */
return 0;
+ info->qualifier = AARCH64_OPND_QLF_S_D;
+ /* Index encoded in "Q". */
+ info->reglist.index = QSsize >> 3;
}
break;
default:
const aarch64_inst *inst ATTRIBUTE_UNUSED)
{
int64_t imm;
- /* Maximum of two fields to extract. */
- assert (self->fields[2] == FLD_NIL);
- if (self->fields[1] == FLD_NIL)
- imm = extract_field (self->fields[0], code, 0);
- else
- /* e.g. TBZ b5:b40. */
- imm = extract_fields (code, 0, 2, self->fields[0], self->fields[1]);
-
- if (info->type == AARCH64_OPND_FPIMM)
- info->imm.is_fp = 1;
+ imm = extract_all_fields (self, code);
if (operand_need_sign_extension (self))
imm = sign_extend (imm, get_operand_fields_width (self) - 1);
return 1;
}
+/* Decode an 8-bit floating-point immediate. */
+int
+aarch64_ext_fpimm (const aarch64_operand *self, aarch64_opnd_info *info,
+ const aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ info->imm.value = extract_all_fields (self, code);
+ info->imm.is_fp = 1;
+ return 1;
+}
+
/* Decode scale for e.g. SCVTF <Dd>, <Wn>, #<fbits>. */
int
aarch64_ext_fbits (const aarch64_operand *self ATTRIBUTE_UNUSED,
return 1;
}
-/* Decode logical immediate for e.g. ORR <Wd|WSP>, <Wn>, #<imm>. */
-
-int
-aarch64_ext_limm (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+/* Return true if VALUE is a valid logical immediate encoding, storing the
+ decoded value in *RESULT if so. ESIZE is the number of bytes in the
+ decoded immediate. */
+static int
+decode_limm (uint32_t esize, aarch64_insn value, int64_t *result)
{
uint64_t imm, mask;
- uint32_t sf;
uint32_t N, R, S;
unsigned simd_size;
- aarch64_insn value;
-
- value = extract_fields (code, 0, 3, FLD_N, FLD_immr, FLD_imms);
- assert (inst->operands[0].qualifier == AARCH64_OPND_QLF_W
- || inst->operands[0].qualifier == AARCH64_OPND_QLF_X);
- sf = aarch64_get_qualifier_esize (inst->operands[0].qualifier) != 4;
/* value is N:immr:imms. */
S = value & 0x3f;
R = (value >> 6) & 0x3f;
N = (value >> 12) & 0x1;
- if (sf == 0 && N == 1)
- return 0;
-
/* The immediate value is S+1 bits to 1, left rotated by SIMDsize - R
(in other words, right rotated by R), then replicated. */
if (N != 0)
/* Top bits are IGNORED. */
R &= simd_size - 1;
}
+
+ if (simd_size > esize * 8)
+ return 0;
+
/* NOTE: if S = simd_size - 1 we get 0xf..f which is rejected. */
if (S == simd_size - 1)
return 0;
switch (simd_size)
{
case 2: imm = (imm << 2) | imm;
+ /* Fall through. */
case 4: imm = (imm << 4) | imm;
+ /* Fall through. */
case 8: imm = (imm << 8) | imm;
+ /* Fall through. */
case 16: imm = (imm << 16) | imm;
+ /* Fall through. */
case 32: imm = (imm << 32) | imm;
+ /* Fall through. */
case 64: break;
default: assert (0); return 0;
}
- info->imm.value = sf ? imm : imm & 0xffffffff;
+ *result = imm & ~((uint64_t) -1 << (esize * 4) << (esize * 4));
+
+ return 1;
+}
+
+/* Decode a logical immediate for e.g. ORR <Wd|WSP>, <Wn>, #<imm>. */
+int
+aarch64_ext_limm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst)
+{
+ uint32_t esize;
+ aarch64_insn value;
+
+ value = extract_fields (code, 0, 3, self->fields[0], self->fields[1],
+ self->fields[2]);
+ esize = aarch64_get_qualifier_esize (inst->operands[0].qualifier);
+ return decode_limm (esize, value, &info->imm.value);
+}
+/* Decode a logical immediate for the BIC alias of AND (etc.). */
+int
+aarch64_ext_inv_limm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst)
+{
+ if (!aarch64_ext_limm (self, info, code, inst))
+ return 0;
+ info->imm.value = ~info->imm.value;
return 1;
}
default: assert (0); return 0;
}
- for (i = 0; sysins_ops[i].template != NULL; ++i)
+ for (i = 0; sysins_ops[i].name != NULL; ++i)
if (sysins_ops[i].value == value)
{
info->sysins_op = sysins_ops + i;
DEBUG_TRACE ("%s found value: %x, has_xt: %d, i: %d.",
- info->sysins_op->template,
+ info->sysins_op->name,
(unsigned)info->sysins_op->value,
- info->sysins_op->has_xt, i);
+ aarch64_sys_ins_reg_has_xt (info->sysins_op), i);
return 1;
}
return 1;
}
+/* Decode the hint number for an alias taking an operand. Set info->hint_option
+ to the matching name/value pair in aarch64_hint_options. */
+
+int
+aarch64_ext_hint (const aarch64_operand *self ATTRIBUTE_UNUSED,
+ aarch64_opnd_info *info,
+ aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ /* CRm:op2. */
+ unsigned hint_number;
+ int i;
+
+ hint_number = extract_fields (code, 0, 2, FLD_CRm, FLD_op2);
+
+ for (i = 0; aarch64_hint_options[i].name != NULL; i++)
+ {
+ if (hint_number == aarch64_hint_options[i].value)
+ {
+ info->hint_option = &(aarch64_hint_options[i]);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/* Decode the extended register operand for e.g.
STR <Qt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}]. */
int
return 1;
}
+
+/* Decode an SVE address [<base>, #<offset>*<factor>, MUL VL],
+ where <offset> is given by the OFFSET parameter and where <factor> is
+ 1 plus SELF's operand-dependent value. fields[0] specifies the field
+ that holds <base>. */
+static int
+aarch64_ext_sve_addr_reg_mul_vl (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ int64_t offset)
+{
+ info->addr.base_regno = extract_field (self->fields[0], code, 0);
+ info->addr.offset.imm = offset * (1 + get_operand_specific_data (self));
+ info->addr.offset.is_reg = FALSE;
+ info->addr.writeback = FALSE;
+ info->addr.preind = TRUE;
+ if (offset != 0)
+ info->shifter.kind = AARCH64_MOD_MUL_VL;
+ info->shifter.amount = 1;
+ info->shifter.operator_present = (info->addr.offset.imm != 0);
+ info->shifter.amount_present = FALSE;
+ return 1;
+}
+
+/* Decode an SVE address [<base>, #<simm4>*<factor>, MUL VL],
+ where <simm4> is a 4-bit signed value and where <factor> is 1 plus
+ SELF's operand-dependent value. fields[0] specifies the field that
+ holds <base>. <simm4> is encoded in the SVE_imm4 field. */
+int
+aarch64_ext_sve_addr_ri_s4xvl (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ int offset;
+
+ offset = extract_field (FLD_SVE_imm4, code, 0);
+ offset = ((offset + 8) & 15) - 8;
+ return aarch64_ext_sve_addr_reg_mul_vl (self, info, code, offset);
+}
+
+/* Decode an SVE address [<base>, #<simm6>*<factor>, MUL VL],
+ where <simm6> is a 6-bit signed value and where <factor> is 1 plus
+ SELF's operand-dependent value. fields[0] specifies the field that
+ holds <base>. <simm6> is encoded in the SVE_imm6 field. */
+int
+aarch64_ext_sve_addr_ri_s6xvl (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ int offset;
+
+ offset = extract_field (FLD_SVE_imm6, code, 0);
+ offset = (((offset + 32) & 63) - 32);
+ return aarch64_ext_sve_addr_reg_mul_vl (self, info, code, offset);
+}
+
+/* Decode an SVE address [<base>, #<simm9>*<factor>, MUL VL],
+ where <simm9> is a 9-bit signed value and where <factor> is 1 plus
+ SELF's operand-dependent value. fields[0] specifies the field that
+ holds <base>. <simm9> is encoded in the concatenation of the SVE_imm6
+ and imm3 fields, with imm3 being the less-significant part. */
+int
+aarch64_ext_sve_addr_ri_s9xvl (const aarch64_operand *self,
+ aarch64_opnd_info *info,
+ aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ int offset;
+
+ offset = extract_fields (code, 0, 2, FLD_SVE_imm6, FLD_imm3);
+ offset = (((offset + 256) & 511) - 256);
+ return aarch64_ext_sve_addr_reg_mul_vl (self, info, code, offset);
+}
+
+/* Decode an SVE address [<base>, #<offset> << <shift>], where <offset>
+ is given by the OFFSET parameter and where <shift> is SELF's operand-
+ dependent value. fields[0] specifies the base register field <base>. */
+static int
+aarch64_ext_sve_addr_reg_imm (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ int64_t offset)
+{
+ info->addr.base_regno = extract_field (self->fields[0], code, 0);
+ info->addr.offset.imm = offset * (1 << get_operand_specific_data (self));
+ info->addr.offset.is_reg = FALSE;
+ info->addr.writeback = FALSE;
+ info->addr.preind = TRUE;
+ info->shifter.operator_present = FALSE;
+ info->shifter.amount_present = FALSE;
+ return 1;
+}
+
+/* Decode an SVE address [X<n>, #<SVE_imm6> << <shift>], where <SVE_imm6>
+ is a 6-bit unsigned number and where <shift> is SELF's operand-dependent
+ value. fields[0] specifies the base register field. */
+int
+aarch64_ext_sve_addr_ri_u6 (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ int offset = extract_field (FLD_SVE_imm6, code, 0);
+ return aarch64_ext_sve_addr_reg_imm (self, info, code, offset);
+}
+
+/* Decode an SVE address [X<n>, X<m>{, LSL #<shift>}], where <shift>
+ is SELF's operand-dependent value. fields[0] specifies the base
+ register field and fields[1] specifies the offset register field. */
+int
+aarch64_ext_sve_addr_rr_lsl (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ int index;
+
+ index = extract_field (self->fields[1], code, 0);
+ if (index == 31 && (self->flags & OPD_F_NO_ZR) != 0)
+ return 0;
+
+ info->addr.base_regno = extract_field (self->fields[0], code, 0);
+ info->addr.offset.regno = index;
+ info->addr.offset.is_reg = TRUE;
+ info->addr.writeback = FALSE;
+ info->addr.preind = TRUE;
+ info->shifter.kind = AARCH64_MOD_LSL;
+ info->shifter.amount = get_operand_specific_data (self);
+ info->shifter.operator_present = (info->shifter.amount != 0);
+ info->shifter.amount_present = (info->shifter.amount != 0);
+ return 1;
+}
+
+/* Decode an SVE address [X<n>, Z<m>.<T>, (S|U)XTW {#<shift>}], where
+ <shift> is SELF's operand-dependent value. fields[0] specifies the
+ base register field, fields[1] specifies the offset register field and
+ fields[2] is a single-bit field that selects SXTW over UXTW. */
+int
+aarch64_ext_sve_addr_rz_xtw (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ info->addr.base_regno = extract_field (self->fields[0], code, 0);
+ info->addr.offset.regno = extract_field (self->fields[1], code, 0);
+ info->addr.offset.is_reg = TRUE;
+ info->addr.writeback = FALSE;
+ info->addr.preind = TRUE;
+ if (extract_field (self->fields[2], code, 0))
+ info->shifter.kind = AARCH64_MOD_SXTW;
+ else
+ info->shifter.kind = AARCH64_MOD_UXTW;
+ info->shifter.amount = get_operand_specific_data (self);
+ info->shifter.operator_present = TRUE;
+ info->shifter.amount_present = (info->shifter.amount != 0);
+ return 1;
+}
+
+/* Decode an SVE address [Z<n>.<T>, #<imm5> << <shift>], where <imm5> is a
+ 5-bit unsigned number and where <shift> is SELF's operand-dependent value.
+ fields[0] specifies the base register field. */
+int
+aarch64_ext_sve_addr_zi_u5 (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ int offset = extract_field (FLD_imm5, code, 0);
+ return aarch64_ext_sve_addr_reg_imm (self, info, code, offset);
+}
+
+/* Decode an SVE address [Z<n>.<T>, Z<m>.<T>{, <modifier> {#<msz>}}],
+ where <modifier> is given by KIND and where <msz> is a 2-bit unsigned
+ number. fields[0] specifies the base register field and fields[1]
+ specifies the offset register field. */
+static int
+aarch64_ext_sve_addr_zz (const aarch64_operand *self, aarch64_opnd_info *info,
+ aarch64_insn code, enum aarch64_modifier_kind kind)
+{
+ info->addr.base_regno = extract_field (self->fields[0], code, 0);
+ info->addr.offset.regno = extract_field (self->fields[1], code, 0);
+ info->addr.offset.is_reg = TRUE;
+ info->addr.writeback = FALSE;
+ info->addr.preind = TRUE;
+ info->shifter.kind = kind;
+ info->shifter.amount = extract_field (FLD_SVE_msz, code, 0);
+ info->shifter.operator_present = (kind != AARCH64_MOD_LSL
+ || info->shifter.amount != 0);
+ info->shifter.amount_present = (info->shifter.amount != 0);
+ return 1;
+}
+
+/* Decode an SVE address [Z<n>.<T>, Z<m>.<T>{, LSL #<msz>}], where
+ <msz> is a 2-bit unsigned number. fields[0] specifies the base register
+ field and fields[1] specifies the offset register field. */
+int
+aarch64_ext_sve_addr_zz_lsl (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ return aarch64_ext_sve_addr_zz (self, info, code, AARCH64_MOD_LSL);
+}
+
+/* Decode an SVE address [Z<n>.<T>, Z<m>.<T>, SXTW {#<msz>}], where
+ <msz> is a 2-bit unsigned number. fields[0] specifies the base register
+ field and fields[1] specifies the offset register field. */
+int
+aarch64_ext_sve_addr_zz_sxtw (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ return aarch64_ext_sve_addr_zz (self, info, code, AARCH64_MOD_SXTW);
+}
+
+/* Decode an SVE address [Z<n>.<T>, Z<m>.<T>, UXTW {#<msz>}], where
+ <msz> is a 2-bit unsigned number. fields[0] specifies the base register
+ field and fields[1] specifies the offset register field. */
+int
+aarch64_ext_sve_addr_zz_uxtw (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ return aarch64_ext_sve_addr_zz (self, info, code, AARCH64_MOD_UXTW);
+}
+
+/* Finish decoding an SVE arithmetic immediate, given that INFO already
+ has the raw field value and that the low 8 bits decode to VALUE. */
+static int
+decode_sve_aimm (aarch64_opnd_info *info, int64_t value)
+{
+ info->shifter.kind = AARCH64_MOD_LSL;
+ info->shifter.amount = 0;
+ if (info->imm.value & 0x100)
+ {
+ if (value == 0)
+ /* Decode 0x100 as #0, LSL #8. */
+ info->shifter.amount = 8;
+ else
+ value *= 256;
+ }
+ info->shifter.operator_present = (info->shifter.amount != 0);
+ info->shifter.amount_present = (info->shifter.amount != 0);
+ info->imm.value = value;
+ return 1;
+}
+
+/* Decode an SVE ADD/SUB immediate. */
+int
+aarch64_ext_sve_aimm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst)
+{
+ return (aarch64_ext_imm (self, info, code, inst)
+ && decode_sve_aimm (info, (uint8_t) info->imm.value));
+}
+
+/* Decode an SVE CPY/DUP immediate. */
+int
+aarch64_ext_sve_asimm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst)
+{
+ return (aarch64_ext_imm (self, info, code, inst)
+ && decode_sve_aimm (info, (int8_t) info->imm.value));
+}
+
+/* Decode a single-bit immediate that selects between #0.5 and #1.0.
+ The fields array specifies which field to use. */
+int
+aarch64_ext_sve_float_half_one (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ if (extract_field (self->fields[0], code, 0))
+ info->imm.value = 0x3f800000;
+ else
+ info->imm.value = 0x3f000000;
+ info->imm.is_fp = TRUE;
+ return 1;
+}
+
+/* Decode a single-bit immediate that selects between #0.5 and #2.0.
+ The fields array specifies which field to use. */
+int
+aarch64_ext_sve_float_half_two (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ if (extract_field (self->fields[0], code, 0))
+ info->imm.value = 0x40000000;
+ else
+ info->imm.value = 0x3f000000;
+ info->imm.is_fp = TRUE;
+ return 1;
+}
+
+/* Decode a single-bit immediate that selects between #0.0 and #1.0.
+ The fields array specifies which field to use. */
+int
+aarch64_ext_sve_float_zero_one (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ if (extract_field (self->fields[0], code, 0))
+ info->imm.value = 0x3f800000;
+ else
+ info->imm.value = 0x0;
+ info->imm.is_fp = TRUE;
+ return 1;
+}
+
+/* Decode Zn[MM], where MM has a 7-bit triangular encoding. The fields
+ array specifies which field to use for Zn. MM is encoded in the
+ concatenation of imm5 and SVE_tszh, with imm5 being the less
+ significant part. */
+int
+aarch64_ext_sve_index (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ int val;
+
+ info->reglane.regno = extract_field (self->fields[0], code, 0);
+ val = extract_fields (code, 0, 2, FLD_SVE_tszh, FLD_imm5);
+ if ((val & 15) == 0)
+ return 0;
+ while ((val & 1) == 0)
+ val /= 2;
+ info->reglane.index = val / 2;
+ return 1;
+}
+
+/* Decode a logical immediate for the MOV alias of SVE DUPM. */
+int
+aarch64_ext_sve_limm_mov (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst)
+{
+ int esize = aarch64_get_qualifier_esize (inst->operands[0].qualifier);
+ return (aarch64_ext_limm (self, info, code, inst)
+ && aarch64_sve_dupm_mov_immediate_p (info->imm.value, esize));
+}
+
+/* Decode {Zn.<T> - Zm.<T>}. The fields array specifies which field
+ to use for Zn. The opcode-dependent value specifies the number
+ of registers in the list. */
+int
+aarch64_ext_sve_reglist (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED)
+{
+ info->reglist.first_regno = extract_field (self->fields[0], code, 0);
+ info->reglist.num_regs = get_opcode_dependent_value (inst->opcode);
+ return 1;
+}
+
+/* Decode <pattern>{, MUL #<amount>}. The fields array specifies which
+ fields to use for <pattern>. <amount> - 1 is encoded in the SVE_imm4
+ field. */
+int
+aarch64_ext_sve_scale (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst)
+{
+ int val;
+
+ if (!aarch64_ext_imm (self, info, code, inst))
+ return 0;
+ val = extract_field (FLD_SVE_imm4, code, 0);
+ info->shifter.kind = AARCH64_MOD_MUL;
+ info->shifter.amount = val + 1;
+ info->shifter.operator_present = (val != 0);
+ info->shifter.amount_present = (val != 0);
+ return 1;
+}
+
+/* Return the top set bit in VALUE, which is expected to be relatively
+ small. */
+static uint64_t
+get_top_bit (uint64_t value)
+{
+ while ((value & -value) != value)
+ value -= value & -value;
+ return value;
+}
+
+/* Decode an SVE shift-left immediate. */
+int
+aarch64_ext_sve_shlimm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst)
+{
+ if (!aarch64_ext_imm (self, info, code, inst)
+ || info->imm.value == 0)
+ return 0;
+
+ info->imm.value -= get_top_bit (info->imm.value);
+ return 1;
+}
+
+/* Decode an SVE shift-right immediate. */
+int
+aarch64_ext_sve_shrimm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst)
+{
+ if (!aarch64_ext_imm (self, info, code, inst)
+ || info->imm.value == 0)
+ return 0;
+
+ info->imm.value = get_top_bit (info->imm.value) * 2 - info->imm.value;
+ return 1;
+}
\f
/* Bitfields that are commonly used to encode certain operands' information
may be partially used as part of the base opcode in some instructions.
static int
do_misc_decoding (aarch64_inst *inst)
{
+ unsigned int value;
switch (inst->opcode->op)
{
case OP_FCVT:
return decode_fcvt (inst);
+
case OP_FCVTN:
case OP_FCVTN2:
case OP_FCVTL:
case OP_FCVTL2:
return decode_asimd_fcvt (inst);
+
case OP_FCVTXN_S:
return decode_asisd_fcvtxn (inst);
+
+ case OP_MOV_P_P:
+ case OP_MOVS_P_P:
+ value = extract_field (FLD_SVE_Pn, inst->value, 0);
+ return (value == extract_field (FLD_SVE_Pm, inst->value, 0)
+ && value == extract_field (FLD_SVE_Pg4_10, inst->value, 0));
+
+ case OP_MOV_Z_P_Z:
+ return (extract_field (FLD_SVE_Zd, inst->value, 0)
+ == extract_field (FLD_SVE_Zm_16, inst->value, 0));
+
+ case OP_MOV_Z_V:
+ /* Index must be zero. */
+ value = extract_fields (inst->value, 0, 2, FLD_SVE_tszh, FLD_imm5);
+ return value == 1 || value == 2 || value == 4 || value == 8;
+
+ case OP_MOV_Z_Z:
+ return (extract_field (FLD_SVE_Zn, inst->value, 0)
+ == extract_field (FLD_SVE_Zm_16, inst->value, 0));
+
+ case OP_MOV_Z_Zi:
+ /* Index must be nonzero. */
+ value = extract_fields (inst->value, 0, 2, FLD_SVE_tszh, FLD_imm5);
+ return value != 1 && value != 2 && value != 4 && value != 8;
+
+ case OP_MOVM_P_P_P:
+ return (extract_field (FLD_SVE_Pd, inst->value, 0)
+ == extract_field (FLD_SVE_Pm, inst->value, 0));
+
+ case OP_MOVZS_P_P_P:
+ case OP_MOVZ_P_P_P:
+ return (extract_field (FLD_SVE_Pn, inst->value, 0)
+ == extract_field (FLD_SVE_Pm, inst->value, 0));
+
+ case OP_NOTS_P_P_P_Z:
+ case OP_NOT_P_P_P_Z:
+ return (extract_field (FLD_SVE_Pm, inst->value, 0)
+ == extract_field (FLD_SVE_Pg4_10, inst->value, 0));
+
default:
return 0;
}
&& extract_field (FLD_N, inst->value, 0) != value)
return 0;
}
+ /* 'sf' field. */
+ if (inst->opcode->flags & F_LSE_SZ)
+ {
+ idx = select_operand_for_sf_field_coding (inst->opcode);
+ value = extract_field (FLD_lse_sz, inst->value, 0);
+ inst->operands[idx].qualifier = get_greg_qualifier_from_value (value);
+ }
/* size:Q fields. */
if (inst->opcode->flags & F_SIZEQ)
return decode_sizeq (inst);
}
/* The instruction written:
+ BFC <Xd>, #<lsb>, #<width>
+ is equivalent to:
+ BFM <Xd>, XZR, #((64-<lsb>)&0x3f), #(<width>-1). */
+
+static int
+convert_bfm_to_bfc (aarch64_inst *inst)
+{
+ int64_t immr, imms, val;
+
+ /* Should have been assured by the base opcode value. */
+ assert (inst->operands[1].reg.regno == 0x1f);
+
+ immr = inst->operands[2].imm.value;
+ imms = inst->operands[3].imm.value;
+ val = inst->operands[2].qualifier == AARCH64_OPND_QLF_imm_0_31 ? 32 : 64;
+ if (imms < immr)
+ {
+ /* Drop XZR from the second operand. */
+ copy_operand_info (inst, 1, 2);
+ copy_operand_info (inst, 2, 3);
+ inst->operands[3].type = AARCH64_OPND_NIL;
+
+ /* Recalculate the immediates. */
+ inst->operands[1].imm.value = (val - immr) & (val - 1);
+ inst->operands[2].imm.value = imms + 1;
+
+ /* The two opcodes have different qualifiers for the operands; reset to
+ help the checking. */
+ reset_operand_qualifier (inst, 1);
+ reset_operand_qualifier (inst, 2);
+ reset_operand_qualifier (inst, 3);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* The instruction written:
LSL <Xd>, <Xn>, #<shift>
is equivalent to:
UBFM <Xd>, <Xn>, #((64-<shift>)&0x3f), #(63-<shift>). */
/* CINC <Wd>, <Wn>, <cond>
is equivalent to:
- CSINC <Wd>, <Wn>, <Wn>, invert(<cond>). */
+ CSINC <Wd>, <Wn>, <Wn>, invert(<cond>)
+ where <cond> is not AL or NV. */
static int
convert_from_csel (aarch64_inst *inst)
{
- if (inst->operands[1].reg.regno == inst->operands[2].reg.regno)
+ if (inst->operands[1].reg.regno == inst->operands[2].reg.regno
+ && (inst->operands[3].cond->value & 0xe) != 0xe)
{
copy_operand_info (inst, 2, 3);
inst->operands[2].cond = get_inverted_cond (inst->operands[3].cond);
/* CSET <Wd>, <cond>
is equivalent to:
- CSINC <Wd>, WZR, WZR, invert(<cond>). */
+ CSINC <Wd>, WZR, WZR, invert(<cond>)
+ where <cond> is not AL or NV. */
static int
convert_csinc_to_cset (aarch64_inst *inst)
{
if (inst->operands[1].reg.regno == 0x1f
- && inst->operands[2].reg.regno == 0x1f)
+ && inst->operands[2].reg.regno == 0x1f
+ && (inst->operands[3].cond->value & 0xe) != 0xe)
{
copy_operand_info (inst, 1, 3);
inst->operands[1].cond = get_inverted_cond (inst->operands[3].cond);
case OP_BFI:
case OP_UBFIZ:
return convert_bfm_to_bfi (inst);
+ case OP_BFC:
+ return convert_bfm_to_bfc (inst);
case OP_MOV_V:
return convert_orr_to_mov (inst);
case OP_MOV_IMM_WIDE:
for (; alias; alias = aarch64_find_next_alias_opcode (alias))
{
DEBUG_TRACE ("try %s", alias->name);
- assert (alias_opcode_p (alias));
+ assert (alias_opcode_p (alias) || opcode_has_alias (opcode));
/* An alias can be a pseudo opcode which will never be used in the
disassembly, e.g. BIC logical immediate is such a pseudo opcode
}
}
+/* Some instructions (including all SVE ones) use the instruction class
+ to describe how a qualifiers_list index is represented in the instruction
+ encoding. If INST is such an instruction, decode the appropriate fields
+ and fill in the operand qualifiers accordingly. Return true if no
+ problems are found. */
+
+static bfd_boolean
+aarch64_decode_variant_using_iclass (aarch64_inst *inst)
+{
+ int i, variant;
+
+ variant = 0;
+ switch (inst->opcode->iclass)
+ {
+ case sve_cpy:
+ variant = extract_fields (inst->value, 0, 2, FLD_size, FLD_SVE_M_14);
+ break;
+
+ case sve_index:
+ i = extract_field (FLD_SVE_tsz, inst->value, 0);
+ if (i == 0)
+ return FALSE;
+ while ((i & 1) == 0)
+ {
+ i >>= 1;
+ variant += 1;
+ }
+ break;
+
+ case sve_limm:
+ /* Pick the smallest applicable element size. */
+ if ((inst->value & 0x20600) == 0x600)
+ variant = 0;
+ else if ((inst->value & 0x20400) == 0x400)
+ variant = 1;
+ else if ((inst->value & 0x20000) == 0)
+ variant = 2;
+ else
+ variant = 3;
+ break;
+
+ case sve_misc:
+ /* sve_misc instructions have only a single variant. */
+ break;
+
+ case sve_movprfx:
+ variant = extract_fields (inst->value, 0, 2, FLD_size, FLD_SVE_M_16);
+ break;
+
+ case sve_pred_zm:
+ variant = extract_field (FLD_SVE_M_4, inst->value, 0);
+ break;
+
+ case sve_shift_pred:
+ i = extract_fields (inst->value, 0, 2, FLD_SVE_tszh, FLD_SVE_tszl_8);
+ sve_shift:
+ if (i == 0)
+ return FALSE;
+ while (i != 1)
+ {
+ i >>= 1;
+ variant += 1;
+ }
+ break;
+
+ case sve_shift_unpred:
+ i = extract_fields (inst->value, 0, 2, FLD_SVE_tszh, FLD_SVE_tszl_19);
+ goto sve_shift;
+
+ case sve_size_bhs:
+ variant = extract_field (FLD_size, inst->value, 0);
+ if (variant >= 3)
+ return FALSE;
+ break;
+
+ case sve_size_bhsd:
+ variant = extract_field (FLD_size, inst->value, 0);
+ break;
+
+ case sve_size_hsd:
+ i = extract_field (FLD_size, inst->value, 0);
+ if (i < 1)
+ return FALSE;
+ variant = i - 1;
+ break;
+
+ case sve_size_sd:
+ variant = extract_field (FLD_SVE_sz, inst->value, 0);
+ break;
+
+ default:
+ /* No mapping between instruction class and qualifiers. */
+ return TRUE;
+ }
+
+ for (i = 0; i < AARCH64_MAX_OPND_NUM; ++i)
+ inst->operands[i].qualifier = inst->opcode->qualifiers_list[variant][i];
+ return TRUE;
+}
/* Decode the CODE according to OPCODE; fill INST. Return 0 if the decoding
fails, which meanes that CODE is not an instruction of OPCODE; otherwise
return 1.
goto decode_fail;
}
+ /* Possibly use the instruction class to determine the correct
+ qualifier. */
+ if (!aarch64_decode_variant_using_iclass (inst))
+ {
+ DEBUG_TRACE ("iclass-based decoder FAIL");
+ goto decode_fail;
+ }
+
/* Call operand decoders. */
for (i = 0; i < AARCH64_MAX_OPND_NUM; ++i)
{
const aarch64_operand *opnd;
enum aarch64_opnd type;
+
type = opcode->operands[i];
if (type == AARCH64_OPND_NIL)
break;
}
}
+ /* If the opcode has a verifier, then check it now. */
+ if (opcode->verifier && ! opcode->verifier (opcode, code))
+ {
+ DEBUG_TRACE ("operand verifier FAIL");
+ goto decode_fail;
+ }
+
/* Match the qualifiers. */
if (aarch64_match_operands_constraint (inst, NULL) == 1)
{
}
}
-/* Decode INSN and fill in *INST the instruction information. */
+/* Decode INSN and fill in *INST the instruction information. An alias
+ opcode may be filled in *INSN if NOALIASES_P is FALSE. Return zero on
+ success. */
-static int
-disas_aarch64_insn (uint64_t pc ATTRIBUTE_UNUSED, uint32_t insn,
- aarch64_inst *inst)
+int
+aarch64_decode_insn (aarch64_insn insn, aarch64_inst *inst,
+ bfd_boolean noaliases_p)
{
const aarch64_opcode *opcode = aarch64_opcode_lookup (insn);
{
/* But only one opcode can be decoded successfully for, as the
decoding routine will check the constraint carefully. */
- if (aarch64_opcode_decode (opcode, insn, inst, no_aliases) == 1)
+ if (aarch64_opcode_decode (opcode, insn, inst, noaliases_p) == 1)
return ERR_OK;
opcode = aarch64_find_next_opcode (opcode);
}
int i, pcrel_p, num_printed;
for (i = 0, num_printed = 0; i < AARCH64_MAX_OPND_NUM; ++i)
{
- const size_t size = 128;
- char str[size];
+ char str[128];
/* We regard the opcode operand info more, however we also look into
the inst->operands to support the disassembling of the optional
operand.
break;
/* Generate the operand string in STR. */
- aarch64_print_operand (str, size, pc, opcode, opnds, i, &pcrel_p,
+ aarch64_print_operand (str, sizeof (str), pc, opcode, opnds, i, &pcrel_p,
&info->target);
/* Print the delimiter (taking account of omitted operand(s)). */
}
}
+/* Set NAME to a copy of INST's mnemonic with the "." suffix removed. */
+
+static void
+remove_dot_suffix (char *name, const aarch64_inst *inst)
+{
+ char *ptr;
+ size_t len;
+
+ ptr = strchr (inst->opcode->name, '.');
+ assert (ptr && inst->cond);
+ len = ptr - inst->opcode->name;
+ assert (len < 8);
+ strncpy (name, inst->opcode->name, len);
+ name[len] = '\0';
+}
+
/* Print the instruction mnemonic name. */
static void
/* For instructions that are truly conditionally executed, e.g. b.cond,
prepare the full mnemonic name with the corresponding condition
suffix. */
- char name[8], *ptr;
- size_t len;
-
- ptr = strchr (inst->opcode->name, '.');
- assert (ptr && inst->cond);
- len = ptr - inst->opcode->name;
- assert (len < 8);
- strncpy (name, inst->opcode->name, len);
- name [len] = '\0';
+ char name[8];
+
+ remove_dot_suffix (name, inst);
(*info->fprintf_func) (info->stream, "%s.%s", name, inst->cond->names[0]);
}
else
(*info->fprintf_func) (info->stream, "%s", inst->opcode->name);
}
+/* Decide whether we need to print a comment after the operands of
+ instruction INST. */
+
+static void
+print_comment (const aarch64_inst *inst, struct disassemble_info *info)
+{
+ if (inst->opcode->flags & F_COND)
+ {
+ char name[8];
+ unsigned int i, num_conds;
+
+ remove_dot_suffix (name, inst);
+ num_conds = ARRAY_SIZE (inst->cond->names);
+ for (i = 1; i < num_conds && inst->cond->names[i]; ++i)
+ (*info->fprintf_func) (info->stream, "%s %s.%s",
+ i == 1 ? " //" : ",",
+ name, inst->cond->names[i]);
+ }
+}
+
/* Print the instruction according to *INST. */
static void
{
print_mnemonic_name (inst, info);
print_operands (pc, inst->opcode, inst->operands, info);
+ print_comment (inst, info);
}
/* Entry-point of the instruction disassembler and printer. */
addresses, since the addend is not currently pc-relative. */
pc = 0;
- ret = disas_aarch64_insn (pc, word, &inst);
+ ret = aarch64_decode_insn (word, &inst, no_aliases);
if (((word >> 21) & 0x3ff) == 1)
{