123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436 |
- /* aarch64-dis.c -- AArch64 disassembler.
- Copyright (C) 2009-2015 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 "bfd_stdint.h"
- #include "dis-asm.h"
- #include "libiberty.h"
- #include "opintl.h"
- #include "aarch64-dis.h"
- #include "elf-bfd.h"
- #define ERR_OK 0
- #define ERR_UND -1
- #define ERR_UNP -3
- #define ERR_NYI -5
- #define INSNLEN 4
- /* Cached mapping symbol state. */
- enum map_type
- {
- MAP_INSN,
- MAP_DATA
- };
- static enum map_type last_type;
- static int last_mapping_sym = -1;
- static bfd_vma last_mapping_addr = 0;
- /* Other options */
- static int no_aliases = 0; /* If set disassemble as most general inst. */
- 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 (CONST_STRNEQ (option, "no-aliases"))
- {
- no_aliases = 1;
- return;
- }
- if (CONST_STRNEQ (option, "aliases"))
- {
- no_aliases = 0;
- return;
- }
- #ifdef DEBUG_AARCH64
- if (CONST_STRNEQ (option, "debug_dump"))
- {
- debug_dump = 1;
- return;
- }
- #endif /* DEBUG_AARCH64 */
- /* Invalid option. */
- fprintf (stderr, _("Unrecognised disassembler option: %s\n"), 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. */
- static inline 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);
- }
- return value;
- }
- /* Sign-extend bit I of VALUE. */
- static inline int32_t
- sign_extend (aarch64_insn value, unsigned i)
- {
- uint32_t ret = value;
- assert (i < 32);
- if ((value >> i) & 0x1)
- {
- uint32_t val = (uint32_t)(-1) << i;
- ret = ret | val;
- }
- return (int32_t) ret;
- }
- /* 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;
- assert (value <= 0x1
- && aarch64_get_qualifier_standard_value (qualifier) == value);
- return qualifier;
- }
- /* Given VALUE, return qualifier for a vector register. */
- 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;
- assert (value <= 0x8
- && aarch64_get_qualifier_standard_value (qualifier) == value);
- return qualifier;
- }
- /* 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;
- assert (value <= 0x4
- && aarch64_get_qualifier_standard_value (qualifier) == value);
- return qualifier;
- }
- /* 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. */
- assert (inst->operands[i].qualifier == AARCH64_OPND_QLF_NIL);
- if (aarch64_find_best_match (inst, inst->opcode->qualifiers_list,
- i, qualifiers))
- return qualifiers[i];
- else
- return AARCH64_OPND_QLF_NIL;
- }
- /* Operand extractors. */
- int
- aarch64_ext_regno (const aarch64_operand *self, aarch64_opnd_info *info,
- const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- info->reg.regno = extract_field (self->fields[0], code, 0);
- 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 <ic_op>{, <Xt>}. */
- int
- aarch64_ext_regrt_sysins (const aarch64_operand *self, aarch64_opnd_info *info,
- const aarch64_insn code,
- const aarch64_inst *inst 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 = inst->operands[0].sysins_op->has_xt;
- return 1;
- }
- /* e.g. SQDMLAL <Va><d>, <Vb><n>, <Vm>.<Ts>[<index>]. */
- int
- aarch64_ext_reglane (const aarch64_operand *self, aarch64_opnd_info *info,
- const aarch64_insn code,
- const aarch64_inst *inst 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, code, 0);
- /* Depend on AARCH64_OPND_Ed to determine the qualifier. */
- info->qualifier = get_expected_qualifier (inst, info->idx);
- 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 0;
- info->qualifier = get_sreg_qualifier_from_value (pos);
- info->reglane.index = (unsigned) (value >> 1);
- }
- }
- 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);
- switch (info->qualifier)
- {
- case AARCH64_OPND_QLF_S_H:
- /* h:l:m */
- info->reglane.index = extract_fields (code, 0, 3, FLD_H, FLD_L,
- FLD_M);
- info->reglane.regno &= 0xf;
- break;
- case AARCH64_OPND_QLF_S_S:
- /* 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 0;
- }
- }
- return 1;
- }
- int
- aarch64_ext_reglist (const aarch64_operand *self, aarch64_opnd_info *info,
- const aarch64_insn code,
- const aarch64_inst *inst 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;
- return 1;
- }
- /* Decode Rt and opcode fields of Vt in AdvSIMD load/store instructions. */
- int
- aarch64_ext_ldst_reglist (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst)
- {
- 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);
- if (expected_num != data[value].num_elements || data[value].is_reserved)
- return 0;
- info->reglist.num_regs = data[value].num_regs;
- return 1;
- }
- /* Decode Rt and S fields of Vt in AdvSIMD load single structure to all
- lanes instructions. */
- int
- aarch64_ext_ldst_reglist_r (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst)
- {
- 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;
- return 1;
- }
- /* Decode Q, opcode<2:1>, S, size and Rt fields of Vt in AdvSIMD
- load/store single element instructions. */
- int
- 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_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 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;
- /* Index encoded in "Q:S". */
- info->reglist.index = QSsize >> 2;
- }
- else
- {
- 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:
- return 0;
- }
- info->reglist.has_index = 1;
- info->reglist.num_regs = 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);
- return 1;
- }
- /* Decode fields immh:immb and/or Q for e.g.
- SSHR <Vd>.<T>, <Vn>.<T>, #<shift>
- or SSHR <V><d>, <V><n>, #<shift>. */
- int
- aarch64_ext_advsimd_imm_shift (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst)
- {
- 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 0;
- 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);
- }
- else
- info->qualifier = get_sreg_qualifier_from_value (pos);
- 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 1;
- }
- /* Decode shift immediate for e.g. sshr (imm). */
- int
- aarch64_ext_shll_imm (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst 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 0;
- }
- info->imm.value = imm;
- return 1;
- }
- /* Decode imm for e.g. BFM <Wd>, <Wn>, #<immr>, #<imms>.
- value in the field(s) will be extracted as unsigned immediate value. */
- int
- aarch64_ext_imm (const aarch64_operand *self, aarch64_opnd_info *info,
- const aarch64_insn code,
- 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;
- 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;
- if (info->type == AARCH64_OPND_ADDR_ADRP)
- imm <<= 12;
- info->imm.value = imm;
- return 1;
- }
- /* Decode imm and its shifter for e.g. MOVZ <Wd>, #<imm16>{, LSL #<shift>}. */
- int
- aarch64_ext_imm_half (const aarch64_operand *self, aarch64_opnd_info *info,
- const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- aarch64_ext_imm (self, info, code, inst);
- info->shifter.kind = AARCH64_MOD_LSL;
- info->shifter.amount = extract_field (FLD_hw, code, 0) << 4;
- return 1;
- }
- /* Decode cmode and "a:b:c:d:e:f:g:h" for e.g.
- MOVI <Vd>.<T>, #<imm8> {, LSL #<amount>}. */
- int
- 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)
- {
- 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);
- 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: assert (0); return 0;
- }
- /* 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:
- assert (0);
- return 0;
- }
- return 1;
- }
- /* Decode scale for e.g. SCVTF <Dd>, <Wn>, #<fbits>. */
- int
- aarch64_ext_fbits (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- info->imm.value = 64- extract_field (FLD_scale, code, 0);
- return 1;
- }
- /* Decode arithmetic immediate for e.g.
- SUBS <Wd>, <Wn|WSP>, #<imm> {, <shift>}. */
- int
- aarch64_ext_aimm (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- aarch64_insn value;
- info->shifter.kind = AARCH64_MOD_LSL;
- /* shift */
- value = extract_field (FLD_shift, code, 0);
- if (value >= 2)
- return 0;
- info->shifter.amount = value ? 12 : 0;
- /* imm12 (unsigned) */
- info->imm.value = extract_field (FLD_imm12, code, 0);
- return 1;
- }
- /* Decode logical immediate for e.g. ORR <Wd|WSP>, <Wn>, #<imm>. */
- int
- aarch64_ext_limm (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info, const aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- 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)
- {
- 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 0;
- }
- mask = (1ull << simd_size) - 1;
- /* Top bits are IGNORED. */
- R &= simd_size - 1;
- }
- /* NOTE: if S = simd_size - 1 we get 0xf..f which is rejected. */
- if (S == simd_size - 1)
- return 0;
- /* 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;
- case 4: imm = (imm << 4) | imm;
- case 8: imm = (imm << 8) | imm;
- case 16: imm = (imm << 16) | imm;
- case 32: imm = (imm << 32) | imm;
- case 64: break;
- default: assert (0); return 0;
- }
- info->imm.value = sf ? imm : imm & 0xffffffff;
- return 1;
- }
- /* Decode Ft for e.g. STR <Qt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}]
- or LDP <Qt1>, <Qt2>, [<Xn|SP>], #<imm>. */
- int
- aarch64_ext_ft (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- const aarch64_insn code, const aarch64_inst *inst)
- {
- 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 0;
- }
- info->qualifier = qualifier;
- }
- else
- {
- /* opc1:size */
- value = extract_fields (code, 0, 2, FLD_opc1, FLD_ldst_size);
- if (value > 0x4)
- return 0;
- info->qualifier = get_sreg_qualifier_from_value (value);
- }
- return 1;
- }
- /* Decode the address operand for e.g. STXRB <Ws>, <Wt>, [<Xn|SP>{,#0}]. */
- int
- aarch64_ext_addr_simple (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- /* Rn */
- info->addr.base_regno = extract_field (FLD_Rn, code, 0);
- return 1;
- }
- /* Decode the address operand for e.g.
- STR <Qt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}]. */
- int
- aarch64_ext_addr_regoff (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code, const aarch64_inst *inst)
- {
- 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);
- /* 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 1;
- }
- /* Decode the address operand for e.g. LDRSW <Xt>, [<Xn|SP>], #<simm>. */
- int
- aarch64_ext_addr_simm (const aarch64_operand *self, aarch64_opnd_info *info,
- aarch64_insn code, const aarch64_inst *inst)
- {
- aarch64_insn imm;
- info->qualifier = get_expected_qualifier (inst, info->idx);
- /* 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)
- /* 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 1;
- }
- /* Decode the address operand for e.g. LDRSW <Xt>, [<Xn|SP>{, #<simm>}]. */
- int
- aarch64_ext_addr_uimm12 (const aarch64_operand *self, aarch64_opnd_info *info,
- aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- int shift;
- info->qualifier = get_expected_qualifier (inst, info->idx);
- 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 1;
- }
- /* Decode the address operand for e.g.
- LD1 {<Vt>.<T>, <Vt2>.<T>, <Vt3>.<T>}, [<Xn|SP>], <Xm|#<amount>>. */
- int
- aarch64_ext_simd_addr_post (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code, const aarch64_inst *inst)
- {
- /* 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 1;
- }
- /* Decode the condition operand for e.g. CSEL <Xd>, <Xn>, <Xm>, <cond>. */
- int
- aarch64_ext_cond (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code, const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- aarch64_insn value;
- /* cond */
- value = extract_field (FLD_cond, code, 0);
- info->cond = get_cond_from_value (value);
- return 1;
- }
- /* Decode the system register operand for e.g. MRS <Xt>, <systemreg>. */
- int
- aarch64_ext_sysreg (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- /* op0:op1:CRn:CRm:op2 */
- info->sysreg = extract_fields (code, 0, 5, FLD_op0, FLD_op1, FLD_CRn,
- FLD_CRm, FLD_op2);
- return 1;
- }
- /* Decode the PSTATE field operand for e.g. MSR <pstatefield>, #<imm>. */
- int
- aarch64_ext_pstatefield (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info, aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- int i;
- /* 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)
- return 1;
- /* Reserved value in <pstatefield>. */
- return 0;
- }
- /* Decode the system instruction op operand for e.g. AT <at_op>, <Xt>. */
- int
- aarch64_ext_sysins_op (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code,
- const aarch64_inst *inst 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;
- default: assert (0); return 0;
- }
- 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,
- info->sysins_op->has_xt, i);
- return 1;
- }
- return 0;
- }
- /* Decode the memory barrier option operand for e.g. DMB <option>|#<imm>. */
- int
- aarch64_ext_barrier (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code,
- const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- /* CRm */
- info->barrier = aarch64_barrier_options + extract_field (FLD_CRm, code, 0);
- return 1;
- }
- /* Decode the prefetch operation option operand for e.g.
- PRFM <prfop>, [<Xn|SP>{, #<pimm>}]. */
- int
- aarch64_ext_prfop (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code, const aarch64_inst *inst ATTRIBUTE_UNUSED)
- {
- /* prfop in Rt */
- info->prfop = aarch64_prfops + extract_field (FLD_Rt, code, 0);
- return 1;
- }
- /* Decode the extended register operand for e.g.
- STR <Qt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}]. */
- int
- aarch64_ext_reg_extended (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code,
- const aarch64_inst *inst 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, 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 1;
- }
- /* Decode the shifted register operand for e.g.
- SUBS <Xd>, <Xn>, <Xm> {, <shift> #<amount>}. */
- int
- aarch64_ext_reg_shifted (const aarch64_operand *self ATTRIBUTE_UNUSED,
- aarch64_opnd_info *info,
- aarch64_insn code,
- const aarch64_inst *inst 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 0;
- /* imm6 */
- info->shifter.amount = extract_field (FLD_imm6, code, 0);
- /* This makes the constraint checking happy. */
- info->shifter.operator_present = 1;
- 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.
- 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);
- 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:
- assert (0);
- 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)
- {
- 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);
- 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->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);
- }
- /* 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);
- 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->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->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].imm.value == 0)
- {
- inst->operands[2].type = AARCH64_OPND_NIL;
- return 1;
- }
- return 0;
- }
- /* Convert
- UBFM <Xd>, <Xn>, #<shift>, #63.
- to
- LSR <Xd>, <Xn>, #<shift>. */
- static int
- convert_bfm_to_sr (aarch64_inst *inst)
- {
- int64_t imms, val;
- imms = inst->operands[3].imm.value;
- val = inst->operands[2].qualifier == AARCH64_OPND_QLF_imm_0_31 ? 31 : 63;
- if (imms == val)
- {
- inst->operands[3].type = AARCH64_OPND_NIL;
- return 1;
- }
- return 0;
- }
- /* Convert MOV to ORR. */
- static int
- convert_orr_to_mov (aarch64_inst *inst)
- {
- /* MOV <Vd>.<T>, <Vn>.<T>
- is equivalent to:
- ORR <Vd>.<T>, <Vn>.<T>, <Vn>.<T>. */
- if (inst->operands[1].reg.regno == inst->operands[2].reg.regno)
- {
- inst->operands[2].type = AARCH64_OPND_NIL;
- return 1;
- }
- return 0;
- }
- /* When <imms> >= <immr>, the instruction written:
- SBFX <Xd>, <Xn>, #<lsb>, #<width>
- is equivalent to:
- SBFM <Xd>, <Xn>, #<lsb>, #(<lsb>+<width>-1). */
- static int
- convert_bfm_to_bfx (aarch64_inst *inst)
- {
- int64_t immr, imms;
- immr = inst->operands[2].imm.value;
- imms = inst->operands[3].imm.value;
- if (imms >= immr)
- {
- int64_t lsb = immr;
- inst->operands[2].imm.value = lsb;
- inst->operands[3].imm.value = imms + 1 - lsb;
- /* The two opcodes have different qualifiers for
- the immediate operands; reset to help the checking. */
- reset_operand_qualifier (inst, 2);
- reset_operand_qualifier (inst, 3);
- return 1;
- }
- return 0;
- }
- /* When <imms> < <immr>, the instruction written:
- SBFIZ <Xd>, <Xn>, #<lsb>, #<width>
- is equivalent to:
- SBFM <Xd>, <Xn>, #((64-<lsb>)&0x3f), #(<width>-1). */
- static int
- convert_bfm_to_bfi (aarch64_inst *inst)
- {
- int64_t immr, imms, val;
- 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)
- {
- inst->operands[2].imm.value = (val - immr) & (val - 1);
- inst->operands[3].imm.value = imms + 1;
- /* The two opcodes have different qualifiers for
- the immediate operands; reset to help the checking. */
- reset_operand_qualifier (inst, 2);
- reset_operand_qualifier (inst, 3);
- return 1;
- }
- return 0;
- }
- /* The instruction written:
- LSL <Xd>, <Xn>, #<shift>
- is equivalent to:
- UBFM <Xd>, <Xn>, #((64-<shift>)&0x3f), #(63-<shift>). */
- static int
- convert_ubfm_to_lsl (aarch64_inst *inst)
- {
- int64_t immr = inst->operands[2].imm.value;
- int64_t imms = inst->operands[3].imm.value;
- int64_t val
- = inst->operands[2].qualifier == AARCH64_OPND_QLF_imm_0_31 ? 31 : 63;
- if ((immr == 0 && imms == val) || immr == imms + 1)
- {
- inst->operands[3].type = AARCH64_OPND_NIL;
- inst->operands[2].imm.value = val - imms;
- return 1;
- }
- return 0;
- }
- /* CINC <Wd>, <Wn>, <cond>
- is equivalent to:
- CSINC <Wd>, <Wn>, <Wn>, invert(<cond>)
- where <cond> is not AL or NV. */
- static int
- convert_from_csel (aarch64_inst *inst)
- {
- 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);
- inst->operands[3].type = AARCH64_OPND_NIL;
- return 1;
- }
- return 0;
- }
- /* CSET <Wd>, <cond>
- is equivalent to:
- CSINC <Wd>, WZR, WZR, invert(<cond>)
- where <cond> 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[3].cond->value & 0xe) != 0xe)
- {
- copy_operand_info (inst, 1, 3);
- inst->operands[1].cond = get_inverted_cond (inst->operands[3].cond);
- inst->operands[3].type = AARCH64_OPND_NIL;
- inst->operands[2].type = AARCH64_OPND_NIL;
- return 1;
- }
- return 0;
- }
- /* MOV <Wd>, #<imm>
- is equivalent to:
- MOVZ <Wd>, #<imm16>, LSL #<shift>.
- A disassembler may output ORR, MOVZ and MOVN as a MOV mnemonic, except when
- ORR has an immediate that could be generated by a MOVZ or MOVN instruction,
- or where a MOVN has an immediate that could be encoded by MOVZ, or where
- MOVZ/MOVN #0 have a shift amount other than LSL #0, in which case the
- machine-instruction mnemonic must be used. */
- static int
- convert_movewide_to_mov (aarch64_inst *inst)
- {
- uint64_t value = inst->operands[1].imm.value;
- /* MOVZ/MOVN #0 have a shift amount other than LSL #0. */
- if (value == 0 && inst->operands[1].shifter.amount != 0)
- return 0;
- inst->operands[1].type = AARCH64_OPND_IMM_MOV;
- inst->operands[1].shifter.kind = AARCH64_MOD_NONE;
- value <<= inst->operands[1].shifter.amount;
- /* As an alias convertor, it has to be clear that the INST->OPCODE
- is the opcode of the real instruction. */
- if (inst->opcode->op == OP_MOVN)
- {
- int is32 = inst->operands[0].qualifier == AARCH64_OPND_QLF_W;
- value = ~value;
- /* A MOVN has an immediate that could be encoded by MOVZ. */
- if (aarch64_wide_constant_p (value, is32, NULL) == TRUE)
- return 0;
- }
- inst->operands[1].imm.value = value;
- inst->operands[1].shifter.amount = 0;
- return 1;
- }
- /* MOV <Wd>, #<imm>
- is equivalent to:
- ORR <Wd>, WZR, #<imm>.
- A disassembler may output ORR, MOVZ and MOVN as a MOV mnemonic, except when
- ORR has an immediate that could be generated by a MOVZ or MOVN instruction,
- or where a MOVN has an immediate that could be encoded by MOVZ, or where
- MOVZ/MOVN #0 have a shift amount other than LSL #0, in which case the
- machine-instruction mnemonic must be used. */
- static int
- convert_movebitmask_to_mov (aarch64_inst *inst)
- {
- int is32;
- uint64_t value;
- /* Should have been assured by the base opcode value. */
- assert (inst->operands[1].reg.regno == 0x1f);
- copy_operand_info (inst, 1, 2);
- is32 = inst->operands[0].qualifier == AARCH64_OPND_QLF_W;
- inst->operands[1].type = AARCH64_OPND_IMM_MOV;
- value = inst->operands[1].imm.value;
- /* ORR has an immediate that could be generated by a MOVZ or MOVN
- instruction. */
- if (inst->operands[0].reg.regno != 0x1f
- && (aarch64_wide_constant_p (value, is32, NULL) == TRUE
- || aarch64_wide_constant_p (~value, is32, NULL) == TRUE))
- return 0;
- inst->operands[2].type = AARCH64_OPND_NIL;
- return 1;
- }
- /* Some alias opcodes are disassembled by being converted from their real-form.
- N.B. INST->OPCODE is the real opcode rather than the alias. */
- static int
- convert_to_alias (aarch64_inst *inst, const aarch64_opcode *alias)
- {
- switch (alias->op)
- {
- case OP_ASR_IMM:
- case OP_LSR_IMM:
- return convert_bfm_to_sr (inst);
- case OP_LSL_IMM:
- return convert_ubfm_to_lsl (inst);
- case OP_CINC:
- case OP_CINV:
- case OP_CNEG:
- return convert_from_csel (inst);
- case OP_CSET:
- case OP_CSETM:
- return convert_csinc_to_cset (inst);
- case OP_UBFX:
- case OP_BFXIL:
- case OP_SBFX:
- return convert_bfm_to_bfx (inst);
- case OP_SBFIZ:
- case OP_BFI:
- case OP_UBFIZ:
- return convert_bfm_to_bfi (inst);
- case OP_MOV_V:
- return convert_orr_to_mov (inst);
- case OP_MOV_IMM_WIDE:
- case OP_MOV_IMM_WIDEN:
- return convert_movewide_to_mov (inst);
- case OP_MOV_IMM_LOG:
- return convert_movebitmask_to_mov (inst);
- case OP_ROR_IMM:
- return convert_extr_to_ror (inst);
- case OP_SXTL:
- case OP_SXTL2:
- case OP_UXTL:
- case OP_UXTL2:
- return convert_shll_to_xtl (inst);
- default:
- return 0;
- }
- }
- static int aarch64_opcode_decode (const aarch64_opcode *, const aarch64_insn,
- aarch64_inst *, int);
- /* Given the instruction information in *INST, check if the instruction has
- any alias form that can be used to represent *INST. If the answer is yes,
- update *INST to be in the form of the determined alias. */
- /* In the opcode description table, the following flags are used in opcode
- entries to help establish the relations between the real and alias opcodes:
- F_ALIAS: opcode is an alias
- F_HAS_ALIAS: opcode has alias(es)
- F_P1
- F_P2
- F_P3: Disassembly preference priority 1-3 (the larger the
- higher). If nothing is specified, it is the priority
- 0 by default, i.e. the lowest priority.
- Although the relation between the machine and the alias instructions are not
- explicitly described, it can be easily determined from the base opcode
- values, masks and the flags F_ALIAS and F_HAS_ALIAS in their opcode
- description entries:
- The mask of an alias opcode must be equal to or a super-set (i.e. more
- constrained) of that of the aliased opcode; so is the base opcode value.
- if (opcode_has_alias (real) && alias_opcode_p (opcode)
- && (opcode->mask & real->mask) == real->mask
- && (real->mask & opcode->opcode) == (real->mask & real->opcode))
- then OPCODE is an alias of, and only of, the REAL instruction
- The alias relationship is forced flat-structured to keep related algorithm
- simple; an opcode entry cannot be flagged with both F_ALIAS and F_HAS_ALIAS.
- During the disassembling, the decoding decision tree (in
- opcodes/aarch64-dis-2.c) always returns an machine instruction opcode entry;
- if the decoding of such a machine instruction succeeds (and -Mno-aliases is
- not specified), the disassembler will check whether there is any alias
- instruction exists for this real instruction. If there is, the disassembler
- will try to disassemble the 32-bit binary again using the alias's rule, or
- try to convert the IR to the form of the alias. In the case of the multiple
- aliases, the aliases are tried one by one from the highest priority
- (currently the flag F_P3) to the lowest priority (no priority flag), and the
- first succeeds first adopted.
- You may ask why there is a need for the conversion of IR from one form to
- another in handling certain aliases. This is because on one hand it avoids
- adding more operand code to handle unusual encoding/decoding; on other
- hand, during the disassembling, the conversion is an effective approach to
- check the condition of an alias (as an alias may be adopted only if certain
- conditions are met).
- In order to speed up the alias opcode lookup, aarch64-gen has preprocessed
- aarch64_opcode_table and generated aarch64_find_alias_opcode and
- aarch64_find_next_alias_opcode (in opcodes/aarch64-dis-2.c) to help. */
- static void
- determine_disassembling_preference (struct aarch64_inst *inst)
- {
- const aarch64_opcode *opcode;
- const aarch64_opcode *alias;
- opcode = inst->opcode;
- /* This opcode does not have an alias, so use itself. */
- if (opcode_has_alias (opcode) == FALSE)
- return;
- alias = aarch64_find_alias_opcode (opcode);
- assert (alias);
- #ifdef DEBUG_AARCH64
- if (debug_dump)
- {
- const aarch64_opcode *tmp = alias;
- printf ("#### LIST orderd: ");
- while (tmp)
- {
- printf ("%s, ", tmp->name);
- tmp = aarch64_find_next_alias_opcode (tmp);
- }
- printf ("\n");
- }
- #endif /* DEBUG_AARCH64 */
- for (; alias; alias = aarch64_find_next_alias_opcode (alias))
- {
- DEBUG_TRACE ("try %s", alias->name);
- assert (alias_opcode_p (alias));
- /* 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
- aliasing AND. */
- if (pseudo_opcode_p (alias))
- {
- DEBUG_TRACE ("skip pseudo %s", alias->name);
- continue;
- }
- if ((inst->value & alias->mask) != alias->opcode)
- {
- DEBUG_TRACE ("skip %s as base opcode not match", alias->name);
- continue;
- }
- /* No need to do any complicated transformation on operands, if the alias
- opcode does not have any operand. */
- if (aarch64_num_of_operands (alias) == 0 && alias->opcode == inst->value)
- {
- DEBUG_TRACE ("succeed with 0-operand opcode %s", alias->name);
- aarch64_replace_opcode (inst, alias);
- return;
- }
- if (alias->flags & F_CONV)
- {
- aarch64_inst copy;
- memcpy (©, inst, sizeof (aarch64_inst));
- /* ALIAS is the preference as long as the instruction can be
- successfully converted to the form of ALIAS. */
- if (convert_to_alias (©, alias) == 1)
- {
- aarch64_replace_opcode (©, alias);
- assert (aarch64_match_operands_constraint (©, NULL));
- DEBUG_TRACE ("succeed with %s via conversion", alias->name);
- memcpy (inst, ©, sizeof (aarch64_inst));
- return;
- }
- }
- else
- {
- /* Directly decode the alias opcode. */
- aarch64_inst temp;
- memset (&temp, '\0', sizeof (aarch64_inst));
- if (aarch64_opcode_decode (alias, inst->value, &temp, 1) == 1)
- {
- DEBUG_TRACE ("succeed with %s via direct decoding", alias->name);
- memcpy (inst, &temp, sizeof (aarch64_inst));
- return;
- }
- }
- }
- }
- /* 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.
- If OPCODE has alias(es) and NOALIASES_P is 0, an alias opcode may be
- determined and used to disassemble CODE; this is done just before the
- return. */
- static int
- aarch64_opcode_decode (const aarch64_opcode *opcode, const aarch64_insn code,
- aarch64_inst *inst, int noaliases_p)
- {
- int i;
- DEBUG_TRACE ("enter with %s", opcode->name);
- assert (opcode && inst);
- /* Check the base opcode. */
- if ((code & opcode->mask) != (opcode->opcode & opcode->mask))
- {
- DEBUG_TRACE ("base opcode match FAIL");
- goto decode_fail;
- }
- /* Clear inst. */
- memset (inst, '\0', sizeof (aarch64_inst));
- inst->opcode = opcode;
- inst->value = code;
- /* Assign operand codes and indexes. */
- for (i = 0; i < AARCH64_MAX_OPND_NUM; ++i)
- {
- if (opcode->operands[i] == AARCH64_OPND_NIL)
- break;
- inst->operands[i].type = opcode->operands[i];
- inst->operands[i].idx = i;
- }
- /* Call the opcode decoder indicated by flags. */
- if (opcode_has_special_coder (opcode) && do_special_decoding (inst) == 0)
- {
- DEBUG_TRACE ("opcode flag-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;
- opnd = &aarch64_operands[type];
- if (operand_has_extractor (opnd)
- && (! aarch64_extract_operand (opnd, &inst->operands[i], code, inst)))
- {
- DEBUG_TRACE ("operand decoder FAIL at operand %d", i);
- goto decode_fail;
- }
- }
- /* Match the qualifiers. */
- if (aarch64_match_operands_constraint (inst, NULL) == 1)
- {
- /* Arriving here, the CODE has been determined as a valid instruction
- of OPCODE and *INST has been filled with information of this OPCODE
- instruction. Before the return, check if the instruction has any
- alias and should be disassembled in the form of its alias instead.
- If the answer is yes, *INST will be updated. */
- if (!noaliases_p)
- determine_disassembling_preference (inst);
- DEBUG_TRACE ("SUCCESS");
- return 1;
- }
- else
- {
- DEBUG_TRACE ("constraint matching FAIL");
- }
- decode_fail:
- return 0;
- }
- /* This does some user-friendly fix-up to *INST. It is currently focus on
- the adjustment of qualifiers to help the printed instruction
- recognized/understood more easily. */
- static void
- user_friendly_fixup (aarch64_inst *inst)
- {
- switch (inst->opcode->iclass)
- {
- case testbranch:
- /* TBNZ Xn|Wn, #uimm6, label
- Test and Branch Not Zero: conditionally jumps to label if bit number
- uimm6 in register Xn is not zero. The bit number implies the width of
- the register, which may be written and should be disassembled as Wn if
- uimm is less than 32. Limited to a branch offset range of +/- 32KiB.
- */
- if (inst->operands[1].imm.value < 32)
- inst->operands[0].qualifier = AARCH64_OPND_QLF_W;
- break;
- default: break;
- }
- }
- /* Decode INSN and fill in *INST the instruction information. Return zero
- on success. */
- int
- aarch64_decode_insn (aarch64_insn insn, aarch64_inst *inst)
- {
- const aarch64_opcode *opcode = aarch64_opcode_lookup (insn);
- #ifdef DEBUG_AARCH64
- if (debug_dump)
- {
- const aarch64_opcode *tmp = opcode;
- printf ("\n");
- DEBUG_TRACE ("opcode lookup:");
- while (tmp != NULL)
- {
- aarch64_verbose (" %s", tmp->name);
- tmp = aarch64_find_next_opcode (tmp);
- }
- }
- #endif /* DEBUG_AARCH64 */
- /* A list of opcodes may have been found, as aarch64_opcode_lookup cannot
- distinguish some opcodes, e.g. SSHR and MOVI, which almost share the same
- opcode field and value, apart from the difference that one of them has an
- extra field as part of the opcode, but such a field is used for operand
- encoding in other opcode(s) ('immh' in the case of the example). */
- while (opcode != NULL)
- {
- /* 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)
- return ERR_OK;
- opcode = aarch64_find_next_opcode (opcode);
- }
- return ERR_UND;
- }
- /* Print operands. */
- static void
- print_operands (bfd_vma pc, const aarch64_opcode *opcode,
- const aarch64_opnd_info *opnds, struct disassemble_info *info)
- {
- 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];
- /* We regard the opcode operand info more, however we also look into
- the inst->operands to support the disassembling of the optional
- operand.
- The two operand code should be the same in all cases, apart from
- when the operand can be optional. */
- if (opcode->operands[i] == AARCH64_OPND_NIL
- || opnds[i].type == AARCH64_OPND_NIL)
- break;
- /* Generate the operand string in STR. */
- aarch64_print_operand (str, size, pc, opcode, opnds, i, &pcrel_p,
- &info->target);
- /* Print the delimiter (taking account of omitted operand(s)). */
- if (str[0] != '\0')
- (*info->fprintf_func) (info->stream, "%s",
- num_printed++ == 0 ? "\t" : ", ");
- /* Print the operand. */
- if (pcrel_p)
- (*info->print_address_func) (info->target, info);
- else
- (*info->fprintf_func) (info->stream, "%s", str);
- }
- }
- /* Print the instruction mnemonic name. */
- static void
- print_mnemonic_name (const aarch64_inst *inst, struct disassemble_info *info)
- {
- if (inst->opcode->flags & F_COND)
- {
- /* 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';
- (*info->fprintf_func) (info->stream, "%s.%s", name, inst->cond->names[0]);
- }
- else
- (*info->fprintf_func) (info->stream, "%s", inst->opcode->name);
- }
- /* Print the instruction according to *INST. */
- static void
- print_aarch64_insn (bfd_vma pc, const aarch64_inst *inst,
- struct disassemble_info *info)
- {
- print_mnemonic_name (inst, info);
- print_operands (pc, inst->opcode, inst->operands, info);
- }
- /* Entry-point of the instruction disassembler and printer. */
- static void
- print_insn_aarch64_word (bfd_vma pc,
- uint32_t word,
- struct disassemble_info *info)
- {
- static const char *err_msg[6] =
- {
- [ERR_OK] = "_",
- [-ERR_UND] = "undefined",
- [-ERR_UNP] = "unpredictable",
- [-ERR_NYI] = "NYI"
- };
- int ret;
- aarch64_inst inst;
- info->insn_info_valid = 1;
- info->branch_delay_insns = 0;
- info->data_size = 0;
- info->target = 0;
- info->target2 = 0;
- if (info->flags & INSN_HAS_RELOC)
- /* If the instruction has a reloc associated with it, then
- the offset field in the instruction will actually be the
- addend for the reloc. (If we are using REL type relocs).
- In such cases, we can ignore the pc when computing
- addresses, since the addend is not currently pc-relative. */
- pc = 0;
- ret = aarch64_decode_insn (word, &inst);
- if (((word >> 21) & 0x3ff) == 1)
- {
- /* RESERVED for ALES. */
- assert (ret != ERR_OK);
- ret = ERR_NYI;
- }
- switch (ret)
- {
- case ERR_UND:
- case ERR_UNP:
- case ERR_NYI:
- /* Handle undefined instructions. */
- info->insn_type = dis_noninsn;
- (*info->fprintf_func) (info->stream,".inst\t0x%08x ; %s",
- word, err_msg[-ret]);
- break;
- case ERR_OK:
- user_friendly_fixup (&inst);
- print_aarch64_insn (pc, &inst, info);
- break;
- default:
- abort ();
- }
- }
- /* Disallow mapping symbols ($x, $d etc) from
- being displayed in symbol relative addresses. */
- bfd_boolean
- aarch64_symbol_is_valid (asymbol * sym,
- struct disassemble_info * info ATTRIBUTE_UNUSED)
- {
- const char * name;
- if (sym == NULL)
- return FALSE;
- name = bfd_asymbol_name (sym);
- return name
- && (name[0] != '$'
- || (name[1] != 'x' && name[1] != 'd')
- || (name[2] != '\0' && name[2] != '.'));
- }
- /* Print data bytes on INFO->STREAM. */
- static void
- print_insn_data (bfd_vma pc ATTRIBUTE_UNUSED,
- uint32_t word,
- struct disassemble_info *info)
- {
- switch (info->bytes_per_chunk)
- {
- case 1:
- info->fprintf_func (info->stream, ".byte\t0x%02x", word);
- break;
- case 2:
- info->fprintf_func (info->stream, ".short\t0x%04x", word);
- break;
- case 4:
- info->fprintf_func (info->stream, ".word\t0x%08x", word);
- break;
- default:
- abort ();
- }
- }
- /* Try to infer the code or data type from a symbol.
- Returns nonzero if *MAP_TYPE was set. */
- static int
- get_sym_code_type (struct disassemble_info *info, int n,
- enum map_type *map_type)
- {
- elf_symbol_type *es;
- unsigned int type;
- const char *name;
- es = *(elf_symbol_type **)(info->symtab + n);
- type = ELF_ST_TYPE (es->internal_elf_sym.st_info);
- /* If the symbol has function type then use that. */
- if (type == STT_FUNC)
- {
- *map_type = MAP_INSN;
- return TRUE;
- }
- /* Check for mapping symbols. */
- name = bfd_asymbol_name(info->symtab[n]);
- if (name[0] == '$'
- && (name[1] == 'x' || name[1] == 'd')
- && (name[2] == '\0' || name[2] == '.'))
- {
- *map_type = (name[1] == 'x' ? MAP_INSN : MAP_DATA);
- return TRUE;
- }
- return FALSE;
- }
- /* Entry-point of the AArch64 disassembler. */
- int
- print_insn_aarch64 (bfd_vma pc,
- struct disassemble_info *info)
- {
- bfd_byte buffer[INSNLEN];
- int status;
- void (*printer) (bfd_vma, uint32_t, struct disassemble_info *);
- bfd_boolean found = FALSE;
- unsigned int size = 4;
- unsigned long data;
- if (info->disassembler_options)
- {
- set_default_aarch64_dis_options (info);
- parse_aarch64_dis_options (info->disassembler_options);
- /* To avoid repeated parsing of these options, we remove them here. */
- info->disassembler_options = NULL;
- }
- /* Aarch64 instructions are always little-endian */
- info->endian_code = BFD_ENDIAN_LITTLE;
- /* First check the full symtab for a mapping symbol, even if there
- are no usable non-mapping symbols for this address. */
- if (info->symtab_size != 0
- && bfd_asymbol_flavour (*info->symtab) == bfd_target_elf_flavour)
- {
- enum map_type type = MAP_INSN;
- int last_sym = -1;
- bfd_vma addr;
- int n;
- if (pc <= last_mapping_addr)
- last_mapping_sym = -1;
- /* Start scanning at the start of the function, or wherever
- we finished last time. */
- n = info->symtab_pos + 1;
- if (n < last_mapping_sym)
- n = last_mapping_sym;
- /* Scan up to the location being disassembled. */
- for (; n < info->symtab_size; n++)
- {
- addr = bfd_asymbol_value (info->symtab[n]);
- if (addr > pc)
- break;
- if ((info->section == NULL
- || info->section == info->symtab[n]->section)
- && get_sym_code_type (info, n, &type))
- {
- last_sym = n;
- found = TRUE;
- }
- }
- if (!found)
- {
- n = info->symtab_pos;
- if (n < last_mapping_sym)
- n = last_mapping_sym;
- /* No mapping symbol found at this address. Look backwards
- for a preceeding one. */
- for (; n >= 0; n--)
- {
- if (get_sym_code_type (info, n, &type))
- {
- last_sym = n;
- found = TRUE;
- break;
- }
- }
- }
- last_mapping_sym = last_sym;
- last_type = type;
- /* Look a little bit ahead to see if we should print out
- less than four bytes of data. If there's a symbol,
- mapping or otherwise, after two bytes then don't
- print more. */
- if (last_type == MAP_DATA)
- {
- size = 4 - (pc & 3);
- for (n = last_sym + 1; n < info->symtab_size; n++)
- {
- addr = bfd_asymbol_value (info->symtab[n]);
- if (addr > pc)
- {
- if (addr - pc < size)
- size = addr - pc;
- break;
- }
- }
- /* If the next symbol is after three bytes, we need to
- print only part of the data, so that we can use either
- .byte or .short. */
- if (size == 3)
- size = (pc & 1) ? 1 : 2;
- }
- }
- if (last_type == MAP_DATA)
- {
- /* size was set above. */
- info->bytes_per_chunk = size;
- info->display_endian = info->endian;
- printer = print_insn_data;
- }
- else
- {
- info->bytes_per_chunk = size = INSNLEN;
- info->display_endian = info->endian_code;
- printer = print_insn_aarch64_word;
- }
- status = (*info->read_memory_func) (pc, buffer, size, info);
- if (status != 0)
- {
- (*info->memory_error_func) (status, pc, info);
- return -1;
- }
- data = bfd_get_bits (buffer, size * 8,
- info->display_endian == BFD_ENDIAN_BIG);
- (*printer) (pc, data, info);
- return size;
- }
- void
- print_aarch64_disassembler_options (FILE *stream)
- {
- fprintf (stream, _("\n\
- The following AARCH64 specific disassembler options are supported for use\n\
- with the -M switch (multiple options should be separated by commas):\n"));
- fprintf (stream, _("\n\
- no-aliases Don't print instruction aliases.\n"));
- fprintf (stream, _("\n\
- aliases Do print instruction aliases.\n"));
- #ifdef DEBUG_AARCH64
- fprintf (stream, _("\n\
- debug_dump Temp switch for debug trace.\n"));
- #endif /* DEBUG_AARCH64 */
- fprintf (stream, _("\n"));
- }
|