/* aarch64-dis.c -- AArch64 disassembler.
- Copyright 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
+ Copyright (C) 2009-2019 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of the GNU opcodes library.
#include "sysdep.h"
#include "bfd_stdint.h"
-#include "dis-asm.h"
+#include "disassemble.h"
#include "libiberty.h"
#include "opintl.h"
#include "aarch64-dis.h"
#include "elf-bfd.h"
-#define ERR_OK 0
-#define ERR_UND -1
-#define ERR_UNP -3
-#define ERR_NYI -5
-
#define INSNLEN 4
/* Cached mapping symbol state. */
static enum map_type last_type;
static int last_mapping_sym = -1;
+static bfd_vma last_stop_offset = 0;
static bfd_vma last_mapping_addr = 0;
/* Other options */
static int no_aliases = 0; /* If set disassemble as most general inst. */
-\f
+\fstatic int no_notes = 1; /* If set do not print disassemble notes in the
+ output as comments. */
+
+/* Currently active instruction sequence. */
+static aarch64_instr_sequence insn_sequence;
static void
set_default_aarch64_dis_options (struct disassemble_info *info ATTRIBUTE_UNUSED)
return;
}
+ if (CONST_STRNEQ (option, "no-notes"))
+ {
+ no_notes = 1;
+ return;
+ }
+
+ if (CONST_STRNEQ (option, "notes"))
+ {
+ no_notes = 0;
+ return;
+ }
+
#ifdef DEBUG_AARCH64
if (CONST_STRNEQ (option, "debug_dump"))
{
#endif /* DEBUG_AARCH64 */
/* Invalid option. */
- fprintf (stderr, _("Unrecognised disassembler option: %s\n"), option);
+ opcodes_error_handler (_("unrecognised disassembler option: %s"), option);
}
static void
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;
/* Operand extractors. */
-int
+bfd_boolean
aarch64_ext_regno (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
info->reg.regno = extract_field (self->fields[0], code, 0);
- return 1;
+ return TRUE;
+}
+
+bfd_boolean
+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,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ assert (info->idx == 1
+ || info->idx ==3);
+ info->reg.regno = inst->operands[info->idx - 1].reg.regno + 1;
+ return TRUE;
}
/* e.g. IC <ic_op>{, <Xt>}. */
-int
+bfd_boolean
aarch64_ext_regrt_sysins (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
info->reg.regno = extract_field (self->fields[0], code, 0);
assert (info->idx == 1
/* 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;
+ return TRUE;
}
/* e.g. SQDMLAL <Va><d>, <Vb><n>, <Vm>.<Ts>[<index>]. */
-int
+bfd_boolean
aarch64_ext_reglane (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
/* regno */
info->reglane.regno = extract_field (self->fields[0], code,
while (++pos <= 3 && (value & 0x1) == 0)
value >>= 1;
if (pos > 3)
- return 0;
+ return FALSE;
info->qualifier = get_sreg_qualifier_from_value (pos);
info->reglane.index = (unsigned) (value >> 1);
}
}
+ else if (inst->opcode->iclass == dotproduct)
+ {
+ /* Need information in other operand(s) to help decoding. */
+ info->qualifier = get_expected_qualifier (inst, info->idx);
+ switch (info->qualifier)
+ {
+ case AARCH64_OPND_QLF_S_4B:
+ /* L:H */
+ info->reglane.index = extract_fields (code, 0, 2, FLD_H, FLD_L);
+ info->reglane.regno &= 0x1f;
+ break;
+ default:
+ return FALSE;
+ }
+ }
+ else if (inst->opcode->iclass == cryptosm3)
+ {
+ /* index for e.g. SM3TT2A <Vd>.4S, <Vn>.4S, <Vm>S[<imm2>]. */
+ info->reglane.index = extract_field (FLD_SM3_imm2, code, 0);
+ }
else
{
/* Index only for e.g. SQDMLAL <Va><d>, <Vb><n>, <Vm>.<Ts>[<index>]
switch (info->qualifier)
{
case AARCH64_OPND_QLF_S_H:
- /* h:l:m */
- info->reglane.index = extract_fields (code, 0, 3, FLD_H, FLD_L,
- FLD_M);
- info->reglane.regno &= 0xf;
+ if (info->type == AARCH64_OPND_Em16)
+ {
+ /* h:l:m */
+ info->reglane.index = extract_fields (code, 0, 3, FLD_H, FLD_L,
+ FLD_M);
+ info->reglane.regno &= 0xf;
+ }
+ else
+ {
+ /* h:l */
+ info->reglane.index = extract_fields (code, 0, 2, FLD_H, FLD_L);
+ }
break;
case AARCH64_OPND_QLF_S_S:
/* h:l */
info->reglane.index = extract_field (FLD_H, code, 0);
break;
default:
- return 0;
+ return FALSE;
+ }
+
+ if (inst->opcode->op == OP_FCMLA_ELEM
+ && info->qualifier != AARCH64_OPND_QLF_S_H)
+ {
+ /* Complex operand takes two elements. */
+ if (info->reglane.index & 1)
+ return FALSE;
+ info->reglane.index /= 2;
}
}
- return 1;
+ return TRUE;
}
-int
+bfd_boolean
aarch64_ext_reglist (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
/* R */
info->reglist.first_regno = extract_field (self->fields[0], code, 0);
/* len */
info->reglist.num_regs = extract_field (FLD_len, code, 0) + 1;
- return 1;
+ return TRUE;
}
/* Decode Rt and opcode fields of Vt in AdvSIMD load/store instructions. */
-int
+bfd_boolean
aarch64_ext_ldst_reglist (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst)
+ const aarch64_inst *inst,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn value;
/* Number of elements in each structure to be loaded/stored. */
info->reglist.first_regno = extract_field (FLD_Rt, code, 0);
/* opcode */
value = extract_field (FLD_opcode, code, 0);
+ /* PR 21595: Check for a bogus value. */
+ if (value >= ARRAY_SIZE (data))
+ return FALSE;
if (expected_num != data[value].num_elements || data[value].is_reserved)
- return 0;
+ return FALSE;
info->reglist.num_regs = data[value].num_regs;
- return 1;
+ return TRUE;
}
/* Decode Rt and S fields of Vt in AdvSIMD load single structure to all
lanes instructions. */
-int
+bfd_boolean
aarch64_ext_ldst_reglist_r (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst)
+ const aarch64_inst *inst,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn value;
if (info->reglist.num_regs == 1 && value == (aarch64_insn) 1)
info->reglist.num_regs = 2;
- return 1;
+ return TRUE;
}
/* Decode Q, opcode<2:1>, S, size and Rt fields of Vt in AdvSIMD
load/store single element instructions. */
-int
+bfd_boolean
aarch64_ext_ldst_elemlist (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_field field = {0, 0};
aarch64_insn QSsize; /* fields Q:S:size. */
info->reglist.index = QSsize;
break;
case 0x1:
+ if (QSsize & 0x1)
+ /* UND. */
+ return FALSE;
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 FALSE;
if ((QSsize & 0x1) == 0)
{
info->qualifier = AARCH64_OPND_QLF_S_S;
}
else
{
+ if (extract_field (FLD_S, code, 0))
+ /* UND */
+ return FALSE;
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;
}
break;
default:
- return 0;
+ return FALSE;
}
info->reglist.has_index = 1;
info->reglist.num_regs = get_opcode_dependent_value (inst->opcode);
assert (info->reglist.num_regs >= 1 && info->reglist.num_regs <= 4);
- return 1;
+ return TRUE;
}
/* Decode fields immh:immb and/or Q for e.g.
SSHR <Vd>.<T>, <Vn>.<T>, #<shift>
or SSHR <V><d>, <V><n>, #<shift>. */
-int
+bfd_boolean
aarch64_ext_advsimd_imm_shift (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst)
+ const aarch64_inst *inst,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int pos;
aarch64_insn Q, imm, immh;
immh = extract_field (FLD_immh, code, 0);
if (immh == 0)
- return 0;
+ return FALSE;
imm = extract_fields (code, 0, 2, FLD_immh, FLD_immb);
pos = 4;
/* Get highest set bit in immh. */
1xxx (UInt(immh:immb)-64) */
info->imm.value = imm - (8 << pos);
- return 1;
+ return TRUE;
}
/* Decode shift immediate for e.g. sshr (imm). */
-int
+bfd_boolean
aarch64_ext_shll_imm (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int64_t imm;
aarch64_insn val;
case 0: imm = 8; break;
case 1: imm = 16; break;
case 2: imm = 32; break;
- default: return 0;
+ default: return FALSE;
}
info->imm.value = imm;
- return 1;
+ return TRUE;
}
/* Decode imm for e.g. BFM <Wd>, <Wn>, #<immr>, #<imms>.
value in the field(s) will be extracted as unsigned immediate value. */
-int
+bfd_boolean
aarch64_ext_imm (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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);
if (operand_need_shift_by_two (self))
imm <<= 2;
+ else if (operand_need_shift_by_four (self))
+ imm <<= 4;
if (info->type == AARCH64_OPND_ADDR_ADRP)
imm <<= 12;
info->imm.value = imm;
- return 1;
+ return TRUE;
}
/* Decode imm and its shifter for e.g. MOVZ <Wd>, #<imm16>{, LSL #<shift>}. */
-int
+bfd_boolean
aarch64_ext_imm_half (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors)
{
- aarch64_ext_imm (self, info, code, inst);
+ aarch64_ext_imm (self, info, code, inst, errors);
info->shifter.kind = AARCH64_MOD_LSL;
info->shifter.amount = extract_field (FLD_hw, code, 0) << 4;
- return 1;
+ return TRUE;
}
/* Decode cmode and "a:b:c:d:e:f:g:h" for e.g.
MOVI <Vd>.<T>, #<imm8> {, LSL #<amount>}. */
-int
+bfd_boolean
aarch64_ext_advsimd_imm_modified (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
uint64_t imm;
enum aarch64_opnd_qualifier opnd0_qualifier = inst->operands[0].qualifier;
case 4: gen_sub_field (FLD_cmode, 1, 2, &field); break; /* per word */
case 2: gen_sub_field (FLD_cmode, 1, 1, &field); break; /* per half */
case 1: gen_sub_field (FLD_cmode, 1, 0, &field); break; /* per byte */
- default: assert (0); return 0;
+ default: assert (0); return FALSE;
}
/* 00: 0; 01: 8; 10:16; 11:24. */
info->shifter.amount = extract_field_2 (&field, code, 0) << 3;
break;
default:
assert (0);
- return 0;
+ return FALSE;
}
- return 1;
+ return TRUE;
+}
+
+/* Decode an 8-bit floating-point immediate. */
+bfd_boolean
+aarch64_ext_fpimm (const aarch64_operand *self, aarch64_opnd_info *info,
+ const aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ info->imm.value = extract_all_fields (self, code);
+ info->imm.is_fp = 1;
+ return TRUE;
+}
+
+/* Decode a 1-bit rotate immediate (#90 or #270). */
+bfd_boolean
+aarch64_ext_imm_rotate1 (const aarch64_operand *self, aarch64_opnd_info *info,
+ const aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ uint64_t rot = extract_field (self->fields[0], code, 0);
+ assert (rot < 2U);
+ info->imm.value = rot * 180 + 90;
+ return TRUE;
+}
+
+/* Decode a 2-bit rotate immediate (#0, #90, #180 or #270). */
+bfd_boolean
+aarch64_ext_imm_rotate2 (const aarch64_operand *self, aarch64_opnd_info *info,
+ const aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ uint64_t rot = extract_field (self->fields[0], code, 0);
+ assert (rot < 4U);
+ info->imm.value = rot * 90;
+ return TRUE;
}
/* Decode scale for e.g. SCVTF <Dd>, <Wn>, #<fbits>. */
-int
+bfd_boolean
aarch64_ext_fbits (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
info->imm.value = 64- extract_field (FLD_scale, code, 0);
- return 1;
+ return TRUE;
}
/* Decode arithmetic immediate for e.g.
SUBS <Wd>, <Wn|WSP>, #<imm> {, <shift>}. */
-int
+bfd_boolean
aarch64_ext_aimm (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn value;
/* shift */
value = extract_field (FLD_shift, code, 0);
if (value >= 2)
- return 0;
+ return FALSE;
info->shifter.amount = value ? 12 : 0;
/* imm12 (unsigned) */
info->imm.value = extract_field (FLD_imm12, code, 0);
- return 1;
+ return TRUE;
}
-/* 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 bfd_boolean
+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)
case 0x30 ... 0x37: /* 110xxx */ simd_size = 8; S &= 0x7; break;
case 0x38 ... 0x3b: /* 1110xx */ simd_size = 4; S &= 0x3; break;
case 0x3c ... 0x3d: /* 11110x */ simd_size = 2; S &= 0x1; break;
- default: return 0;
+ default: return FALSE;
}
mask = (1ull << simd_size) - 1;
/* Top bits are IGNORED. */
R &= simd_size - 1;
}
+
+ if (simd_size > esize * 8)
+ return FALSE;
+
/* NOTE: if S = simd_size - 1 we get 0xf..f which is rejected. */
if (S == simd_size - 1)
- return 0;
+ return FALSE;
/* S+1 consecutive bits to 1. */
/* NOTE: S can't be 63 due to detection above. */
imm = (1ull << (S + 1)) - 1;
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;
+ return TRUE;
+}
+
+/* Decode a logical immediate for e.g. ORR <Wd|WSP>, <Wn>, #<imm>. */
+bfd_boolean
+aarch64_ext_limm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ 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.). */
+bfd_boolean
+aarch64_ext_inv_limm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst,
+ aarch64_operand_error *errors)
+{
+ if (!aarch64_ext_limm (self, info, code, inst, errors))
+ return FALSE;
+ info->imm.value = ~info->imm.value;
+ return TRUE;
}
/* Decode Ft for e.g. STR <Qt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}]
or LDP <Qt1>, <Qt2>, [<Xn|SP>], #<imm>. */
-int
+bfd_boolean
aarch64_ext_ft (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
- const aarch64_insn code, const aarch64_inst *inst)
+ const aarch64_insn code, const aarch64_inst *inst,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn value;
case 0: qualifier = AARCH64_OPND_QLF_S_S; break;
case 1: qualifier = AARCH64_OPND_QLF_S_D; break;
case 2: qualifier = AARCH64_OPND_QLF_S_Q; break;
- default: return 0;
+ default: return FALSE;
}
info->qualifier = qualifier;
}
/* opc1:size */
value = extract_fields (code, 0, 2, FLD_opc1, FLD_ldst_size);
if (value > 0x4)
- return 0;
+ return FALSE;
info->qualifier = get_sreg_qualifier_from_value (value);
}
- return 1;
+ return TRUE;
}
/* Decode the address operand for e.g. STXRB <Ws>, <Wt>, [<Xn|SP>{,#0}]. */
-int
+bfd_boolean
aarch64_ext_addr_simple (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
/* Rn */
info->addr.base_regno = extract_field (FLD_Rn, code, 0);
- return 1;
+ return TRUE;
+}
+
+/* Decode the address operand for e.g.
+ stlur <Xt>, [<Xn|SP>{, <amount>}]. */
+bfd_boolean
+aarch64_ext_addr_offset (const aarch64_operand *self ATTRIBUTE_UNUSED,
+ aarch64_opnd_info *info,
+ aarch64_insn code, const aarch64_inst *inst,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ info->qualifier = get_expected_qualifier (inst, info->idx);
+
+ /* Rn */
+ info->addr.base_regno = extract_field (self->fields[0], code, 0);
+
+ /* simm9 */
+ aarch64_insn imm = extract_fields (code, 0, 1, self->fields[1]);
+ info->addr.offset.imm = sign_extend (imm, 8);
+ if (extract_field (self->fields[2], code, 0) == 1) {
+ info->addr.writeback = 1;
+ info->addr.preind = 1;
+ }
+ return TRUE;
}
/* Decode the address operand for e.g.
STR <Qt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}]. */
-int
+bfd_boolean
aarch64_ext_addr_regoff (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
- aarch64_insn code, const aarch64_inst *inst)
+ aarch64_insn code, const aarch64_inst *inst,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn S, value;
info->shifter.amount_present = 1;
}
- return 1;
+ return TRUE;
}
/* Decode the address operand for e.g. LDRSW <Xt>, [<Xn|SP>], #<simm>. */
-int
+bfd_boolean
aarch64_ext_addr_simm (const aarch64_operand *self, aarch64_opnd_info *info,
- aarch64_insn code, const aarch64_inst *inst)
+ aarch64_insn code, const aarch64_inst *inst,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn imm;
info->qualifier = get_expected_qualifier (inst, info->idx);
/* simm (imm9 or imm7) */
imm = extract_field (self->fields[0], code, 0);
info->addr.offset.imm = sign_extend (imm, fields[self->fields[0]].width - 1);
- if (self->fields[0] == FLD_imm7)
+ if (self->fields[0] == FLD_imm7
+ || info->qualifier == AARCH64_OPND_QLF_imm_tag)
/* scaled immediate in ld/st pair instructions. */
info->addr.offset.imm *= aarch64_get_qualifier_esize (info->qualifier);
/* qualifier */
info->addr.postind = 1;
}
- return 1;
+ return TRUE;
}
/* Decode the address operand for e.g. LDRSW <Xt>, [<Xn|SP>{, #<simm>}]. */
-int
+bfd_boolean
aarch64_ext_addr_uimm12 (const aarch64_operand *self, aarch64_opnd_info *info,
aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int shift;
info->qualifier = get_expected_qualifier (inst, info->idx);
info->addr.base_regno = extract_field (self->fields[0], code, 0);
/* uimm12 */
info->addr.offset.imm = extract_field (self->fields[1], code, 0) << shift;
- return 1;
+ return TRUE;
+}
+
+/* Decode the address operand for e.g. LDRAA <Xt>, [<Xn|SP>{, #<simm>}]. */
+bfd_boolean
+aarch64_ext_addr_simm10 (const aarch64_operand *self, aarch64_opnd_info *info,
+ aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ aarch64_insn imm;
+
+ info->qualifier = get_expected_qualifier (inst, info->idx);
+ /* Rn */
+ info->addr.base_regno = extract_field (self->fields[0], code, 0);
+ /* simm10 */
+ imm = extract_fields (code, 0, 2, self->fields[1], self->fields[2]);
+ info->addr.offset.imm = sign_extend (imm, 9) << 3;
+ if (extract_field (self->fields[3], code, 0) == 1) {
+ info->addr.writeback = 1;
+ info->addr.preind = 1;
+ }
+ return TRUE;
}
/* Decode the address operand for e.g.
LD1 {<Vt>.<T>, <Vt2>.<T>, <Vt3>.<T>}, [<Xn|SP>], <Xm|#<amount>>. */
-int
+bfd_boolean
aarch64_ext_simd_addr_post (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
- aarch64_insn code, const aarch64_inst *inst)
+ aarch64_insn code, const aarch64_inst *inst,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
/* The opcode dependent area stores the number of elements in
each structure to be loaded/stored. */
info->addr.offset.is_reg = 1;
info->addr.writeback = 1;
- return 1;
+ return TRUE;
}
/* Decode the condition operand for e.g. CSEL <Xd>, <Xn>, <Xm>, <cond>. */
-int
+bfd_boolean
aarch64_ext_cond (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
- aarch64_insn code, const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ aarch64_insn code, const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn value;
/* cond */
value = extract_field (FLD_cond, code, 0);
info->cond = get_cond_from_value (value);
- return 1;
+ return TRUE;
}
/* Decode the system register operand for e.g. MRS <Xt>, <systemreg>. */
-int
+bfd_boolean
aarch64_ext_sysreg (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
/* op0:op1:CRn:CRm:op2 */
- info->sysreg = extract_fields (code, 0, 5, FLD_op0, FLD_op1, FLD_CRn,
- FLD_CRm, FLD_op2);
- return 1;
+ info->sysreg.value = extract_fields (code, 0, 5, FLD_op0, FLD_op1, FLD_CRn,
+ FLD_CRm, FLD_op2);
+ info->sysreg.flags = 0;
+
+ /* If a system instruction, check which restrictions should be on the register
+ value during decoding, these will be enforced then. */
+ if (inst->opcode->iclass == ic_system)
+ {
+ /* Check to see if it's read-only, else check if it's write only.
+ if it's both or unspecified don't care. */
+ if ((inst->opcode->flags & (F_SYS_READ | F_SYS_WRITE)) == F_SYS_READ)
+ info->sysreg.flags = F_REG_READ;
+ else if ((inst->opcode->flags & (F_SYS_READ | F_SYS_WRITE))
+ == F_SYS_WRITE)
+ info->sysreg.flags = F_REG_WRITE;
+ }
+
+ return TRUE;
}
/* Decode the PSTATE field operand for e.g. MSR <pstatefield>, #<imm>. */
-int
+bfd_boolean
aarch64_ext_pstatefield (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int i;
/* op1:op2 */
info->pstatefield = extract_fields (code, 0, 2, FLD_op1, FLD_op2);
for (i = 0; aarch64_pstatefields[i].name != NULL; ++i)
if (aarch64_pstatefields[i].value == (aarch64_insn)info->pstatefield)
- return 1;
+ return TRUE;
/* Reserved value in <pstatefield>. */
- return 0;
+ return FALSE;
}
/* Decode the system instruction op operand for e.g. AT <at_op>, <Xt>. */
-int
+bfd_boolean
aarch64_ext_sysins_op (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int i;
aarch64_insn value;
case AARCH64_OPND_SYSREG_DC: sysins_ops = aarch64_sys_regs_dc; break;
case AARCH64_OPND_SYSREG_IC: sysins_ops = aarch64_sys_regs_ic; break;
case AARCH64_OPND_SYSREG_TLBI: sysins_ops = aarch64_sys_regs_tlbi; break;
- default: assert (0); return 0;
+ case AARCH64_OPND_SYSREG_SR:
+ sysins_ops = aarch64_sys_regs_sr;
+ /* Let's remove op2 for rctx. Refer to comments in the definition of
+ aarch64_sys_regs_sr[]. */
+ value = value & ~(0x7);
+ break;
+ default: assert (0); return FALSE;
}
- 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);
- return 1;
+ aarch64_sys_ins_reg_has_xt (info->sysins_op), i);
+ return TRUE;
}
- return 0;
+ return FALSE;
}
/* Decode the memory barrier option operand for e.g. DMB <option>|#<imm>. */
-int
+bfd_boolean
aarch64_ext_barrier (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
/* CRm */
info->barrier = aarch64_barrier_options + extract_field (FLD_CRm, code, 0);
- return 1;
+ return TRUE;
}
/* Decode the prefetch operation option operand for e.g.
PRFM <prfop>, [<Xn|SP>{, #<pimm>}]. */
-int
-aarch64_ext_prfop (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code, const aarch64_inst *inst ATTRIBUTE_UNUSED)
+bfd_boolean
+aarch64_ext_prfop (const aarch64_operand *self ATTRIBUTE_UNUSED,
+ aarch64_opnd_info *info,
+ aarch64_insn code, const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ /* prfop in Rt */
+ info->prfop = aarch64_prfops + extract_field (FLD_Rt, code, 0);
+ return TRUE;
+}
+
+/* Decode the hint number for an alias taking an operand. Set info->hint_option
+ to the matching name/value pair in aarch64_hint_options. */
+
+bfd_boolean
+aarch64_ext_hint (const aarch64_operand *self ATTRIBUTE_UNUSED,
+ aarch64_opnd_info *info,
+ aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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 == HINT_VAL (aarch64_hint_options[i].value))
+ {
+ info->hint_option = &(aarch64_hint_options[i]);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Decode the extended register operand for e.g.
+ STR <Qt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}]. */
+bfd_boolean
+aarch64_ext_reg_extended (const aarch64_operand *self ATTRIBUTE_UNUSED,
+ aarch64_opnd_info *info,
+ aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ aarch64_insn value;
+
+ /* Rm */
+ info->reg.regno = extract_field (FLD_Rm, code, 0);
+ /* option */
+ value = extract_field (FLD_option, code, 0);
+ info->shifter.kind =
+ aarch64_get_operand_modifier_from_value (value, TRUE /* extend_p */);
+ /* imm3 */
+ info->shifter.amount = extract_field (FLD_imm3, code, 0);
+
+ /* This makes the constraint checking happy. */
+ info->shifter.operator_present = 1;
+
+ /* Assume inst->operands[0].qualifier has been resolved. */
+ assert (inst->operands[0].qualifier != AARCH64_OPND_QLF_NIL);
+ info->qualifier = AARCH64_OPND_QLF_W;
+ if (inst->operands[0].qualifier == AARCH64_OPND_QLF_X
+ && (info->shifter.kind == AARCH64_MOD_UXTX
+ || info->shifter.kind == AARCH64_MOD_SXTX))
+ info->qualifier = AARCH64_OPND_QLF_X;
+
+ return TRUE;
+}
+
+/* Decode the shifted register operand for e.g.
+ SUBS <Xd>, <Xn>, <Xm> {, <shift> #<amount>}. */
+bfd_boolean
+aarch64_ext_reg_shifted (const aarch64_operand *self ATTRIBUTE_UNUSED,
+ aarch64_opnd_info *info,
+ aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ aarch64_insn value;
+
+ /* Rm */
+ info->reg.regno = extract_field (FLD_Rm, code, 0);
+ /* shift */
+ value = extract_field (FLD_shift, code, 0);
+ info->shifter.kind =
+ aarch64_get_operand_modifier_from_value (value, FALSE /* extend_p */);
+ if (info->shifter.kind == AARCH64_MOD_ROR
+ && inst->opcode->iclass != log_shift)
+ /* ROR is not available for the shifted register operand in arithmetic
+ instructions. */
+ return FALSE;
+ /* imm6 */
+ info->shifter.amount = extract_field (FLD_imm6, code, 0);
+
+ /* This makes the constraint checking happy. */
+ info->shifter.operator_present = 1;
+
+ return TRUE;
+}
+
+/* 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 bfd_boolean
+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 TRUE;
+}
+
+/* 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. */
+bfd_boolean
+aarch64_ext_sve_addr_ri_s4xvl (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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. */
+bfd_boolean
+aarch64_ext_sve_addr_ri_s6xvl (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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. */
+bfd_boolean
+aarch64_ext_sve_addr_ri_s9xvl (const aarch64_operand *self,
+ aarch64_opnd_info *info,
+ aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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 bfd_boolean
+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 TRUE;
+}
+
+/* Decode an SVE address [X<n>, #<SVE_imm4> << <shift>], where <SVE_imm4>
+ is a 4-bit signed number and where <shift> is SELF's operand-dependent
+ value. fields[0] specifies the base register field. */
+bfd_boolean
+aarch64_ext_sve_addr_ri_s4 (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ int offset = sign_extend (extract_field (FLD_SVE_imm4, code, 0), 3);
+ return aarch64_ext_sve_addr_reg_imm (self, info, code, offset);
+}
+
+/* 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. */
+bfd_boolean
+aarch64_ext_sve_addr_ri_u6 (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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. */
+bfd_boolean
+aarch64_ext_sve_addr_rr_lsl (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ int index_regno;
+
+ index_regno = extract_field (self->fields[1], code, 0);
+ if (index_regno == 31 && (self->flags & OPD_F_NO_ZR) != 0)
+ return FALSE;
+
+ info->addr.base_regno = extract_field (self->fields[0], code, 0);
+ info->addr.offset.regno = index_regno;
+ 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 TRUE;
+}
+
+/* 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. */
+bfd_boolean
+aarch64_ext_sve_addr_rz_xtw (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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 TRUE;
+}
+
+/* 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. */
+bfd_boolean
+aarch64_ext_sve_addr_zi_u5 (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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 bfd_boolean
+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 TRUE;
+}
+
+/* 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. */
+bfd_boolean
+aarch64_ext_sve_addr_zz_lsl (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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. */
+bfd_boolean
+aarch64_ext_sve_addr_zz_sxtw (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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. */
+bfd_boolean
+aarch64_ext_sve_addr_zz_uxtw (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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 bfd_boolean
+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 TRUE;
+}
+
+/* Decode an SVE ADD/SUB immediate. */
+bfd_boolean
+aarch64_ext_sve_aimm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst,
+ aarch64_operand_error *errors)
+{
+ return (aarch64_ext_imm (self, info, code, inst, errors)
+ && decode_sve_aimm (info, (uint8_t) info->imm.value));
+}
+
+/* Decode an SVE CPY/DUP immediate. */
+bfd_boolean
+aarch64_ext_sve_asimm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst,
+ aarch64_operand_error *errors)
+{
+ return (aarch64_ext_imm (self, info, code, inst, errors)
+ && 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. */
+bfd_boolean
+aarch64_ext_sve_float_half_one (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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 TRUE;
+}
+
+/* Decode a single-bit immediate that selects between #0.5 and #2.0.
+ The fields array specifies which field to use. */
+bfd_boolean
+aarch64_ext_sve_float_half_two (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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 TRUE;
+}
+
+/* Decode a single-bit immediate that selects between #0.0 and #1.0.
+ The fields array specifies which field to use. */
+bfd_boolean
+aarch64_ext_sve_float_zero_one (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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 TRUE;
+}
+
+/* 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. */
+bfd_boolean
+aarch64_ext_sve_index (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors 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 & 31) == 0)
+ return 0;
+ while ((val & 1) == 0)
+ val /= 2;
+ info->reglane.index = val / 2;
+ return TRUE;
+}
+
+/* Decode a logical immediate for the MOV alias of SVE DUPM. */
+bfd_boolean
+aarch64_ext_sve_limm_mov (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst,
+ aarch64_operand_error *errors)
{
- /* prfop in Rt */
- info->prfop = aarch64_prfops + extract_field (FLD_Rt, code, 0);
- return 1;
+ int esize = aarch64_get_qualifier_esize (inst->operands[0].qualifier);
+ return (aarch64_ext_limm (self, info, code, inst, errors)
+ && aarch64_sve_dupm_mov_immediate_p (info->imm.value, esize));
}
-/* Decode the extended register operand for e.g.
- STR <Qt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}]. */
-int
-aarch64_ext_reg_extended (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+/* Decode Zn[MM], where Zn occupies the least-significant part of the field
+ and where MM occupies the most-significant part. The operand-dependent
+ value specifies the number of bits in Zn. */
+bfd_boolean
+aarch64_ext_sve_quad_index (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
- aarch64_insn value;
+ unsigned int reg_bits = get_operand_specific_data (self);
+ unsigned int val = extract_all_fields (self, code);
+ info->reglane.regno = val & ((1 << reg_bits) - 1);
+ info->reglane.index = val >> reg_bits;
+ return TRUE;
+}
- /* Rm */
- info->reg.regno = extract_field (FLD_Rm, code, 0);
- /* option */
- value = extract_field (FLD_option, code, 0);
- info->shifter.kind =
- aarch64_get_operand_modifier_from_value (value, TRUE /* extend_p */);
- /* imm3 */
- info->shifter.amount = extract_field (FLD_imm3, code, 0);
+/* 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. */
+bfd_boolean
+aarch64_ext_sve_reglist (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst ATTRIBUTE_UNUSED,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+ info->reglist.first_regno = extract_field (self->fields[0], code, 0);
+ info->reglist.num_regs = get_opcode_dependent_value (inst->opcode);
+ return TRUE;
+}
- /* This makes the constraint checking happy. */
- info->shifter.operator_present = 1;
+/* Decode <pattern>{, MUL #<amount>}. The fields array specifies which
+ fields to use for <pattern>. <amount> - 1 is encoded in the SVE_imm4
+ field. */
+bfd_boolean
+aarch64_ext_sve_scale (const aarch64_operand *self,
+ aarch64_opnd_info *info, aarch64_insn code,
+ const aarch64_inst *inst, aarch64_operand_error *errors)
+{
+ int val;
- /* Assume inst->operands[0].qualifier has been resolved. */
- assert (inst->operands[0].qualifier != AARCH64_OPND_QLF_NIL);
- info->qualifier = AARCH64_OPND_QLF_W;
- if (inst->operands[0].qualifier == AARCH64_OPND_QLF_X
- && (info->shifter.kind == AARCH64_MOD_UXTX
- || info->shifter.kind == AARCH64_MOD_SXTX))
- info->qualifier = AARCH64_OPND_QLF_X;
+ if (!aarch64_ext_imm (self, info, code, inst, errors))
+ return FALSE;
+ 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 TRUE;
+}
- 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 the shifted register operand for e.g.
- SUBS <Xd>, <Xn>, <Xm> {, <shift> #<amount>}. */
-int
-aarch64_ext_reg_shifted (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
+/* Decode an SVE shift-left immediate. */
+bfd_boolean
+aarch64_ext_sve_shlimm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst, aarch64_operand_error *errors)
{
- aarch64_insn value;
+ if (!aarch64_ext_imm (self, info, code, inst, errors)
+ || info->imm.value == 0)
+ return FALSE;
- /* Rm */
- info->reg.regno = extract_field (FLD_Rm, code, 0);
- /* shift */
- value = extract_field (FLD_shift, code, 0);
- info->shifter.kind =
- aarch64_get_operand_modifier_from_value (value, FALSE /* extend_p */);
- if (info->shifter.kind == AARCH64_MOD_ROR
- && inst->opcode->iclass != log_shift)
- /* ROR is not available for the shifted register operand in arithmetic
- instructions. */
- return 0;
- /* imm6 */
- info->shifter.amount = extract_field (FLD_imm6, code, 0);
+ info->imm.value -= get_top_bit (info->imm.value);
+ return TRUE;
+}
- /* This makes the constraint checking happy. */
- info->shifter.operator_present = 1;
+/* Decode an SVE shift-right immediate. */
+bfd_boolean
+aarch64_ext_sve_shrimm (const aarch64_operand *self,
+ aarch64_opnd_info *info, const aarch64_insn code,
+ const aarch64_inst *inst, aarch64_operand_error *errors)
+{
+ if (!aarch64_ext_imm (self, info, code, inst, errors)
+ || info->imm.value == 0)
+ return FALSE;
- return 1;
+ info->imm.value = get_top_bit (info->imm.value) * 2 - info->imm.value;
+ return TRUE;
}
\f
/* Bitfields that are commonly used to encode certain operands' information
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 > 0 && value <= 16 && value == (value & -value);
+
+ 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 > 0 && value != (value & -value);
+
+ 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);
return 0;
}
+/* 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:
/* 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);
int is32 = inst->operands[0].qualifier == AARCH64_OPND_QLF_W;
value = ~value;
/* A MOVN has an immediate that could be encoded by MOVZ. */
- if (aarch64_wide_constant_p (value, is32, NULL) == TRUE)
+ if (aarch64_wide_constant_p (value, is32, NULL))
return 0;
}
inst->operands[1].imm.value = value;
/* ORR has an immediate that could be generated by a MOVZ or MOVN
instruction. */
if (inst->operands[0].reg.regno != 0x1f
- && (aarch64_wide_constant_p (value, is32, NULL) == TRUE
- || aarch64_wide_constant_p (~value, is32, NULL) == TRUE))
+ && (aarch64_wide_constant_p (value, is32, NULL)
+ || aarch64_wide_constant_p (~value, is32, NULL)))
return 0;
inst->operands[2].type = AARCH64_OPND_NIL;
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:
}
}
-static int aarch64_opcode_decode (const aarch64_opcode *, const aarch64_insn,
- aarch64_inst *, int);
+static bfd_boolean
+aarch64_opcode_decode (const aarch64_opcode *, const aarch64_insn,
+ aarch64_inst *, int, aarch64_operand_error *errors);
/* Given the instruction information in *INST, check if the instruction has
any alias form that can be used to represent *INST. If the answer is yes,
aarch64_find_next_alias_opcode (in opcodes/aarch64-dis-2.c) to help. */
static void
-determine_disassembling_preference (struct aarch64_inst *inst)
+determine_disassembling_preference (struct aarch64_inst *inst,
+ aarch64_operand_error *errors)
{
const aarch64_opcode *opcode;
const aarch64_opcode *alias;
opcode = inst->opcode;
/* This opcode does not have an alias, so use itself. */
- if (opcode_has_alias (opcode) == FALSE)
+ if (!opcode_has_alias (opcode))
return;
alias = aarch64_find_alias_opcode (opcode);
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
/* Directly decode the alias opcode. */
aarch64_inst temp;
memset (&temp, '\0', sizeof (aarch64_inst));
- if (aarch64_opcode_decode (alias, inst->value, &temp, 1) == 1)
+ if (aarch64_opcode_decode (alias, inst->value, &temp, 1, errors) == 1)
{
DEBUG_TRACE ("succeed with %s via direct decoding", alias->name);
memcpy (inst, &temp, sizeof (aarch64_inst));
}
}
+/* 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_fields (inst->value, 0, 2, FLD_SVE_tszh, FLD_imm5);
+ if ((i & 31) == 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_bh:
+ case sve_size_sd:
+ variant = extract_field (FLD_SVE_sz, inst->value, 0);
+ break;
+
+ case sve_size_sd2:
+ variant = extract_field (FLD_SVE_sz2, inst->value, 0);
+ break;
+
+ case sve_size_hsd2:
+ i = extract_field (FLD_SVE_size, inst->value, 0);
+ if (i < 1)
+ return FALSE;
+ variant = i - 1;
+ break;
+
+ case sve_size_013:
+ i = extract_field (FLD_size, inst->value, 0);
+ if (i == 2)
+ return FALSE;
+ if (i == 3)
+ variant = 2;
+ else
+ variant = i;
+ break;
+
+ case sve_shift_tsz_bhsd:
+ i = extract_fields (inst->value, 0, 2, FLD_SVE_tszh, FLD_SVE_tszl_19);
+ if (i == 0)
+ return FALSE;
+ while (i != 1)
+ {
+ i >>= 1;
+ variant += 1;
+ }
+ break;
+
+ case sve_size_tsz_bhs:
+ i = extract_fields (inst->value, 0, 2, FLD_SVE_sz, FLD_SVE_tszl_19);
+ while (i != 1)
+ {
+ if (i & 1)
+ return FALSE;
+ i >>= 1;
+ variant += 1;
+ }
+ break;
+
+ case sve_shift_tsz_hsd:
+ i = extract_fields (inst->value, 0, 2, FLD_SVE_sz, FLD_SVE_tszl_19);
+ if (i == 0)
+ return FALSE;
+ while (i != 1)
+ {
+ i >>= 1;
+ variant += 1;
+ }
+ 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.
determined and used to disassemble CODE; this is done just before the
return. */
-static int
+static bfd_boolean
aarch64_opcode_decode (const aarch64_opcode *opcode, const aarch64_insn code,
- aarch64_inst *inst, int noaliases_p)
+ aarch64_inst *inst, int noaliases_p,
+ aarch64_operand_error *errors)
{
int i;
assert (opcode && inst);
+ /* Clear inst. */
+ memset (inst, '\0', sizeof (aarch64_inst));
+
/* Check the base opcode. */
if ((code & opcode->mask) != (opcode->opcode & opcode->mask))
{
goto decode_fail;
}
- /* Clear inst. */
- memset (inst, '\0', sizeof (aarch64_inst));
-
inst->opcode = opcode;
inst->value = code;
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;
opnd = &aarch64_operands[type];
if (operand_has_extractor (opnd)
- && (! aarch64_extract_operand (opnd, &inst->operands[i], code, inst)))
+ && (! aarch64_extract_operand (opnd, &inst->operands[i], code, inst,
+ errors)))
{
DEBUG_TRACE ("operand decoder FAIL at operand %d", i);
goto decode_fail;
}
}
+ /* If the opcode has a verifier, then check it now. */
+ if (opcode->verifier
+ && opcode->verifier (inst, code, 0, FALSE, errors, NULL) != ERR_OK)
+ {
+ DEBUG_TRACE ("operand verifier FAIL");
+ goto decode_fail;
+ }
+
/* Match the qualifiers. */
if (aarch64_match_operands_constraint (inst, NULL) == 1)
{
alias and should be disassembled in the form of its alias instead.
If the answer is yes, *INST will be updated. */
if (!noaliases_p)
- determine_disassembling_preference (inst);
+ determine_disassembling_preference (inst, errors);
DEBUG_TRACE ("SUCCESS");
- return 1;
+ return TRUE;
}
else
{
}
decode_fail:
- return 0;
+ return FALSE;
}
\f
/* This does some user-friendly fix-up to *INST. It is currently focus on
}
}
-/* 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)
+enum err_type
+aarch64_decode_insn (aarch64_insn insn, aarch64_inst *inst,
+ bfd_boolean noaliases_p,
+ aarch64_operand_error *errors)
{
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, errors) == 1)
return ERR_OK;
opcode = aarch64_find_next_opcode (opcode);
}
static void
print_operands (bfd_vma pc, const aarch64_opcode *opcode,
- const aarch64_opnd_info *opnds, struct disassemble_info *info)
+ const aarch64_opnd_info *opnds, struct disassemble_info *info,
+ bfd_boolean *has_notes)
{
+ char *notes = NULL;
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,
- &info->target);
+ aarch64_print_operand (str, sizeof (str), pc, opcode, opnds, i, &pcrel_p,
+ &info->target, ¬es);
/* Print the delimiter (taking account of omitted operand(s)). */
if (str[0] != '\0')
else
(*info->fprintf_func) (info->stream, "%s", str);
}
+
+ if (notes && !no_notes)
+ {
+ *has_notes = TRUE;
+ (*info->fprintf_func) (info->stream, " // note: %s", notes);
+ }
+}
+
+/* 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. */
/* 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]);
+ }
+}
+
+/* Build notes from verifiers into a string for printing. */
+
+static void
+print_verifier_notes (aarch64_operand_error *detail,
+ struct disassemble_info *info)
+{
+ if (no_notes)
+ return;
+
+ /* The output of the verifier cannot be a fatal error, otherwise the assembly
+ would not have succeeded. We can safely ignore these. */
+ assert (detail->non_fatal);
+ assert (detail->error);
+
+ /* If there are multiple verifier messages, concat them up to 1k. */
+ (*info->fprintf_func) (info->stream, " // note: %s", detail->error);
+ if (detail->index >= 0)
+ (*info->fprintf_func) (info->stream, " at operand %d", detail->index + 1);
+}
+
/* Print the instruction according to *INST. */
static void
print_aarch64_insn (bfd_vma pc, const aarch64_inst *inst,
- struct disassemble_info *info)
+ const aarch64_insn code,
+ struct disassemble_info *info,
+ aarch64_operand_error *mismatch_details)
{
+ bfd_boolean has_notes = FALSE;
+
print_mnemonic_name (inst, info);
- print_operands (pc, inst->opcode, inst->operands, info);
+ print_operands (pc, inst->opcode, inst->operands, info, &has_notes);
+ print_comment (inst, info);
+
+ /* We've already printed a note, not enough space to print more so exit.
+ Usually notes shouldn't overlap so it shouldn't happen that we have a note
+ from a register and instruction at the same time. */
+ if (has_notes)
+ return;
+
+ /* Always run constraint verifiers, this is needed because constraints need to
+ maintain a global state regardless of whether the instruction has the flag
+ set or not. */
+ enum err_type result = verify_constraints (inst, code, pc, FALSE,
+ mismatch_details, &insn_sequence);
+ switch (result)
+ {
+ case ERR_UND:
+ case ERR_UNP:
+ case ERR_NYI:
+ assert (0);
+ case ERR_VFI:
+ print_verifier_notes (mismatch_details, info);
+ break;
+ default:
+ break;
+ }
}
/* Entry-point of the instruction disassembler and printer. */
static void
print_insn_aarch64_word (bfd_vma pc,
uint32_t word,
- struct disassemble_info *info)
+ struct disassemble_info *info,
+ aarch64_operand_error *errors)
{
- static const char *err_msg[6] =
+ static const char *err_msg[ERR_NR_ENTRIES+1] =
{
- [ERR_OK] = "_",
- [-ERR_UND] = "undefined",
- [-ERR_UNP] = "unpredictable",
- [-ERR_NYI] = "NYI"
+ [ERR_OK] = "_",
+ [ERR_UND] = "undefined",
+ [ERR_UNP] = "unpredictable",
+ [ERR_NYI] = "NYI"
};
- int ret;
+ enum err_type ret;
aarch64_inst inst;
info->insn_info_valid = 1;
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, errors);
if (((word >> 21) & 0x3ff) == 1)
{
/* Handle undefined instructions. */
info->insn_type = dis_noninsn;
(*info->fprintf_func) (info->stream,".inst\t0x%08x ; %s",
- word, err_msg[-ret]);
+ word, err_msg[ret]);
break;
case ERR_OK:
user_friendly_fixup (&inst);
- print_aarch64_insn (pc, &inst, info);
+ print_aarch64_insn (pc, &inst, word, info, errors);
break;
default:
abort ();
static void
print_insn_data (bfd_vma pc ATTRIBUTE_UNUSED,
uint32_t word,
- struct disassemble_info *info)
+ struct disassemble_info *info,
+ aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
switch (info->bytes_per_chunk)
{
unsigned int type;
const char *name;
+ /* If the symbol is in a different section, ignore it. */
+ if (info->section != NULL && info->section != info->symtab[n]->section)
+ return FALSE;
+
es = *(elf_symbol_type **)(info->symtab + n);
type = ELF_ST_TYPE (es->internal_elf_sym.st_info);
{
bfd_byte buffer[INSNLEN];
int status;
- void (*printer) (bfd_vma, uint32_t, struct disassemble_info *);
+ void (*printer) (bfd_vma, uint32_t, struct disassemble_info *,
+ aarch64_operand_error *);
bfd_boolean found = FALSE;
unsigned int size = 4;
unsigned long data;
+ aarch64_operand_error errors;
if (info->disassembler_options)
{
/* Aarch64 instructions are always little-endian */
info->endian_code = BFD_ENDIAN_LITTLE;
+ /* Default to DATA. A text section is required by the ABI to contain an
+ INSN mapping symbol at the start. A data section has no such
+ requirement, hence if no mapping symbol is found the section must
+ contain only data. This however isn't very useful if the user has
+ fully stripped the binaries. If this is the case use the section
+ attributes to determine the default. If we have no section default to
+ INSN as well, as we may be disassembling some raw bytes on a baremetal
+ HEX file or similar. */
+ enum map_type type = MAP_DATA;
+ if ((info->section && info->section->flags & SEC_CODE) || !info->section)
+ type = MAP_INSN;
+
/* First check the full symtab for a mapping symbol, even if there
are no usable non-mapping symbols for this address. */
if (info->symtab_size != 0
&& bfd_asymbol_flavour (*info->symtab) == bfd_target_elf_flavour)
{
- enum map_type type = MAP_INSN;
int last_sym = -1;
- bfd_vma addr;
+ bfd_vma addr, section_vma = 0;
+ bfd_boolean can_use_search_opt_p;
int n;
if (pc <= last_mapping_addr)
/* Start scanning at the start of the function, or wherever
we finished last time. */
n = info->symtab_pos + 1;
- if (n < last_mapping_sym)
+
+ /* If the last stop offset is different from the current one it means we
+ are disassembling a different glob of bytes. As such the optimization
+ would not be safe and we should start over. */
+ can_use_search_opt_p = last_mapping_sym >= 0
+ && info->stop_offset == last_stop_offset;
+
+ if (n >= last_mapping_sym && can_use_search_opt_p)
n = last_mapping_sym;
- /* Scan up to the location being disassembled. */
+ /* Look down while we haven't passed the location being disassembled.
+ The reason for this is that there's no defined order between a symbol
+ and an mapping symbol that may be at the same address. We may have to
+ look at least one position ahead. */
for (; n < info->symtab_size; n++)
{
addr = bfd_asymbol_value (info->symtab[n]);
if (addr > pc)
break;
- if ((info->section == NULL
- || info->section == info->symtab[n]->section)
- && get_sym_code_type (info, n, &type))
+ if (get_sym_code_type (info, n, &type))
{
last_sym = n;
found = TRUE;
if (!found)
{
n = info->symtab_pos;
- if (n < last_mapping_sym)
+ if (n >= last_mapping_sym && can_use_search_opt_p)
n = last_mapping_sym;
/* No mapping symbol found at this address. Look backwards
- for a preceeding one. */
+ for a preceeding one, but don't go pass the section start
+ otherwise a data section with no mapping symbol can pick up
+ a text mapping symbol of a preceeding section. The documentation
+ says section can be NULL, in which case we will seek up all the
+ way to the top. */
+ if (info->section)
+ section_vma = info->section->vma;
+
for (; n >= 0; n--)
{
+ addr = bfd_asymbol_value (info->symtab[n]);
+ if (addr < section_vma)
+ break;
+
if (get_sym_code_type (info, n, &type))
{
last_sym = n;
last_mapping_sym = last_sym;
last_type = type;
+ last_stop_offset = info->stop_offset;
/* Look a little bit ahead to see if we should print out
less than four bytes of data. If there's a symbol,
size = (pc & 1) ? 1 : 2;
}
}
+ else
+ last_type = type;
- if (last_type == MAP_DATA)
+ /* PR 10263: Disassemble data if requested to do so by the user. */
+ if (last_type == MAP_DATA && ((info->flags & DISASSEMBLE_DATA) == 0))
{
/* size was set above. */
info->bytes_per_chunk = size;
data = bfd_get_bits (buffer, size * 8,
info->display_endian == BFD_ENDIAN_BIG);
- (*printer) (pc, data, info);
+ (*printer) (pc, data, info, &errors);
return size;
}
fprintf (stream, _("\n\
aliases Do print instruction aliases.\n"));
+ fprintf (stream, _("\n\
+ no-notes Don't print instruction notes.\n"));
+
+ fprintf (stream, _("\n\
+ notes Do print instruction notes.\n"));
+
#ifdef DEBUG_AARCH64
fprintf (stream, _("\n\
debug_dump Temp switch for debug trace.\n"));