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