blob: 6e945705319be33dc37017cd24fe9b1b934b0615 [file] [log] [blame]
/* aarch64-dis.c -- AArch64 disassembler.
Copyright (C) 2009-2024 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of the GNU opcodes library.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
It is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING3. If not,
see <http://www.gnu.org/licenses/>. */
#include "sysdep.h"
#include <stdint.h>
#include "disassemble.h"
#include "libiberty.h"
#include "opintl.h"
#include "aarch64-dis.h"
#include "elf-bfd.h"
#include "safe-ctype.h"
#include "obstack.h"
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
#define INSNLEN 4
/* This character is used to encode style information within the output
buffers. See get_style_text and print_operands for more details. */
#define STYLE_MARKER_CHAR '\002'
/* Cached mapping symbol state. */
enum map_type
{
MAP_INSN,
MAP_DATA
};
static aarch64_feature_set arch_variant; /* See select_aarch64_variant. */
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. */
static 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)
{
}
static void
parse_aarch64_dis_option (const char *option, unsigned int len ATTRIBUTE_UNUSED)
{
/* Try to match options that are simple flags */
if (startswith (option, "no-aliases"))
{
no_aliases = 1;
return;
}
if (startswith (option, "aliases"))
{
no_aliases = 0;
return;
}
if (startswith (option, "no-notes"))
{
no_notes = 1;
return;
}
if (startswith (option, "notes"))
{
no_notes = 0;
return;
}
#ifdef DEBUG_AARCH64
if (startswith (option, "debug_dump"))
{
debug_dump = 1;
return;
}
#endif /* DEBUG_AARCH64 */
/* Invalid option. */
opcodes_error_handler (_("unrecognised disassembler option: %s"), option);
}
static void
parse_aarch64_dis_options (const char *options)
{
const char *option_end;
if (options == NULL)
return;
while (*options != '\0')
{
/* Skip empty options. */
if (*options == ',')
{
options++;
continue;
}
/* We know that *options is neither NUL or a comma. */
option_end = options + 1;
while (*option_end != ',' && *option_end != '\0')
option_end++;
parse_aarch64_dis_option (options, option_end - options);
/* Go on to the next one. If option_end points to a comma, it
will be skipped above. */
options = option_end;
}
}
/* Functions doing the instruction disassembling. */
/* The unnamed arguments consist of the number of fields and information about
these fields where the VALUE will be extracted from CODE and returned.
MASK can be zero or the base mask of the opcode.
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 fields H:L:M should be passed in
the order of H, L, M. */
aarch64_insn
extract_fields (aarch64_insn code, aarch64_insn mask, ...)
{
uint32_t num;
const aarch64_field *field;
enum aarch64_field_kind kind;
va_list va;
va_start (va, mask);
num = va_arg (va, uint32_t);
assert (num <= 5);
aarch64_insn value = 0x0;
while (num--)
{
kind = va_arg (va, enum aarch64_field_kind);
field = &fields[kind];
value <<= field->width;
value |= extract_field (kind, code, mask);
}
va_end (va);
return value;
}
/* Extract the value of all fields in SELF->fields after START from
instruction CODE. The least significant bit comes from the final field. */
static aarch64_insn
extract_all_fields_after (const aarch64_operand *self, unsigned int start,
aarch64_insn code)
{
aarch64_insn value;
unsigned int i;
enum aarch64_field_kind kind;
value = 0;
for (i = start;
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;
}
/* 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)
{
return extract_all_fields_after (self, 0, code);
}
/* Sign-extend bit I of VALUE. */
static inline uint64_t
sign_extend (aarch64_insn value, unsigned i)
{
uint64_t ret, sign;
assert (i < 32);
ret = value;
sign = (uint64_t) 1 << i;
return ((ret & (sign + sign - 1)) ^ sign) - sign;
}
/* N.B. the following inline helpfer functions create a dependency on the
order of operand qualifier enumerators. */
/* Given VALUE, return qualifier for a general purpose register. */
static inline enum aarch64_opnd_qualifier
get_greg_qualifier_from_value (aarch64_insn value)
{
enum aarch64_opnd_qualifier qualifier = AARCH64_OPND_QLF_W + value;
if (value <= 0x1
&& aarch64_get_qualifier_standard_value (qualifier) == value)
return qualifier;
return AARCH64_OPND_QLF_ERR;
}
/* 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;
if (value <= 0x8
&& aarch64_get_qualifier_standard_value (qualifier) == value)
return qualifier;
return AARCH64_OPND_QLF_ERR;
}
/* Given VALUE, return qualifier for an FP or AdvSIMD scalar register. */
static inline enum aarch64_opnd_qualifier
get_sreg_qualifier_from_value (aarch64_insn value)
{
enum aarch64_opnd_qualifier qualifier = AARCH64_OPND_QLF_S_B + value;
if (value <= 0x4
&& aarch64_get_qualifier_standard_value (qualifier) == value)
return qualifier;
return AARCH64_OPND_QLF_ERR;
}
/* Given the instruction in *INST which is probably half way through the
decoding and our caller wants to know the expected qualifier for operand
I. Return such a qualifier if we can establish it; otherwise return
AARCH64_OPND_QLF_NIL. */
static aarch64_opnd_qualifier_t
get_expected_qualifier (const aarch64_inst *inst, int i)
{
aarch64_opnd_qualifier_seq_t qualifiers;
/* Should not be called if the qualifier is known. */
if (inst->operands[i].qualifier == AARCH64_OPND_QLF_NIL)
{
int invalid_count;
if (aarch64_find_best_match (inst, inst->opcode->qualifiers_list,
i, qualifiers, &invalid_count))
return qualifiers[i];
else
return AARCH64_OPND_QLF_NIL;
}
else
return AARCH64_OPND_QLF_ERR;
}
/* Operand extractors. */
bool
aarch64_ext_none (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info ATTRIBUTE_UNUSED,
const aarch64_insn code ATTRIBUTE_UNUSED,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
return true;
}
bool
aarch64_ext_regno (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->reg.regno = (extract_field (self->fields[0], code, 0)
+ get_operand_specific_data (self));
return true;
}
bool
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 == 2
|| info->idx == 3
|| info->idx == 5);
unsigned prev_regno = inst->operands[info->idx - 1].reg.regno;
info->reg.regno = (prev_regno == 0x1f) ? 0x1f
: prev_regno + 1;
return true;
}
/* e.g. IC <ic_op>{, <Xt>}. */
bool
aarch64_ext_regrt_sysins (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->reg.regno = extract_field (self->fields[0], code, 0);
assert (info->idx == 1
&& (aarch64_get_operand_class (inst->operands[0].type)
== AARCH64_OPND_CLASS_SYSTEM));
/* This will make the constraint checking happy and more importantly will
help the disassembler determine whether this operand is optional or
not. */
info->present = aarch64_sys_ins_reg_has_xt (inst->operands[0].sysins_op);
return true;
}
/* e.g. SQDMLAL <Va><d>, <Vb><n>, <Vm>.<Ts>[<index>]. */
bool
aarch64_ext_reglane (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
/* regno */
info->reglane.regno = extract_field (self->fields[0], code,
inst->opcode->mask);
/* Index and/or type. */
if (inst->opcode->iclass == asisdone
|| inst->opcode->iclass == asimdins)
{
if (info->type == AARCH64_OPND_En
&& inst->opcode->operands[0] == AARCH64_OPND_Ed)
{
unsigned shift;
/* index2 for e.g. INS <Vd>.<Ts>[<index1>], <Vn>.<Ts>[<index2>]. */
assert (info->idx == 1); /* Vn */
aarch64_insn value = extract_field (FLD_imm4_11, code, 0);
/* Depend on AARCH64_OPND_Ed to determine the qualifier. */
info->qualifier = get_expected_qualifier (inst, info->idx);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
shift = get_logsz (aarch64_get_qualifier_esize (info->qualifier));
info->reglane.index = value >> shift;
}
else
{
/* index and type for e.g. DUP <V><d>, <Vn>.<T>[<index>].
imm5<3:0> <V>
0000 RESERVED
xxx1 B
xx10 H
x100 S
1000 D */
int pos = -1;
aarch64_insn value = extract_field (FLD_imm5, code, 0);
while (++pos <= 3 && (value & 0x1) == 0)
value >>= 1;
if (pos > 3)
return false;
info->qualifier = get_sreg_qualifier_from_value (pos);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
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);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
switch (info->qualifier)
{
case AARCH64_OPND_QLF_S_4B:
case AARCH64_OPND_QLF_S_2H:
/* L:H */
info->reglane.index = extract_fields (code, 0, 2, FLD_H, FLD_L);
info->reglane.regno &= 0x1f;
break;
case AARCH64_OPND_QLF_S_2B:
/* h:l:m */
info->reglane.index = extract_fields (code, 0, 3, FLD_H, FLD_L,
FLD_M);
info->reglane.regno &= 0xf;
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>]
or SQDMLAL <Va><d>, <Vb><n>, <Vm>.<Ts>[<index>]. */
/* Need information in other operand(s) to help decoding. */
info->qualifier = get_expected_qualifier (inst, info->idx);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
switch (info->qualifier)
{
case AARCH64_OPND_QLF_S_B:
/* H:imm3 */
info->reglane.index = extract_fields (code, 0, 2, FLD_H,
FLD_imm3_19);
info->reglane.regno &= 0x7;
break;
case AARCH64_OPND_QLF_S_H:
case AARCH64_OPND_QLF_S_2B:
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:
case AARCH64_OPND_QLF_S_4B:
/* h:l */
info->reglane.index = extract_fields (code, 0, 2, FLD_H, FLD_L);
break;
case AARCH64_OPND_QLF_S_D:
/* H */
info->reglane.index = extract_field (FLD_H, code, 0);
break;
default:
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 true;
}
bool
aarch64_ext_reglist (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
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;
info->reglist.stride = 1;
return true;
}
/* Decode Rt and opcode fields of Vt in AdvSIMD load/store instructions. */
bool
aarch64_ext_ldst_reglist (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
const aarch64_inst *inst,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn value;
/* Number of elements in each structure to be loaded/stored. */
unsigned expected_num = get_opcode_dependent_value (inst->opcode);
struct
{
unsigned is_reserved;
unsigned num_regs;
unsigned num_elements;
} data [] =
{ {0, 4, 4},
{1, 4, 4},
{0, 4, 1},
{0, 4, 2},
{0, 3, 3},
{1, 3, 3},
{0, 3, 1},
{0, 1, 1},
{0, 2, 2},
{1, 2, 2},
{0, 2, 1},
};
/* Rt */
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 false;
info->reglist.num_regs = data[value].num_regs;
info->reglist.stride = 1;
return true;
}
/* Decode Rt and S fields of Vt in AdvSIMD load single structure to all
lanes instructions. */
bool
aarch64_ext_ldst_reglist_r (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
const aarch64_inst *inst,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn value;
/* Rt */
info->reglist.first_regno = extract_field (FLD_Rt, code, 0);
/* S */
value = extract_field (FLD_S, code, 0);
/* Number of registers is equal to the number of elements in
each structure to be loaded/stored. */
info->reglist.num_regs = get_opcode_dependent_value (inst->opcode);
assert (info->reglist.num_regs >= 1 && info->reglist.num_regs <= 4);
/* Except when it is LD1R. */
if (info->reglist.num_regs == 1 && value == (aarch64_insn) 1)
info->reglist.num_regs = 2;
info->reglist.stride = 1;
return true;
}
/* Decode AdvSIMD vector register list for AdvSIMD lut instructions.
The number of of registers in the list is determined by the opcode
flag. */
bool
aarch64_ext_lut_reglist (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->reglist.first_regno = extract_field (self->fields[0], code, 0);
info->reglist.num_regs = get_opcode_dependent_value (inst->opcode);
info->reglist.stride = 1;
return true;
}
/* Decode Q, opcode<2:1>, S, size and Rt fields of Vt in AdvSIMD
load/store single element instructions. */
bool
aarch64_ext_ldst_elemlist (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_field field = {0, 0};
aarch64_insn QSsize; /* fields Q:S:size. */
aarch64_insn opcodeh2; /* opcode<2:1> */
/* Rt */
info->reglist.first_regno = extract_field (FLD_Rt, code, 0);
/* Decode the index, opcode<2:1> and size. */
gen_sub_field (FLD_asisdlso_opcode, 1, 2, &field);
opcodeh2 = extract_field_2 (&field, code, 0);
QSsize = extract_fields (code, 0, 3, FLD_Q, FLD_S, FLD_vldst_size);
switch (opcodeh2)
{
case 0x0:
info->qualifier = AARCH64_OPND_QLF_S_B;
/* Index encoded in "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;
/* Index encoded in "Q:S". */
info->reglist.index = QSsize >> 2;
}
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;
}
break;
default:
return false;
}
info->reglist.has_index = 1;
info->reglist.num_regs = 0;
info->reglist.stride = 1;
/* Number of registers is equal to the number of elements in
each structure to be loaded/stored. */
info->reglist.num_regs = get_opcode_dependent_value (inst->opcode);
assert (info->reglist.num_regs >= 1 && info->reglist.num_regs <= 4);
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>. */
bool
aarch64_ext_advsimd_imm_shift (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
const aarch64_inst *inst,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int pos;
aarch64_insn Q, imm, immh;
enum aarch64_insn_class iclass = inst->opcode->iclass;
immh = extract_field (FLD_immh, code, 0);
if (immh == 0)
return false;
imm = extract_fields (code, 0, 2, FLD_immh, FLD_immb);
pos = 4;
/* Get highest set bit in immh. */
while (--pos >= 0 && (immh & 0x8) == 0)
immh <<= 1;
assert ((iclass == asimdshf || iclass == asisdshf)
&& (info->type == AARCH64_OPND_IMM_VLSR
|| info->type == AARCH64_OPND_IMM_VLSL));
if (iclass == asimdshf)
{
Q = extract_field (FLD_Q, code, 0);
/* immh Q <T>
0000 x SEE AdvSIMD modified immediate
0001 0 8B
0001 1 16B
001x 0 4H
001x 1 8H
01xx 0 2S
01xx 1 4S
1xxx 0 RESERVED
1xxx 1 2D */
info->qualifier =
get_vreg_qualifier_from_value ((pos << 1) | (int) Q);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return false;
}
else
{
info->qualifier = get_sreg_qualifier_from_value (pos);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
}
if (info->type == AARCH64_OPND_IMM_VLSR)
/* immh <shift>
0000 SEE AdvSIMD modified immediate
0001 (16-UInt(immh:immb))
001x (32-UInt(immh:immb))
01xx (64-UInt(immh:immb))
1xxx (128-UInt(immh:immb)) */
info->imm.value = (16 << pos) - imm;
else
/* immh:immb
immh <shift>
0000 SEE AdvSIMD modified immediate
0001 (UInt(immh:immb)-8)
001x (UInt(immh:immb)-16)
01xx (UInt(immh:immb)-32)
1xxx (UInt(immh:immb)-64) */
info->imm.value = imm - (8 << pos);
return true;
}
/* Decode shift immediate for e.g. sshr (imm). */
bool
aarch64_ext_shll_imm (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int64_t imm;
aarch64_insn val;
val = extract_field (FLD_size, code, 0);
switch (val)
{
case 0: imm = 8; break;
case 1: imm = 16; break;
case 2: imm = 32; break;
default: return false;
}
info->imm.value = imm;
return true;
}
/* Decode imm for e.g. BFM <Wd>, <Wn>, #<immr>, #<imms>.
value in the field(s) will be extracted as unsigned immediate value. */
bool
aarch64_ext_imm (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
const aarch64_inst *inst,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
uint64_t imm;
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_three (self))
imm <<= 3;
else if (operand_need_shift_by_four (self))
imm <<= 4;
if (info->type == AARCH64_OPND_ADDR_ADRP)
imm <<= 12;
if (inst->operands[0].type == AARCH64_OPND_PSTATEFIELD
&& inst->operands[0].sysreg.flags & F_IMM_IN_CRM)
imm &= PSTATE_DECODE_CRM_IMM (inst->operands[0].sysreg.flags);
info->imm.value = imm;
return true;
}
/* Decode imm and its shifter for e.g. MOVZ <Wd>, #<imm16>{, LSL #<shift>}. */
bool
aarch64_ext_imm_half (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors)
{
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 true;
}
/* Decode cmode and "a:b:c:d:e:f:g:h" for e.g.
MOVI <Vd>.<T>, #<imm8> {, LSL #<amount>}. */
bool
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,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
uint64_t imm;
enum aarch64_opnd_qualifier opnd0_qualifier = inst->operands[0].qualifier;
aarch64_field field = {0, 0};
assert (info->idx == 1);
if (info->type == AARCH64_OPND_SIMD_FPIMM)
info->imm.is_fp = 1;
/* a:b:c:d:e:f:g:h */
imm = extract_fields (code, 0, 2, FLD_abc, FLD_defgh);
if (!info->imm.is_fp && aarch64_get_qualifier_esize (opnd0_qualifier) == 8)
{
/* Either MOVI <Dd>, #<imm>
or MOVI <Vd>.2D, #<imm>.
<imm> is a 64-bit immediate
'aaaaaaaabbbbbbbbccccccccddddddddeeeeeeeeffffffffgggggggghhhhhhhh',
encoded in "a:b:c:d:e:f:g:h". */
int i;
unsigned abcdefgh = imm;
for (imm = 0ull, i = 0; i < 8; i++)
if (((abcdefgh >> i) & 0x1) != 0)
imm |= 0xffull << (8 * i);
}
info->imm.value = imm;
/* cmode */
info->qualifier = get_expected_qualifier (inst, info->idx);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
switch (info->qualifier)
{
case AARCH64_OPND_QLF_NIL:
/* no shift */
info->shifter.kind = AARCH64_MOD_NONE;
return 1;
case AARCH64_OPND_QLF_LSL:
/* shift zeros */
info->shifter.kind = AARCH64_MOD_LSL;
switch (aarch64_get_qualifier_esize (opnd0_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: return false;
}
/* 00: 0; 01: 8; 10:16; 11:24. */
info->shifter.amount = extract_field_2 (&field, code, 0) << 3;
break;
case AARCH64_OPND_QLF_MSL:
/* shift ones */
info->shifter.kind = AARCH64_MOD_MSL;
gen_sub_field (FLD_cmode, 0, 1, &field); /* per word */
info->shifter.amount = extract_field_2 (&field, code, 0) ? 16 : 8;
break;
default:
return false;
}
return true;
}
/* Decode an 8-bit floating-point immediate. */
bool
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). */
bool
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). */
bool
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>. */
bool
aarch64_ext_fbits (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
info->imm.value = 64- extract_field (FLD_scale, code, 0);
return true;
}
/* Decode arithmetic immediate for e.g.
SUBS <Wd>, <Wn|WSP>, #<imm> {, <shift>}. */
bool
aarch64_ext_aimm (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info, const aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn value;
info->shifter.kind = AARCH64_MOD_LSL;
/* shift */
value = extract_field (FLD_shift, code, 0);
if (value >= 2)
return false;
info->shifter.amount = value ? 12 : 0;
/* imm12 (unsigned) */
info->imm.value = extract_field (FLD_imm12, code, 0);
return true;
}
/* 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 bool
decode_limm (uint32_t esize, aarch64_insn value, int64_t *result)
{
uint64_t imm, mask;
uint32_t N, R, S;
unsigned simd_size;
/* value is N:immr:imms. */
S = value & 0x3f;
R = (value >> 6) & 0x3f;
N = (value >> 12) & 0x1;
/* 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)
{
simd_size = 64;
mask = 0xffffffffffffffffull;
}
else
{
switch (S)
{
case 0x00 ... 0x1f: /* 0xxxxx */ simd_size = 32; break;
case 0x20 ... 0x2f: /* 10xxxx */ simd_size = 16; S &= 0xf; break;
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 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 false;
/* S+1 consecutive bits to 1. */
/* NOTE: S can't be 63 due to detection above. */
imm = (1ull << (S + 1)) - 1;
/* Rotate to the left by simd_size - R. */
if (R != 0)
imm = ((imm << (simd_size - R)) & mask) | (imm >> R);
/* Replicate the value according to SIMD size. */
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: return 0;
}
*result = imm & ~((uint64_t) -1 << (esize * 4) << (esize * 4));
return true;
}
/* Decode a logical immediate for e.g. ORR <Wd|WSP>, <Wn>, #<imm>. */
bool
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.). */
bool
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>. */
bool
aarch64_ext_ft (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
const aarch64_insn code, const aarch64_inst *inst,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn value;
/* Rt */
info->reg.regno = extract_field (FLD_Rt, code, 0);
/* size */
value = extract_field (FLD_ldst_size, code, 0);
if (inst->opcode->iclass == ldstpair_indexed
|| inst->opcode->iclass == ldstnapair_offs
|| inst->opcode->iclass == ldstpair_off
|| inst->opcode->iclass == loadlit)
{
enum aarch64_opnd_qualifier qualifier;
switch (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 false;
}
info->qualifier = qualifier;
}
else
{
/* opc1:size */
value = extract_fields (code, 0, 2, FLD_opc1, FLD_ldst_size);
if (value > 0x4)
return false;
info->qualifier = get_sreg_qualifier_from_value (value);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return false;
}
return true;
}
/* Decode the address operand for e.g. STXRB <Ws>, <Wt>, [<Xn|SP>{,#0}]. */
bool
aarch64_ext_addr_simple (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)
{
/* Rn */
info->addr.base_regno = extract_field (FLD_Rn, code, 0);
return true;
}
/* Decode the address operand for rcpc3 instructions with optional load/store
datasize offset, e.g. STILPP <Xs>, <Xt>, [<Xn|SP>{,#-16}]! and
LIDAP <Xs>, <Xt>, [<Xn|SP>]{,#-16}. */
bool
aarch64_ext_rcpc3_addr_opt_offset (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *err ATTRIBUTE_UNUSED)
{
info->addr.base_regno = extract_field (FLD_Rn, code, 0);
if (!extract_field (FLD_opc2, code, 0))
{
info->addr.writeback = 1;
enum aarch64_opnd type;
for (int i = 0; i < AARCH64_MAX_OPND_NUM; i++)
{
aarch64_opnd_info opnd = info[i];
type = opnd.type;
if (aarch64_operands[type].op_class == AARCH64_OPND_CLASS_ADDRESS)
break;
}
assert (aarch64_operands[type].op_class == AARCH64_OPND_CLASS_ADDRESS);
int offset = calc_ldst_datasize (inst->operands);
switch (type)
{
case AARCH64_OPND_RCPC3_ADDR_OPT_PREIND_WB:
case AARCH64_OPND_RCPC3_ADDR_PREIND_WB:
info->addr.offset.imm = -offset;
info->addr.preind = 1;
break;
case AARCH64_OPND_RCPC3_ADDR_OPT_POSTIND:
case AARCH64_OPND_RCPC3_ADDR_POSTIND:
info->addr.offset.imm = offset;
info->addr.postind = 1;
break;
default:
return false;
}
}
return true;
}
bool
aarch64_ext_rcpc3_addr_offset (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)
{
info->qualifier = get_expected_qualifier (inst, info->idx);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
/* 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);
return true;
}
/* Decode the address operand for e.g.
stlur <Xt>, [<Xn|SP>{, <amount>}]. */
bool
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);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
/* 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>}}]. */
bool
aarch64_ext_addr_regoff (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
aarch64_insn code, const aarch64_inst *inst,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn S, value;
/* Rn */
info->addr.base_regno = extract_field (FLD_Rn, code, 0);
/* Rm */
info->addr.offset.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 */);
/* Fix-up the shifter kind; although the table-driven approach is
efficient, it is slightly inflexible, thus needing this fix-up. */
if (info->shifter.kind == AARCH64_MOD_UXTX)
info->shifter.kind = AARCH64_MOD_LSL;
/* S */
S = extract_field (FLD_S, code, 0);
if (S == 0)
{
info->shifter.amount = 0;
info->shifter.amount_present = 0;
}
else
{
int size;
/* Need information in other operand(s) to help achieve the decoding
from 'S' field. */
info->qualifier = get_expected_qualifier (inst, info->idx);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
/* Get the size of the data element that is accessed, which may be
different from that of the source register size, e.g. in strb/ldrb. */
size = aarch64_get_qualifier_esize (info->qualifier);
info->shifter.amount = get_logsz (size);
info->shifter.amount_present = 1;
}
return true;
}
/* Decode the address operand for e.g. LDRSW <Xt>, [<Xn|SP>], #<simm>. */
bool
aarch64_ext_addr_simm (const aarch64_operand *self, aarch64_opnd_info *info,
aarch64_insn code, const aarch64_inst *inst,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
aarch64_insn imm;
info->qualifier = get_expected_qualifier (inst, info->idx);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
/* Rn */
info->addr.base_regno = extract_field (FLD_Rn, code, 0);
/* 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
|| 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 */
if (inst->opcode->iclass == ldst_unscaled
|| inst->opcode->iclass == ldstnapair_offs
|| inst->opcode->iclass == ldstpair_off
|| inst->opcode->iclass == ldst_unpriv)
info->addr.writeback = 0;
else
{
/* pre/post- index */
info->addr.writeback = 1;
if (extract_field (self->fields[1], code, 0) == 1)
info->addr.preind = 1;
else
info->addr.postind = 1;
}
return true;
}
/* Decode the address operand for e.g. LDRSW <Xt>, [<Xn|SP>{, #<simm>}]. */
bool
aarch64_ext_addr_uimm12 (const aarch64_operand *self, aarch64_opnd_info *info,
aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int shift;
info->qualifier = get_expected_qualifier (inst, info->idx);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
shift = get_logsz (aarch64_get_qualifier_esize (info->qualifier));
/* Rn */
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 true;
}
/* Decode the address operand for e.g. LDRAA <Xt>, [<Xn|SP>{, #<simm>}]. */
bool
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);
if (info->qualifier == AARCH64_OPND_QLF_ERR)
return 0;
/* 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>>. */
bool
aarch64_ext_simd_addr_post (const aarch64_operand *self ATTRIBUTE_UNUSED,
aarch64_opnd_info *info,
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. */
int is_ld1r = get_opcode_dependent_value (inst->opcode) == 1;
/* Rn */
info->addr.base_regno = extract_field (FLD_Rn, code, 0);
/* Rm | #<amount> */
info->addr.offset.regno = extract_field (FLD_Rm, code, 0);
if (info->addr.offset.regno == 31)
{
if (inst->opcode->operands[0] == AARCH64_OPND_LVt_AL)
/* Special handling of loading single structure to all lane. */
info->addr.offset.imm = (is_ld1r ? 1
: inst->operands[0].reglist.num_regs)
* aarch64_get_qualifier_esize (inst->operands[0].qualifier);
else
info->addr.offset.imm = inst->operands[0].reglist.num_regs
* aarch64_get_qualifier_esize (inst->operands[0].qualifier)
* aarch64_get_qualifier_nelem (inst->operands[0].qualifier);
}
else
info->addr.offset.is_reg = 1;
info->addr.writeback = 1;
return true;
}
/* Decode the condition operand for e.g. CSEL <Xd>, <Xn>, <Xm>, <cond>. */
bool
aarch64_ext_cond (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;
/* cond */
value = extract_field (FLD_cond, code, 0);
info->cond = get_cond_from_value (value);
return true;
}
/* Decode the system register operand for e.g. MRS <Xt>, <systemreg>. */
bool
aarch64_ext_sysreg (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)
{
/* op0:op1:CRn:CRm:op2 */
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>. */
bool
aarch64_ext_pstatefield (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)
{
int i;
aarch64_insn fld_crm = extract_field (FLD_CRm, code, 0);
/* 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)
{
/* PSTATEFIELD name can be encoded partially in CRm[3:1]. */
uint32_t flags = aarch64_pstatefields[i].flags;
if ((flags & F_REG_IN_CRM)
&& ((fld_crm & 0xe) != PSTATE_DECODE_CRM (flags)))
continue;
info->sysreg.flags = flags;
return true;
}
/* Reserved value in <pstatefield>. */
return false;
}
/* Decode the system instruction op operand for e.g. AT <at_op>, <Xt>. */
bool
aarch64_ext_sysins_op (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)
{
int i;
aarch64_insn value;
const aarch64_sys_ins_reg *sysins_ops;
/* op0:op1:CRn:CRm:op2 */
value = extract_fields (code, 0, 5,
FLD_op0, FLD_op1, FLD_CRn,
FLD_CRm, FLD_op2);
switch (info->type)
{
case AARCH64_OPND_SYSREG_AT: sysins_ops = aarch64_sys_regs_at; break;
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;
case AARCH64_OPND_SYSREG_TLBIP: sysins_ops = aarch64_sys_regs_tlbi; break;
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: return false;
}
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->name,
(unsigned)info->sysins_op->value,
aarch64_sys_ins_reg_has_xt (info->sysins_op), i);
return true;
}
return false;
}
/* Decode the memory barrier option operand for e.g. DMB <option>|#<imm>. */
bool
aarch64_ext_barrier (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 */
info->barrier = aarch64_barrier_options + extract_field (FLD_CRm, code, 0);
return true;
}
/* Decode the memory barrier option operand for DSB <option>nXS|#<imm>. */
bool
aarch64_ext_barrier_dsb_nxs (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)
{
/* For the DSB nXS barrier variant immediate is encoded in 2-bit field. */
aarch64_insn field = extract_field (FLD_CRm_dsb_nxs, code, 0);
info->barrier = aarch64_barrier_dsb_nxs_options + field;
return true;
}
/* Decode the prefetch operation option operand for e.g.
PRFM <prfop>, [<Xn|SP>{, #<pimm>}]. */
bool
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. */
bool
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>}}]. */
bool
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_10, 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>}. */
bool
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_10, code, 0);
/* This makes the constraint checking happy. */
info->shifter.operator_present = 1;
return true;
}
/* Decode the LSL-shifted register operand for e.g.
ADDPT <Xd|SP>, <Xn|SP>, <Xm>{, LSL #<amount>}. */
bool
aarch64_ext_reg_lsl_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)
{
/* Rm */
info->reg.regno = extract_field (FLD_Rm, code, 0);
/* imm3 */
info->shifter.kind = AARCH64_MOD_LSL;
info->shifter.amount = extract_field (FLD_imm3_10, code, 0);
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 bool
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. */
bool
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. */
bool
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. */
bool
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_10);
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 bool
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. */
bool
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. */
bool
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. */
bool
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. */
bool
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. */
bool
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 bool
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. */
bool
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. */
bool
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. */
bool
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 bool
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. */
bool
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));
}
bool
aarch64_ext_sve_aligned_reglist (const aarch64_operand *self,
aarch64_opnd_info *info, aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
unsigned int num_regs = get_operand_specific_data (self);
unsigned int val = extract_field (self->fields[0], code, 0);
info->reglist.first_regno = val * num_regs;
info->reglist.num_regs = num_regs;
info->reglist.stride = 1;
return true;
}
/* Decode an SVE CPY/DUP immediate. */
bool
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. */
bool
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. */
bool
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. */
bool
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 SME instruction such as MOVZA ZA tile slice to vector. */
bool
aarch64_ext_sme_za_tile_to_vec (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 Qsize; /* fields Q:S:size. */
int fld_v = extract_field (self->fields[0], code, 0);
int fld_rv = extract_field (self->fields[1], code, 0);
int fld_zan_imm = extract_field (FLD_imm4_5, code, 0);
Qsize = extract_fields (inst->value, 0, 2, FLD_SME_size_22, FLD_SME_Q);
switch (Qsize)
{
case 0x0:
info->qualifier = AARCH64_OPND_QLF_S_B;
info->indexed_za.regno = 0;
info->indexed_za.index.imm = fld_zan_imm;
break;
case 0x2:
info->qualifier = AARCH64_OPND_QLF_S_H;
info->indexed_za.regno = fld_zan_imm >> 3;
info->indexed_za.index.imm = fld_zan_imm & 0x07;
break;
case 0x4:
info->qualifier = AARCH64_OPND_QLF_S_S;
info->indexed_za.regno = fld_zan_imm >> 2;
info->indexed_za.index.imm = fld_zan_imm & 0x03;
break;
case 0x6:
info->qualifier = AARCH64_OPND_QLF_S_D;
info->indexed_za.regno = fld_zan_imm >> 1;
info->indexed_za.index.imm = fld_zan_imm & 0x01;
break;
case 0x7:
info->qualifier = AARCH64_OPND_QLF_S_Q;
info->indexed_za.regno = fld_zan_imm;
break;
default:
return false;
}
info->indexed_za.index.regno = fld_rv + 12;
info->indexed_za.v = fld_v;
return true;
}
/* Decode ZA tile vector, vector indicator, vector selector, qualifier and
immediate on numerous SME instruction fields such as MOVA. */
bool
aarch64_ext_sme_za_hv_tiles (const aarch64_operand *self,
aarch64_opnd_info *info, aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int fld_size = extract_field (self->fields[0], code, 0);
int fld_q = extract_field (self->fields[1], code, 0);
int fld_v = extract_field (self->fields[2], code, 0);
int fld_rv = extract_field (self->fields[3], code, 0);
int fld_zan_imm = extract_field (self->fields[4], code, 0);
/* Deduce qualifier encoded in size and Q fields. */
if (fld_size == 0)
{
info->indexed_za.regno = 0;
info->indexed_za.index.imm = fld_zan_imm;
}
else if (fld_size == 1)
{
info->indexed_za.regno = fld_zan_imm >> 3;
info->indexed_za.index.imm = fld_zan_imm & 0x07;
}
else if (fld_size == 2)
{
info->indexed_za.regno = fld_zan_imm >> 2;
info->indexed_za.index.imm = fld_zan_imm & 0x03;
}
else if (fld_size == 3 && fld_q == 0)
{
info->indexed_za.regno = fld_zan_imm >> 1;
info->indexed_za.index.imm = fld_zan_imm & 0x01;
}
else if (fld_size == 3 && fld_q == 1)
{
info->indexed_za.regno = fld_zan_imm;
info->indexed_za.index.imm = 0;
}
else
return false;
info->indexed_za.index.regno = fld_rv + 12;
info->indexed_za.v = fld_v;
return true;
}
bool
aarch64_ext_sme_za_hv_tiles_range (const aarch64_operand *self,
aarch64_opnd_info *info, aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors
ATTRIBUTE_UNUSED)
{
int ebytes = aarch64_get_qualifier_esize (info->qualifier);
int range_size = get_opcode_dependent_value (inst->opcode);
int fld_v = extract_field (self->fields[0], code, 0);
int fld_rv = extract_field (self->fields[1], code, 0);
int fld_zan_imm = extract_field (self->fields[2], code, 0);
int max_value = 16 / range_size / ebytes;
if (max_value == 0)
max_value = 1;
int regno = fld_zan_imm / max_value;
if (regno >= ebytes)
return false;
info->indexed_za.regno = regno;
info->indexed_za.index.imm = (fld_zan_imm % max_value) * range_size;
info->indexed_za.index.countm1 = range_size - 1;
info->indexed_za.index.regno = fld_rv + 12;
info->indexed_za.v = fld_v;
return true;
}
/* Decode in SME instruction ZERO list of up to eight 64-bit element tile names
separated by commas, encoded in the "imm8" field.
For programmer convenience an assembler must also accept the names of
32-bit, 16-bit and 8-bit element tiles which are converted into the
corresponding set of 64-bit element tiles.
*/
bool
aarch64_ext_sme_za_list (const aarch64_operand *self,
aarch64_opnd_info *info, aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int mask = extract_field (self->fields[0], code, 0);
info->imm.value = mask;
return true;
}
/* Decode ZA array vector select register (Rv field), optional vector and
memory offset (imm4_11 field).
*/
bool
aarch64_ext_sme_za_array (const aarch64_operand *self,
aarch64_opnd_info *info, aarch64_insn code,
const aarch64_inst *inst,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int regno = extract_field (self->fields[0], code, 0);
if (info->type == AARCH64_OPND_SME_ZA_array_off4)
regno += 12;
else
regno += 8;
int imm = extract_field (self->fields[1], code, 0);
int num_offsets = get_operand_specific_data (self);
if (num_offsets == 0)
num_offsets = 1;
info->indexed_za.index.regno = regno;
info->indexed_za.index.imm = imm * num_offsets;
info->indexed_za.index.countm1 = num_offsets - 1;
info->indexed_za.group_size = get_opcode_dependent_value (inst->opcode);
return true;
}
/* Decode two ZA tile slice (V, Rv, off3| ZAn ,off2 | ZAn, ol| ZAn) feilds. */
bool
aarch64_ext_sme_za_vrs1 (const aarch64_operand *self,
aarch64_opnd_info *info, aarch64_insn code,
const aarch64_inst *inst,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int v = extract_field (self->fields[0], code, 0);
int regno = 12 + extract_field (self->fields[1], code, 0);
int imm, za_reg, num_offset = 2;
switch (info->qualifier)
{
case AARCH64_OPND_QLF_S_B:
imm = extract_field (self->fields[2], code, 0);
info->indexed_za.index.imm = imm * num_offset;
break;
case AARCH64_OPND_QLF_S_H:
case AARCH64_OPND_QLF_S_S:
za_reg = extract_field (self->fields[2], code, 0);
imm = extract_field (self->fields[3], code, 0);
info->indexed_za.index.imm = imm * num_offset;
info->indexed_za.regno = za_reg;
break;
case AARCH64_OPND_QLF_S_D:
za_reg = extract_field (self->fields[2], code, 0);
info->indexed_za.regno = za_reg;
break;
default:
return false;
}
info->indexed_za.index.regno = regno;
info->indexed_za.index.countm1 = num_offset - 1;
info->indexed_za.v = v;
info->indexed_za.group_size = get_opcode_dependent_value (inst->opcode);
return true;
}
/* Decode four ZA tile slice (V, Rv, off3| ZAn ,off2 | ZAn, ol| ZAn) feilds. */
bool
aarch64_ext_sme_za_vrs2 (const aarch64_operand *self,
aarch64_opnd_info *info, aarch64_insn code,
const aarch64_inst *inst,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int v = extract_field (self->fields[0], code, 0);
int regno = 12 + extract_field (self->fields[1], code, 0);
int imm, za_reg, num_offset =4;
switch (info->qualifier)
{
case AARCH64_OPND_QLF_S_B:
imm = extract_field (self->fields[2], code, 0);
info->indexed_za.index.imm = imm * num_offset;
break;
case AARCH64_OPND_QLF_S_H:
za_reg = extract_field (self->fields[2], code, 0);
imm = extract_field (self->fields[3], code, 0);
info->indexed_za.index.imm = imm * num_offset;
info->indexed_za.regno = za_reg;
break;
case AARCH64_OPND_QLF_S_S:
case AARCH64_OPND_QLF_S_D:
za_reg = extract_field (self->fields[2], code, 0);
info->indexed_za.regno = za_reg;
break;
default:
return false;
}
info->indexed_za.index.regno = regno;
info->indexed_za.index.countm1 = num_offset - 1;
info->indexed_za.v = v;
info->indexed_za.group_size = get_opcode_dependent_value (inst->opcode);
return true;
}
bool
aarch64_ext_sme_addr_ri_u4xvl (const aarch64_operand *self,
aarch64_opnd_info *info, aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int regno = extract_field (self->fields[0], code, 0);
int imm = extract_field (self->fields[1], code, 0);
info->addr.base_regno = regno;
info->addr.offset.imm = imm;
/* MUL VL operator is always present for this operand. */
info->shifter.kind = AARCH64_MOD_MUL_VL;
info->shifter.operator_present = (imm != 0);
return true;
}
/* Decode {SM|ZA} filed for SMSTART and SMSTOP instructions. */
bool
aarch64_ext_sme_sm_za (const aarch64_operand *self,
aarch64_opnd_info *info, aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
info->pstatefield = 0x1b;
aarch64_insn fld_crm = extract_field (self->fields[0], code, 0);
fld_crm >>= 1; /* CRm[3:1]. */
if (fld_crm == 0x1)
info->reg.regno = 's';
else if (fld_crm == 0x2)
info->reg.regno = 'z';
else
return false;
return true;
}
bool
aarch64_ext_sme_pred_reg_with_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 fld_rm = extract_field (self->fields[0], code, 0);
aarch64_insn fld_pn = extract_field (self->fields[1], code, 0);
aarch64_insn fld_i1 = extract_field (self->fields[2], code, 0);
aarch64_insn fld_tszh = extract_field (self->fields[3], code, 0);
aarch64_insn fld_tszl = extract_field (self->fields[4], code, 0);
int imm;
info->indexed_za.regno = fld_pn;
info->indexed_za.index.regno = fld_rm + 12;
if (fld_tszl & 0x1)
imm = (fld_i1 << 3) | (fld_tszh << 2) | (fld_tszl >> 1);
else if (fld_tszl & 0x2)
imm = (fld_i1 << 2) | (fld_tszh << 1) | (fld_tszl >> 2);
else if (fld_tszl & 0x4)
imm = (fld_i1 << 1) | fld_tszh;
else if (fld_tszh)
imm = fld_i1;
else
return false;
info->indexed_za.index.imm = imm;
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. */
bool
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_all_fields_after (self, 1, code);
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. */
bool
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)
{
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 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. */
bool
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)
{
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;
}
/* 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. */
bool
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);
info->reglist.stride = 1;
return true;
}
/* 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. */
bool
aarch64_ext_sve_reglist_zt (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_operand_specific_data (self);
info->reglist.stride = 1;
return true;
}
/* Decode a strided register list. The first field holds the top bit
(0 or 16) and the second field holds the lower bits. The stride is
16 divided by the list length. */
bool
aarch64_ext_sve_strided_reglist (const aarch64_operand *self,
aarch64_opnd_info *info, aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors
ATTRIBUTE_UNUSED)
{
unsigned int upper = extract_field (self->fields[0], code, 0);
unsigned int lower = extract_field (self->fields[1], code, 0);
info->reglist.first_regno = upper * 16 + lower;
info->reglist.num_regs = get_operand_specific_data (self);
info->reglist.stride = 16 / info->reglist.num_regs;
return true;
}
/* Decode <pattern>{, MUL #<amount>}. The fields array specifies which
fields to use for <pattern>. <amount> - 1 is encoded in the SVE_imm4
field. */
bool
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;
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 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. */
bool
aarch64_ext_sve_shlimm (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;
info->imm.value -= get_top_bit (info->imm.value);
return true;
}
/* Decode an SVE shift-right immediate. */
bool
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;
info->imm.value = get_top_bit (info->imm.value) * 2 - info->imm.value;
return true;
}
/* Decode X0-X30. Register 31 is unallocated. */
bool
aarch64_ext_x0_to_x30 (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->reg.regno = extract_field (self->fields[0], code, 0);
return info->reg.regno <= 30;
}
/* Decode an indexed register, with the first field being the register
number and the remaining fields being the index. */
bool
aarch64_ext_simple_index (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
int bias = get_operand_specific_data (self);
info->reglane.regno = extract_field (self->fields[0], code, 0) + bias;
info->reglane.index = extract_all_fields_after (self, 1, code);
return true;
}
/* Decode a plain shift-right immediate, when there is only a single
element size. */
bool
aarch64_ext_plain_shrimm (const aarch64_operand *self, aarch64_opnd_info *info,
const aarch64_insn code,
const aarch64_inst *inst ATTRIBUTE_UNUSED,
aarch64_operand_error *errors ATTRIBUTE_UNUSED)
{
unsigned int base = 1 << get_operand_field_width (self, 0);
info->imm.value = base - extract_field (self->fields[0], code, 0);
return true;
}
/* Bitfields that are commonly used to encode certain operands' information
may be partially used as part of the base opcode in some instructions.
For example, the bit 1 of the field 'size' in
FCVTXN <Vb><d>, <Va><n>
is actually part of the base opcode, while only size<0> is available
for encoding the register type. Another example is the AdvSIMD
instruction ORR (register), in which the field 'size' is also used for
the base opcode, leaving only the field 'Q' available to encode the
vector register arrangement specifier '8B' or '16B'.
This function tries to deduce the qualifier from the value of partially
constrained field(s). Given the VALUE of such a field or fields, the
qualifiers CANDIDATES and the MASK (indicating which bits are valid for
operand encoding), the function returns the matching qualifier or
AARCH64_OPND_QLF_NIL if nothing matches.
N.B. CANDIDATES is a group of possible qualifiers that are valid for
one operand; it has a maximum of AARCH64_MAX_QLF_SEQ_NUM qualifiers and
may end with AARCH64_OPND_QLF_NIL. */
static enum aarch64_opnd_qualifier
get_qualifier_from_partial_encoding (aarch64_insn value,
const enum aarch64_opnd_qualifier* \
candidates,
aarch64_insn mask)
{
int i;
DEBUG_TRACE ("enter with value: %d, mask: %d", (int)value, (int)mask);
for (i = 0; i < AARCH64_MAX_QLF_SEQ_NUM; ++i)
{
aarch64_insn standard_value;
if (candidates[i] == AARCH64_OPND_QLF_NIL)
break;
standard_value = aarch64_get_qualifier_standard_value (candidates[i]);
if ((standard_value & mask) == (value & mask))
return candidates[i];
}
return AARCH64_OPND_QLF_NIL;
}
/* Given a list of qualifier sequences, return all possible valid qualifiers
for operand IDX in QUALIFIERS.
Assume QUALIFIERS is an array whose length is large enough. */
static void
get_operand_possible_qualifiers (int idx,
const aarch64_opnd_qualifier_seq_t *list,
enum aarch64_opnd_qualifier *qualifiers)
{
int i;
for (i = 0; i < AARCH64_MAX_QLF_SEQ_NUM; ++i)
if ((qualifiers[i] = list[i][idx]) == AARCH64_OPND_QLF_NIL)
break;
}
/* Decode the size Q field for e.g. SHADD.
We tag one operand with the qualifer according to the code;
whether the qualifier is valid for this opcode or not, it is the
duty of the semantic checking. */
static int
decode_sizeq (aarch64_inst *inst)
{
int idx;
enum aarch64_opnd_qualifier qualifier;
aarch64_insn code;
aarch64_insn value, mask;
enum aarch64_field_kind fld_sz;
enum aarch64_opnd_qualifier candidates[AARCH64_MAX_QLF_SEQ_NUM];
if (inst->opcode->iclass == asisdlse
|| inst->opcode->iclass == asisdlsep
|| inst->opcode->iclass == asisdlso
|| inst->opcode->iclass == asisdlsop)
fld_sz = FLD_vldst_size;
else
fld_sz = FLD_size;
code = inst->value;
value = extract_fields (code, inst->opcode->mask, 2, fld_sz, FLD_Q);
/* Obtain the info that which bits of fields Q and size are actually
available for operand encoding. Opcodes like FMAXNM and FMLA have
size[1] unavailable. */
mask = extract_fields (~inst->opcode->mask, 0, 2, fld_sz, FLD_Q);
/* The index of the operand we are going to tag a qualifier and the qualifer
itself are reasoned from the value of the size and Q fields and the
possible valid qualifier lists. */
idx = aarch64_select_operand_for_sizeq_field_coding (inst->opcode);
DEBUG_TRACE ("key idx: %d", idx);
/* For most related instruciton, size:Q are fully available for operand
encoding. */
if (mask == 0x7)
{
inst->operands[idx].qualifier = get_vreg_qualifier_from_value (value);
if (inst->operands[idx].qualifier == AARCH64_OPND_QLF_ERR)
return 0;
return 1;
}
get_operand_possible_qualifiers (idx, inst->opcode->qualifiers_list,
candidates);
#ifdef DEBUG_AARCH64
if (debug_dump)
{
int i;
for (i = 0; candidates[i] != AARCH64_OPND_QLF_NIL
&& i < AARCH64_MAX_QLF_SEQ_NUM; ++i)
DEBUG_TRACE ("qualifier %d: %s", i,
aarch64_get_qualifier_name(candidates[i]));
DEBUG_TRACE ("%d, %d", (int)value, (int)mask);
}
#endif /* DEBUG_AARCH64 */
qualifier = get_qualifier_from_partial_encoding (value, candidates, mask);
if (qualifier == AARCH64_OPND_QLF_NIL)
return 0;
inst->operands[idx].qualifier = qualifier;
return 1;
}
/* Decode size[0]:Q, i.e. bit 22 and bit 30, for
e.g. FCVTN<Q> <Vd>.<Tb>, <Vn>.<Ta>. */
static int
decode_asimd_fcvt (aarch64_inst *inst)
{
aarch64_field field = {0, 0};
aarch64_insn value;
enum aarch64_opnd_qualifier qualifier;
gen_sub_field (FLD_size, 0, 1, &field);
value = extract_field_2 (&field, inst->value, 0);
qualifier = value == 0 ? AARCH64_OPND_QLF_V_4S
: AARCH64_OPND_QLF_V_2D;
switch (inst->opcode->op)
{
case OP_FCVTN:
case OP_FCVTN2:
/* FCVTN<Q> <Vd>.<Tb>, <Vn>.<Ta>. */
inst->operands[1].qualifier = qualifier;
break;
case OP_FCVTL:
case OP_FCVTL2:
/* FCVTL<Q> <Vd>.<Ta>, <Vn>.<Tb>. */
inst->operands[0].qualifier = qualifier;
break;
default:
return 0;
}
return 1;
}
/* Decode size[0], i.e. bit 22, for
e.g. FCVTXN <Vb><d>, <Va><n>. */
static int
decode_asisd_fcvtxn (aarch64_inst *inst)
{
aarch64_field field = {0, 0};
gen_sub_field (FLD_size, 0, 1, &field);
if (!extract_field_2 (&field, inst->value, 0))
return 0;
inst->operands[0].qualifier = AARCH64_OPND_QLF_S_S;
return 1;
}
/* Decode the 'opc' field for e.g. FCVT <Dd>, <Sn>. */
static int
decode_fcvt (aarch64_inst *inst)
{
enum aarch64_opnd_qualifier qualifier;
aarch64_insn value;
const aarch64_field field = {15, 2};
/* opc dstsize */
value = extract_field_2 (&field, inst->value, 0);
switch (value)
{
case 0: qualifier = AARCH64_OPND_QLF_S_S; break;
case 1: qualifier = AARCH64_OPND_QLF_S_D; break;
case 3: qualifier = AARCH64_OPND_QLF_S_H; break;
default: return 0;
}
inst->operands[0].qualifier = qualifier;
return 1;
}
/* Do miscellaneous decodings that are not common enough to be driven by
flags. */
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;
}
}
/* Opcodes that have fields shared by multiple operands are usually flagged
with flags. In this function, we detect such flags, decode the related
field(s) and store the information in one of the related operands. The
'one' operand is not any operand but one of the operands that can
accommadate all the information that has been decoded. */
static int
do_special_decoding (aarch64_inst *inst)
{
int idx;
aarch64_insn value;
/* Condition for truly conditional executed instructions, e.g. b.cond. */
if (inst->opcode->flags & F_COND)
{
value = extract_field (FLD_cond2, inst->value, 0);
inst->cond = get_cond_from_value (value);
}
/* 'sf' field. */
if (inst->opcode->flags & F_SF)
{
idx = select_operand_for_sf_field_coding (inst->opcode);
value = extract_field (FLD_sf, inst->value, 0);
inst->operands[idx].qualifier = get_greg_qualifier_from_value (value);
if (inst->operands[idx].qualifier == AARCH64_OPND_QLF_ERR)
return 0;
if ((inst->opcode->flags & F_N)
&& 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);
if (inst->operands[idx].qualifier == AARCH64_OPND_QLF_ERR)
return 0;
}
/* rcpc3 'size' field. */
if (inst->opcode->flags & F_RCPC3_SIZE)
{
value = extract_field (FLD_rcpc3_size, inst->value, 0);
for (int i = 0;
aarch64_operands[inst->operands[i].type].op_class != AARCH64_OPND_CLASS_ADDRESS;
i++)
{
if (aarch64_operands[inst->operands[i].type].op_class
== AARCH64_OPND_CLASS_INT_REG)
{
inst->operands[i].qualifier = get_greg_qualifier_from_value (value & 1);
if (inst->operands[i].qualifier == AARCH64_OPND_QLF_ERR)
return 0;
}
else if (aarch64_operands[inst->operands[i].type].op_class
== AARCH64_OPND_CLASS_FP_REG)
{
value += (extract_field (FLD_opc1, inst->value, 0) << 2);
inst->operands[i].qualifier = get_sreg_qualifier_from_value (value);
if (inst->operands[i].qualifier == AARCH64_OPND_QLF_ERR)
return 0;
}
}
}
/* size:Q fields. */
if (inst->opcode->flags & F_SIZEQ)
return decode_sizeq (inst);
if (inst->opcode->flags & F_FPTYPE)
{
idx = select_operand_for_fptype_field_coding (inst->opcode);
value = extract_field (FLD_type, inst->value, 0);
switch (value)
{
case 0: inst->operands[idx].qualifier = AARCH64_OPND_QLF_S_S; break;
case 1: inst->operands[idx].qualifier = AARCH64_OPND_QLF_S_D; break;
case 3: inst->operands[idx].qualifier = AARCH64_OPND_QLF_S_H; break;
default: return 0;
}
}
if (inst->opcode->flags & F_SSIZE)
{
/* N.B. some opcodes like FCMGT <V><d>, <V><n>, #0 have the size[1] as part
of the base opcode. */
aarch64_insn mask;
enum aarch64_opnd_qualifier candidates[AARCH64_MAX_QLF_SEQ_NUM];
idx = select_operand_for_scalar_size_field_coding (inst->opcode);
value = extract_field (FLD_size, inst->value, inst->opcode->mask);
mask = extract_field (FLD_size, ~inst->opcode->mask, 0);
/* For most related instruciton, the 'size' field is fully available for
operand encoding. */
if (mask == 0x3)
{
inst->operands[idx].qualifier = get_sreg_qualifier_from_value (value);
if (inst->operands[idx].qualifier == AARCH64_OPND_QLF_ERR)
return 0;
}
else
{
get_operand_possible_qualifiers (idx, inst->opcode->qualifiers_list,
candidates);
inst->operands[idx].qualifier
= get_qualifier_from_partial_encoding (value, candidates, mask);
}
}
if (inst->opcode->flags & F_T)
{
/* Num of consecutive '0's on the right side of imm5<3:0>. */
int num = 0;
unsigned val, Q;
assert (aarch64_get_operand_class (inst->opcode->operands[0])
== AARCH64_OPND_CLASS_SIMD_REG);
/* imm5<3:0> q <t>
0000 x reserved
xxx1 0 8b
xxx1 1 16b
xx10 0 4h
xx10 1 8h
x100 0 2s
x100 1 4s
1000 0 reserved
1000 1 2d */
val = extract_field (FLD_imm5, inst->value, 0);
while ((val & 0x1) == 0 && ++num <= 3)
val >>= 1;
if (num > 3)
return 0;
Q = (unsigned) extract_field (FLD_Q, inst->value, inst->opcode->mask);
inst->operands[0].qualifier =
get_vreg_qualifier_from_value ((num << 1) | Q);
if (inst->operands[0].qualifier == AARCH64_OPND_QLF_ERR)
return 0;
}
if ((inst->opcode->flags & F_OPD_SIZE) && inst->opcode->iclass == sve2_urqvs)
{
unsigned size;
size = (unsigned) extract_field (FLD_size, inst->value,
inst->opcode->mask);
inst->operands[0].qualifier
= get_vreg_qualifier_from_value (1 + (size << 1));
if (inst->operands[0].qualifier == AARCH64_OPND_QLF_ERR)
return 0;
inst->operands[2].qualifier = get_sreg_qualifier_from_value (size);
if (inst->operands[2].qualifier == AARCH64_OPND_QLF_ERR)
return 0;
}
if (inst->opcode->flags & F_GPRSIZE_IN_Q)
{
/* Use Rt to encode in the case of e.g.
STXP <Ws>, <Xt1>, <Xt2>, [<Xn|SP>{,#0}]. */
idx = aarch64_operand_index (inst->opcode->operands, AARCH64_OPND_Rt);
if (idx == -1)
{
/* Otherwise use the result operand, which has to be a integer
register. */
assert (aarch64_get_operand_class (inst->opcode->operands[0])
== AARCH64_OPND_CLASS_INT_REG);
idx = 0;
}
assert (idx == 0 || idx == 1);
value = extract_field (FLD_Q, inst->value, 0);
inst->operands[idx].qualifier = get_greg_qualifier_from_value (value);
if (inst->operands[idx].qualifier == AARCH64_OPND_QLF_ERR)
return 0;
}
if (inst->opcode->flags & F_LDS_SIZE)
{
aarch64_field field = {0, 0};
assert (aarch64_get_operand_class (inst->opcode->operands[0])
== AARCH64_OPND_CLASS_INT_REG);
gen_sub_field (FLD_opc, 0, 1, &field);
value = extract_field_2 (&field, inst->value, 0);
inst->operands[0].qualifier
= value ? AARCH64_OPND_QLF_W : AARCH64_OPND_QLF_X;
}
/* Miscellaneous decoding; done as the last step. */
if (inst->opcode->flags & F_MISC)
return do_misc_decoding (inst);
return 1;
}
/* Converters converting a real opcode instruction to its alias form. */
/* ROR <Wd>, <Ws>, #<shift>
is equivalent to:
EXTR <Wd>, <Ws>, <Ws>, #<shift>. */
static int
convert_extr_to_ror (aarch64_inst *inst)
{
if (inst->operands[1].reg.regno == inst->operands[2].reg.regno)
{
copy_operand_info (inst, 2, 3);
inst->operands[3].type = AARCH64_OPND_NIL;
return 1;
}
return 0;
}
/* UXTL<Q> <Vd>.<Ta>, <Vn>.<Tb>
is equivalent to:
USHLL<Q> <Vd>.<Ta>, <Vn>.<Tb>, #0. */
static int
convert_shll_to_xtl (aarch64_inst *inst)
{
if (inst->operands[2].<