X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=opcodes%2Faarch64-dis.c;h=4c3b521d0de9f73f5944c7469fe71c2d8997ae19;hb=8a7f0c1b5ae35d041886855ac7ca9b9533e8788a;hp=d8bd29010bd32f5d217be64c4086634627435619;hpb=e30181a58decaecae0e2544b7a489915d3bcc611;p=deliverable%2Fbinutils-gdb.git diff --git a/opcodes/aarch64-dis.c b/opcodes/aarch64-dis.c index d8bd29010b..4c3b521d0d 100644 --- a/opcodes/aarch64-dis.c +++ b/opcodes/aarch64-dis.c @@ -1,5 +1,5 @@ /* aarch64-dis.c -- AArch64 disassembler. - Copyright 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc. + Copyright (C) 2009-2016 Free Software Foundation, Inc. Contributed by ARM Ltd. This file is part of the GNU opcodes library. @@ -24,12 +24,7 @@ #include "libiberty.h" #include "opintl.h" #include "aarch64-dis.h" - -#if !defined(EMBEDDED_ENV) -#define SYMTAB_AVAILABLE 1 #include "elf-bfd.h" -#include "elf/aarch64.h" -#endif #define ERR_OK 0 #define ERR_UND -1 @@ -125,7 +120,7 @@ parse_aarch64_dis_options (const char *options) 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 in SQDMLAL , , .[] - is encoded in H:L:M in some cases, the the fields H:L:M should be passed in + is encoded in H:L:M in some cases, the fields H:L:M should be passed in the order of H, L, M. */ static inline aarch64_insn @@ -150,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) @@ -178,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; @@ -229,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, @@ -242,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; } @@ -427,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; @@ -440,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: @@ -556,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); @@ -662,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, @@ -1015,14 +1056,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; } @@ -1055,6 +1096,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 @@ -1359,6 +1427,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); @@ -1581,6 +1656,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: @@ -1606,12 +1720,14 @@ convert_ubfm_to_lsl (aarch64_inst *inst) /* CINC , , is equivalent to: - CSINC , , , invert(). */ + CSINC , , , invert() + where is not AL or NV. */ static int convert_from_csel (aarch64_inst *inst) { - if (inst->operands[1].reg.regno == inst->operands[2].reg.regno) + if (inst->operands[1].reg.regno == inst->operands[2].reg.regno + && (inst->operands[3].cond->value & 0xe) != 0xe) { copy_operand_info (inst, 2, 3); inst->operands[2].cond = get_inverted_cond (inst->operands[3].cond); @@ -1623,13 +1739,15 @@ convert_from_csel (aarch64_inst *inst) /* CSET , is equivalent to: - CSINC , WZR, WZR, invert(). */ + CSINC , WZR, WZR, invert() + where is not AL or NV. */ static int convert_csinc_to_cset (aarch64_inst *inst) { if (inst->operands[1].reg.regno == 0x1f - && inst->operands[2].reg.regno == 0x1f) + && inst->operands[2].reg.regno == 0x1f + && (inst->operands[3].cond->value & 0xe) != 0xe) { copy_operand_info (inst, 1, 3); inst->operands[1].cond = get_inverted_cond (inst->operands[3].cond); @@ -1736,6 +1854,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: @@ -1843,7 +1963,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 @@ -1949,6 +2069,7 @@ aarch64_opcode_decode (const aarch64_opcode *opcode, const aarch64_insn code, { const aarch64_operand *opnd; enum aarch64_opnd type; + type = opcode->operands[i]; if (type == AARCH64_OPND_NIL) break; @@ -1961,6 +2082,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) { @@ -2006,11 +2134,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); @@ -2037,7 +2167,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); } @@ -2054,8 +2184,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. @@ -2066,7 +2195,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)). */ @@ -2149,7 +2278,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) {