/* aarch64-dis.c -- AArch64 disassembler.
- Copyright (C) 2009-2015 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.
return value;
}
+/* Extract the value of all fields in SELF->fields from instruction CODE.
+ The least significant bit comes from the final field. */
+
+static aarch64_insn
+extract_all_fields (const aarch64_operand *self, aarch64_insn code)
+{
+ aarch64_insn value;
+ unsigned int i;
+ enum aarch64_field_kind kind;
+
+ value = 0;
+ for (i = 0; i < ARRAY_SIZE (self->fields) && self->fields[i] != FLD_NIL; ++i)
+ {
+ kind = self->fields[i];
+ value <<= fields[kind].width;
+ value |= extract_field (kind, code, 0);
+ }
+ return value;
+}
+
/* Sign-extend bit I of VALUE. */
static inline int32_t
sign_extend (aarch64_insn value, unsigned i)
return qualifier;
}
-/* Given VALUE, return qualifier for a vector register. */
+/* Given VALUE, return qualifier for a vector register. This does not support
+ decoding instructions that accept the 2H vector type. */
+
static inline enum aarch64_opnd_qualifier
get_vreg_qualifier_from_value (aarch64_insn value)
{
enum aarch64_opnd_qualifier qualifier = AARCH64_OPND_QLF_V_8B + value;
+ /* Instructions using vector type 2H should not call this function. Skip over
+ the 2H qualifier. */
+ if (qualifier >= AARCH64_OPND_QLF_V_2H)
+ qualifier += 1;
+
assert (value <= 0x8
&& aarch64_get_qualifier_standard_value (qualifier) == value);
return qualifier;
/* 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;
}
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);
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 <Dd>, <Wn>, #<fbits>. */
int
aarch64_ext_fbits (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;
}
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 <Qt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}]. */
int
return 0;
}
+/* The instruction written:
+ BFC <Xd>, #<lsb>, #<width>
+ is equivalent to:
+ BFM <Xd>, XZR, #((64-<lsb>)&0x3f), #(<width>-1). */
+
+static int
+convert_bfm_to_bfc (aarch64_inst *inst)
+{
+ int64_t immr, imms, val;
+
+ /* Should have been assured by the base opcode value. */
+ assert (inst->operands[1].reg.regno == 0x1f);
+
+ immr = inst->operands[2].imm.value;
+ imms = inst->operands[3].imm.value;
+ val = inst->operands[2].qualifier == AARCH64_OPND_QLF_imm_0_31 ? 32 : 64;
+ if (imms < immr)
+ {
+ /* Drop XZR from the second operand. */
+ copy_operand_info (inst, 1, 2);
+ copy_operand_info (inst, 2, 3);
+ inst->operands[3].type = AARCH64_OPND_NIL;
+
+ /* Recalculate the immediates. */
+ inst->operands[1].imm.value = (val - immr) & (val - 1);
+ inst->operands[2].imm.value = imms + 1;
+
+ /* The two opcodes have different qualifiers for the operands; reset to
+ help the checking. */
+ reset_operand_qualifier (inst, 1);
+ reset_operand_qualifier (inst, 2);
+ reset_operand_qualifier (inst, 3);
+
+ return 1;
+ }
+
+ return 0;
+}
+
/* The instruction written:
LSL <Xd>, <Xn>, #<shift>
is equivalent to:
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:
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
{
const aarch64_operand *opnd;
enum aarch64_opnd type;
+
type = opcode->operands[i];
if (type == AARCH64_OPND_NIL)
break;
}
}
+ /* 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)
{
}
}
-/* 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);
{
/* 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);
}
int i, pcrel_p, num_printed;
for (i = 0, num_printed = 0; i < AARCH64_MAX_OPND_NUM; ++i)
{
- const size_t size = 128;
- char str[size];
+ char str[128];
/* We regard the opcode operand info more, however we also look into
the inst->operands to support the disassembling of the optional
operand.
break;
/* Generate the operand string in STR. */
- aarch64_print_operand (str, size, pc, opcode, opnds, i, &pcrel_p,
+ aarch64_print_operand (str, sizeof (str), pc, opcode, opnds, i, &pcrel_p,
&info->target);
/* Print the delimiter (taking account of omitted operand(s)). */
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)
{