| /* 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].<
|