7582 lines
250 KiB
C++
7582 lines
250 KiB
C++
// Copyright 2015, VIXL authors
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
// * Neither the name of ARM Limited nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without
|
|
// specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
|
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
|
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include <bitset>
|
|
#include <cstdlib>
|
|
#include <sstream>
|
|
|
|
#include "disasm-aarch64.h"
|
|
|
|
namespace vixl {
|
|
namespace aarch64 {
|
|
|
|
const Disassembler::FormToVisitorFnMap *Disassembler::GetFormToVisitorFnMap() {
|
|
static const FormToVisitorFnMap form_to_visitor = {
|
|
DEFAULT_FORM_TO_VISITOR_MAP(Disassembler),
|
|
{"autia1716_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"autiasp_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"autiaz_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"autib1716_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"autibsp_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"autibz_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"axflag_m_pstate"_h, &Disassembler::DisassembleNoArgs},
|
|
{"cfinv_m_pstate"_h, &Disassembler::DisassembleNoArgs},
|
|
{"csdb_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"dgh_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"ssbb_only_barriers"_h, &Disassembler::DisassembleNoArgs},
|
|
{"esb_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"isb_bi_barriers"_h, &Disassembler::DisassembleNoArgs},
|
|
{"nop_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"pacia1716_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"paciasp_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"paciaz_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"pacib1716_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"pacibsp_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"pacibz_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"sev_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"sevl_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"wfe_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"wfi_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"xaflag_m_pstate"_h, &Disassembler::DisassembleNoArgs},
|
|
{"xpaclri_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"yield_hi_hints"_h, &Disassembler::DisassembleNoArgs},
|
|
{"abs_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"cls_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"clz_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"cnt_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"neg_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"rev16_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"rev32_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"rev64_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"sqabs_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"sqneg_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"suqadd_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"urecpe_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"ursqrte_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"usqadd_asimdmisc_r"_h, &Disassembler::VisitNEON2RegMisc},
|
|
{"not_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegLogical},
|
|
{"rbit_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegLogical},
|
|
{"xtn_asimdmisc_n"_h, &Disassembler::DisassembleNEON2RegExtract},
|
|
{"sqxtn_asimdmisc_n"_h, &Disassembler::DisassembleNEON2RegExtract},
|
|
{"uqxtn_asimdmisc_n"_h, &Disassembler::DisassembleNEON2RegExtract},
|
|
{"sqxtun_asimdmisc_n"_h, &Disassembler::DisassembleNEON2RegExtract},
|
|
{"shll_asimdmisc_s"_h, &Disassembler::DisassembleNEON2RegExtract},
|
|
{"sadalp_asimdmisc_p"_h, &Disassembler::DisassembleNEON2RegAddlp},
|
|
{"saddlp_asimdmisc_p"_h, &Disassembler::DisassembleNEON2RegAddlp},
|
|
{"uadalp_asimdmisc_p"_h, &Disassembler::DisassembleNEON2RegAddlp},
|
|
{"uaddlp_asimdmisc_p"_h, &Disassembler::DisassembleNEON2RegAddlp},
|
|
{"cmeq_asimdmisc_z"_h, &Disassembler::DisassembleNEON2RegCompare},
|
|
{"cmge_asimdmisc_z"_h, &Disassembler::DisassembleNEON2RegCompare},
|
|
{"cmgt_asimdmisc_z"_h, &Disassembler::DisassembleNEON2RegCompare},
|
|
{"cmle_asimdmisc_z"_h, &Disassembler::DisassembleNEON2RegCompare},
|
|
{"cmlt_asimdmisc_z"_h, &Disassembler::DisassembleNEON2RegCompare},
|
|
{"fcmeq_asimdmisc_fz"_h, &Disassembler::DisassembleNEON2RegFPCompare},
|
|
{"fcmge_asimdmisc_fz"_h, &Disassembler::DisassembleNEON2RegFPCompare},
|
|
{"fcmgt_asimdmisc_fz"_h, &Disassembler::DisassembleNEON2RegFPCompare},
|
|
{"fcmle_asimdmisc_fz"_h, &Disassembler::DisassembleNEON2RegFPCompare},
|
|
{"fcmlt_asimdmisc_fz"_h, &Disassembler::DisassembleNEON2RegFPCompare},
|
|
{"fcvtl_asimdmisc_l"_h, &Disassembler::DisassembleNEON2RegFPConvert},
|
|
{"fcvtn_asimdmisc_n"_h, &Disassembler::DisassembleNEON2RegFPConvert},
|
|
{"fcvtxn_asimdmisc_n"_h, &Disassembler::DisassembleNEON2RegFPConvert},
|
|
{"fabs_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fcvtas_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fcvtau_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fcvtms_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fcvtmu_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fcvtns_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fcvtnu_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fcvtps_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fcvtpu_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fcvtzs_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fcvtzu_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fneg_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frecpe_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frint32x_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frint32z_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frint64x_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frint64z_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frinta_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frinti_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frintm_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frintn_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frintp_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frintx_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frintz_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"frsqrte_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"fsqrt_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"scvtf_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"ucvtf_asimdmisc_r"_h, &Disassembler::DisassembleNEON2RegFP},
|
|
{"smlal_asimdelem_l"_h, &Disassembler::DisassembleNEONMulByElementLong},
|
|
{"smlsl_asimdelem_l"_h, &Disassembler::DisassembleNEONMulByElementLong},
|
|
{"smull_asimdelem_l"_h, &Disassembler::DisassembleNEONMulByElementLong},
|
|
{"umlal_asimdelem_l"_h, &Disassembler::DisassembleNEONMulByElementLong},
|
|
{"umlsl_asimdelem_l"_h, &Disassembler::DisassembleNEONMulByElementLong},
|
|
{"umull_asimdelem_l"_h, &Disassembler::DisassembleNEONMulByElementLong},
|
|
{"sqdmull_asimdelem_l"_h, &Disassembler::DisassembleNEONMulByElementLong},
|
|
{"sqdmlal_asimdelem_l"_h, &Disassembler::DisassembleNEONMulByElementLong},
|
|
{"sqdmlsl_asimdelem_l"_h, &Disassembler::DisassembleNEONMulByElementLong},
|
|
{"sdot_asimdelem_d"_h, &Disassembler::DisassembleNEONDotProdByElement},
|
|
{"udot_asimdelem_d"_h, &Disassembler::DisassembleNEONDotProdByElement},
|
|
{"usdot_asimdelem_d"_h, &Disassembler::DisassembleNEONDotProdByElement},
|
|
{"sudot_asimdelem_d"_h, &Disassembler::DisassembleNEONDotProdByElement},
|
|
{"fmlal2_asimdelem_lh"_h,
|
|
&Disassembler::DisassembleNEONFPMulByElementLong},
|
|
{"fmlal_asimdelem_lh"_h,
|
|
&Disassembler::DisassembleNEONFPMulByElementLong},
|
|
{"fmlsl2_asimdelem_lh"_h,
|
|
&Disassembler::DisassembleNEONFPMulByElementLong},
|
|
{"fmlsl_asimdelem_lh"_h,
|
|
&Disassembler::DisassembleNEONFPMulByElementLong},
|
|
{"fcmla_asimdelem_c_h"_h,
|
|
&Disassembler::DisassembleNEONComplexMulByElement},
|
|
{"fcmla_asimdelem_c_s"_h,
|
|
&Disassembler::DisassembleNEONComplexMulByElement},
|
|
{"fmla_asimdelem_rh_h"_h,
|
|
&Disassembler::DisassembleNEONHalfFPMulByElement},
|
|
{"fmls_asimdelem_rh_h"_h,
|
|
&Disassembler::DisassembleNEONHalfFPMulByElement},
|
|
{"fmulx_asimdelem_rh_h"_h,
|
|
&Disassembler::DisassembleNEONHalfFPMulByElement},
|
|
{"fmul_asimdelem_rh_h"_h,
|
|
&Disassembler::DisassembleNEONHalfFPMulByElement},
|
|
{"fmla_asimdelem_r_sd"_h, &Disassembler::DisassembleNEONFPMulByElement},
|
|
{"fmls_asimdelem_r_sd"_h, &Disassembler::DisassembleNEONFPMulByElement},
|
|
{"fmulx_asimdelem_r_sd"_h, &Disassembler::DisassembleNEONFPMulByElement},
|
|
{"fmul_asimdelem_r_sd"_h, &Disassembler::DisassembleNEONFPMulByElement},
|
|
{"mla_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"mls_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"mul_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"saba_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"sabd_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"shadd_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"shsub_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"smaxp_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"smax_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"sminp_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"smin_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"srhadd_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"uaba_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"uabd_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"uhadd_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"uhsub_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"umaxp_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"umax_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"uminp_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"umin_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"urhadd_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameNoD},
|
|
{"and_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameLogical},
|
|
{"bic_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameLogical},
|
|
{"bif_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameLogical},
|
|
{"bit_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameLogical},
|
|
{"bsl_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameLogical},
|
|
{"eor_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameLogical},
|
|
{"orr_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameLogical},
|
|
{"orn_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameLogical},
|
|
{"pmul_asimdsame_only"_h, &Disassembler::DisassembleNEON3SameLogical},
|
|
{"fmlal2_asimdsame_f"_h, &Disassembler::DisassembleNEON3SameFHM},
|
|
{"fmlal_asimdsame_f"_h, &Disassembler::DisassembleNEON3SameFHM},
|
|
{"fmlsl2_asimdsame_f"_h, &Disassembler::DisassembleNEON3SameFHM},
|
|
{"fmlsl_asimdsame_f"_h, &Disassembler::DisassembleNEON3SameFHM},
|
|
{"sri_asimdshf_r"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"srshr_asimdshf_r"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"srsra_asimdshf_r"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"sshr_asimdshf_r"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"ssra_asimdshf_r"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"urshr_asimdshf_r"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"ursra_asimdshf_r"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"ushr_asimdshf_r"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"usra_asimdshf_r"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"scvtf_asimdshf_c"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"ucvtf_asimdshf_c"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"fcvtzs_asimdshf_c"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"fcvtzu_asimdshf_c"_h, &Disassembler::DisassembleNEONShiftRightImm},
|
|
{"ushll_asimdshf_l"_h, &Disassembler::DisassembleNEONShiftLeftLongImm},
|
|
{"sshll_asimdshf_l"_h, &Disassembler::DisassembleNEONShiftLeftLongImm},
|
|
{"shrn_asimdshf_n"_h, &Disassembler::DisassembleNEONShiftRightNarrowImm},
|
|
{"rshrn_asimdshf_n"_h, &Disassembler::DisassembleNEONShiftRightNarrowImm},
|
|
{"sqshrn_asimdshf_n"_h,
|
|
&Disassembler::DisassembleNEONShiftRightNarrowImm},
|
|
{"sqrshrn_asimdshf_n"_h,
|
|
&Disassembler::DisassembleNEONShiftRightNarrowImm},
|
|
{"sqshrun_asimdshf_n"_h,
|
|
&Disassembler::DisassembleNEONShiftRightNarrowImm},
|
|
{"sqrshrun_asimdshf_n"_h,
|
|
&Disassembler::DisassembleNEONShiftRightNarrowImm},
|
|
{"uqshrn_asimdshf_n"_h,
|
|
&Disassembler::DisassembleNEONShiftRightNarrowImm},
|
|
{"uqrshrn_asimdshf_n"_h,
|
|
&Disassembler::DisassembleNEONShiftRightNarrowImm},
|
|
{"sqdmlal_asisdelem_l"_h,
|
|
&Disassembler::DisassembleNEONScalarSatMulLongIndex},
|
|
{"sqdmlsl_asisdelem_l"_h,
|
|
&Disassembler::DisassembleNEONScalarSatMulLongIndex},
|
|
{"sqdmull_asisdelem_l"_h,
|
|
&Disassembler::DisassembleNEONScalarSatMulLongIndex},
|
|
{"fmla_asisdelem_rh_h"_h, &Disassembler::DisassembleNEONFPScalarMulIndex},
|
|
{"fmla_asisdelem_r_sd"_h, &Disassembler::DisassembleNEONFPScalarMulIndex},
|
|
{"fmls_asisdelem_rh_h"_h, &Disassembler::DisassembleNEONFPScalarMulIndex},
|
|
{"fmls_asisdelem_r_sd"_h, &Disassembler::DisassembleNEONFPScalarMulIndex},
|
|
{"fmulx_asisdelem_rh_h"_h,
|
|
&Disassembler::DisassembleNEONFPScalarMulIndex},
|
|
{"fmulx_asisdelem_r_sd"_h,
|
|
&Disassembler::DisassembleNEONFPScalarMulIndex},
|
|
{"fmul_asisdelem_rh_h"_h, &Disassembler::DisassembleNEONFPScalarMulIndex},
|
|
{"fmul_asisdelem_r_sd"_h, &Disassembler::DisassembleNEONFPScalarMulIndex},
|
|
{"fabd_asisdsame_only"_h, &Disassembler::DisassembleNEONFPScalar3Same},
|
|
{"facge_asisdsame_only"_h, &Disassembler::DisassembleNEONFPScalar3Same},
|
|
{"facgt_asisdsame_only"_h, &Disassembler::DisassembleNEONFPScalar3Same},
|
|
{"fcmeq_asisdsame_only"_h, &Disassembler::DisassembleNEONFPScalar3Same},
|
|
{"fcmge_asisdsame_only"_h, &Disassembler::DisassembleNEONFPScalar3Same},
|
|
{"fcmgt_asisdsame_only"_h, &Disassembler::DisassembleNEONFPScalar3Same},
|
|
{"fmulx_asisdsame_only"_h, &Disassembler::DisassembleNEONFPScalar3Same},
|
|
{"frecps_asisdsame_only"_h, &Disassembler::DisassembleNEONFPScalar3Same},
|
|
{"frsqrts_asisdsame_only"_h, &Disassembler::DisassembleNEONFPScalar3Same},
|
|
{"sqrdmlah_asisdsame2_only"_h, &Disassembler::VisitNEONScalar3Same},
|
|
{"sqrdmlsh_asisdsame2_only"_h, &Disassembler::VisitNEONScalar3Same},
|
|
{"cmeq_asisdsame_only"_h, &Disassembler::DisassembleNEONScalar3SameOnlyD},
|
|
{"cmge_asisdsame_only"_h, &Disassembler::DisassembleNEONScalar3SameOnlyD},
|
|
{"cmgt_asisdsame_only"_h, &Disassembler::DisassembleNEONScalar3SameOnlyD},
|
|
{"cmhi_asisdsame_only"_h, &Disassembler::DisassembleNEONScalar3SameOnlyD},
|
|
{"cmhs_asisdsame_only"_h, &Disassembler::DisassembleNEONScalar3SameOnlyD},
|
|
{"cmtst_asisdsame_only"_h,
|
|
&Disassembler::DisassembleNEONScalar3SameOnlyD},
|
|
{"add_asisdsame_only"_h, &Disassembler::DisassembleNEONScalar3SameOnlyD},
|
|
{"sub_asisdsame_only"_h, &Disassembler::DisassembleNEONScalar3SameOnlyD},
|
|
{"fmaxnmv_asimdall_only_h"_h,
|
|
&Disassembler::DisassembleNEONFP16AcrossLanes},
|
|
{"fmaxv_asimdall_only_h"_h,
|
|
&Disassembler::DisassembleNEONFP16AcrossLanes},
|
|
{"fminnmv_asimdall_only_h"_h,
|
|
&Disassembler::DisassembleNEONFP16AcrossLanes},
|
|
{"fminv_asimdall_only_h"_h,
|
|
&Disassembler::DisassembleNEONFP16AcrossLanes},
|
|
{"fmaxnmv_asimdall_only_sd"_h,
|
|
&Disassembler::DisassembleNEONFPAcrossLanes},
|
|
{"fminnmv_asimdall_only_sd"_h,
|
|
&Disassembler::DisassembleNEONFPAcrossLanes},
|
|
{"fmaxv_asimdall_only_sd"_h, &Disassembler::DisassembleNEONFPAcrossLanes},
|
|
{"fminv_asimdall_only_sd"_h, &Disassembler::DisassembleNEONFPAcrossLanes},
|
|
{"shl_asisdshf_r"_h, &Disassembler::DisassembleNEONScalarShiftImmOnlyD},
|
|
{"sli_asisdshf_r"_h, &Disassembler::DisassembleNEONScalarShiftImmOnlyD},
|
|
{"sri_asisdshf_r"_h, &Disassembler::DisassembleNEONScalarShiftImmOnlyD},
|
|
{"srshr_asisdshf_r"_h, &Disassembler::DisassembleNEONScalarShiftImmOnlyD},
|
|
{"srsra_asisdshf_r"_h, &Disassembler::DisassembleNEONScalarShiftImmOnlyD},
|
|
{"sshr_asisdshf_r"_h, &Disassembler::DisassembleNEONScalarShiftImmOnlyD},
|
|
{"ssra_asisdshf_r"_h, &Disassembler::DisassembleNEONScalarShiftImmOnlyD},
|
|
{"urshr_asisdshf_r"_h, &Disassembler::DisassembleNEONScalarShiftImmOnlyD},
|
|
{"ursra_asisdshf_r"_h, &Disassembler::DisassembleNEONScalarShiftImmOnlyD},
|
|
{"ushr_asisdshf_r"_h, &Disassembler::DisassembleNEONScalarShiftImmOnlyD},
|
|
{"usra_asisdshf_r"_h, &Disassembler::DisassembleNEONScalarShiftImmOnlyD},
|
|
{"sqrshrn_asisdshf_n"_h,
|
|
&Disassembler::DisassembleNEONScalarShiftRightNarrowImm},
|
|
{"sqrshrun_asisdshf_n"_h,
|
|
&Disassembler::DisassembleNEONScalarShiftRightNarrowImm},
|
|
{"sqshrn_asisdshf_n"_h,
|
|
&Disassembler::DisassembleNEONScalarShiftRightNarrowImm},
|
|
{"sqshrun_asisdshf_n"_h,
|
|
&Disassembler::DisassembleNEONScalarShiftRightNarrowImm},
|
|
{"uqrshrn_asisdshf_n"_h,
|
|
&Disassembler::DisassembleNEONScalarShiftRightNarrowImm},
|
|
{"uqshrn_asisdshf_n"_h,
|
|
&Disassembler::DisassembleNEONScalarShiftRightNarrowImm},
|
|
{"cmeq_asisdmisc_z"_h, &Disassembler::DisassembleNEONScalar2RegMiscOnlyD},
|
|
{"cmge_asisdmisc_z"_h, &Disassembler::DisassembleNEONScalar2RegMiscOnlyD},
|
|
{"cmgt_asisdmisc_z"_h, &Disassembler::DisassembleNEONScalar2RegMiscOnlyD},
|
|
{"cmle_asisdmisc_z"_h, &Disassembler::DisassembleNEONScalar2RegMiscOnlyD},
|
|
{"cmlt_asisdmisc_z"_h, &Disassembler::DisassembleNEONScalar2RegMiscOnlyD},
|
|
{"abs_asisdmisc_r"_h, &Disassembler::DisassembleNEONScalar2RegMiscOnlyD},
|
|
{"neg_asisdmisc_r"_h, &Disassembler::DisassembleNEONScalar2RegMiscOnlyD},
|
|
{"fcmeq_asisdmisc_fz"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcmge_asisdmisc_fz"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcmgt_asisdmisc_fz"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcmle_asisdmisc_fz"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcmlt_asisdmisc_fz"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcvtas_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcvtau_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcvtms_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcvtmu_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcvtns_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcvtnu_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcvtps_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcvtpu_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcvtxn_asisdmisc_n"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcvtzs_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"fcvtzu_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"frecpe_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"frecpx_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"frsqrte_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"scvtf_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"ucvtf_asisdmisc_r"_h, &Disassembler::DisassembleNEONFPScalar2RegMisc},
|
|
{"adclb_z_zzz"_h, &Disassembler::DisassembleSVEAddSubCarry},
|
|
{"adclt_z_zzz"_h, &Disassembler::DisassembleSVEAddSubCarry},
|
|
{"addhnb_z_zz"_h, &Disassembler::DisassembleSVEAddSubHigh},
|
|
{"addhnt_z_zz"_h, &Disassembler::DisassembleSVEAddSubHigh},
|
|
{"addp_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"aesd_z_zz"_h, &Disassembler::Disassemble_ZdnB_ZdnB_ZmB},
|
|
{"aese_z_zz"_h, &Disassembler::Disassemble_ZdnB_ZdnB_ZmB},
|
|
{"aesimc_z_z"_h, &Disassembler::Disassemble_ZdnB_ZdnB},
|
|
{"aesmc_z_z"_h, &Disassembler::Disassemble_ZdnB_ZdnB},
|
|
{"bcax_z_zzz"_h, &Disassembler::DisassembleSVEBitwiseTernary},
|
|
{"bdep_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmT},
|
|
{"bext_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmT},
|
|
{"bgrp_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmT},
|
|
{"bsl1n_z_zzz"_h, &Disassembler::DisassembleSVEBitwiseTernary},
|
|
{"bsl2n_z_zzz"_h, &Disassembler::DisassembleSVEBitwiseTernary},
|
|
{"bsl_z_zzz"_h, &Disassembler::DisassembleSVEBitwiseTernary},
|
|
{"cadd_z_zz"_h, &Disassembler::DisassembleSVEComplexIntAddition},
|
|
{"cdot_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb_const},
|
|
{"cdot_z_zzzi_d"_h, &Disassembler::Disassemble_ZdaD_ZnH_ZmH_imm_const},
|
|
{"cdot_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnB_ZmB_imm_const},
|
|
{"cmla_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnT_ZmT_const},
|
|
{"cmla_z_zzzi_h"_h, &Disassembler::Disassemble_ZdaH_ZnH_ZmH_imm_const},
|
|
{"cmla_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnS_ZmS_imm_const},
|
|
{"eor3_z_zzz"_h, &Disassembler::DisassembleSVEBitwiseTernary},
|
|
{"eorbt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmT},
|
|
{"eortb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmT},
|
|
{"ext_z_zi_con"_h, &Disassembler::Disassemble_ZdB_Zn1B_Zn2B_imm},
|
|
{"faddp_z_p_zz"_h, &Disassembler::DisassembleSVEFPPair},
|
|
{"fcvtlt_z_p_z_h2s"_h, &Disassembler::Disassemble_ZdS_PgM_ZnH},
|
|
{"fcvtlt_z_p_z_s2d"_h, &Disassembler::Disassemble_ZdD_PgM_ZnS},
|
|
{"fcvtnt_z_p_z_d2s"_h, &Disassembler::Disassemble_ZdS_PgM_ZnD},
|
|
{"fcvtnt_z_p_z_s2h"_h, &Disassembler::Disassemble_ZdH_PgM_ZnS},
|
|
{"fcvtx_z_p_z_d2s"_h, &Disassembler::Disassemble_ZdS_PgM_ZnD},
|
|
{"fcvtxnt_z_p_z_d2s"_h, &Disassembler::Disassemble_ZdS_PgM_ZnD},
|
|
{"flogb_z_p_z"_h, &Disassembler::DisassembleSVEFlogb},
|
|
{"fmaxnmp_z_p_zz"_h, &Disassembler::DisassembleSVEFPPair},
|
|
{"fmaxp_z_p_zz"_h, &Disassembler::DisassembleSVEFPPair},
|
|
{"fminnmp_z_p_zz"_h, &Disassembler::DisassembleSVEFPPair},
|
|
{"fminp_z_p_zz"_h, &Disassembler::DisassembleSVEFPPair},
|
|
{"fmlalb_z_zzz"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH},
|
|
{"fmlalb_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH_imm},
|
|
{"fmlalt_z_zzz"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH},
|
|
{"fmlalt_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH_imm},
|
|
{"fmlslb_z_zzz"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH},
|
|
{"fmlslb_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH_imm},
|
|
{"fmlslt_z_zzz"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH},
|
|
{"fmlslt_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH_imm},
|
|
{"histcnt_z_p_zz"_h, &Disassembler::Disassemble_ZdT_PgZ_ZnT_ZmT},
|
|
{"histseg_z_zz"_h, &Disassembler::Disassemble_ZdB_ZnB_ZmB},
|
|
{"ldnt1b_z_p_ar_d_64_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtD_PgZ_ZnD_Xm},
|
|
{"ldnt1b_z_p_ar_s_x32_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtS_PgZ_ZnS_Xm},
|
|
{"ldnt1d_z_p_ar_d_64_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtD_PgZ_ZnD_Xm},
|
|
{"ldnt1h_z_p_ar_d_64_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtD_PgZ_ZnD_Xm},
|
|
{"ldnt1h_z_p_ar_s_x32_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtS_PgZ_ZnS_Xm},
|
|
{"ldnt1sb_z_p_ar_d_64_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtD_PgZ_ZnD_Xm},
|
|
{"ldnt1sb_z_p_ar_s_x32_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtS_PgZ_ZnS_Xm},
|
|
{"ldnt1sh_z_p_ar_d_64_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtD_PgZ_ZnD_Xm},
|
|
{"ldnt1sh_z_p_ar_s_x32_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtS_PgZ_ZnS_Xm},
|
|
{"ldnt1sw_z_p_ar_d_64_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtD_PgZ_ZnD_Xm},
|
|
{"ldnt1w_z_p_ar_d_64_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtD_PgZ_ZnD_Xm},
|
|
{"ldnt1w_z_p_ar_s_x32_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtS_PgZ_ZnS_Xm},
|
|
{"match_p_p_zz"_h, &Disassembler::Disassemble_PdT_PgZ_ZnT_ZmT},
|
|
{"mla_z_zzzi_d"_h, &Disassembler::Disassemble_ZdD_ZnD_ZmD_imm},
|
|
{"mla_z_zzzi_h"_h, &Disassembler::Disassemble_ZdH_ZnH_ZmH_imm},
|
|
{"mla_z_zzzi_s"_h, &Disassembler::Disassemble_ZdS_ZnS_ZmS_imm},
|
|
{"mls_z_zzzi_d"_h, &Disassembler::Disassemble_ZdD_ZnD_ZmD_imm},
|
|
{"mls_z_zzzi_h"_h, &Disassembler::Disassemble_ZdH_ZnH_ZmH_imm},
|
|
{"mls_z_zzzi_s"_h, &Disassembler::Disassemble_ZdS_ZnS_ZmS_imm},
|
|
{"mul_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmT},
|
|
{"mul_z_zzi_d"_h, &Disassembler::Disassemble_ZdD_ZnD_ZmD_imm},
|
|
{"mul_z_zzi_h"_h, &Disassembler::Disassemble_ZdH_ZnH_ZmH_imm},
|
|
{"mul_z_zzi_s"_h, &Disassembler::Disassemble_ZdS_ZnS_ZmS_imm},
|
|
{"nbsl_z_zzz"_h, &Disassembler::DisassembleSVEBitwiseTernary},
|
|
{"nmatch_p_p_zz"_h, &Disassembler::Disassemble_PdT_PgZ_ZnT_ZmT},
|
|
{"pmul_z_zz"_h, &Disassembler::Disassemble_ZdB_ZnB_ZmB},
|
|
{"pmullb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"pmullt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"raddhnb_z_zz"_h, &Disassembler::DisassembleSVEAddSubHigh},
|
|
{"raddhnt_z_zz"_h, &Disassembler::DisassembleSVEAddSubHigh},
|
|
{"rax1_z_zz"_h, &Disassembler::Disassemble_ZdD_ZnD_ZmD},
|
|
{"rshrnb_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"rshrnt_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"rsubhnb_z_zz"_h, &Disassembler::DisassembleSVEAddSubHigh},
|
|
{"rsubhnt_z_zz"_h, &Disassembler::DisassembleSVEAddSubHigh},
|
|
{"saba_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnT_ZmT},
|
|
{"sabalb_z_zzz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"sabalt_z_zzz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"sabdlb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"sabdlt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"sadalp_z_p_z"_h, &Disassembler::Disassemble_ZdaT_PgM_ZnTb},
|
|
{"saddlb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"saddlbt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"saddlt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"saddwb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmTb},
|
|
{"saddwt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmTb},
|
|
{"sbclb_z_zzz"_h, &Disassembler::DisassembleSVEAddSubCarry},
|
|
{"sbclt_z_zzz"_h, &Disassembler::DisassembleSVEAddSubCarry},
|
|
{"shadd_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"shrnb_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"shrnt_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"shsub_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"shsubr_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"sli_z_zzi"_h, &Disassembler::VisitSVEBitwiseShiftUnpredicated},
|
|
{"sm4e_z_zz"_h, &Disassembler::Disassemble_ZdnS_ZdnS_ZmS},
|
|
{"sm4ekey_z_zz"_h, &Disassembler::Disassemble_ZdS_ZnS_ZmS},
|
|
{"smaxp_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"sminp_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"smlalb_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"smlalb_z_zzzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"smlalb_z_zzzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"smlalt_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"smlalt_z_zzzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"smlalt_z_zzzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"smlslb_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"smlslb_z_zzzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"smlslb_z_zzzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"smlslt_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"smlslt_z_zzzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"smlslt_z_zzzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"smulh_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmT},
|
|
{"smullb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"smullb_z_zzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"smullb_z_zzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"smullt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"smullt_z_zzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"smullt_z_zzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"splice_z_p_zz_con"_h, &Disassembler::Disassemble_ZdT_Pg_Zn1T_Zn2T},
|
|
{"sqabs_z_p_z"_h, &Disassembler::Disassemble_ZdT_PgM_ZnT},
|
|
{"sqadd_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"sqcadd_z_zz"_h, &Disassembler::DisassembleSVEComplexIntAddition},
|
|
{"sqdmlalb_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"sqdmlalb_z_zzzi_d"_h, &Disassembler::Disassemble_ZdaD_ZnS_ZmS_imm},
|
|
{"sqdmlalb_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH_imm},
|
|
{"sqdmlalbt_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"sqdmlalt_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"sqdmlalt_z_zzzi_d"_h, &Disassembler::Disassemble_ZdaD_ZnS_ZmS_imm},
|
|
{"sqdmlalt_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH_imm},
|
|
{"sqdmlslb_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"sqdmlslb_z_zzzi_d"_h, &Disassembler::Disassemble_ZdaD_ZnS_ZmS_imm},
|
|
{"sqdmlslb_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH_imm},
|
|
{"sqdmlslbt_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"sqdmlslt_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"sqdmlslt_z_zzzi_d"_h, &Disassembler::Disassemble_ZdaD_ZnS_ZmS_imm},
|
|
{"sqdmlslt_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnH_ZmH_imm},
|
|
{"sqdmulh_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmT},
|
|
{"sqdmulh_z_zzi_d"_h, &Disassembler::Disassemble_ZdD_ZnD_ZmD_imm},
|
|
{"sqdmulh_z_zzi_h"_h, &Disassembler::Disassemble_ZdH_ZnH_ZmH_imm},
|
|
{"sqdmulh_z_zzi_s"_h, &Disassembler::Disassemble_ZdS_ZnS_ZmS_imm},
|
|
{"sqdmullb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"sqdmullb_z_zzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"sqdmullb_z_zzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"sqdmullt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"sqdmullt_z_zzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"sqdmullt_z_zzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"sqneg_z_p_z"_h, &Disassembler::Disassemble_ZdT_PgM_ZnT},
|
|
{"sqrdcmlah_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnT_ZmT_const},
|
|
{"sqrdcmlah_z_zzzi_h"_h,
|
|
&Disassembler::Disassemble_ZdaH_ZnH_ZmH_imm_const},
|
|
{"sqrdcmlah_z_zzzi_s"_h,
|
|
&Disassembler::Disassemble_ZdaS_ZnS_ZmS_imm_const},
|
|
{"sqrdmlah_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnT_ZmT},
|
|
{"sqrdmlah_z_zzzi_d"_h, &Disassembler::Disassemble_ZdaD_ZnD_ZmD_imm},
|
|
{"sqrdmlah_z_zzzi_h"_h, &Disassembler::Disassemble_ZdaH_ZnH_ZmH_imm},
|
|
{"sqrdmlah_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnS_ZmS_imm},
|
|
{"sqrdmlsh_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnT_ZmT},
|
|
{"sqrdmlsh_z_zzzi_d"_h, &Disassembler::Disassemble_ZdaD_ZnD_ZmD_imm},
|
|
{"sqrdmlsh_z_zzzi_h"_h, &Disassembler::Disassemble_ZdaH_ZnH_ZmH_imm},
|
|
{"sqrdmlsh_z_zzzi_s"_h, &Disassembler::Disassemble_ZdaS_ZnS_ZmS_imm},
|
|
{"sqrdmulh_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmT},
|
|
{"sqrdmulh_z_zzi_d"_h, &Disassembler::Disassemble_ZdD_ZnD_ZmD_imm},
|
|
{"sqrdmulh_z_zzi_h"_h, &Disassembler::Disassemble_ZdH_ZnH_ZmH_imm},
|
|
{"sqrdmulh_z_zzi_s"_h, &Disassembler::Disassemble_ZdS_ZnS_ZmS_imm},
|
|
{"sqrshl_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"sqrshlr_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"sqrshrnb_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"sqrshrnt_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"sqrshrunb_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"sqrshrunt_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"sqshl_z_p_zi"_h, &Disassembler::VisitSVEBitwiseShiftByImm_Predicated},
|
|
{"sqshl_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"sqshlr_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"sqshlu_z_p_zi"_h, &Disassembler::VisitSVEBitwiseShiftByImm_Predicated},
|
|
{"sqshrnb_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"sqshrnt_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"sqshrunb_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"sqshrunt_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"sqsub_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"sqsubr_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"sqxtnb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb},
|
|
{"sqxtnt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb},
|
|
{"sqxtunb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb},
|
|
{"sqxtunt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb},
|
|
{"srhadd_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"sri_z_zzi"_h, &Disassembler::VisitSVEBitwiseShiftUnpredicated},
|
|
{"srshl_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"srshlr_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"srshr_z_p_zi"_h, &Disassembler::VisitSVEBitwiseShiftByImm_Predicated},
|
|
{"srsra_z_zi"_h, &Disassembler::VisitSVEBitwiseShiftUnpredicated},
|
|
{"sshllb_z_zi"_h, &Disassembler::DisassembleSVEShiftLeftImm},
|
|
{"sshllt_z_zi"_h, &Disassembler::DisassembleSVEShiftLeftImm},
|
|
{"ssra_z_zi"_h, &Disassembler::VisitSVEBitwiseShiftUnpredicated},
|
|
{"ssublb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"ssublbt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"ssublt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"ssubltb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"ssubwb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmTb},
|
|
{"ssubwt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmTb},
|
|
{"stnt1b_z_p_ar_d_64_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtD_Pg_ZnD_Xm},
|
|
{"stnt1b_z_p_ar_s_x32_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtS_Pg_ZnS_Xm},
|
|
{"stnt1d_z_p_ar_d_64_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtD_Pg_ZnD_Xm},
|
|
{"stnt1h_z_p_ar_d_64_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtD_Pg_ZnD_Xm},
|
|
{"stnt1h_z_p_ar_s_x32_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtS_Pg_ZnS_Xm},
|
|
{"stnt1w_z_p_ar_d_64_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtD_Pg_ZnD_Xm},
|
|
{"stnt1w_z_p_ar_s_x32_unscaled"_h,
|
|
&Disassembler::Disassemble_ZtS_Pg_ZnS_Xm},
|
|
{"subhnb_z_zz"_h, &Disassembler::DisassembleSVEAddSubHigh},
|
|
{"subhnt_z_zz"_h, &Disassembler::DisassembleSVEAddSubHigh},
|
|
{"suqadd_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"tbl_z_zz_2"_h, &Disassembler::Disassemble_ZdT_Zn1T_Zn2T_ZmT},
|
|
{"tbx_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmT},
|
|
{"uaba_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnT_ZmT},
|
|
{"uabalb_z_zzz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"uabalt_z_zzz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"uabdlb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"uabdlt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"uadalp_z_p_z"_h, &Disassembler::Disassemble_ZdaT_PgM_ZnTb},
|
|
{"uaddlb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"uaddlt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"uaddwb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmTb},
|
|
{"uaddwt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmTb},
|
|
{"uhadd_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"uhsub_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"uhsubr_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"umaxp_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"uminp_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"umlalb_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"umlalb_z_zzzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"umlalb_z_zzzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"umlalt_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"umlalt_z_zzzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"umlalt_z_zzzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"umlslb_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"umlslb_z_zzzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"umlslb_z_zzzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"umlslt_z_zzz"_h, &Disassembler::Disassemble_ZdaT_ZnTb_ZmTb},
|
|
{"umlslt_z_zzzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"umlslt_z_zzzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"umulh_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmT},
|
|
{"umullb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"umullb_z_zzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"umullb_z_zzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"umullt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"umullt_z_zzi_d"_h, &Disassembler::Disassemble_ZdD_ZnS_ZmS_imm},
|
|
{"umullt_z_zzi_s"_h, &Disassembler::Disassemble_ZdS_ZnH_ZmH_imm},
|
|
{"uqadd_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"uqrshl_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"uqrshlr_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"uqrshrnb_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"uqrshrnt_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"uqshl_z_p_zi"_h, &Disassembler::VisitSVEBitwiseShiftByImm_Predicated},
|
|
{"uqshl_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"uqshlr_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"uqshrnb_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"uqshrnt_z_zi"_h, &Disassembler::DisassembleSVEShiftRightImm},
|
|
{"uqsub_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"uqsubr_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"uqxtnb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb},
|
|
{"uqxtnt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb},
|
|
{"urecpe_z_p_z"_h, &Disassembler::Disassemble_ZdS_PgM_ZnS},
|
|
{"urhadd_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"urshl_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"urshlr_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"urshr_z_p_zi"_h, &Disassembler::VisitSVEBitwiseShiftByImm_Predicated},
|
|
{"ursqrte_z_p_z"_h, &Disassembler::Disassemble_ZdS_PgM_ZnS},
|
|
{"ursra_z_zi"_h, &Disassembler::VisitSVEBitwiseShiftUnpredicated},
|
|
{"ushllb_z_zi"_h, &Disassembler::DisassembleSVEShiftLeftImm},
|
|
{"ushllt_z_zi"_h, &Disassembler::DisassembleSVEShiftLeftImm},
|
|
{"usqadd_z_p_zz"_h, &Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT},
|
|
{"usra_z_zi"_h, &Disassembler::VisitSVEBitwiseShiftUnpredicated},
|
|
{"usublb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"usublt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnTb_ZmTb},
|
|
{"usubwb_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmTb},
|
|
{"usubwt_z_zz"_h, &Disassembler::Disassemble_ZdT_ZnT_ZmTb},
|
|
{"whilege_p_p_rr"_h,
|
|
&Disassembler::VisitSVEIntCompareScalarCountAndLimit},
|
|
{"whilegt_p_p_rr"_h,
|
|
&Disassembler::VisitSVEIntCompareScalarCountAndLimit},
|
|
{"whilehi_p_p_rr"_h,
|
|
&Disassembler::VisitSVEIntCompareScalarCountAndLimit},
|
|
{"whilehs_p_p_rr"_h,
|
|
&Disassembler::VisitSVEIntCompareScalarCountAndLimit},
|
|
{"whilerw_p_rr"_h, &Disassembler::VisitSVEIntCompareScalarCountAndLimit},
|
|
{"whilewr_p_rr"_h, &Disassembler::VisitSVEIntCompareScalarCountAndLimit},
|
|
{"xar_z_zzi"_h, &Disassembler::Disassemble_ZdnT_ZdnT_ZmT_const},
|
|
{"fmmla_z_zzz_s"_h, &Disassembler::Disassemble_ZdaT_ZnT_ZmT},
|
|
{"fmmla_z_zzz_d"_h, &Disassembler::Disassemble_ZdaT_ZnT_ZmT},
|
|
{"smmla_z_zzz"_h, &Disassembler::Disassemble_ZdaS_ZnB_ZmB},
|
|
{"ummla_z_zzz"_h, &Disassembler::Disassemble_ZdaS_ZnB_ZmB},
|
|
{"usmmla_z_zzz"_h, &Disassembler::Disassemble_ZdaS_ZnB_ZmB},
|
|
{"usdot_z_zzz_s"_h, &Disassembler::Disassemble_ZdaS_ZnB_ZmB},
|
|
{"smmla_asimdsame2_g"_h, &Disassembler::Disassemble_Vd4S_Vn16B_Vm16B},
|
|
{"ummla_asimdsame2_g"_h, &Disassembler::Disassemble_Vd4S_Vn16B_Vm16B},
|
|
{"usmmla_asimdsame2_g"_h, &Disassembler::Disassemble_Vd4S_Vn16B_Vm16B},
|
|
{"ld1row_z_p_bi_u32"_h,
|
|
&Disassembler::VisitSVELoadAndBroadcastQOWord_ScalarPlusImm},
|
|
{"ld1row_z_p_br_contiguous"_h,
|
|
&Disassembler::VisitSVELoadAndBroadcastQOWord_ScalarPlusScalar},
|
|
{"ld1rod_z_p_bi_u64"_h,
|
|
&Disassembler::VisitSVELoadAndBroadcastQOWord_ScalarPlusImm},
|
|
{"ld1rod_z_p_br_contiguous"_h,
|
|
&Disassembler::VisitSVELoadAndBroadcastQOWord_ScalarPlusScalar},
|
|
{"ld1rob_z_p_bi_u8"_h,
|
|
&Disassembler::VisitSVELoadAndBroadcastQOWord_ScalarPlusImm},
|
|
{"ld1rob_z_p_br_contiguous"_h,
|
|
&Disassembler::VisitSVELoadAndBroadcastQOWord_ScalarPlusScalar},
|
|
{"ld1roh_z_p_bi_u16"_h,
|
|
&Disassembler::VisitSVELoadAndBroadcastQOWord_ScalarPlusImm},
|
|
{"ld1roh_z_p_br_contiguous"_h,
|
|
&Disassembler::VisitSVELoadAndBroadcastQOWord_ScalarPlusScalar},
|
|
{"usdot_z_zzzi_s"_h, &Disassembler::VisitSVEMulIndex},
|
|
{"sudot_z_zzzi_s"_h, &Disassembler::VisitSVEMulIndex},
|
|
{"usdot_asimdsame2_d"_h, &Disassembler::VisitNEON3SameExtra},
|
|
{"addg_64_addsub_immtags"_h,
|
|
&Disassembler::Disassemble_XdSP_XnSP_uimm6_uimm4},
|
|
{"gmi_64g_dp_2src"_h, &Disassembler::Disassemble_Xd_XnSP_Xm},
|
|
{"irg_64i_dp_2src"_h, &Disassembler::Disassemble_XdSP_XnSP_Xm},
|
|
{"ldg_64loffset_ldsttags"_h, &Disassembler::DisassembleMTELoadTag},
|
|
{"st2g_64soffset_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"st2g_64spost_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"st2g_64spre_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"stgp_64_ldstpair_off"_h, &Disassembler::DisassembleMTEStoreTagPair},
|
|
{"stgp_64_ldstpair_post"_h, &Disassembler::DisassembleMTEStoreTagPair},
|
|
{"stgp_64_ldstpair_pre"_h, &Disassembler::DisassembleMTEStoreTagPair},
|
|
{"stg_64soffset_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"stg_64spost_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"stg_64spre_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"stz2g_64soffset_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"stz2g_64spost_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"stz2g_64spre_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"stzg_64soffset_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"stzg_64spost_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"stzg_64spre_ldsttags"_h, &Disassembler::DisassembleMTEStoreTag},
|
|
{"subg_64_addsub_immtags"_h,
|
|
&Disassembler::Disassemble_XdSP_XnSP_uimm6_uimm4},
|
|
{"subps_64s_dp_2src"_h, &Disassembler::Disassemble_Xd_XnSP_XmSP},
|
|
{"subp_64s_dp_2src"_h, &Disassembler::Disassemble_Xd_XnSP_XmSP},
|
|
{"cpyen_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyern_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyewn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpye_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfen_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfern_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfewn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfe_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfmn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfmrn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfmwn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfm_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfpn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfprn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfpwn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyfp_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpymn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpymrn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpymwn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpym_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpypn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyprn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpypwn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"cpyp_cpy_memcms"_h, &Disassembler::DisassembleCpy},
|
|
{"seten_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"sete_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"setgen_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"setge_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"setgmn_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"setgm_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"setgpn_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"setgp_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"setmn_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"setm_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"setpn_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"setp_set_memcms"_h, &Disassembler::DisassembleSet},
|
|
{"abs_32_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
|
|
{"abs_64_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
|
|
{"cnt_32_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
|
|
{"cnt_64_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
|
|
{"ctz_32_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
|
|
{"ctz_64_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
|
|
{"smax_32_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
|
|
{"smax_64_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
|
|
{"smin_32_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
|
|
{"smin_64_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
|
|
{"umax_32_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
|
|
{"umax_64_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
|
|
{"umin_32_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
|
|
{"umin_64_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
|
|
{"smax_32_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
|
|
{"smax_64_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
|
|
{"smin_32_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
|
|
{"smin_64_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
|
|
{"umax_32u_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
|
|
{"umax_64u_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
|
|
{"umin_32u_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
|
|
{"umin_64u_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
|
|
};
|
|
return &form_to_visitor;
|
|
} // NOLINT(readability/fn_size)
|
|
|
|
Disassembler::Disassembler() {
|
|
buffer_size_ = 256;
|
|
buffer_ = reinterpret_cast<char *>(malloc(buffer_size_));
|
|
buffer_pos_ = 0;
|
|
own_buffer_ = true;
|
|
code_address_offset_ = 0;
|
|
}
|
|
|
|
Disassembler::Disassembler(char *text_buffer, int buffer_size) {
|
|
buffer_size_ = buffer_size;
|
|
buffer_ = text_buffer;
|
|
buffer_pos_ = 0;
|
|
own_buffer_ = false;
|
|
code_address_offset_ = 0;
|
|
}
|
|
|
|
Disassembler::~Disassembler() {
|
|
if (own_buffer_) {
|
|
free(buffer_);
|
|
}
|
|
}
|
|
|
|
char *Disassembler::GetOutput() { return buffer_; }
|
|
|
|
void Disassembler::VisitAddSubImmediate(const Instruction *instr) {
|
|
bool rd_is_zr = RdIsZROrSP(instr);
|
|
bool stack_op =
|
|
(rd_is_zr || RnIsZROrSP(instr)) && (instr->GetImmAddSub() == 0) ? true
|
|
: false;
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Rds, 'Rns, 'IAddSub";
|
|
const char *form_cmp = "'Rns, 'IAddSub";
|
|
const char *form_mov = "'Rds, 'Rns";
|
|
|
|
switch (form_hash_) {
|
|
case "add_32_addsub_imm"_h:
|
|
case "add_64_addsub_imm"_h:
|
|
if (stack_op) {
|
|
mnemonic = "mov";
|
|
form = form_mov;
|
|
}
|
|
break;
|
|
case "adds_32s_addsub_imm"_h:
|
|
case "adds_64s_addsub_imm"_h:
|
|
if (rd_is_zr) {
|
|
mnemonic = "cmn";
|
|
form = form_cmp;
|
|
}
|
|
break;
|
|
case "subs_32s_addsub_imm"_h:
|
|
case "subs_64s_addsub_imm"_h:
|
|
if (rd_is_zr) {
|
|
mnemonic = "cmp";
|
|
form = form_cmp;
|
|
}
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitAddSubShifted(const Instruction *instr) {
|
|
bool rd_is_zr = RdIsZROrSP(instr);
|
|
bool rn_is_zr = RnIsZROrSP(instr);
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Rd, 'Rn, 'Rm'NDP";
|
|
const char *form_cmp = "'Rn, 'Rm'NDP";
|
|
const char *form_neg = "'Rd, 'Rm'NDP";
|
|
|
|
if (instr->GetShiftDP() == ROR) {
|
|
// Add/sub/adds/subs don't allow ROR as a shift mode.
|
|
VisitUnallocated(instr);
|
|
return;
|
|
}
|
|
|
|
switch (form_hash_) {
|
|
case "adds_32_addsub_shift"_h:
|
|
case "adds_64_addsub_shift"_h:
|
|
if (rd_is_zr) {
|
|
mnemonic = "cmn";
|
|
form = form_cmp;
|
|
}
|
|
break;
|
|
case "sub_32_addsub_shift"_h:
|
|
case "sub_64_addsub_shift"_h:
|
|
if (rn_is_zr) {
|
|
mnemonic = "neg";
|
|
form = form_neg;
|
|
}
|
|
break;
|
|
case "subs_32_addsub_shift"_h:
|
|
case "subs_64_addsub_shift"_h:
|
|
if (rd_is_zr) {
|
|
mnemonic = "cmp";
|
|
form = form_cmp;
|
|
} else if (rn_is_zr) {
|
|
mnemonic = "negs";
|
|
form = form_neg;
|
|
}
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitAddSubExtended(const Instruction *instr) {
|
|
bool rd_is_zr = RdIsZROrSP(instr);
|
|
const char *mnemonic = "";
|
|
Extend mode = static_cast<Extend>(instr->GetExtendMode());
|
|
const char *form = ((mode == UXTX) || (mode == SXTX)) ? "'Rds, 'Rns, 'Xm'Ext"
|
|
: "'Rds, 'Rns, 'Wm'Ext";
|
|
const char *form_cmp =
|
|
((mode == UXTX) || (mode == SXTX)) ? "'Rns, 'Xm'Ext" : "'Rns, 'Wm'Ext";
|
|
|
|
switch (instr->Mask(AddSubExtendedMask)) {
|
|
case ADD_w_ext:
|
|
case ADD_x_ext:
|
|
mnemonic = "add";
|
|
break;
|
|
case ADDS_w_ext:
|
|
case ADDS_x_ext: {
|
|
mnemonic = "adds";
|
|
if (rd_is_zr) {
|
|
mnemonic = "cmn";
|
|
form = form_cmp;
|
|
}
|
|
break;
|
|
}
|
|
case SUB_w_ext:
|
|
case SUB_x_ext:
|
|
mnemonic = "sub";
|
|
break;
|
|
case SUBS_w_ext:
|
|
case SUBS_x_ext: {
|
|
mnemonic = "subs";
|
|
if (rd_is_zr) {
|
|
mnemonic = "cmp";
|
|
form = form_cmp;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitAddSubWithCarry(const Instruction *instr) {
|
|
bool rn_is_zr = RnIsZROrSP(instr);
|
|
const char *mnemonic = "";
|
|
const char *form = "'Rd, 'Rn, 'Rm";
|
|
const char *form_neg = "'Rd, 'Rm";
|
|
|
|
switch (instr->Mask(AddSubWithCarryMask)) {
|
|
case ADC_w:
|
|
case ADC_x:
|
|
mnemonic = "adc";
|
|
break;
|
|
case ADCS_w:
|
|
case ADCS_x:
|
|
mnemonic = "adcs";
|
|
break;
|
|
case SBC_w:
|
|
case SBC_x: {
|
|
mnemonic = "sbc";
|
|
if (rn_is_zr) {
|
|
mnemonic = "ngc";
|
|
form = form_neg;
|
|
}
|
|
break;
|
|
}
|
|
case SBCS_w:
|
|
case SBCS_x: {
|
|
mnemonic = "sbcs";
|
|
if (rn_is_zr) {
|
|
mnemonic = "ngcs";
|
|
form = form_neg;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitRotateRightIntoFlags(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Xn, 'IRr, 'INzcv");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitEvaluateIntoFlags(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Wn");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitLogicalImmediate(const Instruction *instr) {
|
|
bool rd_is_zr = RdIsZROrSP(instr);
|
|
bool rn_is_zr = RnIsZROrSP(instr);
|
|
const char *mnemonic = "";
|
|
const char *form = "'Rds, 'Rn, 'ITri";
|
|
|
|
if (instr->GetImmLogical() == 0) {
|
|
// The immediate encoded in the instruction is not in the expected format.
|
|
Format(instr, "unallocated", "(LogicalImmediate)");
|
|
return;
|
|
}
|
|
|
|
switch (instr->Mask(LogicalImmediateMask)) {
|
|
case AND_w_imm:
|
|
case AND_x_imm:
|
|
mnemonic = "and";
|
|
break;
|
|
case ORR_w_imm:
|
|
case ORR_x_imm: {
|
|
mnemonic = "orr";
|
|
unsigned reg_size =
|
|
(instr->GetSixtyFourBits() == 1) ? kXRegSize : kWRegSize;
|
|
if (rn_is_zr && !IsMovzMovnImm(reg_size, instr->GetImmLogical())) {
|
|
mnemonic = "mov";
|
|
form = "'Rds, 'ITri";
|
|
}
|
|
break;
|
|
}
|
|
case EOR_w_imm:
|
|
case EOR_x_imm:
|
|
mnemonic = "eor";
|
|
break;
|
|
case ANDS_w_imm:
|
|
case ANDS_x_imm: {
|
|
mnemonic = "ands";
|
|
if (rd_is_zr) {
|
|
mnemonic = "tst";
|
|
form = "'Rn, 'ITri";
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
bool Disassembler::IsMovzMovnImm(unsigned reg_size, uint64_t value) {
|
|
VIXL_ASSERT((reg_size == kXRegSize) ||
|
|
((reg_size == kWRegSize) && (value <= 0xffffffff)));
|
|
|
|
// Test for movz: 16 bits set at positions 0, 16, 32 or 48.
|
|
if (((value & UINT64_C(0xffffffffffff0000)) == 0) ||
|
|
((value & UINT64_C(0xffffffff0000ffff)) == 0) ||
|
|
((value & UINT64_C(0xffff0000ffffffff)) == 0) ||
|
|
((value & UINT64_C(0x0000ffffffffffff)) == 0)) {
|
|
return true;
|
|
}
|
|
|
|
// Test for movn: NOT(16 bits set at positions 0, 16, 32 or 48).
|
|
if ((reg_size == kXRegSize) &&
|
|
(((~value & UINT64_C(0xffffffffffff0000)) == 0) ||
|
|
((~value & UINT64_C(0xffffffff0000ffff)) == 0) ||
|
|
((~value & UINT64_C(0xffff0000ffffffff)) == 0) ||
|
|
((~value & UINT64_C(0x0000ffffffffffff)) == 0))) {
|
|
return true;
|
|
}
|
|
if ((reg_size == kWRegSize) && (((value & 0xffff0000) == 0xffff0000) ||
|
|
((value & 0x0000ffff) == 0x0000ffff))) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void Disassembler::VisitLogicalShifted(const Instruction *instr) {
|
|
bool rd_is_zr = RdIsZROrSP(instr);
|
|
bool rn_is_zr = RnIsZROrSP(instr);
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Rd, 'Rn, 'Rm'NLo";
|
|
|
|
switch (form_hash_) {
|
|
case "ands_32_log_shift"_h:
|
|
case "ands_64_log_shift"_h:
|
|
if (rd_is_zr) {
|
|
mnemonic = "tst";
|
|
form = "'Rn, 'Rm'NLo";
|
|
}
|
|
break;
|
|
case "orr_32_log_shift"_h:
|
|
case "orr_64_log_shift"_h:
|
|
if (rn_is_zr && (instr->GetImmDPShift() == 0) &&
|
|
(instr->GetShiftDP() == LSL)) {
|
|
mnemonic = "mov";
|
|
form = "'Rd, 'Rm";
|
|
}
|
|
break;
|
|
case "orn_32_log_shift"_h:
|
|
case "orn_64_log_shift"_h:
|
|
if (rn_is_zr) {
|
|
mnemonic = "mvn";
|
|
form = "'Rd, 'Rm'NLo";
|
|
}
|
|
break;
|
|
}
|
|
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitConditionalCompareRegister(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Rn, 'Rm, 'INzcv, 'Cond");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitConditionalCompareImmediate(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Rn, 'IP, 'INzcv, 'Cond");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitConditionalSelect(const Instruction *instr) {
|
|
bool rnm_is_zr = (RnIsZROrSP(instr) && RmIsZROrSP(instr));
|
|
bool rn_is_rm = (instr->GetRn() == instr->GetRm());
|
|
const char *mnemonic = "";
|
|
const char *form = "'Rd, 'Rn, 'Rm, 'Cond";
|
|
const char *form_test = "'Rd, 'CInv";
|
|
const char *form_update = "'Rd, 'Rn, 'CInv";
|
|
|
|
Condition cond = static_cast<Condition>(instr->GetCondition());
|
|
bool invertible_cond = (cond != al) && (cond != nv);
|
|
|
|
switch (instr->Mask(ConditionalSelectMask)) {
|
|
case CSEL_w:
|
|
case CSEL_x:
|
|
mnemonic = "csel";
|
|
break;
|
|
case CSINC_w:
|
|
case CSINC_x: {
|
|
mnemonic = "csinc";
|
|
if (rnm_is_zr && invertible_cond) {
|
|
mnemonic = "cset";
|
|
form = form_test;
|
|
} else if (rn_is_rm && invertible_cond) {
|
|
mnemonic = "cinc";
|
|
form = form_update;
|
|
}
|
|
break;
|
|
}
|
|
case CSINV_w:
|
|
case CSINV_x: {
|
|
mnemonic = "csinv";
|
|
if (rnm_is_zr && invertible_cond) {
|
|
mnemonic = "csetm";
|
|
form = form_test;
|
|
} else if (rn_is_rm && invertible_cond) {
|
|
mnemonic = "cinv";
|
|
form = form_update;
|
|
}
|
|
break;
|
|
}
|
|
case CSNEG_w:
|
|
case CSNEG_x: {
|
|
mnemonic = "csneg";
|
|
if (rn_is_rm && invertible_cond) {
|
|
mnemonic = "cneg";
|
|
form = form_update;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitBitfield(const Instruction *instr) {
|
|
unsigned s = instr->GetImmS();
|
|
unsigned r = instr->GetImmR();
|
|
unsigned rd_size_minus_1 =
|
|
((instr->GetSixtyFourBits() == 1) ? kXRegSize : kWRegSize) - 1;
|
|
const char *mnemonic = "";
|
|
const char *form = "";
|
|
const char *form_shift_right = "'Rd, 'Rn, 'IBr";
|
|
const char *form_extend = "'Rd, 'Wn";
|
|
const char *form_bfiz = "'Rd, 'Rn, 'IBZ-r, 'IBs+1";
|
|
const char *form_bfc = "'Rd, 'IBZ-r, 'IBs+1";
|
|
const char *form_bfx = "'Rd, 'Rn, 'IBr, 'IBs-r+1";
|
|
const char *form_lsl = "'Rd, 'Rn, 'IBZ-r";
|
|
|
|
if (instr->GetSixtyFourBits() != instr->GetBitN()) {
|
|
VisitUnallocated(instr);
|
|
return;
|
|
}
|
|
|
|
if ((instr->GetSixtyFourBits() == 0) && ((s > 31) || (r > 31))) {
|
|
VisitUnallocated(instr);
|
|
return;
|
|
}
|
|
|
|
switch (instr->Mask(BitfieldMask)) {
|
|
case SBFM_w:
|
|
case SBFM_x: {
|
|
mnemonic = "sbfx";
|
|
form = form_bfx;
|
|
if (r == 0) {
|
|
form = form_extend;
|
|
if (s == 7) {
|
|
mnemonic = "sxtb";
|
|
} else if (s == 15) {
|
|
mnemonic = "sxth";
|
|
} else if ((s == 31) && (instr->GetSixtyFourBits() == 1)) {
|
|
mnemonic = "sxtw";
|
|
} else {
|
|
form = form_bfx;
|
|
}
|
|
} else if (s == rd_size_minus_1) {
|
|
mnemonic = "asr";
|
|
form = form_shift_right;
|
|
} else if (s < r) {
|
|
mnemonic = "sbfiz";
|
|
form = form_bfiz;
|
|
}
|
|
break;
|
|
}
|
|
case UBFM_w:
|
|
case UBFM_x: {
|
|
mnemonic = "ubfx";
|
|
form = form_bfx;
|
|
if (r == 0) {
|
|
form = form_extend;
|
|
if (s == 7) {
|
|
mnemonic = "uxtb";
|
|
} else if (s == 15) {
|
|
mnemonic = "uxth";
|
|
} else {
|
|
form = form_bfx;
|
|
}
|
|
}
|
|
if (s == rd_size_minus_1) {
|
|
mnemonic = "lsr";
|
|
form = form_shift_right;
|
|
} else if (r == s + 1) {
|
|
mnemonic = "lsl";
|
|
form = form_lsl;
|
|
} else if (s < r) {
|
|
mnemonic = "ubfiz";
|
|
form = form_bfiz;
|
|
}
|
|
break;
|
|
}
|
|
case BFM_w:
|
|
case BFM_x: {
|
|
mnemonic = "bfxil";
|
|
form = form_bfx;
|
|
if (s < r) {
|
|
if (instr->GetRn() == kZeroRegCode) {
|
|
mnemonic = "bfc";
|
|
form = form_bfc;
|
|
} else {
|
|
mnemonic = "bfi";
|
|
form = form_bfiz;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitExtract(const Instruction *instr) {
|
|
const char *mnemonic = "";
|
|
const char *form = "'Rd, 'Rn, 'Rm, 'IExtract";
|
|
|
|
switch (instr->Mask(ExtractMask)) {
|
|
case EXTR_w:
|
|
case EXTR_x: {
|
|
if (instr->GetRn() == instr->GetRm()) {
|
|
mnemonic = "ror";
|
|
form = "'Rd, 'Rn, 'IExtract";
|
|
} else {
|
|
mnemonic = "extr";
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitPCRelAddressing(const Instruction *instr) {
|
|
switch (instr->Mask(PCRelAddressingMask)) {
|
|
case ADR:
|
|
Format(instr, "adr", "'Xd, 'AddrPCRelByte");
|
|
break;
|
|
case ADRP:
|
|
Format(instr, "adrp", "'Xd, 'AddrPCRelPage");
|
|
break;
|
|
default:
|
|
Format(instr, "unimplemented", "(PCRelAddressing)");
|
|
}
|
|
}
|
|
|
|
|
|
void Disassembler::VisitConditionalBranch(const Instruction *instr) {
|
|
// We can't use the mnemonic directly here, as there's no space between it and
|
|
// the condition. Assert that we have the correct mnemonic, then use "b"
|
|
// explicitly for formatting the output.
|
|
VIXL_ASSERT(form_hash_ == "b_only_condbranch"_h);
|
|
Format(instr, "b.'CBrn", "'TImmCond");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitUnconditionalBranchToRegister(
|
|
const Instruction *instr) {
|
|
const char *form = "'Xn";
|
|
|
|
switch (form_hash_) {
|
|
case "ret_64r_branch_reg"_h:
|
|
if (instr->GetRn() == kLinkRegCode) {
|
|
form = "";
|
|
}
|
|
break;
|
|
case "retaa_64e_branch_reg"_h:
|
|
case "retab_64e_branch_reg"_h:
|
|
form = "";
|
|
break;
|
|
case "braa_64p_branch_reg"_h:
|
|
case "brab_64p_branch_reg"_h:
|
|
case "blraa_64p_branch_reg"_h:
|
|
case "blrab_64p_branch_reg"_h:
|
|
form = "'Xn, 'Xds";
|
|
break;
|
|
}
|
|
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitUnconditionalBranch(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'TImmUncn");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitDataProcessing1Source(const Instruction *instr) {
|
|
const char *form = "'Rd, 'Rn";
|
|
|
|
switch (form_hash_) {
|
|
case "pacia_64p_dp_1src"_h:
|
|
case "pacda_64p_dp_1src"_h:
|
|
case "autia_64p_dp_1src"_h:
|
|
case "autda_64p_dp_1src"_h:
|
|
case "pacib_64p_dp_1src"_h:
|
|
case "pacdb_64p_dp_1src"_h:
|
|
case "autib_64p_dp_1src"_h:
|
|
case "autdb_64p_dp_1src"_h:
|
|
form = "'Xd, 'Xns";
|
|
break;
|
|
case "paciza_64z_dp_1src"_h:
|
|
case "pacdza_64z_dp_1src"_h:
|
|
case "autiza_64z_dp_1src"_h:
|
|
case "autdza_64z_dp_1src"_h:
|
|
case "pacizb_64z_dp_1src"_h:
|
|
case "pacdzb_64z_dp_1src"_h:
|
|
case "autizb_64z_dp_1src"_h:
|
|
case "autdzb_64z_dp_1src"_h:
|
|
case "xpacd_64z_dp_1src"_h:
|
|
case "xpaci_64z_dp_1src"_h:
|
|
form = "'Xd";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitDataProcessing2Source(const Instruction *instr) {
|
|
std::string mnemonic = mnemonic_;
|
|
const char *form = "'Rd, 'Rn, 'Rm";
|
|
|
|
switch (form_hash_) {
|
|
case "asrv_32_dp_2src"_h:
|
|
case "asrv_64_dp_2src"_h:
|
|
case "lslv_32_dp_2src"_h:
|
|
case "lslv_64_dp_2src"_h:
|
|
case "lsrv_32_dp_2src"_h:
|
|
case "lsrv_64_dp_2src"_h:
|
|
case "rorv_32_dp_2src"_h:
|
|
case "rorv_64_dp_2src"_h:
|
|
// Drop the last 'v' character.
|
|
VIXL_ASSERT(mnemonic[3] == 'v');
|
|
mnemonic.pop_back();
|
|
break;
|
|
case "pacga_64p_dp_2src"_h:
|
|
form = "'Xd, 'Xn, 'Xms";
|
|
break;
|
|
case "crc32x_64c_dp_2src"_h:
|
|
case "crc32cx_64c_dp_2src"_h:
|
|
form = "'Wd, 'Wn, 'Xm";
|
|
break;
|
|
}
|
|
Format(instr, mnemonic.c_str(), form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitDataProcessing3Source(const Instruction *instr) {
|
|
bool ra_is_zr = RaIsZROrSP(instr);
|
|
const char *mnemonic = "";
|
|
const char *form = "'Xd, 'Wn, 'Wm, 'Xa";
|
|
const char *form_rrr = "'Rd, 'Rn, 'Rm";
|
|
const char *form_rrrr = "'Rd, 'Rn, 'Rm, 'Ra";
|
|
const char *form_xww = "'Xd, 'Wn, 'Wm";
|
|
const char *form_xxx = "'Xd, 'Xn, 'Xm";
|
|
|
|
switch (instr->Mask(DataProcessing3SourceMask)) {
|
|
case MADD_w:
|
|
case MADD_x: {
|
|
mnemonic = "madd";
|
|
form = form_rrrr;
|
|
if (ra_is_zr) {
|
|
mnemonic = "mul";
|
|
form = form_rrr;
|
|
}
|
|
break;
|
|
}
|
|
case MSUB_w:
|
|
case MSUB_x: {
|
|
mnemonic = "msub";
|
|
form = form_rrrr;
|
|
if (ra_is_zr) {
|
|
mnemonic = "mneg";
|
|
form = form_rrr;
|
|
}
|
|
break;
|
|
}
|
|
case SMADDL_x: {
|
|
mnemonic = "smaddl";
|
|
if (ra_is_zr) {
|
|
mnemonic = "smull";
|
|
form = form_xww;
|
|
}
|
|
break;
|
|
}
|
|
case SMSUBL_x: {
|
|
mnemonic = "smsubl";
|
|
if (ra_is_zr) {
|
|
mnemonic = "smnegl";
|
|
form = form_xww;
|
|
}
|
|
break;
|
|
}
|
|
case UMADDL_x: {
|
|
mnemonic = "umaddl";
|
|
if (ra_is_zr) {
|
|
mnemonic = "umull";
|
|
form = form_xww;
|
|
}
|
|
break;
|
|
}
|
|
case UMSUBL_x: {
|
|
mnemonic = "umsubl";
|
|
if (ra_is_zr) {
|
|
mnemonic = "umnegl";
|
|
form = form_xww;
|
|
}
|
|
break;
|
|
}
|
|
case SMULH_x: {
|
|
mnemonic = "smulh";
|
|
form = form_xxx;
|
|
break;
|
|
}
|
|
case UMULH_x: {
|
|
mnemonic = "umulh";
|
|
form = form_xxx;
|
|
break;
|
|
}
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::DisassembleMinMaxImm(const Instruction *instr) {
|
|
const char *suffix = (instr->ExtractBit(18) == 0) ? "'s1710" : "'u1710";
|
|
FormatWithDecodedMnemonic(instr, "'Rd, 'Rn, #", suffix);
|
|
}
|
|
|
|
void Disassembler::VisitCompareBranch(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Rt, 'TImmCmpa");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitTestBranch(const Instruction *instr) {
|
|
// If the top bit of the immediate is clear, the tested register is
|
|
// disassembled as Wt, otherwise Xt. As the top bit of the immediate is
|
|
// encoded in bit 31 of the instruction, we can reuse the Rt form, which
|
|
// uses bit 31 (normally "sf") to choose the register size.
|
|
FormatWithDecodedMnemonic(instr, "'Rt, 'It, 'TImmTest");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitMoveWideImmediate(const Instruction *instr) {
|
|
const char *mnemonic = "";
|
|
const char *form = "'Rd, 'IMoveImm";
|
|
|
|
// Print the shift separately for movk, to make it clear which half word will
|
|
// be overwritten. Movn and movz print the computed immediate, which includes
|
|
// shift calculation.
|
|
switch (instr->Mask(MoveWideImmediateMask)) {
|
|
case MOVN_w:
|
|
case MOVN_x:
|
|
if ((instr->GetImmMoveWide()) || (instr->GetShiftMoveWide() == 0)) {
|
|
if ((instr->GetSixtyFourBits() == 0) &&
|
|
(instr->GetImmMoveWide() == 0xffff)) {
|
|
mnemonic = "movn";
|
|
} else {
|
|
mnemonic = "mov";
|
|
form = "'Rd, 'IMoveNeg";
|
|
}
|
|
} else {
|
|
mnemonic = "movn";
|
|
}
|
|
break;
|
|
case MOVZ_w:
|
|
case MOVZ_x:
|
|
if ((instr->GetImmMoveWide()) || (instr->GetShiftMoveWide() == 0))
|
|
mnemonic = "mov";
|
|
else
|
|
mnemonic = "movz";
|
|
break;
|
|
case MOVK_w:
|
|
case MOVK_x:
|
|
mnemonic = "movk";
|
|
form = "'Rd, 'IMoveLSL";
|
|
break;
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
#define LOAD_STORE_LIST(V) \
|
|
V(STRB_w, "'Wt") \
|
|
V(STRH_w, "'Wt") \
|
|
V(STR_w, "'Wt") \
|
|
V(STR_x, "'Xt") \
|
|
V(LDRB_w, "'Wt") \
|
|
V(LDRH_w, "'Wt") \
|
|
V(LDR_w, "'Wt") \
|
|
V(LDR_x, "'Xt") \
|
|
V(LDRSB_x, "'Xt") \
|
|
V(LDRSH_x, "'Xt") \
|
|
V(LDRSW_x, "'Xt") \
|
|
V(LDRSB_w, "'Wt") \
|
|
V(LDRSH_w, "'Wt") \
|
|
V(STR_b, "'Bt") \
|
|
V(STR_h, "'Ht") \
|
|
V(STR_s, "'St") \
|
|
V(STR_d, "'Dt") \
|
|
V(LDR_b, "'Bt") \
|
|
V(LDR_h, "'Ht") \
|
|
V(LDR_s, "'St") \
|
|
V(LDR_d, "'Dt") \
|
|
V(STR_q, "'Qt") \
|
|
V(LDR_q, "'Qt")
|
|
|
|
void Disassembler::VisitLoadStorePreIndex(const Instruction *instr) {
|
|
const char *form = "(LoadStorePreIndex)";
|
|
const char *suffix = ", ['Xns'ILSi]!";
|
|
|
|
switch (instr->Mask(LoadStorePreIndexMask)) {
|
|
#define LS_PREINDEX(A, B) \
|
|
case A##_pre: \
|
|
form = B; \
|
|
break;
|
|
LOAD_STORE_LIST(LS_PREINDEX)
|
|
#undef LS_PREINDEX
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitLoadStorePostIndex(const Instruction *instr) {
|
|
const char *form = "(LoadStorePostIndex)";
|
|
const char *suffix = ", ['Xns]'ILSi";
|
|
|
|
switch (instr->Mask(LoadStorePostIndexMask)) {
|
|
#define LS_POSTINDEX(A, B) \
|
|
case A##_post: \
|
|
form = B; \
|
|
break;
|
|
LOAD_STORE_LIST(LS_POSTINDEX)
|
|
#undef LS_POSTINDEX
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitLoadStoreUnsignedOffset(const Instruction *instr) {
|
|
const char *form = "(LoadStoreUnsignedOffset)";
|
|
const char *suffix = ", ['Xns'ILU]";
|
|
|
|
switch (instr->Mask(LoadStoreUnsignedOffsetMask)) {
|
|
#define LS_UNSIGNEDOFFSET(A, B) \
|
|
case A##_unsigned: \
|
|
form = B; \
|
|
break;
|
|
LOAD_STORE_LIST(LS_UNSIGNEDOFFSET)
|
|
#undef LS_UNSIGNEDOFFSET
|
|
case PRFM_unsigned:
|
|
form = "'prefOp";
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitLoadStoreRCpcUnscaledOffset(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Wt, ['Xns'ILS]";
|
|
const char *form_x = "'Xt, ['Xns'ILS]";
|
|
|
|
switch (form_hash_) {
|
|
case "ldapursb_64_ldapstl_unscaled"_h:
|
|
case "ldapursh_64_ldapstl_unscaled"_h:
|
|
case "ldapursw_64_ldapstl_unscaled"_h:
|
|
case "ldapur_64_ldapstl_unscaled"_h:
|
|
case "stlur_64_ldapstl_unscaled"_h:
|
|
form = form_x;
|
|
break;
|
|
}
|
|
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitLoadStoreRegisterOffset(const Instruction *instr) {
|
|
const char *form = "(LoadStoreRegisterOffset)";
|
|
const char *suffix = ", ['Xns, 'Offsetreg]";
|
|
|
|
switch (instr->Mask(LoadStoreRegisterOffsetMask)) {
|
|
#define LS_REGISTEROFFSET(A, B) \
|
|
case A##_reg: \
|
|
form = B; \
|
|
break;
|
|
LOAD_STORE_LIST(LS_REGISTEROFFSET)
|
|
#undef LS_REGISTEROFFSET
|
|
case PRFM_reg:
|
|
form = "'prefOp";
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitLoadStoreUnscaledOffset(const Instruction *instr) {
|
|
const char *form = "'Wt";
|
|
const char *suffix = ", ['Xns'ILS]";
|
|
|
|
switch (form_hash_) {
|
|
case "ldur_64_ldst_unscaled"_h:
|
|
case "ldursb_64_ldst_unscaled"_h:
|
|
case "ldursh_64_ldst_unscaled"_h:
|
|
case "ldursw_64_ldst_unscaled"_h:
|
|
case "stur_64_ldst_unscaled"_h:
|
|
form = "'Xt";
|
|
break;
|
|
case "ldur_b_ldst_unscaled"_h:
|
|
case "stur_b_ldst_unscaled"_h:
|
|
form = "'Bt";
|
|
break;
|
|
case "ldur_h_ldst_unscaled"_h:
|
|
case "stur_h_ldst_unscaled"_h:
|
|
form = "'Ht";
|
|
break;
|
|
case "ldur_s_ldst_unscaled"_h:
|
|
case "stur_s_ldst_unscaled"_h:
|
|
form = "'St";
|
|
break;
|
|
case "ldur_d_ldst_unscaled"_h:
|
|
case "stur_d_ldst_unscaled"_h:
|
|
form = "'Dt";
|
|
break;
|
|
case "ldur_q_ldst_unscaled"_h:
|
|
case "stur_q_ldst_unscaled"_h:
|
|
form = "'Qt";
|
|
break;
|
|
case "prfum_p_ldst_unscaled"_h:
|
|
form = "'prefOp";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitLoadLiteral(const Instruction *instr) {
|
|
const char *form = "'Wt";
|
|
const char *suffix = ", 'ILLiteral 'LValue";
|
|
|
|
switch (form_hash_) {
|
|
case "ldr_64_loadlit"_h:
|
|
case "ldrsw_64_loadlit"_h:
|
|
form = "'Xt";
|
|
break;
|
|
case "ldr_s_loadlit"_h:
|
|
form = "'St";
|
|
break;
|
|
case "ldr_d_loadlit"_h:
|
|
form = "'Dt";
|
|
break;
|
|
case "ldr_q_loadlit"_h:
|
|
form = "'Qt";
|
|
break;
|
|
case "prfm_p_loadlit"_h:
|
|
form = "'prefOp";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
|
|
#define LOAD_STORE_PAIR_LIST(V) \
|
|
V(STP_w, "'Wt, 'Wt2", "2") \
|
|
V(LDP_w, "'Wt, 'Wt2", "2") \
|
|
V(LDPSW_x, "'Xt, 'Xt2", "2") \
|
|
V(STP_x, "'Xt, 'Xt2", "3") \
|
|
V(LDP_x, "'Xt, 'Xt2", "3") \
|
|
V(STP_s, "'St, 'St2", "2") \
|
|
V(LDP_s, "'St, 'St2", "2") \
|
|
V(STP_d, "'Dt, 'Dt2", "3") \
|
|
V(LDP_d, "'Dt, 'Dt2", "3") \
|
|
V(LDP_q, "'Qt, 'Qt2", "4") \
|
|
V(STP_q, "'Qt, 'Qt2", "4")
|
|
|
|
void Disassembler::VisitLoadStorePairPostIndex(const Instruction *instr) {
|
|
const char *form = "(LoadStorePairPostIndex)";
|
|
|
|
switch (instr->Mask(LoadStorePairPostIndexMask)) {
|
|
#define LSP_POSTINDEX(A, B, C) \
|
|
case A##_post: \
|
|
form = B ", ['Xns]'ILP" C "i"; \
|
|
break;
|
|
LOAD_STORE_PAIR_LIST(LSP_POSTINDEX)
|
|
#undef LSP_POSTINDEX
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitLoadStorePairPreIndex(const Instruction *instr) {
|
|
const char *form = "(LoadStorePairPreIndex)";
|
|
|
|
switch (instr->Mask(LoadStorePairPreIndexMask)) {
|
|
#define LSP_PREINDEX(A, B, C) \
|
|
case A##_pre: \
|
|
form = B ", ['Xns'ILP" C "i]!"; \
|
|
break;
|
|
LOAD_STORE_PAIR_LIST(LSP_PREINDEX)
|
|
#undef LSP_PREINDEX
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitLoadStorePairOffset(const Instruction *instr) {
|
|
const char *form = "(LoadStorePairOffset)";
|
|
|
|
switch (instr->Mask(LoadStorePairOffsetMask)) {
|
|
#define LSP_OFFSET(A, B, C) \
|
|
case A##_off: \
|
|
form = B ", ['Xns'ILP" C "]"; \
|
|
break;
|
|
LOAD_STORE_PAIR_LIST(LSP_OFFSET)
|
|
#undef LSP_OFFSET
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitLoadStorePairNonTemporal(const Instruction *instr) {
|
|
const char *form = "'Wt, 'Wt2, ['Xns'ILP2]";
|
|
|
|
switch (form_hash_) {
|
|
case "ldnp_64_ldstnapair_offs"_h:
|
|
case "stnp_64_ldstnapair_offs"_h:
|
|
form = "'Xt, 'Xt2, ['Xns'ILP3]";
|
|
break;
|
|
case "ldnp_s_ldstnapair_offs"_h:
|
|
case "stnp_s_ldstnapair_offs"_h:
|
|
form = "'St, 'St2, ['Xns'ILP2]";
|
|
break;
|
|
case "ldnp_d_ldstnapair_offs"_h:
|
|
case "stnp_d_ldstnapair_offs"_h:
|
|
form = "'Dt, 'Dt2, ['Xns'ILP3]";
|
|
break;
|
|
case "ldnp_q_ldstnapair_offs"_h:
|
|
case "stnp_q_ldstnapair_offs"_h:
|
|
form = "'Qt, 'Qt2, ['Xns'ILP4]";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
// clang-format off
|
|
#define LOAD_STORE_EXCLUSIVE_LIST(V) \
|
|
V(STXRB_w, "'Ws, 'Wt") \
|
|
V(STXRH_w, "'Ws, 'Wt") \
|
|
V(STXR_w, "'Ws, 'Wt") \
|
|
V(STXR_x, "'Ws, 'Xt") \
|
|
V(LDXR_x, "'Xt") \
|
|
V(STXP_w, "'Ws, 'Wt, 'Wt2") \
|
|
V(STXP_x, "'Ws, 'Xt, 'Xt2") \
|
|
V(LDXP_w, "'Wt, 'Wt2") \
|
|
V(LDXP_x, "'Xt, 'Xt2") \
|
|
V(STLXRB_w, "'Ws, 'Wt") \
|
|
V(STLXRH_w, "'Ws, 'Wt") \
|
|
V(STLXR_w, "'Ws, 'Wt") \
|
|
V(STLXR_x, "'Ws, 'Xt") \
|
|
V(LDAXR_x, "'Xt") \
|
|
V(STLXP_w, "'Ws, 'Wt, 'Wt2") \
|
|
V(STLXP_x, "'Ws, 'Xt, 'Xt2") \
|
|
V(LDAXP_w, "'Wt, 'Wt2") \
|
|
V(LDAXP_x, "'Xt, 'Xt2") \
|
|
V(STLR_x, "'Xt") \
|
|
V(LDAR_x, "'Xt") \
|
|
V(STLLR_x, "'Xt") \
|
|
V(LDLAR_x, "'Xt") \
|
|
V(CAS_w, "'Ws, 'Wt") \
|
|
V(CAS_x, "'Xs, 'Xt") \
|
|
V(CASA_w, "'Ws, 'Wt") \
|
|
V(CASA_x, "'Xs, 'Xt") \
|
|
V(CASL_w, "'Ws, 'Wt") \
|
|
V(CASL_x, "'Xs, 'Xt") \
|
|
V(CASAL_w, "'Ws, 'Wt") \
|
|
V(CASAL_x, "'Xs, 'Xt") \
|
|
V(CASB, "'Ws, 'Wt") \
|
|
V(CASAB, "'Ws, 'Wt") \
|
|
V(CASLB, "'Ws, 'Wt") \
|
|
V(CASALB, "'Ws, 'Wt") \
|
|
V(CASH, "'Ws, 'Wt") \
|
|
V(CASAH, "'Ws, 'Wt") \
|
|
V(CASLH, "'Ws, 'Wt") \
|
|
V(CASALH, "'Ws, 'Wt") \
|
|
V(CASP_w, "'Ws, 'Ws+, 'Wt, 'Wt+") \
|
|
V(CASP_x, "'Xs, 'Xs+, 'Xt, 'Xt+") \
|
|
V(CASPA_w, "'Ws, 'Ws+, 'Wt, 'Wt+") \
|
|
V(CASPA_x, "'Xs, 'Xs+, 'Xt, 'Xt+") \
|
|
V(CASPL_w, "'Ws, 'Ws+, 'Wt, 'Wt+") \
|
|
V(CASPL_x, "'Xs, 'Xs+, 'Xt, 'Xt+") \
|
|
V(CASPAL_w, "'Ws, 'Ws+, 'Wt, 'Wt+") \
|
|
V(CASPAL_x, "'Xs, 'Xs+, 'Xt, 'Xt+")
|
|
// clang-format on
|
|
|
|
|
|
void Disassembler::VisitLoadStoreExclusive(const Instruction *instr) {
|
|
const char *form = "'Wt";
|
|
const char *suffix = ", ['Xns]";
|
|
|
|
switch (instr->Mask(LoadStoreExclusiveMask)) {
|
|
#define LSX(A, B) \
|
|
case A: \
|
|
form = B; \
|
|
break;
|
|
LOAD_STORE_EXCLUSIVE_LIST(LSX)
|
|
#undef LSX
|
|
}
|
|
|
|
switch (instr->Mask(LoadStoreExclusiveMask)) {
|
|
case CASP_w:
|
|
case CASP_x:
|
|
case CASPA_w:
|
|
case CASPA_x:
|
|
case CASPL_w:
|
|
case CASPL_x:
|
|
case CASPAL_w:
|
|
case CASPAL_x:
|
|
if ((instr->GetRs() % 2 == 1) || (instr->GetRt() % 2 == 1)) {
|
|
VisitUnallocated(instr);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitLoadStorePAC(const Instruction *instr) {
|
|
const char *form = "'Xt, ['Xns'ILA]";
|
|
const char *suffix = "";
|
|
switch (form_hash_) {
|
|
case "ldraa_64w_ldst_pac"_h:
|
|
case "ldrab_64w_ldst_pac"_h:
|
|
suffix = "!";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitAtomicMemory(const Instruction *instr) {
|
|
bool is_x = (instr->ExtractBits(31, 30) == 3);
|
|
const char *form = is_x ? "'Xs, 'Xt" : "'Ws, 'Wt";
|
|
const char *suffix = ", ['Xns]";
|
|
|
|
std::string mnemonic = mnemonic_;
|
|
|
|
switch (form_hash_) {
|
|
case "ldaprb_32l_memop"_h:
|
|
case "ldaprh_32l_memop"_h:
|
|
case "ldapr_32l_memop"_h:
|
|
form = "'Wt";
|
|
break;
|
|
case "ldapr_64l_memop"_h:
|
|
form = "'Xt";
|
|
break;
|
|
default:
|
|
// Zero register implies a store instruction.
|
|
if (instr->GetRt() == kZeroRegCode) {
|
|
mnemonic.replace(0, 2, "st");
|
|
form = is_x ? "'Xs" : "'Ws";
|
|
}
|
|
}
|
|
Format(instr, mnemonic.c_str(), form, suffix);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitFPCompare(const Instruction *instr) {
|
|
const char *form = "'Fn, 'Fm";
|
|
switch (form_hash_) {
|
|
case "fcmpe_dz_floatcmp"_h:
|
|
case "fcmpe_hz_floatcmp"_h:
|
|
case "fcmpe_sz_floatcmp"_h:
|
|
case "fcmp_dz_floatcmp"_h:
|
|
case "fcmp_hz_floatcmp"_h:
|
|
case "fcmp_sz_floatcmp"_h:
|
|
form = "'Fn, #0.0";
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitFPConditionalCompare(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Fn, 'Fm, 'INzcv, 'Cond");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitFPConditionalSelect(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Fd, 'Fn, 'Fm, 'Cond");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitFPDataProcessing1Source(const Instruction *instr) {
|
|
const char *form = "'Fd, 'Fn";
|
|
switch (form_hash_) {
|
|
case "fcvt_ds_floatdp1"_h:
|
|
form = "'Dd, 'Sn";
|
|
break;
|
|
case "fcvt_sd_floatdp1"_h:
|
|
form = "'Sd, 'Dn";
|
|
break;
|
|
case "fcvt_hs_floatdp1"_h:
|
|
form = "'Hd, 'Sn";
|
|
break;
|
|
case "fcvt_sh_floatdp1"_h:
|
|
form = "'Sd, 'Hn";
|
|
break;
|
|
case "fcvt_dh_floatdp1"_h:
|
|
form = "'Dd, 'Hn";
|
|
break;
|
|
case "fcvt_hd_floatdp1"_h:
|
|
form = "'Hd, 'Dn";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitFPDataProcessing2Source(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Fd, 'Fn, 'Fm");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitFPDataProcessing3Source(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Fd, 'Fn, 'Fm, 'Fa");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitFPImmediate(const Instruction *instr) {
|
|
const char *form = "'Hd";
|
|
const char *suffix = ", 'IFP";
|
|
switch (form_hash_) {
|
|
case "fmov_s_floatimm"_h:
|
|
form = "'Sd";
|
|
break;
|
|
case "fmov_d_floatimm"_h:
|
|
form = "'Dd";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitFPIntegerConvert(const Instruction *instr) {
|
|
const char *form = "'Rd, 'Fn";
|
|
switch (form_hash_) {
|
|
case "fmov_h32_float2int"_h:
|
|
case "fmov_h64_float2int"_h:
|
|
case "fmov_s32_float2int"_h:
|
|
case "fmov_d64_float2int"_h:
|
|
case "scvtf_d32_float2int"_h:
|
|
case "scvtf_d64_float2int"_h:
|
|
case "scvtf_h32_float2int"_h:
|
|
case "scvtf_h64_float2int"_h:
|
|
case "scvtf_s32_float2int"_h:
|
|
case "scvtf_s64_float2int"_h:
|
|
case "ucvtf_d32_float2int"_h:
|
|
case "ucvtf_d64_float2int"_h:
|
|
case "ucvtf_h32_float2int"_h:
|
|
case "ucvtf_h64_float2int"_h:
|
|
case "ucvtf_s32_float2int"_h:
|
|
case "ucvtf_s64_float2int"_h:
|
|
form = "'Fd, 'Rn";
|
|
break;
|
|
case "fmov_v64i_float2int"_h:
|
|
form = "'Vd.D[1], 'Rn";
|
|
break;
|
|
case "fmov_64vx_float2int"_h:
|
|
form = "'Rd, 'Vn.D[1]";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitFPFixedPointConvert(const Instruction *instr) {
|
|
const char *form = "'Rd, 'Fn";
|
|
const char *suffix = ", 'IFPFBits";
|
|
|
|
switch (form_hash_) {
|
|
case "scvtf_d32_float2fix"_h:
|
|
case "scvtf_d64_float2fix"_h:
|
|
case "scvtf_h32_float2fix"_h:
|
|
case "scvtf_h64_float2fix"_h:
|
|
case "scvtf_s32_float2fix"_h:
|
|
case "scvtf_s64_float2fix"_h:
|
|
case "ucvtf_d32_float2fix"_h:
|
|
case "ucvtf_d64_float2fix"_h:
|
|
case "ucvtf_h32_float2fix"_h:
|
|
case "ucvtf_h64_float2fix"_h:
|
|
case "ucvtf_s32_float2fix"_h:
|
|
case "ucvtf_s64_float2fix"_h:
|
|
form = "'Fd, 'Rn";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::DisassembleNoArgs(const Instruction *instr) {
|
|
Format(instr, mnemonic_.c_str(), "");
|
|
}
|
|
|
|
void Disassembler::VisitSystem(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "(System)";
|
|
const char *suffix = NULL;
|
|
|
|
switch (form_hash_) {
|
|
case "clrex_bn_barriers"_h:
|
|
form = (instr->GetCRm() == 0xf) ? "" : "'IX";
|
|
break;
|
|
case "mrs_rs_systemmove"_h:
|
|
form = "'Xt, 'IY";
|
|
break;
|
|
case "msr_sr_systemmove"_h:
|
|
form = "'IY, 'Xt";
|
|
break;
|
|
case "bti_hb_hints"_h:
|
|
switch (instr->ExtractBits(7, 6)) {
|
|
case 0:
|
|
form = "";
|
|
break;
|
|
case 1:
|
|
form = "c";
|
|
break;
|
|
case 2:
|
|
form = "j";
|
|
break;
|
|
case 3:
|
|
form = "jc";
|
|
break;
|
|
}
|
|
break;
|
|
case "hint_hm_hints"_h:
|
|
form = "'IH";
|
|
break;
|
|
case Hash("dmb_bo_barriers"):
|
|
form = "'M";
|
|
break;
|
|
case Hash("dsb_bo_barriers"): {
|
|
int crm = instr->GetCRm();
|
|
if (crm == 0) {
|
|
mnemonic = "ssbb";
|
|
form = "";
|
|
} else if (crm == 4) {
|
|
mnemonic = "pssbb";
|
|
form = "";
|
|
} else {
|
|
form = "'M";
|
|
}
|
|
break;
|
|
}
|
|
case Hash("sys_cr_systeminstrs"): {
|
|
mnemonic = "dc";
|
|
suffix = ", 'Xt";
|
|
|
|
const std::map<uint32_t, const char *> dcop = {
|
|
{IVAU, "ivau"},
|
|
{CVAC, "cvac"},
|
|
{CVAU, "cvau"},
|
|
{CVAP, "cvap"},
|
|
{CVADP, "cvadp"},
|
|
{CIVAC, "civac"},
|
|
{ZVA, "zva"},
|
|
{GVA, "gva"},
|
|
{GZVA, "gzva"},
|
|
{CGVAC, "cgvac"},
|
|
{CGDVAC, "cgdvac"},
|
|
{CGVAP, "cgvap"},
|
|
{CGDVAP, "cgdvap"},
|
|
{CIGVAC, "cigvac"},
|
|
{CIGDVAC, "cigdvac"},
|
|
};
|
|
|
|
uint32_t sysop = instr->GetSysOp();
|
|
if (dcop.count(sysop)) {
|
|
if (sysop == IVAU) {
|
|
mnemonic = "ic";
|
|
}
|
|
form = dcop.at(sysop);
|
|
} else {
|
|
mnemonic = "sys";
|
|
form = "'G1, 'Kn, 'Km, 'G2";
|
|
if (instr->GetRt() == 31) {
|
|
suffix = NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Format(instr, mnemonic, form, suffix);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitException(const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "'IDebug";
|
|
|
|
switch (instr->Mask(ExceptionMask)) {
|
|
case HLT:
|
|
mnemonic = "hlt";
|
|
break;
|
|
case BRK:
|
|
mnemonic = "brk";
|
|
break;
|
|
case SVC:
|
|
mnemonic = "svc";
|
|
break;
|
|
case HVC:
|
|
mnemonic = "hvc";
|
|
break;
|
|
case SMC:
|
|
mnemonic = "smc";
|
|
break;
|
|
case DCPS1:
|
|
mnemonic = "dcps1";
|
|
form = "{'IDebug}";
|
|
break;
|
|
case DCPS2:
|
|
mnemonic = "dcps2";
|
|
form = "{'IDebug}";
|
|
break;
|
|
case DCPS3:
|
|
mnemonic = "dcps3";
|
|
form = "{'IDebug}";
|
|
break;
|
|
default:
|
|
form = "(Exception)";
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitCrypto2RegSHA(const Instruction *instr) {
|
|
VisitUnimplemented(instr);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitCrypto3RegSHA(const Instruction *instr) {
|
|
VisitUnimplemented(instr);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitCryptoAES(const Instruction *instr) {
|
|
VisitUnimplemented(instr);
|
|
}
|
|
|
|
void Disassembler::DisassembleNEON2RegAddlp(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s";
|
|
|
|
static const NEONFormatMap map_lp_ta =
|
|
{{23, 22, 30}, {NF_4H, NF_8H, NF_2S, NF_4S, NF_1D, NF_2D}};
|
|
NEONFormatDecoder nfd(instr);
|
|
nfd.SetFormatMap(0, &map_lp_ta);
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEON2RegCompare(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, #0";
|
|
NEONFormatDecoder nfd(instr);
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEON2RegFPCompare(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, #0.0";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::FPFormatMap());
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEON2RegFPConvert(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s";
|
|
static const NEONFormatMap map_cvt_ta = {{22}, {NF_4S, NF_2D}};
|
|
|
|
static const NEONFormatMap map_cvt_tb = {{22, 30},
|
|
{NF_4H, NF_8H, NF_2S, NF_4S}};
|
|
NEONFormatDecoder nfd(instr, &map_cvt_tb, &map_cvt_ta);
|
|
|
|
VectorFormat vform_dst = nfd.GetVectorFormat(0);
|
|
switch (form_hash_) {
|
|
case "fcvtl_asimdmisc_l"_h:
|
|
nfd.SetFormatMaps(&map_cvt_ta, &map_cvt_tb);
|
|
break;
|
|
case "fcvtxn_asimdmisc_n"_h:
|
|
if ((vform_dst != kFormat2S) && (vform_dst != kFormat4S)) {
|
|
mnemonic = NULL;
|
|
}
|
|
break;
|
|
}
|
|
Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEON2RegFP(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::FPFormatMap());
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEON2RegLogical(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::LogicalFormatMap());
|
|
if (form_hash_ == "not_asimdmisc_r"_h) {
|
|
mnemonic = "mvn";
|
|
}
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEON2RegExtract(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s";
|
|
const char *suffix = NULL;
|
|
NEONFormatDecoder nfd(instr,
|
|
NEONFormatDecoder::IntegerFormatMap(),
|
|
NEONFormatDecoder::LongIntegerFormatMap());
|
|
|
|
if (form_hash_ == "shll_asimdmisc_s"_h) {
|
|
nfd.SetFormatMaps(nfd.LongIntegerFormatMap(), nfd.IntegerFormatMap());
|
|
switch (instr->GetNEONSize()) {
|
|
case 0:
|
|
suffix = ", #8";
|
|
break;
|
|
case 1:
|
|
suffix = ", #16";
|
|
break;
|
|
case 2:
|
|
suffix = ", #32";
|
|
break;
|
|
}
|
|
}
|
|
Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form), suffix);
|
|
}
|
|
|
|
void Disassembler::VisitNEON2RegMisc(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s";
|
|
NEONFormatDecoder nfd(instr);
|
|
|
|
VectorFormat vform_dst = nfd.GetVectorFormat(0);
|
|
if (vform_dst != kFormatUndefined) {
|
|
uint32_t ls_dst = LaneSizeInBitsFromFormat(vform_dst);
|
|
switch (form_hash_) {
|
|
case "cnt_asimdmisc_r"_h:
|
|
case "rev16_asimdmisc_r"_h:
|
|
if (ls_dst != kBRegSize) {
|
|
mnemonic = NULL;
|
|
}
|
|
break;
|
|
case "rev32_asimdmisc_r"_h:
|
|
if ((ls_dst == kDRegSize) || (ls_dst == kSRegSize)) {
|
|
mnemonic = NULL;
|
|
}
|
|
break;
|
|
case "urecpe_asimdmisc_r"_h:
|
|
case "ursqrte_asimdmisc_r"_h:
|
|
// For urecpe and ursqrte, only S-sized elements are supported. The MSB
|
|
// of the size field is always set by the instruction (0b1x) so we need
|
|
// only check and discard D-sized elements here.
|
|
VIXL_ASSERT((ls_dst == kSRegSize) || (ls_dst == kDRegSize));
|
|
VIXL_FALLTHROUGH();
|
|
case "clz_asimdmisc_r"_h:
|
|
case "cls_asimdmisc_r"_h:
|
|
case "rev64_asimdmisc_r"_h:
|
|
if (ls_dst == kDRegSize) {
|
|
mnemonic = NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::VisitNEON2RegMiscFP16(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.'?30:84h, 'Vn.'?30:84h";
|
|
const char *suffix = NULL;
|
|
|
|
switch (form_hash_) {
|
|
case "fcmeq_asimdmiscfp16_fz"_h:
|
|
case "fcmge_asimdmiscfp16_fz"_h:
|
|
case "fcmgt_asimdmiscfp16_fz"_h:
|
|
case "fcmle_asimdmiscfp16_fz"_h:
|
|
case "fcmlt_asimdmiscfp16_fz"_h:
|
|
suffix = ", #0.0";
|
|
}
|
|
Format(instr, mnemonic, form, suffix);
|
|
}
|
|
|
|
void Disassembler::DisassembleNEON3SameLogical(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::LogicalFormatMap());
|
|
|
|
switch (form_hash_) {
|
|
case "orr_asimdsame_only"_h:
|
|
if (instr->GetRm() == instr->GetRn()) {
|
|
mnemonic = "mov";
|
|
form = "'Vd.%s, 'Vn.%s";
|
|
}
|
|
break;
|
|
case "pmul_asimdsame_only"_h:
|
|
if (instr->GetNEONSize() != 0) {
|
|
mnemonic = NULL;
|
|
}
|
|
}
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEON3SameFHM(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Vd.'?30:42s, 'Vn.'?30:42h, 'Vm.'?30:42h");
|
|
}
|
|
|
|
void Disassembler::DisassembleNEON3SameNoD(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s";
|
|
static const NEONFormatMap map =
|
|
{{23, 22, 30},
|
|
{NF_8B, NF_16B, NF_4H, NF_8H, NF_2S, NF_4S, NF_UNDEF, NF_UNDEF}};
|
|
NEONFormatDecoder nfd(instr, &map);
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::VisitNEON3Same(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s";
|
|
NEONFormatDecoder nfd(instr);
|
|
|
|
if (instr->Mask(NEON3SameFPFMask) == NEON3SameFPFixed) {
|
|
nfd.SetFormatMaps(nfd.FPFormatMap());
|
|
}
|
|
|
|
VectorFormat vform_dst = nfd.GetVectorFormat(0);
|
|
if (vform_dst != kFormatUndefined) {
|
|
uint32_t ls_dst = LaneSizeInBitsFromFormat(vform_dst);
|
|
switch (form_hash_) {
|
|
case "sqdmulh_asimdsame_only"_h:
|
|
case "sqrdmulh_asimdsame_only"_h:
|
|
if ((ls_dst == kBRegSize) || (ls_dst == kDRegSize)) {
|
|
mnemonic = NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::VisitNEON3SameFP16(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s";
|
|
NEONFormatDecoder nfd(instr);
|
|
nfd.SetFormatMaps(nfd.FP16FormatMap());
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::VisitNEON3SameExtra(const Instruction *instr) {
|
|
static const NEONFormatMap map_usdot = {{30}, {NF_8B, NF_16B}};
|
|
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s";
|
|
const char *suffix = NULL;
|
|
|
|
NEONFormatDecoder nfd(instr);
|
|
|
|
switch (form_hash_) {
|
|
case "fcmla_asimdsame2_c"_h:
|
|
suffix = ", #'u1211*90";
|
|
break;
|
|
case "fcadd_asimdsame2_c"_h:
|
|
// Bit 10 is always set, so this gives 90 * 1 or 3.
|
|
suffix = ", #'u1212:1010*90";
|
|
break;
|
|
case "sdot_asimdsame2_d"_h:
|
|
case "udot_asimdsame2_d"_h:
|
|
case "usdot_asimdsame2_d"_h:
|
|
nfd.SetFormatMap(1, &map_usdot);
|
|
nfd.SetFormatMap(2, &map_usdot);
|
|
break;
|
|
default:
|
|
// sqrdml[as]h - nothing to do.
|
|
break;
|
|
}
|
|
|
|
Format(instr, mnemonic, nfd.Substitute(form), suffix);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEON3Different(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s";
|
|
|
|
NEONFormatDecoder nfd(instr);
|
|
nfd.SetFormatMap(0, nfd.LongIntegerFormatMap());
|
|
|
|
switch (form_hash_) {
|
|
case "saddw_asimddiff_w"_h:
|
|
case "ssubw_asimddiff_w"_h:
|
|
case "uaddw_asimddiff_w"_h:
|
|
case "usubw_asimddiff_w"_h:
|
|
nfd.SetFormatMap(1, nfd.LongIntegerFormatMap());
|
|
break;
|
|
case "addhn_asimddiff_n"_h:
|
|
case "raddhn_asimddiff_n"_h:
|
|
case "rsubhn_asimddiff_n"_h:
|
|
case "subhn_asimddiff_n"_h:
|
|
nfd.SetFormatMaps(nfd.LongIntegerFormatMap());
|
|
nfd.SetFormatMap(0, nfd.IntegerFormatMap());
|
|
break;
|
|
case "pmull_asimddiff_l"_h:
|
|
if (nfd.GetVectorFormat(0) != kFormat8H) {
|
|
mnemonic = NULL;
|
|
}
|
|
break;
|
|
case "sqdmlal_asimddiff_l"_h:
|
|
case "sqdmlsl_asimddiff_l"_h:
|
|
case "sqdmull_asimddiff_l"_h:
|
|
if (nfd.GetVectorFormat(0) == kFormat8H) {
|
|
mnemonic = NULL;
|
|
}
|
|
break;
|
|
}
|
|
Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONFPAcrossLanes(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Sd, 'Vn.4s";
|
|
if ((instr->GetNEONQ() == 0) || (instr->ExtractBit(22) == 1)) {
|
|
mnemonic = NULL;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONFP16AcrossLanes(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Hd, 'Vn.'?30:84h");
|
|
}
|
|
|
|
void Disassembler::VisitNEONAcrossLanes(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "%sd, 'Vn.%s";
|
|
|
|
NEONFormatDecoder nfd(instr,
|
|
NEONFormatDecoder::ScalarFormatMap(),
|
|
NEONFormatDecoder::IntegerFormatMap());
|
|
|
|
switch (form_hash_) {
|
|
case "saddlv_asimdall_only"_h:
|
|
case "uaddlv_asimdall_only"_h:
|
|
nfd.SetFormatMap(0, nfd.LongScalarFormatMap());
|
|
}
|
|
|
|
VectorFormat vform_src = nfd.GetVectorFormat(1);
|
|
if ((vform_src == kFormat2S) || (vform_src == kFormat2D)) {
|
|
mnemonic = NULL;
|
|
}
|
|
|
|
Format(instr,
|
|
mnemonic,
|
|
nfd.Substitute(form,
|
|
NEONFormatDecoder::kPlaceholder,
|
|
NEONFormatDecoder::kFormat));
|
|
}
|
|
|
|
void Disassembler::VisitNEONByIndexedElement(const Instruction *instr) {
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'Vf.%s['IVByElemIndex]";
|
|
static const NEONFormatMap map_v =
|
|
{{23, 22, 30},
|
|
{NF_UNDEF, NF_UNDEF, NF_4H, NF_8H, NF_2S, NF_4S, NF_UNDEF, NF_UNDEF}};
|
|
static const NEONFormatMap map_s = {{23, 22},
|
|
{NF_UNDEF, NF_H, NF_S, NF_UNDEF}};
|
|
NEONFormatDecoder nfd(instr, &map_v, &map_v, &map_s);
|
|
Format(instr, mnemonic_.c_str(), nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONMulByElementLong(const Instruction *instr) {
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'Vf.%s['IVByElemIndex]";
|
|
// TODO: Disallow undefined element types for this instruction.
|
|
static const NEONFormatMap map_ta = {{23, 22}, {NF_UNDEF, NF_4S, NF_2D}};
|
|
NEONFormatDecoder nfd(instr,
|
|
&map_ta,
|
|
NEONFormatDecoder::IntegerFormatMap(),
|
|
NEONFormatDecoder::ScalarFormatMap());
|
|
Format(instr, nfd.Mnemonic(mnemonic_.c_str()), nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONDotProdByElement(const Instruction *instr) {
|
|
const char *form = instr->ExtractBit(30) ? "'Vd.4s, 'Vn.16" : "'Vd.2s, 'Vn.8";
|
|
const char *suffix = "b, 'Vm.4b['u1111:2121]";
|
|
Format(instr, mnemonic_.c_str(), form, suffix);
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONFPMulByElement(const Instruction *instr) {
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'Vf.%s['IVByElemIndex]";
|
|
NEONFormatDecoder nfd(instr,
|
|
NEONFormatDecoder::FPFormatMap(),
|
|
NEONFormatDecoder::FPFormatMap(),
|
|
NEONFormatDecoder::FPScalarFormatMap());
|
|
Format(instr, mnemonic_.c_str(), nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONHalfFPMulByElement(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr,
|
|
"'Vd.'?30:84h, 'Vn.'?30:84h, "
|
|
"'Ve.h['IVByElemIndex]");
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONFPMulByElementLong(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr,
|
|
"'Vd.'?30:42s, 'Vn.'?30:42h, "
|
|
"'Ve.h['IVByElemIndexFHM]");
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONComplexMulByElement(
|
|
const Instruction *instr) {
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s['IVByElemIndexRot], #'u1413*90";
|
|
// TODO: Disallow undefined element types for this instruction.
|
|
static const NEONFormatMap map_cn =
|
|
{{23, 22, 30},
|
|
{NF_UNDEF, NF_UNDEF, NF_4H, NF_8H, NF_UNDEF, NF_4S, NF_UNDEF, NF_UNDEF}};
|
|
NEONFormatDecoder nfd(instr,
|
|
&map_cn,
|
|
&map_cn,
|
|
NEONFormatDecoder::ScalarFormatMap());
|
|
Format(instr, mnemonic_.c_str(), nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::VisitNEONCopy(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "(NEONCopy)";
|
|
|
|
NEONFormatDecoder nfd(instr,
|
|
NEONFormatDecoder::TriangularFormatMap(),
|
|
NEONFormatDecoder::TriangularScalarFormatMap());
|
|
|
|
switch (form_hash_) {
|
|
case "ins_asimdins_iv_v"_h:
|
|
mnemonic = "mov";
|
|
nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap());
|
|
form = "'Vd.%s['IVInsIndex1], 'Vn.%s['IVInsIndex2]";
|
|
break;
|
|
case "ins_asimdins_ir_r"_h:
|
|
mnemonic = "mov";
|
|
nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap());
|
|
if (nfd.GetVectorFormat() == kFormatD) {
|
|
form = "'Vd.%s['IVInsIndex1], 'Xn";
|
|
} else {
|
|
form = "'Vd.%s['IVInsIndex1], 'Wn";
|
|
}
|
|
break;
|
|
case "umov_asimdins_w_w"_h:
|
|
case "umov_asimdins_x_x"_h:
|
|
if (instr->Mask(NEON_Q) || ((instr->GetImmNEON5() & 7) == 4)) {
|
|
mnemonic = "mov";
|
|
}
|
|
nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap());
|
|
if (nfd.GetVectorFormat() == kFormatD) {
|
|
form = "'Xd, 'Vn.%s['IVInsIndex1]";
|
|
} else {
|
|
form = "'Wd, 'Vn.%s['IVInsIndex1]";
|
|
}
|
|
break;
|
|
case "smov_asimdins_w_w"_h:
|
|
case "smov_asimdins_x_x"_h: {
|
|
nfd.SetFormatMap(0, nfd.TriangularScalarFormatMap());
|
|
VectorFormat vform = nfd.GetVectorFormat();
|
|
if ((vform == kFormatD) ||
|
|
((vform == kFormatS) && (instr->ExtractBit(30) == 0))) {
|
|
mnemonic = NULL;
|
|
}
|
|
form = "'R30d, 'Vn.%s['IVInsIndex1]";
|
|
break;
|
|
}
|
|
case "dup_asimdins_dv_v"_h:
|
|
form = "'Vd.%s, 'Vn.%s['IVInsIndex1]";
|
|
break;
|
|
case "dup_asimdins_dr_r"_h:
|
|
if (nfd.GetVectorFormat() == kFormat2D) {
|
|
form = "'Vd.%s, 'Xn";
|
|
} else {
|
|
form = "'Vd.%s, 'Wn";
|
|
}
|
|
}
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEONExtract(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'Vm.%s, 'IVExtract";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::LogicalFormatMap());
|
|
if ((instr->GetImmNEONExt() > 7) && (instr->GetNEONQ() == 0)) {
|
|
mnemonic = NULL;
|
|
}
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEONLoadStoreMultiStruct(const Instruction *instr) {
|
|
const char *mnemonic = NULL;
|
|
const char *form = NULL;
|
|
const char *form_1v = "{'Vt.%1$s}, ['Xns]";
|
|
const char *form_2v = "{'Vt.%1$s, 'Vt2.%1$s}, ['Xns]";
|
|
const char *form_3v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s}, ['Xns]";
|
|
const char *form_4v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns]";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap());
|
|
|
|
switch (instr->Mask(NEONLoadStoreMultiStructMask)) {
|
|
case NEON_LD1_1v:
|
|
mnemonic = "ld1";
|
|
form = form_1v;
|
|
break;
|
|
case NEON_LD1_2v:
|
|
mnemonic = "ld1";
|
|
form = form_2v;
|
|
break;
|
|
case NEON_LD1_3v:
|
|
mnemonic = "ld1";
|
|
form = form_3v;
|
|
break;
|
|
case NEON_LD1_4v:
|
|
mnemonic = "ld1";
|
|
form = form_4v;
|
|
break;
|
|
case NEON_LD2:
|
|
mnemonic = "ld2";
|
|
form = form_2v;
|
|
break;
|
|
case NEON_LD3:
|
|
mnemonic = "ld3";
|
|
form = form_3v;
|
|
break;
|
|
case NEON_LD4:
|
|
mnemonic = "ld4";
|
|
form = form_4v;
|
|
break;
|
|
case NEON_ST1_1v:
|
|
mnemonic = "st1";
|
|
form = form_1v;
|
|
break;
|
|
case NEON_ST1_2v:
|
|
mnemonic = "st1";
|
|
form = form_2v;
|
|
break;
|
|
case NEON_ST1_3v:
|
|
mnemonic = "st1";
|
|
form = form_3v;
|
|
break;
|
|
case NEON_ST1_4v:
|
|
mnemonic = "st1";
|
|
form = form_4v;
|
|
break;
|
|
case NEON_ST2:
|
|
mnemonic = "st2";
|
|
form = form_2v;
|
|
break;
|
|
case NEON_ST3:
|
|
mnemonic = "st3";
|
|
form = form_3v;
|
|
break;
|
|
case NEON_ST4:
|
|
mnemonic = "st4";
|
|
form = form_4v;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Work out unallocated encodings.
|
|
bool allocated = (mnemonic != NULL);
|
|
switch (instr->Mask(NEONLoadStoreMultiStructMask)) {
|
|
case NEON_LD2:
|
|
case NEON_LD3:
|
|
case NEON_LD4:
|
|
case NEON_ST2:
|
|
case NEON_ST3:
|
|
case NEON_ST4:
|
|
// LD[2-4] and ST[2-4] cannot use .1d format.
|
|
allocated = (instr->GetNEONQ() != 0) || (instr->GetNEONLSSize() != 3);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (allocated) {
|
|
VIXL_ASSERT(mnemonic != NULL);
|
|
VIXL_ASSERT(form != NULL);
|
|
} else {
|
|
mnemonic = "unallocated";
|
|
form = "(NEONLoadStoreMultiStruct)";
|
|
}
|
|
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEONLoadStoreMultiStructPostIndex(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = NULL;
|
|
const char *form = NULL;
|
|
const char *form_1v = "{'Vt.%1$s}, ['Xns], 'Xmr1";
|
|
const char *form_2v = "{'Vt.%1$s, 'Vt2.%1$s}, ['Xns], 'Xmr2";
|
|
const char *form_3v = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s}, ['Xns], 'Xmr3";
|
|
const char *form_4v =
|
|
"{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns], 'Xmr4";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap());
|
|
|
|
switch (instr->Mask(NEONLoadStoreMultiStructPostIndexMask)) {
|
|
case NEON_LD1_1v_post:
|
|
mnemonic = "ld1";
|
|
form = form_1v;
|
|
break;
|
|
case NEON_LD1_2v_post:
|
|
mnemonic = "ld1";
|
|
form = form_2v;
|
|
break;
|
|
case NEON_LD1_3v_post:
|
|
mnemonic = "ld1";
|
|
form = form_3v;
|
|
break;
|
|
case NEON_LD1_4v_post:
|
|
mnemonic = "ld1";
|
|
form = form_4v;
|
|
break;
|
|
case NEON_LD2_post:
|
|
mnemonic = "ld2";
|
|
form = form_2v;
|
|
break;
|
|
case NEON_LD3_post:
|
|
mnemonic = "ld3";
|
|
form = form_3v;
|
|
break;
|
|
case NEON_LD4_post:
|
|
mnemonic = "ld4";
|
|
form = form_4v;
|
|
break;
|
|
case NEON_ST1_1v_post:
|
|
mnemonic = "st1";
|
|
form = form_1v;
|
|
break;
|
|
case NEON_ST1_2v_post:
|
|
mnemonic = "st1";
|
|
form = form_2v;
|
|
break;
|
|
case NEON_ST1_3v_post:
|
|
mnemonic = "st1";
|
|
form = form_3v;
|
|
break;
|
|
case NEON_ST1_4v_post:
|
|
mnemonic = "st1";
|
|
form = form_4v;
|
|
break;
|
|
case NEON_ST2_post:
|
|
mnemonic = "st2";
|
|
form = form_2v;
|
|
break;
|
|
case NEON_ST3_post:
|
|
mnemonic = "st3";
|
|
form = form_3v;
|
|
break;
|
|
case NEON_ST4_post:
|
|
mnemonic = "st4";
|
|
form = form_4v;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Work out unallocated encodings.
|
|
bool allocated = (mnemonic != NULL);
|
|
switch (instr->Mask(NEONLoadStoreMultiStructPostIndexMask)) {
|
|
case NEON_LD2_post:
|
|
case NEON_LD3_post:
|
|
case NEON_LD4_post:
|
|
case NEON_ST2_post:
|
|
case NEON_ST3_post:
|
|
case NEON_ST4_post:
|
|
// LD[2-4] and ST[2-4] cannot use .1d format.
|
|
allocated = (instr->GetNEONQ() != 0) || (instr->GetNEONLSSize() != 3);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (allocated) {
|
|
VIXL_ASSERT(mnemonic != NULL);
|
|
VIXL_ASSERT(form != NULL);
|
|
} else {
|
|
mnemonic = "unallocated";
|
|
form = "(NEONLoadStoreMultiStructPostIndex)";
|
|
}
|
|
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEONLoadStoreSingleStruct(const Instruction *instr) {
|
|
const char *mnemonic = NULL;
|
|
const char *form = NULL;
|
|
|
|
const char *form_1b = "{'Vt.b}['IVLSLane0], ['Xns]";
|
|
const char *form_1h = "{'Vt.h}['IVLSLane1], ['Xns]";
|
|
const char *form_1s = "{'Vt.s}['IVLSLane2], ['Xns]";
|
|
const char *form_1d = "{'Vt.d}['IVLSLane3], ['Xns]";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap());
|
|
|
|
switch (instr->Mask(NEONLoadStoreSingleStructMask)) {
|
|
case NEON_LD1_b:
|
|
mnemonic = "ld1";
|
|
form = form_1b;
|
|
break;
|
|
case NEON_LD1_h:
|
|
mnemonic = "ld1";
|
|
form = form_1h;
|
|
break;
|
|
case NEON_LD1_s:
|
|
mnemonic = "ld1";
|
|
VIXL_STATIC_ASSERT((NEON_LD1_s | (1 << NEONLSSize_offset)) == NEON_LD1_d);
|
|
form = ((instr->GetNEONLSSize() & 1) == 0) ? form_1s : form_1d;
|
|
break;
|
|
case NEON_ST1_b:
|
|
mnemonic = "st1";
|
|
form = form_1b;
|
|
break;
|
|
case NEON_ST1_h:
|
|
mnemonic = "st1";
|
|
form = form_1h;
|
|
break;
|
|
case NEON_ST1_s:
|
|
mnemonic = "st1";
|
|
VIXL_STATIC_ASSERT((NEON_ST1_s | (1 << NEONLSSize_offset)) == NEON_ST1_d);
|
|
form = ((instr->GetNEONLSSize() & 1) == 0) ? form_1s : form_1d;
|
|
break;
|
|
case NEON_LD1R:
|
|
mnemonic = "ld1r";
|
|
form = "{'Vt.%s}, ['Xns]";
|
|
break;
|
|
case NEON_LD2_b:
|
|
case NEON_ST2_b:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld2" : "st2";
|
|
form = "{'Vt.b, 'Vt2.b}['IVLSLane0], ['Xns]";
|
|
break;
|
|
case NEON_LD2_h:
|
|
case NEON_ST2_h:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld2" : "st2";
|
|
form = "{'Vt.h, 'Vt2.h}['IVLSLane1], ['Xns]";
|
|
break;
|
|
case NEON_LD2_s:
|
|
case NEON_ST2_s:
|
|
VIXL_STATIC_ASSERT((NEON_ST2_s | (1 << NEONLSSize_offset)) == NEON_ST2_d);
|
|
VIXL_STATIC_ASSERT((NEON_LD2_s | (1 << NEONLSSize_offset)) == NEON_LD2_d);
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld2" : "st2";
|
|
if ((instr->GetNEONLSSize() & 1) == 0) {
|
|
form = "{'Vt.s, 'Vt2.s}['IVLSLane2], ['Xns]";
|
|
} else {
|
|
form = "{'Vt.d, 'Vt2.d}['IVLSLane3], ['Xns]";
|
|
}
|
|
break;
|
|
case NEON_LD2R:
|
|
mnemonic = "ld2r";
|
|
form = "{'Vt.%s, 'Vt2.%s}, ['Xns]";
|
|
break;
|
|
case NEON_LD3_b:
|
|
case NEON_ST3_b:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld3" : "st3";
|
|
form = "{'Vt.b, 'Vt2.b, 'Vt3.b}['IVLSLane0], ['Xns]";
|
|
break;
|
|
case NEON_LD3_h:
|
|
case NEON_ST3_h:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld3" : "st3";
|
|
form = "{'Vt.h, 'Vt2.h, 'Vt3.h}['IVLSLane1], ['Xns]";
|
|
break;
|
|
case NEON_LD3_s:
|
|
case NEON_ST3_s:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld3" : "st3";
|
|
if ((instr->GetNEONLSSize() & 1) == 0) {
|
|
form = "{'Vt.s, 'Vt2.s, 'Vt3.s}['IVLSLane2], ['Xns]";
|
|
} else {
|
|
form = "{'Vt.d, 'Vt2.d, 'Vt3.d}['IVLSLane3], ['Xns]";
|
|
}
|
|
break;
|
|
case NEON_LD3R:
|
|
mnemonic = "ld3r";
|
|
form = "{'Vt.%s, 'Vt2.%s, 'Vt3.%s}, ['Xns]";
|
|
break;
|
|
case NEON_LD4_b:
|
|
case NEON_ST4_b:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld4" : "st4";
|
|
form = "{'Vt.b, 'Vt2.b, 'Vt3.b, 'Vt4.b}['IVLSLane0], ['Xns]";
|
|
break;
|
|
case NEON_LD4_h:
|
|
case NEON_ST4_h:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld4" : "st4";
|
|
form = "{'Vt.h, 'Vt2.h, 'Vt3.h, 'Vt4.h}['IVLSLane1], ['Xns]";
|
|
break;
|
|
case NEON_LD4_s:
|
|
case NEON_ST4_s:
|
|
VIXL_STATIC_ASSERT((NEON_LD4_s | (1 << NEONLSSize_offset)) == NEON_LD4_d);
|
|
VIXL_STATIC_ASSERT((NEON_ST4_s | (1 << NEONLSSize_offset)) == NEON_ST4_d);
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld4" : "st4";
|
|
if ((instr->GetNEONLSSize() & 1) == 0) {
|
|
form = "{'Vt.s, 'Vt2.s, 'Vt3.s, 'Vt4.s}['IVLSLane2], ['Xns]";
|
|
} else {
|
|
form = "{'Vt.d, 'Vt2.d, 'Vt3.d, 'Vt4.d}['IVLSLane3], ['Xns]";
|
|
}
|
|
break;
|
|
case NEON_LD4R:
|
|
mnemonic = "ld4r";
|
|
form = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns]";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Work out unallocated encodings.
|
|
bool allocated = (mnemonic != NULL);
|
|
switch (instr->Mask(NEONLoadStoreSingleStructMask)) {
|
|
case NEON_LD1_h:
|
|
case NEON_LD2_h:
|
|
case NEON_LD3_h:
|
|
case NEON_LD4_h:
|
|
case NEON_ST1_h:
|
|
case NEON_ST2_h:
|
|
case NEON_ST3_h:
|
|
case NEON_ST4_h:
|
|
VIXL_ASSERT(allocated);
|
|
allocated = ((instr->GetNEONLSSize() & 1) == 0);
|
|
break;
|
|
case NEON_LD1_s:
|
|
case NEON_LD2_s:
|
|
case NEON_LD3_s:
|
|
case NEON_LD4_s:
|
|
case NEON_ST1_s:
|
|
case NEON_ST2_s:
|
|
case NEON_ST3_s:
|
|
case NEON_ST4_s:
|
|
VIXL_ASSERT(allocated);
|
|
allocated = (instr->GetNEONLSSize() <= 1) &&
|
|
((instr->GetNEONLSSize() == 0) || (instr->GetNEONS() == 0));
|
|
break;
|
|
case NEON_LD1R:
|
|
case NEON_LD2R:
|
|
case NEON_LD3R:
|
|
case NEON_LD4R:
|
|
VIXL_ASSERT(allocated);
|
|
allocated = (instr->GetNEONS() == 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (allocated) {
|
|
VIXL_ASSERT(mnemonic != NULL);
|
|
VIXL_ASSERT(form != NULL);
|
|
} else {
|
|
mnemonic = "unallocated";
|
|
form = "(NEONLoadStoreSingleStruct)";
|
|
}
|
|
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEONLoadStoreSingleStructPostIndex(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = NULL;
|
|
const char *form = NULL;
|
|
|
|
const char *form_1b = "{'Vt.b}['IVLSLane0], ['Xns], 'Xmb1";
|
|
const char *form_1h = "{'Vt.h}['IVLSLane1], ['Xns], 'Xmb2";
|
|
const char *form_1s = "{'Vt.s}['IVLSLane2], ['Xns], 'Xmb4";
|
|
const char *form_1d = "{'Vt.d}['IVLSLane3], ['Xns], 'Xmb8";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::LoadStoreFormatMap());
|
|
|
|
switch (instr->Mask(NEONLoadStoreSingleStructPostIndexMask)) {
|
|
case NEON_LD1_b_post:
|
|
mnemonic = "ld1";
|
|
form = form_1b;
|
|
break;
|
|
case NEON_LD1_h_post:
|
|
mnemonic = "ld1";
|
|
form = form_1h;
|
|
break;
|
|
case NEON_LD1_s_post:
|
|
mnemonic = "ld1";
|
|
VIXL_STATIC_ASSERT((NEON_LD1_s | (1 << NEONLSSize_offset)) == NEON_LD1_d);
|
|
form = ((instr->GetNEONLSSize() & 1) == 0) ? form_1s : form_1d;
|
|
break;
|
|
case NEON_ST1_b_post:
|
|
mnemonic = "st1";
|
|
form = form_1b;
|
|
break;
|
|
case NEON_ST1_h_post:
|
|
mnemonic = "st1";
|
|
form = form_1h;
|
|
break;
|
|
case NEON_ST1_s_post:
|
|
mnemonic = "st1";
|
|
VIXL_STATIC_ASSERT((NEON_ST1_s | (1 << NEONLSSize_offset)) == NEON_ST1_d);
|
|
form = ((instr->GetNEONLSSize() & 1) == 0) ? form_1s : form_1d;
|
|
break;
|
|
case NEON_LD1R_post:
|
|
mnemonic = "ld1r";
|
|
form = "{'Vt.%s}, ['Xns], 'Xmz1";
|
|
break;
|
|
case NEON_LD2_b_post:
|
|
case NEON_ST2_b_post:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld2" : "st2";
|
|
form = "{'Vt.b, 'Vt2.b}['IVLSLane0], ['Xns], 'Xmb2";
|
|
break;
|
|
case NEON_ST2_h_post:
|
|
case NEON_LD2_h_post:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld2" : "st2";
|
|
form = "{'Vt.h, 'Vt2.h}['IVLSLane1], ['Xns], 'Xmb4";
|
|
break;
|
|
case NEON_LD2_s_post:
|
|
case NEON_ST2_s_post:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld2" : "st2";
|
|
if ((instr->GetNEONLSSize() & 1) == 0)
|
|
form = "{'Vt.s, 'Vt2.s}['IVLSLane2], ['Xns], 'Xmb8";
|
|
else
|
|
form = "{'Vt.d, 'Vt2.d}['IVLSLane3], ['Xns], 'Xmb16";
|
|
break;
|
|
case NEON_LD2R_post:
|
|
mnemonic = "ld2r";
|
|
form = "{'Vt.%s, 'Vt2.%s}, ['Xns], 'Xmz2";
|
|
break;
|
|
case NEON_LD3_b_post:
|
|
case NEON_ST3_b_post:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld3" : "st3";
|
|
form = "{'Vt.b, 'Vt2.b, 'Vt3.b}['IVLSLane0], ['Xns], 'Xmb3";
|
|
break;
|
|
case NEON_LD3_h_post:
|
|
case NEON_ST3_h_post:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld3" : "st3";
|
|
form = "{'Vt.h, 'Vt2.h, 'Vt3.h}['IVLSLane1], ['Xns], 'Xmb6";
|
|
break;
|
|
case NEON_LD3_s_post:
|
|
case NEON_ST3_s_post:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld3" : "st3";
|
|
if ((instr->GetNEONLSSize() & 1) == 0)
|
|
form = "{'Vt.s, 'Vt2.s, 'Vt3.s}['IVLSLane2], ['Xns], 'Xmb12";
|
|
else
|
|
form = "{'Vt.d, 'Vt2.d, 'Vt3.d}['IVLSLane3], ['Xns], 'Xmb24";
|
|
break;
|
|
case NEON_LD3R_post:
|
|
mnemonic = "ld3r";
|
|
form = "{'Vt.%s, 'Vt2.%s, 'Vt3.%s}, ['Xns], 'Xmz3";
|
|
break;
|
|
case NEON_LD4_b_post:
|
|
case NEON_ST4_b_post:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld4" : "st4";
|
|
form = "{'Vt.b, 'Vt2.b, 'Vt3.b, 'Vt4.b}['IVLSLane0], ['Xns], 'Xmb4";
|
|
break;
|
|
case NEON_LD4_h_post:
|
|
case NEON_ST4_h_post:
|
|
mnemonic = (instr->GetLdStXLoad()) == 1 ? "ld4" : "st4";
|
|
form = "{'Vt.h, 'Vt2.h, 'Vt3.h, 'Vt4.h}['IVLSLane1], ['Xns], 'Xmb8";
|
|
break;
|
|
case NEON_LD4_s_post:
|
|
case NEON_ST4_s_post:
|
|
mnemonic = (instr->GetLdStXLoad() == 1) ? "ld4" : "st4";
|
|
if ((instr->GetNEONLSSize() & 1) == 0)
|
|
form = "{'Vt.s, 'Vt2.s, 'Vt3.s, 'Vt4.s}['IVLSLane2], ['Xns], 'Xmb16";
|
|
else
|
|
form = "{'Vt.d, 'Vt2.d, 'Vt3.d, 'Vt4.d}['IVLSLane3], ['Xns], 'Xmb32";
|
|
break;
|
|
case NEON_LD4R_post:
|
|
mnemonic = "ld4r";
|
|
form = "{'Vt.%1$s, 'Vt2.%1$s, 'Vt3.%1$s, 'Vt4.%1$s}, ['Xns], 'Xmz4";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Work out unallocated encodings.
|
|
bool allocated = (mnemonic != NULL);
|
|
switch (instr->Mask(NEONLoadStoreSingleStructPostIndexMask)) {
|
|
case NEON_LD1_h_post:
|
|
case NEON_LD2_h_post:
|
|
case NEON_LD3_h_post:
|
|
case NEON_LD4_h_post:
|
|
case NEON_ST1_h_post:
|
|
case NEON_ST2_h_post:
|
|
case NEON_ST3_h_post:
|
|
case NEON_ST4_h_post:
|
|
VIXL_ASSERT(allocated);
|
|
allocated = ((instr->GetNEONLSSize() & 1) == 0);
|
|
break;
|
|
case NEON_LD1_s_post:
|
|
case NEON_LD2_s_post:
|
|
case NEON_LD3_s_post:
|
|
case NEON_LD4_s_post:
|
|
case NEON_ST1_s_post:
|
|
case NEON_ST2_s_post:
|
|
case NEON_ST3_s_post:
|
|
case NEON_ST4_s_post:
|
|
VIXL_ASSERT(allocated);
|
|
allocated = (instr->GetNEONLSSize() <= 1) &&
|
|
((instr->GetNEONLSSize() == 0) || (instr->GetNEONS() == 0));
|
|
break;
|
|
case NEON_LD1R_post:
|
|
case NEON_LD2R_post:
|
|
case NEON_LD3R_post:
|
|
case NEON_LD4R_post:
|
|
VIXL_ASSERT(allocated);
|
|
allocated = (instr->GetNEONS() == 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (allocated) {
|
|
VIXL_ASSERT(mnemonic != NULL);
|
|
VIXL_ASSERT(form != NULL);
|
|
} else {
|
|
mnemonic = "unallocated";
|
|
form = "(NEONLoadStoreSingleStructPostIndex)";
|
|
}
|
|
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEONModifiedImmediate(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vt.%s, 'IVMIImm8, lsl 'IVMIShiftAmt1";
|
|
|
|
static const NEONFormatMap map_h = {{30}, {NF_4H, NF_8H}};
|
|
static const NEONFormatMap map_s = {{30}, {NF_2S, NF_4S}};
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::LogicalFormatMap());
|
|
|
|
switch (form_hash_) {
|
|
case "movi_asimdimm_n_b"_h:
|
|
form = "'Vt.%s, 'IVMIImm8";
|
|
break;
|
|
case "bic_asimdimm_l_hl"_h:
|
|
case "movi_asimdimm_l_hl"_h:
|
|
case "mvni_asimdimm_l_hl"_h:
|
|
case "orr_asimdimm_l_hl"_h:
|
|
nfd.SetFormatMap(0, &map_h);
|
|
break;
|
|
case "movi_asimdimm_m_sm"_h:
|
|
case "mvni_asimdimm_m_sm"_h:
|
|
form = "'Vt.%s, 'IVMIImm8, msl 'IVMIShiftAmt2";
|
|
VIXL_FALLTHROUGH();
|
|
case "bic_asimdimm_l_sl"_h:
|
|
case "movi_asimdimm_l_sl"_h:
|
|
case "mvni_asimdimm_l_sl"_h:
|
|
case "orr_asimdimm_l_sl"_h:
|
|
nfd.SetFormatMap(0, &map_s);
|
|
break;
|
|
case "movi_asimdimm_d_ds"_h:
|
|
form = "'Dd, 'IVMIImm";
|
|
break;
|
|
case "movi_asimdimm_d2_d"_h:
|
|
form = "'Vt.2d, 'IVMIImm";
|
|
break;
|
|
case "fmov_asimdimm_h_h"_h:
|
|
form = "'Vt.%s, 'IFPNeon";
|
|
nfd.SetFormatMap(0, &map_h);
|
|
break;
|
|
case "fmov_asimdimm_s_s"_h:
|
|
form = "'Vt.%s, 'IFPNeon";
|
|
nfd.SetFormatMap(0, &map_s);
|
|
break;
|
|
case "fmov_asimdimm_d2_d"_h:
|
|
form = "'Vt.2d, 'IFPNeon";
|
|
break;
|
|
}
|
|
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONScalar2RegMiscOnlyD(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Dd, 'Dn";
|
|
const char *suffix = ", #0";
|
|
if (instr->GetNEONSize() != 3) {
|
|
mnemonic = NULL;
|
|
}
|
|
switch (form_hash_) {
|
|
case "abs_asisdmisc_r"_h:
|
|
case "neg_asisdmisc_r"_h:
|
|
suffix = NULL;
|
|
}
|
|
Format(instr, mnemonic, form, suffix);
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONFPScalar2RegMisc(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "%sd, %sn";
|
|
const char *suffix = NULL;
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::FPScalarFormatMap());
|
|
switch (form_hash_) {
|
|
case "fcmeq_asisdmisc_fz"_h:
|
|
case "fcmge_asisdmisc_fz"_h:
|
|
case "fcmgt_asisdmisc_fz"_h:
|
|
case "fcmle_asisdmisc_fz"_h:
|
|
case "fcmlt_asisdmisc_fz"_h:
|
|
suffix = ", #0.0";
|
|
break;
|
|
case "fcvtxn_asisdmisc_n"_h:
|
|
if (nfd.GetVectorFormat(0) == kFormatS) { // Source format.
|
|
mnemonic = NULL;
|
|
}
|
|
form = "'Sd, 'Dn";
|
|
}
|
|
Format(instr, mnemonic, nfd.SubstitutePlaceholders(form), suffix);
|
|
}
|
|
|
|
void Disassembler::VisitNEONScalar2RegMisc(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "%sd, %sn";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap());
|
|
switch (form_hash_) {
|
|
case "sqxtn_asisdmisc_n"_h:
|
|
case "sqxtun_asisdmisc_n"_h:
|
|
case "uqxtn_asisdmisc_n"_h:
|
|
nfd.SetFormatMap(1, nfd.LongScalarFormatMap());
|
|
}
|
|
Format(instr, mnemonic, nfd.SubstitutePlaceholders(form));
|
|
}
|
|
|
|
void Disassembler::VisitNEONScalar2RegMiscFP16(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Hd, 'Hn";
|
|
const char *suffix = NULL;
|
|
|
|
switch (form_hash_) {
|
|
case "fcmeq_asisdmiscfp16_fz"_h:
|
|
case "fcmge_asisdmiscfp16_fz"_h:
|
|
case "fcmgt_asisdmiscfp16_fz"_h:
|
|
case "fcmle_asisdmiscfp16_fz"_h:
|
|
case "fcmlt_asisdmiscfp16_fz"_h:
|
|
suffix = ", #0.0";
|
|
}
|
|
Format(instr, mnemonic, form, suffix);
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEONScalar3Diff(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "%sd, %sn, %sm";
|
|
NEONFormatDecoder nfd(instr,
|
|
NEONFormatDecoder::LongScalarFormatMap(),
|
|
NEONFormatDecoder::ScalarFormatMap());
|
|
if (nfd.GetVectorFormat(0) == kFormatH) {
|
|
mnemonic = NULL;
|
|
}
|
|
Format(instr, mnemonic, nfd.SubstitutePlaceholders(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONFPScalar3Same(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "%sd, %sn, %sm";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::FPScalarFormatMap());
|
|
Format(instr, mnemonic, nfd.SubstitutePlaceholders(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONScalar3SameOnlyD(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Dd, 'Dn, 'Dm";
|
|
if (instr->GetNEONSize() != 3) {
|
|
mnemonic = NULL;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitNEONScalar3Same(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "%sd, %sn, %sm";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap());
|
|
VectorFormat vform = nfd.GetVectorFormat(0);
|
|
switch (form_hash_) {
|
|
case "srshl_asisdsame_only"_h:
|
|
case "urshl_asisdsame_only"_h:
|
|
case "sshl_asisdsame_only"_h:
|
|
case "ushl_asisdsame_only"_h:
|
|
if (vform != kFormatD) {
|
|
mnemonic = NULL;
|
|
}
|
|
break;
|
|
case "sqdmulh_asisdsame_only"_h:
|
|
case "sqrdmulh_asisdsame_only"_h:
|
|
if ((vform == kFormatB) || (vform == kFormatD)) {
|
|
mnemonic = NULL;
|
|
}
|
|
}
|
|
Format(instr, mnemonic, nfd.SubstitutePlaceholders(form));
|
|
}
|
|
|
|
void Disassembler::VisitNEONScalar3SameFP16(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Hd, 'Hn, 'Hm");
|
|
}
|
|
|
|
void Disassembler::VisitNEONScalar3SameExtra(const Instruction *instr) {
|
|
USE(instr);
|
|
// Nothing to do - handled by VisitNEONScalar3Same.
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONScalarSatMulLongIndex(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "%sd, %sn, 'Vf.%s['IVByElemIndex]";
|
|
NEONFormatDecoder nfd(instr,
|
|
NEONFormatDecoder::LongScalarFormatMap(),
|
|
NEONFormatDecoder::ScalarFormatMap());
|
|
if (nfd.GetVectorFormat(0) == kFormatH) {
|
|
mnemonic = NULL;
|
|
}
|
|
Format(instr,
|
|
mnemonic,
|
|
nfd.Substitute(form, nfd.kPlaceholder, nfd.kPlaceholder, nfd.kFormat));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONFPScalarMulIndex(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "%sd, %sn, 'Vf.%s['IVByElemIndex]";
|
|
static const NEONFormatMap map = {{23, 22}, {NF_H, NF_UNDEF, NF_S, NF_D}};
|
|
NEONFormatDecoder nfd(instr, &map);
|
|
Format(instr,
|
|
mnemonic,
|
|
nfd.Substitute(form, nfd.kPlaceholder, nfd.kPlaceholder, nfd.kFormat));
|
|
}
|
|
|
|
void Disassembler::VisitNEONScalarByIndexedElement(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "%sd, %sn, 'Vf.%s['IVByElemIndex]";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::ScalarFormatMap());
|
|
VectorFormat vform_dst = nfd.GetVectorFormat(0);
|
|
if ((vform_dst == kFormatB) || (vform_dst == kFormatD)) {
|
|
mnemonic = NULL;
|
|
}
|
|
Format(instr,
|
|
mnemonic,
|
|
nfd.Substitute(form, nfd.kPlaceholder, nfd.kPlaceholder, nfd.kFormat));
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEONScalarCopy(const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(NEONScalarCopy)";
|
|
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::TriangularScalarFormatMap());
|
|
|
|
if (instr->Mask(NEONScalarCopyMask) == NEON_DUP_ELEMENT_scalar) {
|
|
mnemonic = "mov";
|
|
form = "%sd, 'Vn.%s['IVInsIndex1]";
|
|
}
|
|
|
|
Format(instr, mnemonic, nfd.Substitute(form, nfd.kPlaceholder, nfd.kFormat));
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEONScalarPairwise(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
if (form_hash_ == "addp_asisdpair_only"_h) {
|
|
// All pairwise operations except ADDP use bit U to differentiate FP16
|
|
// from FP32/FP64 variations.
|
|
if (instr->GetNEONSize() != 3) {
|
|
mnemonic = NULL;
|
|
}
|
|
Format(instr, mnemonic, "'Dd, 'Vn.2d");
|
|
} else {
|
|
const char *form = "%sd, 'Vn.2%s";
|
|
NEONFormatDecoder nfd(instr,
|
|
NEONFormatDecoder::FPScalarPairwiseFormatMap());
|
|
|
|
Format(instr,
|
|
mnemonic,
|
|
nfd.Substitute(form,
|
|
NEONFormatDecoder::kPlaceholder,
|
|
NEONFormatDecoder::kFormat));
|
|
}
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONScalarShiftImmOnlyD(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Dd, 'Dn, ";
|
|
const char *suffix = "'IsR";
|
|
|
|
if (instr->ExtractBit(22) == 0) {
|
|
// Only D registers are supported.
|
|
mnemonic = NULL;
|
|
}
|
|
|
|
switch (form_hash_) {
|
|
case "shl_asisdshf_r"_h:
|
|
case "sli_asisdshf_r"_h:
|
|
suffix = "'IsL";
|
|
}
|
|
|
|
Format(instr, mnemonic, form, suffix);
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONScalarShiftRightNarrowImm(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "%sd, %sn, 'IsR";
|
|
static const NEONFormatMap map_dst =
|
|
{{22, 21, 20, 19}, {NF_UNDEF, NF_B, NF_H, NF_H, NF_S, NF_S, NF_S, NF_S}};
|
|
static const NEONFormatMap map_src =
|
|
{{22, 21, 20, 19}, {NF_UNDEF, NF_H, NF_S, NF_S, NF_D, NF_D, NF_D, NF_D}};
|
|
NEONFormatDecoder nfd(instr, &map_dst, &map_src);
|
|
Format(instr, mnemonic, nfd.SubstitutePlaceholders(form));
|
|
}
|
|
|
|
void Disassembler::VisitNEONScalarShiftImmediate(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "%sd, %sn, ";
|
|
const char *suffix = "'IsR";
|
|
|
|
// clang-format off
|
|
static const NEONFormatMap map = {{22, 21, 20, 19},
|
|
{NF_UNDEF, NF_B, NF_H, NF_H,
|
|
NF_S, NF_S, NF_S, NF_S,
|
|
NF_D, NF_D, NF_D, NF_D,
|
|
NF_D, NF_D, NF_D, NF_D}};
|
|
// clang-format on
|
|
NEONFormatDecoder nfd(instr, &map);
|
|
switch (form_hash_) {
|
|
case "sqshlu_asisdshf_r"_h:
|
|
case "sqshl_asisdshf_r"_h:
|
|
case "uqshl_asisdshf_r"_h:
|
|
suffix = "'IsL";
|
|
break;
|
|
default:
|
|
if (nfd.GetVectorFormat(0) == kFormatB) {
|
|
mnemonic = NULL;
|
|
}
|
|
}
|
|
Format(instr, mnemonic, nfd.SubstitutePlaceholders(form), suffix);
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONShiftLeftLongImm(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s";
|
|
const char *suffix = ", 'IsL";
|
|
|
|
NEONFormatDecoder nfd(instr,
|
|
NEONFormatDecoder::ShiftLongNarrowImmFormatMap(),
|
|
NEONFormatDecoder::ShiftImmFormatMap());
|
|
|
|
if (instr->GetImmNEONImmb() == 0 &&
|
|
CountSetBits(instr->GetImmNEONImmh(), 32) == 1) { // xtl variant.
|
|
VIXL_ASSERT((form_hash_ == "sshll_asimdshf_l"_h) ||
|
|
(form_hash_ == "ushll_asimdshf_l"_h));
|
|
mnemonic = (form_hash_ == "sshll_asimdshf_l"_h) ? "sxtl" : "uxtl";
|
|
suffix = NULL;
|
|
}
|
|
Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form), suffix);
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONShiftRightImm(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'IsR";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::ShiftImmFormatMap());
|
|
|
|
VectorFormat vform_dst = nfd.GetVectorFormat(0);
|
|
if (vform_dst != kFormatUndefined) {
|
|
uint32_t ls_dst = LaneSizeInBitsFromFormat(vform_dst);
|
|
switch (form_hash_) {
|
|
case "scvtf_asimdshf_c"_h:
|
|
case "ucvtf_asimdshf_c"_h:
|
|
case "fcvtzs_asimdshf_c"_h:
|
|
case "fcvtzu_asimdshf_c"_h:
|
|
if (ls_dst == kBRegSize) {
|
|
mnemonic = NULL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::DisassembleNEONShiftRightNarrowImm(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'IsR";
|
|
|
|
NEONFormatDecoder nfd(instr,
|
|
NEONFormatDecoder::ShiftImmFormatMap(),
|
|
NEONFormatDecoder::ShiftLongNarrowImmFormatMap());
|
|
Format(instr, nfd.Mnemonic(mnemonic), nfd.Substitute(form));
|
|
}
|
|
|
|
void Disassembler::VisitNEONShiftImmediate(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Vd.%s, 'Vn.%s, 'IsL";
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::ShiftImmFormatMap());
|
|
Format(instr, mnemonic, nfd.Substitute(form));
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEONTable(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char form_1v[] = "'Vd.%%s, {'Vn.16b}, 'Vm.%%s";
|
|
const char form_2v[] = "'Vd.%%s, {'Vn.16b, v%d.16b}, 'Vm.%%s";
|
|
const char form_3v[] = "'Vd.%%s, {'Vn.16b, v%d.16b, v%d.16b}, 'Vm.%%s";
|
|
const char form_4v[] =
|
|
"'Vd.%%s, {'Vn.16b, v%d.16b, v%d.16b, v%d.16b}, 'Vm.%%s";
|
|
const char *form = form_1v;
|
|
|
|
NEONFormatDecoder nfd(instr, NEONFormatDecoder::LogicalFormatMap());
|
|
|
|
switch (form_hash_) {
|
|
case "tbl_asimdtbl_l2_2"_h:
|
|
case "tbx_asimdtbl_l2_2"_h:
|
|
form = form_2v;
|
|
break;
|
|
case "tbl_asimdtbl_l3_3"_h:
|
|
case "tbx_asimdtbl_l3_3"_h:
|
|
form = form_3v;
|
|
break;
|
|
case "tbl_asimdtbl_l4_4"_h:
|
|
case "tbx_asimdtbl_l4_4"_h:
|
|
form = form_4v;
|
|
break;
|
|
}
|
|
VIXL_ASSERT(form != NULL);
|
|
|
|
char re_form[sizeof(form_4v) + 6]; // 3 * two-digit substitutions => 6
|
|
int reg_num = instr->GetRn();
|
|
snprintf(re_form,
|
|
sizeof(re_form),
|
|
form,
|
|
(reg_num + 1) % kNumberOfVRegisters,
|
|
(reg_num + 2) % kNumberOfVRegisters,
|
|
(reg_num + 3) % kNumberOfVRegisters);
|
|
|
|
Format(instr, mnemonic, nfd.Substitute(re_form));
|
|
}
|
|
|
|
|
|
void Disassembler::VisitNEONPerm(const Instruction *instr) {
|
|
NEONFormatDecoder nfd(instr);
|
|
FormatWithDecodedMnemonic(instr, nfd.Substitute("'Vd.%s, 'Vn.%s, 'Vm.%s"));
|
|
}
|
|
|
|
void Disassembler::Disassemble_Vd4S_Vn16B_Vm16B(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Vd.4s, 'Vn.16b, 'Vm.16b");
|
|
}
|
|
|
|
void Disassembler::
|
|
VisitSVE32BitGatherLoadHalfwords_ScalarPlus32BitScaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr,
|
|
"{'Zt.s}, 'Pgl/z, ['Xns, 'Zm.s, '?22:suxtw #1]");
|
|
}
|
|
|
|
void Disassembler::VisitSVE32BitGatherLoadWords_ScalarPlus32BitScaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr,
|
|
"{'Zt.s}, 'Pgl/z, ['Xns, 'Zm.s, '?22:suxtw #2]");
|
|
}
|
|
|
|
void Disassembler::VisitSVE32BitGatherLoad_ScalarPlus32BitUnscaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr,
|
|
"{'Zt.s}, 'Pgl/z, ['Xns, 'Zm.s, '?22:suxtw]");
|
|
}
|
|
|
|
void Disassembler::VisitSVE32BitGatherLoad_VectorPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.s}, 'Pgl/z, ['Zn.s]";
|
|
const char *form_imm = "{'Zt.s}, 'Pgl/z, ['Zn.s, #'u2016]";
|
|
const char *form_imm_h = "{'Zt.s}, 'Pgl/z, ['Zn.s, #'u2016*2]";
|
|
const char *form_imm_w = "{'Zt.s}, 'Pgl/z, ['Zn.s, #'u2016*4]";
|
|
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
switch (form_hash_) {
|
|
case "ld1h_z_p_ai_s"_h:
|
|
case "ld1sh_z_p_ai_s"_h:
|
|
case "ldff1h_z_p_ai_s"_h:
|
|
case "ldff1sh_z_p_ai_s"_h:
|
|
form_imm = form_imm_h;
|
|
break;
|
|
case "ld1w_z_p_ai_s"_h:
|
|
case "ldff1w_z_p_ai_s"_h:
|
|
form_imm = form_imm_w;
|
|
break;
|
|
}
|
|
if (instr->ExtractBits(20, 16) != 0) form = form_imm;
|
|
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVE32BitGatherPrefetch_ScalarPlus32BitScaledOffsets(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "'prefSVEOp, 'Pgl, ['Xns, 'Zm.s, '?22:suxtw";
|
|
const char *suffix = NULL;
|
|
|
|
switch (
|
|
instr->Mask(SVE32BitGatherPrefetch_ScalarPlus32BitScaledOffsetsMask)) {
|
|
case PRFB_i_p_bz_s_x32_scaled:
|
|
mnemonic = "prfb";
|
|
suffix = "]";
|
|
break;
|
|
case PRFD_i_p_bz_s_x32_scaled:
|
|
mnemonic = "prfd";
|
|
suffix = " #3]";
|
|
break;
|
|
case PRFH_i_p_bz_s_x32_scaled:
|
|
mnemonic = "prfh";
|
|
suffix = " #1]";
|
|
break;
|
|
case PRFW_i_p_bz_s_x32_scaled:
|
|
mnemonic = "prfw";
|
|
suffix = " #2]";
|
|
break;
|
|
default:
|
|
form = "(SVE32BitGatherPrefetch_ScalarPlus32BitScaledOffsets)";
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVE32BitGatherPrefetch_VectorPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = (instr->ExtractBits(20, 16) != 0)
|
|
? "'prefSVEOp, 'Pgl, ['Zn.s, #'u2016]"
|
|
: "'prefSVEOp, 'Pgl, ['Zn.s]";
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVE32BitScatterStore_ScalarPlus32BitScaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr,
|
|
"{'Zt.s}, 'Pgl, ['Xns, 'Zm.s, '?14:suxtw #'u2423]");
|
|
}
|
|
|
|
void Disassembler::VisitSVE32BitScatterStore_ScalarPlus32BitUnscaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "{'Zt.s}, 'Pgl, ['Xns, 'Zm.s, '?14:suxtw]");
|
|
}
|
|
|
|
void Disassembler::VisitSVE32BitScatterStore_VectorPlusImm(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "{'Zt.s}, 'Pgl, ['Zn.s";
|
|
const char *suffix = NULL;
|
|
|
|
bool is_zero = instr->ExtractBits(20, 16) == 0;
|
|
|
|
switch (instr->Mask(SVE32BitScatterStore_VectorPlusImmMask)) {
|
|
case ST1B_z_p_ai_s:
|
|
mnemonic = "st1b";
|
|
suffix = is_zero ? "]" : ", #'u2016]";
|
|
break;
|
|
case ST1H_z_p_ai_s:
|
|
mnemonic = "st1h";
|
|
suffix = is_zero ? "]" : ", #'u2016*2]";
|
|
break;
|
|
case ST1W_z_p_ai_s:
|
|
mnemonic = "st1w";
|
|
suffix = is_zero ? "]" : ", #'u2016*4]";
|
|
break;
|
|
default:
|
|
form = "(SVE32BitScatterStore_VectorPlusImm)";
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVE64BitGatherLoad_ScalarPlus32BitUnpackedScaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr,
|
|
"{'Zt.d}, 'Pgl/z, ['Xns, 'Zm.d, '?22:suxtw "
|
|
"#'u2423]");
|
|
}
|
|
|
|
void Disassembler::VisitSVE64BitGatherLoad_ScalarPlus64BitScaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr,
|
|
"{'Zt.d}, 'Pgl/z, ['Xns, 'Zm.d, lsl #'u2423]");
|
|
}
|
|
|
|
void Disassembler::VisitSVE64BitGatherLoad_ScalarPlus64BitUnscaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "{'Zt.d}, 'Pgl/z, ['Xns, 'Zm.d]");
|
|
}
|
|
|
|
void Disassembler::
|
|
VisitSVE64BitGatherLoad_ScalarPlusUnpacked32BitUnscaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr,
|
|
"{'Zt.d}, 'Pgl/z, ['Xns, 'Zm.d, '?22:suxtw]");
|
|
}
|
|
|
|
void Disassembler::VisitSVE64BitGatherLoad_VectorPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.d}, 'Pgl/z, ['Zn.d]";
|
|
const char *form_imm[4] = {"{'Zt.d}, 'Pgl/z, ['Zn.d, #'u2016]",
|
|
"{'Zt.d}, 'Pgl/z, ['Zn.d, #'u2016*2]",
|
|
"{'Zt.d}, 'Pgl/z, ['Zn.d, #'u2016*4]",
|
|
"{'Zt.d}, 'Pgl/z, ['Zn.d, #'u2016*8]"};
|
|
|
|
if (instr->ExtractBits(20, 16) != 0) {
|
|
unsigned msz = instr->ExtractBits(24, 23);
|
|
bool sign_extend = instr->ExtractBit(14) == 0;
|
|
if ((msz == kDRegSizeInBytesLog2) && sign_extend) {
|
|
form = "(SVE64BitGatherLoad_VectorPlusImm)";
|
|
} else {
|
|
VIXL_ASSERT(msz < ArrayLength(form_imm));
|
|
form = form_imm[msz];
|
|
}
|
|
}
|
|
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVE64BitGatherPrefetch_ScalarPlus64BitScaledOffsets(
|
|
const Instruction *instr) {
|
|
const char *form = "'prefSVEOp, 'Pgl, ['Xns, 'Zm.d";
|
|
const char *suffix = "]";
|
|
|
|
switch (form_hash_) {
|
|
case "prfh_i_p_bz_d_64_scaled"_h:
|
|
suffix = ", lsl #1]";
|
|
break;
|
|
case "prfs_i_p_bz_d_64_scaled"_h:
|
|
suffix = ", lsl #2]";
|
|
break;
|
|
case "prfd_i_p_bz_d_64_scaled"_h:
|
|
suffix = ", lsl #3]";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::
|
|
VisitSVE64BitGatherPrefetch_ScalarPlusUnpacked32BitScaledOffsets(
|
|
const Instruction *instr) {
|
|
const char *form = "'prefSVEOp, 'Pgl, ['Xns, 'Zm.d, '?22:suxtw ";
|
|
const char *suffix = "]";
|
|
|
|
switch (form_hash_) {
|
|
case "prfh_i_p_bz_d_x32_scaled"_h:
|
|
suffix = "#1]";
|
|
break;
|
|
case "prfs_i_p_bz_d_x32_scaled"_h:
|
|
suffix = "#2]";
|
|
break;
|
|
case "prfd_i_p_bz_d_x32_scaled"_h:
|
|
suffix = "#3]";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVE64BitGatherPrefetch_VectorPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = (instr->ExtractBits(20, 16) != 0)
|
|
? "'prefSVEOp, 'Pgl, ['Zn.d, #'u2016]"
|
|
: "'prefSVEOp, 'Pgl, ['Zn.d]";
|
|
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVE64BitScatterStore_ScalarPlus64BitScaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "{'Zt.d}, 'Pgl, ['Xns, 'Zm.d, lsl #'u2423]");
|
|
}
|
|
|
|
void Disassembler::VisitSVE64BitScatterStore_ScalarPlus64BitUnscaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "{'Zt.d}, 'Pgl, ['Xns, 'Zm.d]");
|
|
}
|
|
|
|
void Disassembler::
|
|
VisitSVE64BitScatterStore_ScalarPlusUnpacked32BitScaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr,
|
|
"{'Zt.d}, 'Pgl, ['Xns, 'Zm.d, '?14:suxtw #'u2423]");
|
|
}
|
|
|
|
void Disassembler::
|
|
VisitSVE64BitScatterStore_ScalarPlusUnpacked32BitUnscaledOffsets(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "{'Zt.d}, 'Pgl, ['Xns, 'Zm.d, '?14:suxtw]");
|
|
}
|
|
|
|
void Disassembler::VisitSVE64BitScatterStore_VectorPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.d}, 'Pgl, ['Zn.d";
|
|
const char *suffix = "]";
|
|
|
|
if (instr->ExtractBits(20, 16) != 0) {
|
|
switch (form_hash_) {
|
|
case "st1b_z_p_ai_d"_h:
|
|
suffix = ", #'u2016]";
|
|
break;
|
|
case "st1h_z_p_ai_d"_h:
|
|
suffix = ", #'u2016*2]";
|
|
break;
|
|
case "st1w_z_p_ai_d"_h:
|
|
suffix = ", #'u2016*4]";
|
|
break;
|
|
case "st1d_z_p_ai_d"_h:
|
|
suffix = ", #'u2016*8]";
|
|
break;
|
|
}
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEBitwiseLogicalWithImm_Unpredicated(
|
|
const Instruction *instr) {
|
|
if (instr->GetSVEImmLogical() == 0) {
|
|
// The immediate encoded in the instruction is not in the expected format.
|
|
Format(instr, "unallocated", "(SVEBitwiseImm)");
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'tl, 'Zd.'tl, 'ITriSvel");
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEBitwiseLogical_Predicated(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zd.'t, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEBitwiseShiftByImm_Predicated(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Zd.'tszp, 'Pgl/m, 'Zd.'tszp, ";
|
|
const char *suffix = NULL;
|
|
unsigned tsize = (instr->ExtractBits(23, 22) << 2) | instr->ExtractBits(9, 8);
|
|
|
|
if (tsize == 0) {
|
|
mnemonic = "unimplemented";
|
|
form = "(SVEBitwiseShiftByImm_Predicated)";
|
|
} else {
|
|
switch (form_hash_) {
|
|
case "lsl_z_p_zi"_h:
|
|
case "sqshl_z_p_zi"_h:
|
|
case "sqshlu_z_p_zi"_h:
|
|
case "uqshl_z_p_zi"_h:
|
|
suffix = "'ITriSvep";
|
|
break;
|
|
case "asrd_z_p_zi"_h:
|
|
case "asr_z_p_zi"_h:
|
|
case "lsr_z_p_zi"_h:
|
|
case "srshr_z_p_zi"_h:
|
|
case "urshr_z_p_zi"_h:
|
|
suffix = "'ITriSveq";
|
|
break;
|
|
default:
|
|
mnemonic = "unimplemented";
|
|
form = "(SVEBitwiseShiftByImm_Predicated)";
|
|
break;
|
|
}
|
|
}
|
|
Format(instr, mnemonic, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEBitwiseShiftByVector_Predicated(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zd.'t, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEBitwiseShiftByWideElements_Predicated(
|
|
const Instruction *instr) {
|
|
if (instr->GetSVESize() == kDRegSizeInBytesLog2) {
|
|
Format(instr, "unallocated", "(SVEBitwiseShiftByWideElements_Predicated)");
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zd.'t, 'Zn.d");
|
|
}
|
|
}
|
|
|
|
static bool SVEMoveMaskPreferred(uint64_t value, int lane_bytes_log2) {
|
|
VIXL_ASSERT(IsUintN(8 << lane_bytes_log2, value));
|
|
|
|
// Duplicate lane-sized value across double word.
|
|
switch (lane_bytes_log2) {
|
|
case 0:
|
|
value *= 0x0101010101010101;
|
|
break;
|
|
case 1:
|
|
value *= 0x0001000100010001;
|
|
break;
|
|
case 2:
|
|
value *= 0x0000000100000001;
|
|
break;
|
|
case 3: // Nothing to do
|
|
break;
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
|
|
if ((value & 0xff) == 0) {
|
|
// Check for 16-bit patterns. Set least-significant 16 bits, to make tests
|
|
// easier; we already checked least-significant byte is zero above.
|
|
uint64_t generic_value = value | 0xffff;
|
|
|
|
// Check 0x00000000_0000pq00 or 0xffffffff_ffffpq00.
|
|
if ((generic_value == 0xffff) || (generic_value == UINT64_MAX)) {
|
|
return false;
|
|
}
|
|
|
|
// Check 0x0000pq00_0000pq00 or 0xffffpq00_ffffpq00.
|
|
uint64_t rotvalue = RotateRight(value, 32, 64);
|
|
if (value == rotvalue) {
|
|
generic_value &= 0xffffffff;
|
|
if ((generic_value == 0xffff) || (generic_value == UINT32_MAX)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check 0xpq00pq00_pq00pq00.
|
|
rotvalue = RotateRight(value, 16, 64);
|
|
if (value == rotvalue) {
|
|
return false;
|
|
}
|
|
} else {
|
|
// Check for 8-bit patterns. Set least-significant byte, to make tests
|
|
// easier.
|
|
uint64_t generic_value = value | 0xff;
|
|
|
|
// Check 0x00000000_000000pq or 0xffffffff_ffffffpq.
|
|
if ((generic_value == 0xff) || (generic_value == UINT64_MAX)) {
|
|
return false;
|
|
}
|
|
|
|
// Check 0x000000pq_000000pq or 0xffffffpq_ffffffpq.
|
|
uint64_t rotvalue = RotateRight(value, 32, 64);
|
|
if (value == rotvalue) {
|
|
generic_value &= 0xffffffff;
|
|
if ((generic_value == 0xff) || (generic_value == UINT32_MAX)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check 0x00pq00pq_00pq00pq or 0xffpqffpq_ffpqffpq.
|
|
rotvalue = RotateRight(value, 16, 64);
|
|
if (value == rotvalue) {
|
|
generic_value &= 0xffff;
|
|
if ((generic_value == 0xff) || (generic_value == UINT16_MAX)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check 0xpqpqpqpq_pqpqpqpq.
|
|
rotvalue = RotateRight(value, 8, 64);
|
|
if (value == rotvalue) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Disassembler::VisitSVEBroadcastBitmaskImm(const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVEBroadcastBitmaskImm)";
|
|
|
|
switch (instr->Mask(SVEBroadcastBitmaskImmMask)) {
|
|
case DUPM_z_i: {
|
|
uint64_t imm = instr->GetSVEImmLogical();
|
|
if (imm != 0) {
|
|
int lane_size = instr->GetSVEBitwiseImmLaneSizeInBytesLog2();
|
|
mnemonic = SVEMoveMaskPreferred(imm, lane_size) ? "mov" : "dupm";
|
|
form = "'Zd.'tl, 'ITriSvel";
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEBroadcastFPImm_Unpredicated(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVEBroadcastFPImm_Unpredicated)";
|
|
|
|
if (instr->GetSVEVectorFormat() != kFormatVnB) {
|
|
switch (instr->Mask(SVEBroadcastFPImm_UnpredicatedMask)) {
|
|
case FDUP_z_i:
|
|
// The preferred disassembly for fdup is "fmov".
|
|
mnemonic = "fmov";
|
|
form = "'Zd.'t, 'IFPSve";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEBroadcastGeneralRegister(const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVEBroadcastGeneralRegister)";
|
|
|
|
switch (instr->Mask(SVEBroadcastGeneralRegisterMask)) {
|
|
case DUP_z_r:
|
|
// The preferred disassembly for dup is "mov".
|
|
mnemonic = "mov";
|
|
if (instr->GetSVESize() == kDRegSizeInBytesLog2) {
|
|
form = "'Zd.'t, 'Xns";
|
|
} else {
|
|
form = "'Zd.'t, 'Wns";
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEBroadcastIndexElement(const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVEBroadcastIndexElement)";
|
|
|
|
switch (instr->Mask(SVEBroadcastIndexElementMask)) {
|
|
case DUP_z_zi: {
|
|
// The tsz field must not be zero.
|
|
int tsz = instr->ExtractBits(20, 16);
|
|
if (tsz != 0) {
|
|
// The preferred disassembly for dup is "mov".
|
|
mnemonic = "mov";
|
|
int imm2 = instr->ExtractBits(23, 22);
|
|
if ((CountSetBits(imm2) + CountSetBits(tsz)) == 1) {
|
|
// If imm2:tsz has one set bit, the index is zero. This is
|
|
// disassembled as a mov from a b/h/s/d/q scalar register.
|
|
form = "'Zd.'ti, 'ti'u0905";
|
|
} else {
|
|
form = "'Zd.'ti, 'Zn.'ti['IVInsSVEIndex]";
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEBroadcastIntImm_Unpredicated(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVEBroadcastIntImm_Unpredicated)";
|
|
|
|
switch (instr->Mask(SVEBroadcastIntImm_UnpredicatedMask)) {
|
|
case DUP_z_i:
|
|
// The encoding of byte-sized lanes with lsl #8 is undefined.
|
|
if ((instr->GetSVEVectorFormat() == kFormatVnB) &&
|
|
(instr->ExtractBit(13) == 1))
|
|
break;
|
|
|
|
// The preferred disassembly for dup is "mov".
|
|
mnemonic = "mov";
|
|
form = (instr->ExtractBit(13) == 0) ? "'Zd.'t, #'s1205"
|
|
: "'Zd.'t, #'s1205, lsl #8";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVECompressActiveElements(const Instruction *instr) {
|
|
// The top bit of size is always set for compact, so 't can only be
|
|
// substituted with types S and D.
|
|
if (instr->ExtractBit(23) == 1) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl, 'Zn.'t");
|
|
} else {
|
|
VisitUnallocated(instr);
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEConditionallyBroadcastElementToVector(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl, 'Zd.'t, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEConditionallyExtractElementToGeneralRegister(
|
|
const Instruction *instr) {
|
|
const char *form = "'Wd, 'Pgl, 'Wd, 'Zn.'t";
|
|
|
|
if (instr->GetSVESize() == kDRegSizeInBytesLog2) {
|
|
form = "'Xd, p'u1210, 'Xd, 'Zn.'t";
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEConditionallyExtractElementToSIMDFPScalar(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'t'u0400, 'Pgl, 't'u0400, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEConditionallyTerminateScalars(
|
|
const Instruction *instr) {
|
|
const char *form = (instr->ExtractBit(22) == 0) ? "'Wn, 'Wm" : "'Xn, 'Xm";
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEConstructivePrefix_Unpredicated(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd, 'Zn");
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousFirstFaultLoad_ScalarPlusScalar(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.'tlss}, 'Pgl/z, ['Xns";
|
|
const char *suffix = "]";
|
|
|
|
if (instr->GetRm() != kZeroRegCode) {
|
|
switch (form_hash_) {
|
|
case "ldff1b_z_p_br_u8"_h:
|
|
case "ldff1b_z_p_br_u16"_h:
|
|
case "ldff1b_z_p_br_u32"_h:
|
|
case "ldff1b_z_p_br_u64"_h:
|
|
case "ldff1sb_z_p_br_s16"_h:
|
|
case "ldff1sb_z_p_br_s32"_h:
|
|
case "ldff1sb_z_p_br_s64"_h:
|
|
suffix = ", 'Xm]";
|
|
break;
|
|
case "ldff1h_z_p_br_u16"_h:
|
|
case "ldff1h_z_p_br_u32"_h:
|
|
case "ldff1h_z_p_br_u64"_h:
|
|
case "ldff1sh_z_p_br_s32"_h:
|
|
case "ldff1sh_z_p_br_s64"_h:
|
|
suffix = ", 'Xm, lsl #1]";
|
|
break;
|
|
case "ldff1w_z_p_br_u32"_h:
|
|
case "ldff1w_z_p_br_u64"_h:
|
|
case "ldff1sw_z_p_br_s64"_h:
|
|
suffix = ", 'Xm, lsl #2]";
|
|
break;
|
|
case "ldff1d_z_p_br_u64"_h:
|
|
suffix = ", 'Xm, lsl #3]";
|
|
break;
|
|
}
|
|
}
|
|
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousNonFaultLoad_ScalarPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.'tlss}, 'Pgl/z, ['Xns";
|
|
const char *suffix =
|
|
(instr->ExtractBits(19, 16) == 0) ? "]" : ", #'s1916, mul vl]";
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousNonTemporalLoad_ScalarPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.b}, 'Pgl/z, ['Xns";
|
|
const char *suffix =
|
|
(instr->ExtractBits(19, 16) == 0) ? "]" : ", #'s1916, mul vl]";
|
|
switch (form_hash_) {
|
|
case "ldnt1d_z_p_bi_contiguous"_h:
|
|
form = "{'Zt.d}, 'Pgl/z, ['Xns";
|
|
break;
|
|
case "ldnt1h_z_p_bi_contiguous"_h:
|
|
form = "{'Zt.h}, 'Pgl/z, ['Xns";
|
|
break;
|
|
case "ldnt1w_z_p_bi_contiguous"_h:
|
|
form = "{'Zt.s}, 'Pgl/z, ['Xns";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousNonTemporalLoad_ScalarPlusScalar(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.b}, 'Pgl/z, ['Xns, 'Rm]";
|
|
switch (form_hash_) {
|
|
case "ldnt1d_z_p_br_contiguous"_h:
|
|
form = "{'Zt.d}, 'Pgl/z, ['Xns, 'Rm, lsl #3]";
|
|
break;
|
|
case "ldnt1h_z_p_br_contiguous"_h:
|
|
form = "{'Zt.h}, 'Pgl/z, ['Xns, 'Rm, lsl #1]";
|
|
break;
|
|
case "ldnt1w_z_p_br_contiguous"_h:
|
|
form = "{'Zt.s}, 'Pgl/z, ['Xns, 'Rm, lsl #2]";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousNonTemporalStore_ScalarPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.b}, 'Pgl, ['Xns";
|
|
const char *suffix =
|
|
(instr->ExtractBits(19, 16) == 0) ? "]" : ", #'s1916, mul vl]";
|
|
|
|
switch (form_hash_) {
|
|
case "stnt1d_z_p_bi_contiguous"_h:
|
|
form = "{'Zt.d}, 'Pgl, ['Xns";
|
|
break;
|
|
case "stnt1h_z_p_bi_contiguous"_h:
|
|
form = "{'Zt.h}, 'Pgl, ['Xns";
|
|
break;
|
|
case "stnt1w_z_p_bi_contiguous"_h:
|
|
form = "{'Zt.s}, 'Pgl, ['Xns";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousNonTemporalStore_ScalarPlusScalar(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVEContiguousNonTemporalStore_ScalarPlusScalar)";
|
|
|
|
switch (instr->Mask(SVEContiguousNonTemporalStore_ScalarPlusScalarMask)) {
|
|
case STNT1B_z_p_br_contiguous:
|
|
mnemonic = "stnt1b";
|
|
form = "{'Zt.b}, 'Pgl, ['Xns, 'Rm]";
|
|
break;
|
|
case STNT1D_z_p_br_contiguous:
|
|
mnemonic = "stnt1d";
|
|
form = "{'Zt.d}, 'Pgl, ['Xns, 'Rm, lsl #3]";
|
|
break;
|
|
case STNT1H_z_p_br_contiguous:
|
|
mnemonic = "stnt1h";
|
|
form = "{'Zt.h}, 'Pgl, ['Xns, 'Rm, lsl #1]";
|
|
break;
|
|
case STNT1W_z_p_br_contiguous:
|
|
mnemonic = "stnt1w";
|
|
form = "{'Zt.s}, 'Pgl, ['Xns, 'Rm, lsl #2]";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousPrefetch_ScalarPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = (instr->ExtractBits(21, 16) != 0)
|
|
? "'prefSVEOp, 'Pgl, ['Xns, #'s2116, mul vl]"
|
|
: "'prefSVEOp, 'Pgl, ['Xns]";
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousPrefetch_ScalarPlusScalar(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVEContiguousPrefetch_ScalarPlusScalar)";
|
|
|
|
if (instr->GetRm() != kZeroRegCode) {
|
|
switch (instr->Mask(SVEContiguousPrefetch_ScalarPlusScalarMask)) {
|
|
case PRFB_i_p_br_s:
|
|
mnemonic = "prfb";
|
|
form = "'prefSVEOp, 'Pgl, ['Xns, 'Rm]";
|
|
break;
|
|
case PRFD_i_p_br_s:
|
|
mnemonic = "prfd";
|
|
form = "'prefSVEOp, 'Pgl, ['Xns, 'Rm, lsl #3]";
|
|
break;
|
|
case PRFH_i_p_br_s:
|
|
mnemonic = "prfh";
|
|
form = "'prefSVEOp, 'Pgl, ['Xns, 'Rm, lsl #1]";
|
|
break;
|
|
case PRFW_i_p_br_s:
|
|
mnemonic = "prfw";
|
|
form = "'prefSVEOp, 'Pgl, ['Xns, 'Rm, lsl #2]";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousStore_ScalarPlusImm(
|
|
const Instruction *instr) {
|
|
// The 'size' field isn't in the usual place here.
|
|
const char *form = "{'Zt.'tls}, 'Pgl, ['Xns, #'s1916, mul vl]";
|
|
if (instr->ExtractBits(19, 16) == 0) {
|
|
form = "{'Zt.'tls}, 'Pgl, ['Xns]";
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousStore_ScalarPlusScalar(
|
|
const Instruction *instr) {
|
|
// The 'size' field isn't in the usual place here.
|
|
FormatWithDecodedMnemonic(instr, "{'Zt.'tls}, 'Pgl, ['Xns, 'Xm'NSveS]");
|
|
}
|
|
|
|
void Disassembler::VisitSVECopyFPImm_Predicated(const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVECopyFPImm_Predicated)";
|
|
|
|
if (instr->GetSVEVectorFormat() != kFormatVnB) {
|
|
switch (instr->Mask(SVECopyFPImm_PredicatedMask)) {
|
|
case FCPY_z_p_i:
|
|
// The preferred disassembly for fcpy is "fmov".
|
|
mnemonic = "fmov";
|
|
form = "'Zd.'t, 'Pm/m, 'IFPSve";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVECopyGeneralRegisterToVector_Predicated(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVECopyGeneralRegisterToVector_Predicated)";
|
|
|
|
switch (instr->Mask(SVECopyGeneralRegisterToVector_PredicatedMask)) {
|
|
case CPY_z_p_r:
|
|
// The preferred disassembly for cpy is "mov".
|
|
mnemonic = "mov";
|
|
form = "'Zd.'t, 'Pgl/m, 'Wns";
|
|
if (instr->GetSVESize() == kXRegSizeInBytesLog2) {
|
|
form = "'Zd.'t, 'Pgl/m, 'Xns";
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVECopyIntImm_Predicated(const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVECopyIntImm_Predicated)";
|
|
const char *suffix = NULL;
|
|
|
|
switch (instr->Mask(SVECopyIntImm_PredicatedMask)) {
|
|
case CPY_z_p_i: {
|
|
// The preferred disassembly for cpy is "mov".
|
|
mnemonic = "mov";
|
|
form = "'Zd.'t, 'Pm/'?14:mz, #'s1205";
|
|
if (instr->ExtractBit(13) != 0) suffix = ", lsl #8";
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVECopySIMDFPScalarRegisterToVector_Predicated(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVECopySIMDFPScalarRegisterToVector_Predicated)";
|
|
|
|
switch (instr->Mask(SVECopySIMDFPScalarRegisterToVector_PredicatedMask)) {
|
|
case CPY_z_p_v:
|
|
// The preferred disassembly for cpy is "mov".
|
|
mnemonic = "mov";
|
|
form = "'Zd.'t, 'Pgl/m, 'Vnv";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEExtractElementToGeneralRegister(
|
|
const Instruction *instr) {
|
|
const char *form = "'Wd, 'Pgl, 'Zn.'t";
|
|
if (instr->GetSVESize() == kDRegSizeInBytesLog2) {
|
|
form = "'Xd, p'u1210, 'Zn.'t";
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEExtractElementToSIMDFPScalarRegister(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'t'u0400, 'Pgl, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEFFRInitialise(const Instruction *instr) {
|
|
DisassembleNoArgs(instr);
|
|
}
|
|
|
|
void Disassembler::VisitSVEFFRWriteFromPredicate(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pn.b");
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPArithmeticWithImm_Predicated(
|
|
const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Pgl/m, 'Zd.'t, #";
|
|
const char *suffix00 = "0.0";
|
|
const char *suffix05 = "0.5";
|
|
const char *suffix10 = "1.0";
|
|
const char *suffix20 = "2.0";
|
|
int i1 = instr->ExtractBit(5);
|
|
const char *suffix = i1 ? suffix10 : suffix00;
|
|
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
return;
|
|
}
|
|
|
|
switch (form_hash_) {
|
|
case "fadd_z_p_zs"_h:
|
|
case "fsubr_z_p_zs"_h:
|
|
case "fsub_z_p_zs"_h:
|
|
suffix = i1 ? suffix10 : suffix05;
|
|
break;
|
|
case "fmul_z_p_zs"_h:
|
|
suffix = i1 ? suffix20 : suffix05;
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPArithmetic_Predicated(const Instruction *instr) {
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zd.'t, 'Zn.'t");
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPConvertPrecision(const Instruction *instr) {
|
|
const char *form = NULL;
|
|
|
|
switch (form_hash_) {
|
|
case "fcvt_z_p_z_d2h"_h:
|
|
form = "'Zd.h, 'Pgl/m, 'Zn.d";
|
|
break;
|
|
case "fcvt_z_p_z_d2s"_h:
|
|
form = "'Zd.s, 'Pgl/m, 'Zn.d";
|
|
break;
|
|
case "fcvt_z_p_z_h2d"_h:
|
|
form = "'Zd.d, 'Pgl/m, 'Zn.h";
|
|
break;
|
|
case "fcvt_z_p_z_h2s"_h:
|
|
form = "'Zd.s, 'Pgl/m, 'Zn.h";
|
|
break;
|
|
case "fcvt_z_p_z_s2d"_h:
|
|
form = "'Zd.d, 'Pgl/m, 'Zn.s";
|
|
break;
|
|
case "fcvt_z_p_z_s2h"_h:
|
|
form = "'Zd.h, 'Pgl/m, 'Zn.s";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPConvertToInt(const Instruction *instr) {
|
|
const char *form = NULL;
|
|
|
|
switch (form_hash_) {
|
|
case "fcvtzs_z_p_z_d2w"_h:
|
|
case "fcvtzu_z_p_z_d2w"_h:
|
|
form = "'Zd.s, 'Pgl/m, 'Zn.d";
|
|
break;
|
|
case "fcvtzs_z_p_z_d2x"_h:
|
|
case "fcvtzu_z_p_z_d2x"_h:
|
|
form = "'Zd.d, 'Pgl/m, 'Zn.d";
|
|
break;
|
|
case "fcvtzs_z_p_z_fp162h"_h:
|
|
case "fcvtzu_z_p_z_fp162h"_h:
|
|
form = "'Zd.h, 'Pgl/m, 'Zn.h";
|
|
break;
|
|
case "fcvtzs_z_p_z_fp162w"_h:
|
|
case "fcvtzu_z_p_z_fp162w"_h:
|
|
form = "'Zd.s, 'Pgl/m, 'Zn.h";
|
|
break;
|
|
case "fcvtzs_z_p_z_fp162x"_h:
|
|
case "fcvtzu_z_p_z_fp162x"_h:
|
|
form = "'Zd.d, 'Pgl/m, 'Zn.h";
|
|
break;
|
|
case "fcvtzs_z_p_z_s2w"_h:
|
|
case "fcvtzu_z_p_z_s2w"_h:
|
|
form = "'Zd.s, 'Pgl/m, 'Zn.s";
|
|
break;
|
|
case "fcvtzs_z_p_z_s2x"_h:
|
|
case "fcvtzu_z_p_z_s2x"_h:
|
|
form = "'Zd.d, 'Pgl/m, 'Zn.s";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPExponentialAccelerator(const Instruction *instr) {
|
|
unsigned size = instr->GetSVESize();
|
|
if ((size == kHRegSizeInBytesLog2) || (size == kSRegSizeInBytesLog2) ||
|
|
(size == kDRegSizeInBytesLog2)) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Zn.'t");
|
|
} else {
|
|
VisitUnallocated(instr);
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPRoundToIntegralValue(const Instruction *instr) {
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zn.'t");
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPTrigMulAddCoefficient(const Instruction *instr) {
|
|
unsigned size = instr->GetSVESize();
|
|
if ((size == kHRegSizeInBytesLog2) || (size == kSRegSizeInBytesLog2) ||
|
|
(size == kDRegSizeInBytesLog2)) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Zd.'t, 'Zn.'t, #'u1816");
|
|
} else {
|
|
VisitUnallocated(instr);
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPTrigSelectCoefficient(const Instruction *instr) {
|
|
unsigned size = instr->GetSVESize();
|
|
if ((size == kHRegSizeInBytesLog2) || (size == kSRegSizeInBytesLog2) ||
|
|
(size == kDRegSizeInBytesLog2)) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Zn.'t, 'Zm.'t");
|
|
} else {
|
|
VisitUnallocated(instr);
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPUnaryOp(const Instruction *instr) {
|
|
if (instr->GetSVESize() == kBRegSizeInBytesLog2) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zn.'t");
|
|
}
|
|
}
|
|
|
|
static const char *IncDecFormHelper(const Instruction *instr,
|
|
const char *reg_pat_mul_form,
|
|
const char *reg_pat_form,
|
|
const char *reg_form) {
|
|
if (instr->ExtractBits(19, 16) == 0) {
|
|
if (instr->ExtractBits(9, 5) == SVE_ALL) {
|
|
// Use the register only form if the multiplier is one (encoded as zero)
|
|
// and the pattern is SVE_ALL.
|
|
return reg_form;
|
|
}
|
|
// Use the register and pattern form if the multiplier is one.
|
|
return reg_pat_form;
|
|
}
|
|
return reg_pat_mul_form;
|
|
}
|
|
|
|
void Disassembler::VisitSVEIncDecRegisterByElementCount(
|
|
const Instruction *instr) {
|
|
const char *form =
|
|
IncDecFormHelper(instr, "'Xd, 'Ipc, mul #'u1916+1", "'Xd, 'Ipc", "'Xd");
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEIncDecVectorByElementCount(
|
|
const Instruction *instr) {
|
|
const char *form = IncDecFormHelper(instr,
|
|
"'Zd.'t, 'Ipc, mul #'u1916+1",
|
|
"'Zd.'t, 'Ipc",
|
|
"'Zd.'t");
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEInsertGeneralRegister(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Wn";
|
|
if (instr->GetSVESize() == kDRegSizeInBytesLog2) {
|
|
form = "'Zd.'t, 'Xn";
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEInsertSIMDFPScalarRegister(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Vnv");
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntAddSubtractImm_Unpredicated(
|
|
const Instruction *instr) {
|
|
const char *form = (instr->ExtractBit(13) == 0)
|
|
? "'Zd.'t, 'Zd.'t, #'u1205"
|
|
: "'Zd.'t, 'Zd.'t, #'u1205, lsl #8";
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntAddSubtractVectors_Predicated(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zd.'t, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntCompareScalarCountAndLimit(
|
|
const Instruction *instr) {
|
|
const char *form =
|
|
(instr->ExtractBit(12) == 0) ? "'Pd.'t, 'Wn, 'Wm" : "'Pd.'t, 'Xn, 'Xm";
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntConvertToFP(const Instruction *instr) {
|
|
const char *form = NULL;
|
|
switch (form_hash_) {
|
|
case "scvtf_z_p_z_h2fp16"_h:
|
|
case "ucvtf_z_p_z_h2fp16"_h:
|
|
form = "'Zd.h, 'Pgl/m, 'Zn.h";
|
|
break;
|
|
case "scvtf_z_p_z_w2d"_h:
|
|
case "ucvtf_z_p_z_w2d"_h:
|
|
form = "'Zd.d, 'Pgl/m, 'Zn.s";
|
|
break;
|
|
case "scvtf_z_p_z_w2fp16"_h:
|
|
case "ucvtf_z_p_z_w2fp16"_h:
|
|
form = "'Zd.h, 'Pgl/m, 'Zn.s";
|
|
break;
|
|
case "scvtf_z_p_z_w2s"_h:
|
|
case "ucvtf_z_p_z_w2s"_h:
|
|
form = "'Zd.s, 'Pgl/m, 'Zn.s";
|
|
break;
|
|
case "scvtf_z_p_z_x2d"_h:
|
|
case "ucvtf_z_p_z_x2d"_h:
|
|
form = "'Zd.d, 'Pgl/m, 'Zn.d";
|
|
break;
|
|
case "scvtf_z_p_z_x2fp16"_h:
|
|
case "ucvtf_z_p_z_x2fp16"_h:
|
|
form = "'Zd.h, 'Pgl/m, 'Zn.d";
|
|
break;
|
|
case "scvtf_z_p_z_x2s"_h:
|
|
case "ucvtf_z_p_z_x2s"_h:
|
|
form = "'Zd.s, 'Pgl/m, 'Zn.d";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntDivideVectors_Predicated(
|
|
const Instruction *instr) {
|
|
unsigned size = instr->GetSVESize();
|
|
if ((size == kSRegSizeInBytesLog2) || (size == kDRegSizeInBytesLog2)) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zd.'t, 'Zn.'t");
|
|
} else {
|
|
VisitUnallocated(instr);
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntMinMaxDifference_Predicated(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zd.'t, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntMinMaxImm_Unpredicated(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Zd.'t, #";
|
|
const char *suffix = "'u1205";
|
|
|
|
switch (form_hash_) {
|
|
case "smax_z_zi"_h:
|
|
case "smin_z_zi"_h:
|
|
suffix = "'s1205";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntMulImm_Unpredicated(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Zd.'t, #'s1205");
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntMulVectors_Predicated(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zd.'t, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVELoadAndBroadcastElement(const Instruction *instr) {
|
|
const char *form = "(SVELoadAndBroadcastElement)";
|
|
const char *suffix_b = ", #'u2116]";
|
|
const char *suffix_h = ", #'u2116*2]";
|
|
const char *suffix_w = ", #'u2116*4]";
|
|
const char *suffix_d = ", #'u2116*8]";
|
|
const char *suffix = NULL;
|
|
|
|
switch (form_hash_) {
|
|
case "ld1rb_z_p_bi_u8"_h:
|
|
form = "{'Zt.b}, 'Pgl/z, ['Xns";
|
|
suffix = suffix_b;
|
|
break;
|
|
case "ld1rb_z_p_bi_u16"_h:
|
|
case "ld1rsb_z_p_bi_s16"_h:
|
|
form = "{'Zt.h}, 'Pgl/z, ['Xns";
|
|
suffix = suffix_b;
|
|
break;
|
|
case "ld1rb_z_p_bi_u32"_h:
|
|
case "ld1rsb_z_p_bi_s32"_h:
|
|
form = "{'Zt.s}, 'Pgl/z, ['Xns";
|
|
suffix = suffix_b;
|
|
break;
|
|
case "ld1rb_z_p_bi_u64"_h:
|
|
case "ld1rsb_z_p_bi_s64"_h:
|
|
form = "{'Zt.d}, 'Pgl/z, ['Xns";
|
|
suffix = suffix_b;
|
|
break;
|
|
case "ld1rh_z_p_bi_u16"_h:
|
|
form = "{'Zt.h}, 'Pgl/z, ['Xns";
|
|
suffix = suffix_h;
|
|
break;
|
|
case "ld1rh_z_p_bi_u32"_h:
|
|
case "ld1rsh_z_p_bi_s32"_h:
|
|
form = "{'Zt.s}, 'Pgl/z, ['Xns";
|
|
suffix = suffix_h;
|
|
break;
|
|
case "ld1rh_z_p_bi_u64"_h:
|
|
case "ld1rsh_z_p_bi_s64"_h:
|
|
form = "{'Zt.d}, 'Pgl/z, ['Xns";
|
|
suffix = suffix_h;
|
|
break;
|
|
case "ld1rw_z_p_bi_u32"_h:
|
|
form = "{'Zt.s}, 'Pgl/z, ['Xns";
|
|
suffix = suffix_w;
|
|
break;
|
|
case "ld1rsw_z_p_bi_s64"_h:
|
|
case "ld1rw_z_p_bi_u64"_h:
|
|
form = "{'Zt.d}, 'Pgl/z, ['Xns";
|
|
suffix = suffix_w;
|
|
break;
|
|
case "ld1rd_z_p_bi_u64"_h:
|
|
form = "{'Zt.d}, 'Pgl/z, ['Xns";
|
|
suffix = suffix_d;
|
|
break;
|
|
}
|
|
|
|
// Hide curly brackets if immediate is zero.
|
|
if (instr->ExtractBits(21, 16) == 0) {
|
|
suffix = "]";
|
|
}
|
|
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVELoadAndBroadcastQOWord_ScalarPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.'tmsz}, 'Pgl/z, ['Xns";
|
|
const char *suffix = ", #'s1916*16]";
|
|
|
|
switch (form_hash_) {
|
|
case "ld1rob_z_p_bi_u8"_h:
|
|
case "ld1rod_z_p_bi_u64"_h:
|
|
case "ld1roh_z_p_bi_u16"_h:
|
|
case "ld1row_z_p_bi_u32"_h:
|
|
suffix = ", #'s1916*32]";
|
|
break;
|
|
}
|
|
if (instr->ExtractBits(19, 16) == 0) suffix = "]";
|
|
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVELoadAndBroadcastQOWord_ScalarPlusScalar(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.'tmsz}, 'Pgl/z, ['Xns, ";
|
|
const char *suffix = "'Rm, lsl #'u2423]";
|
|
|
|
switch (form_hash_) {
|
|
case "ld1rqb_z_p_br_contiguous"_h:
|
|
case "ld1rob_z_p_br_contiguous"_h:
|
|
suffix = "'Rm]";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVELoadMultipleStructures_ScalarPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.'tmsz, 'Zt2.'tmsz}";
|
|
const char *form_3 = "{'Zt.'tmsz, 'Zt2.'tmsz, 'Zt3.'tmsz}";
|
|
const char *form_4 = "{'Zt.'tmsz, 'Zt2.'tmsz, 'Zt3.'tmsz, 'Zt4.'tmsz}";
|
|
const char *suffix = ", 'Pgl/z, ['Xns'ISveSvl]";
|
|
|
|
switch (form_hash_) {
|
|
case "ld3b_z_p_bi_contiguous"_h:
|
|
case "ld3d_z_p_bi_contiguous"_h:
|
|
case "ld3h_z_p_bi_contiguous"_h:
|
|
case "ld3w_z_p_bi_contiguous"_h:
|
|
form = form_3;
|
|
break;
|
|
case "ld4b_z_p_bi_contiguous"_h:
|
|
case "ld4d_z_p_bi_contiguous"_h:
|
|
case "ld4h_z_p_bi_contiguous"_h:
|
|
case "ld4w_z_p_bi_contiguous"_h:
|
|
form = form_4;
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVELoadMultipleStructures_ScalarPlusScalar(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.'tmsz, 'Zt2.'tmsz}";
|
|
const char *form_3 = "{'Zt.'tmsz, 'Zt2.'tmsz, 'Zt3.'tmsz}";
|
|
const char *form_4 = "{'Zt.'tmsz, 'Zt2.'tmsz, 'Zt3.'tmsz, 'Zt4.'tmsz}";
|
|
const char *suffix = ", 'Pgl/z, ['Xns, 'Xm'NSveS]";
|
|
|
|
switch (form_hash_) {
|
|
case "ld3b_z_p_br_contiguous"_h:
|
|
case "ld3d_z_p_br_contiguous"_h:
|
|
case "ld3h_z_p_br_contiguous"_h:
|
|
case "ld3w_z_p_br_contiguous"_h:
|
|
form = form_3;
|
|
break;
|
|
case "ld4b_z_p_br_contiguous"_h:
|
|
case "ld4d_z_p_br_contiguous"_h:
|
|
case "ld4h_z_p_br_contiguous"_h:
|
|
case "ld4w_z_p_br_contiguous"_h:
|
|
form = form_4;
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVELoadPredicateRegister(const Instruction *instr) {
|
|
const char *form = "'Pd, ['Xns, #'s2116:1210, mul vl]";
|
|
if (instr->Mask(0x003f1c00) == 0) {
|
|
form = "'Pd, ['Xns]";
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVELoadVectorRegister(const Instruction *instr) {
|
|
const char *form = "'Zt, ['Xns, #'s2116:1210, mul vl]";
|
|
if (instr->Mask(0x003f1c00) == 0) {
|
|
form = "'Zd, ['Xns]";
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEPartitionBreakCondition(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.b, p'u1310/'?04:mz, 'Pn.b");
|
|
}
|
|
|
|
void Disassembler::VisitSVEPermutePredicateElements(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.'t, 'Pn.'t, 'Pm.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEPredicateFirstActive(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.b, 'Pn, 'Pd.b");
|
|
}
|
|
|
|
void Disassembler::VisitSVEPredicateReadFromFFR_Unpredicated(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.b");
|
|
}
|
|
|
|
void Disassembler::VisitSVEPredicateTest(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "p'u1310, 'Pn.b");
|
|
}
|
|
|
|
void Disassembler::VisitSVEPredicateZero(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.b");
|
|
}
|
|
|
|
void Disassembler::VisitSVEPropagateBreakToNextPartition(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.b, p'u1310/z, 'Pn.b, 'Pd.b");
|
|
}
|
|
|
|
void Disassembler::VisitSVEReversePredicateElements(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.'t, 'Pn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEReverseVectorElements(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEReverseWithinElements(const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "'Zd.'t, 'Pgl/m, 'Zn.'t";
|
|
|
|
unsigned size = instr->GetSVESize();
|
|
switch (instr->Mask(SVEReverseWithinElementsMask)) {
|
|
case RBIT_z_p_z:
|
|
mnemonic = "rbit";
|
|
break;
|
|
case REVB_z_z:
|
|
if ((size == kHRegSizeInBytesLog2) || (size == kSRegSizeInBytesLog2) ||
|
|
(size == kDRegSizeInBytesLog2)) {
|
|
mnemonic = "revb";
|
|
} else {
|
|
form = "(SVEReverseWithinElements)";
|
|
}
|
|
break;
|
|
case REVH_z_z:
|
|
if ((size == kSRegSizeInBytesLog2) || (size == kDRegSizeInBytesLog2)) {
|
|
mnemonic = "revh";
|
|
} else {
|
|
form = "(SVEReverseWithinElements)";
|
|
}
|
|
break;
|
|
case REVW_z_z:
|
|
if (size == kDRegSizeInBytesLog2) {
|
|
mnemonic = "revw";
|
|
} else {
|
|
form = "(SVEReverseWithinElements)";
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVESaturatingIncDecRegisterByElementCount(
|
|
const Instruction *instr) {
|
|
const char *form = IncDecFormHelper(instr,
|
|
"'R20d, 'Ipc, mul #'u1916+1",
|
|
"'R20d, 'Ipc",
|
|
"'R20d");
|
|
const char *form_sx = IncDecFormHelper(instr,
|
|
"'Xd, 'Wd, 'Ipc, mul #'u1916+1",
|
|
"'Xd, 'Wd, 'Ipc",
|
|
"'Xd, 'Wd");
|
|
|
|
switch (form_hash_) {
|
|
case "sqdecb_r_rs_sx"_h:
|
|
case "sqdecd_r_rs_sx"_h:
|
|
case "sqdech_r_rs_sx"_h:
|
|
case "sqdecw_r_rs_sx"_h:
|
|
case "sqincb_r_rs_sx"_h:
|
|
case "sqincd_r_rs_sx"_h:
|
|
case "sqinch_r_rs_sx"_h:
|
|
case "sqincw_r_rs_sx"_h:
|
|
form = form_sx;
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVESaturatingIncDecVectorByElementCount(
|
|
const Instruction *instr) {
|
|
const char *form = IncDecFormHelper(instr,
|
|
"'Zd.'t, 'Ipc, mul #'u1916+1",
|
|
"'Zd.'t, 'Ipc",
|
|
"'Zd.'t");
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEStoreMultipleStructures_ScalarPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.'tmsz, 'Zt2.'tmsz}";
|
|
const char *form_3 = "{'Zt.'tmsz, 'Zt2.'tmsz, 'Zt3.'tmsz}";
|
|
const char *form_4 = "{'Zt.'tmsz, 'Zt2.'tmsz, 'Zt3.'tmsz, 'Zt4.'tmsz}";
|
|
const char *suffix = ", 'Pgl, ['Xns'ISveSvl]";
|
|
|
|
switch (form_hash_) {
|
|
case "st3b_z_p_bi_contiguous"_h:
|
|
case "st3h_z_p_bi_contiguous"_h:
|
|
case "st3w_z_p_bi_contiguous"_h:
|
|
case "st3d_z_p_bi_contiguous"_h:
|
|
form = form_3;
|
|
break;
|
|
case "st4b_z_p_bi_contiguous"_h:
|
|
case "st4h_z_p_bi_contiguous"_h:
|
|
case "st4w_z_p_bi_contiguous"_h:
|
|
case "st4d_z_p_bi_contiguous"_h:
|
|
form = form_4;
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEStoreMultipleStructures_ScalarPlusScalar(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.'tmsz, 'Zt2.'tmsz}";
|
|
const char *form_3 = "{'Zt.'tmsz, 'Zt2.'tmsz, 'Zt3.'tmsz}";
|
|
const char *form_4 = "{'Zt.'tmsz, 'Zt2.'tmsz, 'Zt3.'tmsz, 'Zt4.'tmsz}";
|
|
const char *suffix = ", 'Pgl, ['Xns, 'Xm'NSveS]";
|
|
|
|
switch (form_hash_) {
|
|
case "st3b_z_p_br_contiguous"_h:
|
|
case "st3d_z_p_br_contiguous"_h:
|
|
case "st3h_z_p_br_contiguous"_h:
|
|
case "st3w_z_p_br_contiguous"_h:
|
|
form = form_3;
|
|
break;
|
|
case "st4b_z_p_br_contiguous"_h:
|
|
case "st4d_z_p_br_contiguous"_h:
|
|
case "st4h_z_p_br_contiguous"_h:
|
|
case "st4w_z_p_br_contiguous"_h:
|
|
form = form_4;
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEStorePredicateRegister(const Instruction *instr) {
|
|
const char *form = "'Pd, ['Xns, #'s2116:1210, mul vl]";
|
|
if (instr->Mask(0x003f1c00) == 0) {
|
|
form = "'Pd, ['Xns]";
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEStoreVectorRegister(const Instruction *instr) {
|
|
const char *form = "'Zt, ['Xns, #'s2116:1210, mul vl]";
|
|
if (instr->Mask(0x003f1c00) == 0) {
|
|
form = "'Zd, ['Xns]";
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVETableLookup(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, {'Zn.'t}, 'Zm.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEUnpackPredicateElements(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.h, 'Pn.b");
|
|
}
|
|
|
|
void Disassembler::VisitSVEUnpackVectorElements(const Instruction *instr) {
|
|
if (instr->GetSVESize() == 0) {
|
|
// The lowest lane size of the destination vector is H-sized lane.
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Zn.'th");
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEVectorSplice(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl, 'Zd.'t, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEAddressGeneration(const Instruction *instr) {
|
|
const char *mnemonic = "adr";
|
|
const char *form = "'Zd.d, ['Zn.d, 'Zm.d";
|
|
const char *suffix = NULL;
|
|
|
|
bool msz_is_zero = (instr->ExtractBits(11, 10) == 0);
|
|
|
|
switch (instr->Mask(SVEAddressGenerationMask)) {
|
|
case ADR_z_az_d_s32_scaled:
|
|
suffix = msz_is_zero ? ", sxtw]" : ", sxtw #'u1110]";
|
|
break;
|
|
case ADR_z_az_d_u32_scaled:
|
|
suffix = msz_is_zero ? ", uxtw]" : ", uxtw #'u1110]";
|
|
break;
|
|
case ADR_z_az_s_same_scaled:
|
|
case ADR_z_az_d_same_scaled:
|
|
form = "'Zd.'t, ['Zn.'t, 'Zm.'t";
|
|
suffix = msz_is_zero ? "]" : ", lsl #'u1110]";
|
|
break;
|
|
default:
|
|
mnemonic = "unimplemented";
|
|
form = "(SVEAddressGeneration)";
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEBitwiseLogicalUnpredicated(
|
|
const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "'Zd.d, 'Zn.d, 'Zm.d";
|
|
|
|
switch (instr->Mask(SVEBitwiseLogicalUnpredicatedMask)) {
|
|
case AND_z_zz:
|
|
mnemonic = "and";
|
|
break;
|
|
case BIC_z_zz:
|
|
mnemonic = "bic";
|
|
break;
|
|
case EOR_z_zz:
|
|
mnemonic = "eor";
|
|
break;
|
|
case ORR_z_zz:
|
|
mnemonic = "orr";
|
|
if (instr->GetRn() == instr->GetRm()) {
|
|
mnemonic = "mov";
|
|
form = "'Zd.d, 'Zn.d";
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEBitwiseShiftUnpredicated(const Instruction *instr) {
|
|
const char *mnemonic = "unimplemented";
|
|
const char *form = "(SVEBitwiseShiftUnpredicated)";
|
|
unsigned tsize =
|
|
(instr->ExtractBits(23, 22) << 2) | instr->ExtractBits(20, 19);
|
|
unsigned lane_size = instr->GetSVESize();
|
|
|
|
const char *suffix = NULL;
|
|
const char *form_i = "'Zd.'tszs, 'Zn.'tszs, ";
|
|
|
|
switch (form_hash_) {
|
|
case "asr_z_zi"_h:
|
|
case "lsr_z_zi"_h:
|
|
case "sri_z_zzi"_h:
|
|
case "srsra_z_zi"_h:
|
|
case "ssra_z_zi"_h:
|
|
case "ursra_z_zi"_h:
|
|
case "usra_z_zi"_h:
|
|
if (tsize != 0) {
|
|
// The tsz field must not be zero.
|
|
mnemonic = mnemonic_.c_str();
|
|
form = form_i;
|
|
suffix = "'ITriSves";
|
|
}
|
|
break;
|
|
case "lsl_z_zi"_h:
|
|
case "sli_z_zzi"_h:
|
|
if (tsize != 0) {
|
|
// The tsz field must not be zero.
|
|
mnemonic = mnemonic_.c_str();
|
|
form = form_i;
|
|
suffix = "'ITriSver";
|
|
}
|
|
break;
|
|
case "asr_z_zw"_h:
|
|
case "lsl_z_zw"_h:
|
|
case "lsr_z_zw"_h:
|
|
if (lane_size <= kSRegSizeInBytesLog2) {
|
|
mnemonic = mnemonic_.c_str();
|
|
form = "'Zd.'t, 'Zn.'t, 'Zm.d";
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
Format(instr, mnemonic, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEElementCount(const Instruction *instr) {
|
|
const char *form =
|
|
IncDecFormHelper(instr, "'Xd, 'Ipc, mul #'u1916+1", "'Xd, 'Ipc", "'Xd");
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPAccumulatingReduction(const Instruction *instr) {
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'t'u0400, 'Pgl, 't'u0400, 'Zn.'t");
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPArithmeticUnpredicated(const Instruction *instr) {
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Zn.'t, 'Zm.'t");
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPCompareVectors(const Instruction *instr) {
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.'t, 'Pgl/z, 'Zn.'t, 'Zm.'t");
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPCompareWithZero(const Instruction *instr) {
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.'t, 'Pgl/z, 'Zn.'t, #0.0");
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPComplexAddition(const Instruction *instr) {
|
|
// Bit 15 is always set, so this gives 90 * 1 or 3.
|
|
const char *form = "'Zd.'t, 'Pgl/m, 'Zd.'t, 'Zn.'t, #'u1615*90";
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPComplexMulAdd(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Pgl/m, 'Zn.'t, 'Zm.'t, #'u1413*90";
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPComplexMulAddIndex(const Instruction *instr) {
|
|
const char *form = "'Zd.h, 'Zn.h, z'u1816.h['u2019]";
|
|
const char *suffix = ", #'u1110*90";
|
|
switch (form_hash_) {
|
|
case "fcmla_z_zzzi_s"_h:
|
|
form = "'Zd.s, 'Zn.s, z'u1916.s['u2020]";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPFastReduction(const Instruction *instr) {
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'t'u0400, 'Pgl, 'Zn.'t");
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPMulIndex(const Instruction *instr) {
|
|
const char *form = "'Zd.h, 'Zn.h, z'u1816.h['u2222:2019]";
|
|
switch (form_hash_) {
|
|
case "fmul_z_zzi_d"_h:
|
|
form = "'Zd.d, 'Zn.d, z'u1916.d['u2020]";
|
|
break;
|
|
case "fmul_z_zzi_s"_h:
|
|
form = "'Zd.s, 'Zn.s, z'u1816.s['u2019]";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPMulAdd(const Instruction *instr) {
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zn.'t, 'Zm.'t");
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPMulAddIndex(const Instruction *instr) {
|
|
const char *form = "'Zd.h, 'Zn.h, z'u1816.h['u2222:2019]";
|
|
switch (form_hash_) {
|
|
case "fmla_z_zzzi_s"_h:
|
|
case "fmls_z_zzzi_s"_h:
|
|
form = "'Zd.s, 'Zn.s, z'u1816.s['u2019]";
|
|
break;
|
|
case "fmla_z_zzzi_d"_h:
|
|
case "fmls_z_zzzi_d"_h:
|
|
form = "'Zd.d, 'Zn.d, z'u1916.d['u2020]";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEFPUnaryOpUnpredicated(const Instruction *instr) {
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Zn.'t");
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEIncDecByPredicateCount(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Pn";
|
|
switch (form_hash_) {
|
|
// <Xdn>, <Pg>.<T>
|
|
case "decp_r_p_r"_h:
|
|
case "incp_r_p_r"_h:
|
|
form = "'Xd, 'Pn.'t";
|
|
break;
|
|
// <Xdn>, <Pg>.<T>, <Wdn>
|
|
case "sqdecp_r_p_r_sx"_h:
|
|
case "sqincp_r_p_r_sx"_h:
|
|
form = "'Xd, 'Pn.'t, 'Wd";
|
|
break;
|
|
// <Xdn>, <Pg>.<T>
|
|
case "sqdecp_r_p_r_x"_h:
|
|
case "sqincp_r_p_r_x"_h:
|
|
case "uqdecp_r_p_r_x"_h:
|
|
case "uqincp_r_p_r_x"_h:
|
|
form = "'Xd, 'Pn.'t";
|
|
break;
|
|
// <Wdn>, <Pg>.<T>
|
|
case "uqdecp_r_p_r_uw"_h:
|
|
case "uqincp_r_p_r_uw"_h:
|
|
form = "'Wd, 'Pn.'t";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEIndexGeneration(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, #'s0905, #'s2016";
|
|
bool w_inputs =
|
|
static_cast<unsigned>(instr->GetSVESize()) <= kWRegSizeInBytesLog2;
|
|
|
|
switch (form_hash_) {
|
|
case "index_z_ir"_h:
|
|
form = w_inputs ? "'Zd.'t, #'s0905, 'Wm" : "'Zd.'t, #'s0905, 'Xm";
|
|
break;
|
|
case "index_z_ri"_h:
|
|
form = w_inputs ? "'Zd.'t, 'Wn, #'s2016" : "'Zd.'t, 'Xn, #'s2016";
|
|
break;
|
|
case "index_z_rr"_h:
|
|
form = w_inputs ? "'Zd.'t, 'Wn, 'Wm" : "'Zd.'t, 'Xn, 'Xm";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntArithmeticUnpredicated(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Zn.'t, 'Zm.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntCompareSignedImm(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.'t, 'Pgl/z, 'Zn.'t, #'s2016");
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntCompareUnsignedImm(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.'t, 'Pgl/z, 'Zn.'t, #'u2014");
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntCompareVectors(const Instruction *instr) {
|
|
const char *form = "'Pd.'t, 'Pgl/z, 'Zn.'t, 'Zm.";
|
|
const char *suffix = "d";
|
|
switch (form_hash_) {
|
|
case "cmpeq_p_p_zz"_h:
|
|
case "cmpge_p_p_zz"_h:
|
|
case "cmpgt_p_p_zz"_h:
|
|
case "cmphi_p_p_zz"_h:
|
|
case "cmphs_p_p_zz"_h:
|
|
case "cmpne_p_p_zz"_h:
|
|
suffix = "'t";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntMulAddPredicated(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Pgl/m, ";
|
|
const char *suffix = "'Zn.'t, 'Zm.'t";
|
|
switch (form_hash_) {
|
|
case "mad_z_p_zzz"_h:
|
|
case "msb_z_p_zzz"_h:
|
|
suffix = "'Zm.'t, 'Zn.'t";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntMulAddUnpredicated(const Instruction *instr) {
|
|
if (static_cast<unsigned>(instr->GetSVESize()) >= kSRegSizeInBytesLog2) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Zn.'tq, 'Zm.'tq");
|
|
} else {
|
|
VisitUnallocated(instr);
|
|
}
|
|
}
|
|
|
|
void Disassembler::VisitSVEMovprfx(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/'?16:mz, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntReduction(const Instruction *instr) {
|
|
const char *form = "'Vdv, 'Pgl, 'Zn.'t";
|
|
switch (form_hash_) {
|
|
case "saddv_r_p_z"_h:
|
|
case "uaddv_r_p_z"_h:
|
|
form = "'Dd, 'Pgl, 'Zn.'t";
|
|
break;
|
|
}
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEIntUnaryArithmeticPredicated(
|
|
const Instruction *instr) {
|
|
VectorFormat vform = instr->GetSVEVectorFormat();
|
|
|
|
switch (form_hash_) {
|
|
case "sxtw_z_p_z"_h:
|
|
case "uxtw_z_p_z"_h:
|
|
if (vform == kFormatVnS) {
|
|
VisitUnallocated(instr);
|
|
return;
|
|
}
|
|
VIXL_FALLTHROUGH();
|
|
case "sxth_z_p_z"_h:
|
|
case "uxth_z_p_z"_h:
|
|
if (vform == kFormatVnH) {
|
|
VisitUnallocated(instr);
|
|
return;
|
|
}
|
|
VIXL_FALLTHROUGH();
|
|
case "sxtb_z_p_z"_h:
|
|
case "uxtb_z_p_z"_h:
|
|
case "fabs_z_p_z"_h:
|
|
case "fneg_z_p_z"_h:
|
|
if (vform == kFormatVnB) {
|
|
VisitUnallocated(instr);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Pgl/m, 'Zn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEMulIndex(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Zn.b, z'u1816.b['u2019]";
|
|
|
|
switch (form_hash_) {
|
|
case "sdot_z_zzzi_d"_h:
|
|
case "udot_z_zzzi_d"_h:
|
|
form = "'Zd.d, 'Zn.h, z'u1916.h['u2020]";
|
|
break;
|
|
}
|
|
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEPermuteVectorExtract(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.b, 'Zd.b, 'Zn.b, #'u2016:1210");
|
|
}
|
|
|
|
void Disassembler::VisitSVEPermuteVectorInterleaving(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Zd.'t, 'Zn.'t, 'Zm.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEPredicateCount(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Xd, p'u1310, 'Pn.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEPredicateLogical(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Pd.b, p'u1310/z, 'Pn.b, 'Pm.b";
|
|
|
|
int pd = instr->GetPd();
|
|
int pn = instr->GetPn();
|
|
int pm = instr->GetPm();
|
|
int pg = instr->ExtractBits(13, 10);
|
|
|
|
switch (form_hash_) {
|
|
case "ands_p_p_pp_z"_h:
|
|
if (pn == pm) {
|
|
mnemonic = "movs";
|
|
form = "'Pd.b, p'u1310/z, 'Pn.b";
|
|
}
|
|
break;
|
|
case "and_p_p_pp_z"_h:
|
|
if (pn == pm) {
|
|
mnemonic = "mov";
|
|
form = "'Pd.b, p'u1310/z, 'Pn.b";
|
|
}
|
|
break;
|
|
case "eors_p_p_pp_z"_h:
|
|
if (pm == pg) {
|
|
mnemonic = "nots";
|
|
form = "'Pd.b, 'Pm/z, 'Pn.b";
|
|
}
|
|
break;
|
|
case "eor_p_p_pp_z"_h:
|
|
if (pm == pg) {
|
|
mnemonic = "not";
|
|
form = "'Pd.b, 'Pm/z, 'Pn.b";
|
|
}
|
|
break;
|
|
case "orrs_p_p_pp_z"_h:
|
|
if ((pn == pm) && (pn == pg)) {
|
|
mnemonic = "movs";
|
|
form = "'Pd.b, 'Pn.b";
|
|
}
|
|
break;
|
|
case "orr_p_p_pp_z"_h:
|
|
if ((pn == pm) && (pn == pg)) {
|
|
mnemonic = "mov";
|
|
form = "'Pd.b, 'Pn.b";
|
|
}
|
|
break;
|
|
case "sel_p_p_pp"_h:
|
|
if (pd == pm) {
|
|
mnemonic = "mov";
|
|
form = "'Pd.b, p'u1310/m, 'Pn.b";
|
|
} else {
|
|
form = "'Pd.b, p'u1310, 'Pn.b, 'Pm.b";
|
|
}
|
|
break;
|
|
}
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEPredicateInitialize(const Instruction *instr) {
|
|
const char *form = "'Pd.'t, 'Ipc";
|
|
// Omit the pattern if it is the default ('ALL').
|
|
if (instr->ExtractBits(9, 5) == SVE_ALL) form = "'Pd.'t";
|
|
FormatWithDecodedMnemonic(instr, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEPredicateNextActive(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.'t, 'Pn, 'Pd.'t");
|
|
}
|
|
|
|
void Disassembler::VisitSVEPredicateReadFromFFR_Predicated(
|
|
const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.b, 'Pn/z");
|
|
}
|
|
|
|
void Disassembler::VisitSVEPropagateBreak(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Pd.b, p'u1310/z, 'Pn.b, 'Pm.b");
|
|
}
|
|
|
|
void Disassembler::VisitSVEStackFrameAdjustment(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Xds, 'Xms, #'s1005");
|
|
}
|
|
|
|
void Disassembler::VisitSVEStackFrameSize(const Instruction *instr) {
|
|
FormatWithDecodedMnemonic(instr, "'Xd, #'s1005");
|
|
}
|
|
|
|
void Disassembler::VisitSVEVectorSelect(const Instruction *instr) {
|
|
const char *mnemonic = mnemonic_.c_str();
|
|
const char *form = "'Zd.'t, p'u1310, 'Zn.'t, 'Zm.'t";
|
|
|
|
if (instr->GetRd() == instr->GetRm()) {
|
|
mnemonic = "mov";
|
|
form = "'Zd.'t, p'u1310/m, 'Zn.'t";
|
|
}
|
|
|
|
Format(instr, mnemonic, form);
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousLoad_ScalarPlusImm(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.'tlss}, 'Pgl/z, ['Xns";
|
|
const char *suffix =
|
|
(instr->ExtractBits(19, 16) == 0) ? "]" : ", #'s1916, mul vl]";
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitSVEContiguousLoad_ScalarPlusScalar(
|
|
const Instruction *instr) {
|
|
const char *form = "{'Zt.'tlss}, 'Pgl/z, ['Xns, 'Xm";
|
|
const char *suffix = "]";
|
|
|
|
switch (form_hash_) {
|
|
case "ld1h_z_p_br_u16"_h:
|
|
case "ld1h_z_p_br_u32"_h:
|
|
case "ld1h_z_p_br_u64"_h:
|
|
case "ld1w_z_p_br_u32"_h:
|
|
case "ld1w_z_p_br_u64"_h:
|
|
case "ld1d_z_p_br_u64"_h:
|
|
suffix = ", lsl #'u2423]";
|
|
break;
|
|
case "ld1sh_z_p_br_s32"_h:
|
|
case "ld1sh_z_p_br_s64"_h:
|
|
suffix = ", lsl #1]";
|
|
break;
|
|
case "ld1sw_z_p_br_s64"_h:
|
|
suffix = ", lsl #2]";
|
|
break;
|
|
}
|
|
|
|
FormatWithDecodedMnemonic(instr, form, suffix);
|
|
}
|
|
|
|
void Disassembler::VisitReserved(const Instruction *instr) {
|
|
// UDF is the only instruction in this group, and the Decoder is precise.
|
|
VIXL_ASSERT(instr->Mask(ReservedMask) == UDF);
|
|
Format(instr, "udf", "'IUdf");
|
|
}
|
|
|
|
void Disassembler::VisitUnimplemented(const Instruction *instr) {
|
|
Format(instr, "unimplemented", "(Unimplemented)");
|
|
}
|
|
|
|
|
|
void Disassembler::VisitUnallocated(const Instruction *instr) {
|
|
Format(instr, "unallocated", "(Unallocated)");
|
|
}
|
|
|
|
void Disassembler::Visit(Metadata *metadata, const Instruction *instr) {
|
|
VIXL_ASSERT(metadata->count("form") > 0);
|
|
const std::string &form = (*metadata)["form"];
|
|
form_hash_ = Hash(form.c_str());
|
|
const FormToVisitorFnMap *fv = Disassembler::GetFormToVisitorFnMap();
|
|
FormToVisitorFnMap::const_iterator it = fv->find(form_hash_);
|
|
if (it == fv->end()) {
|
|
VisitUnimplemented(instr);
|
|
} else {
|
|
SetMnemonicFromForm(form);
|
|
(it->second)(this, instr);
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_PdT_PgZ_ZnT_ZmT(const Instruction *instr) {
|
|
const char *form = "'Pd.'t, 'Pgl/z, 'Zn.'t, 'Zm.'t";
|
|
VectorFormat vform = instr->GetSVEVectorFormat();
|
|
|
|
if ((vform == kFormatVnS) || (vform == kFormatVnD)) {
|
|
Format(instr, "unimplemented", "(PdT_PgZ_ZnT_ZmT)");
|
|
} else {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdB_Zn1B_Zn2B_imm(const Instruction *instr) {
|
|
const char *form = "'Zd.b, {'Zn.b, 'Zn2.b}, #'u2016:1210";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdB_ZnB_ZmB(const Instruction *instr) {
|
|
const char *form = "'Zd.b, 'Zn.b, 'Zm.b";
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
} else {
|
|
Format(instr, "unimplemented", "(ZdB_ZnB_ZmB)");
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdD_PgM_ZnS(const Instruction *instr) {
|
|
const char *form = "'Zd.d, 'Pgl/m, 'Zn.s";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdD_ZnD_ZmD(const Instruction *instr) {
|
|
const char *form = "'Zd.d, 'Zn.d, 'Zm.d";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdD_ZnD_ZmD_imm(const Instruction *instr) {
|
|
const char *form = "'Zd.d, 'Zn.d, z'u1916.d['u2020]";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdD_ZnS_ZmS_imm(const Instruction *instr) {
|
|
const char *form = "'Zd.d, 'Zn.s, z'u1916.s['u2020:1111]";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdH_PgM_ZnS(const Instruction *instr) {
|
|
const char *form = "'Zd.h, 'Pgl/m, 'Zn.s";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdH_ZnH_ZmH_imm(const Instruction *instr) {
|
|
const char *form = "'Zd.h, 'Zn.h, z'u1816.h['u2222:2019]";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdS_PgM_ZnD(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Pgl/m, 'Zn.d";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdS_PgM_ZnH(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Pgl/m, 'Zn.h";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdS_PgM_ZnS(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Pgl/m, 'Zn.s";
|
|
if (instr->GetSVEVectorFormat() == kFormatVnS) {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
} else {
|
|
Format(instr, "unimplemented", "(ZdS_PgM_ZnS)");
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdS_ZnH_ZmH_imm(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Zn.h, z'u1816.h['u2019:1111]";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdS_ZnS_ZmS(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Zn.s, 'Zm.s";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdS_ZnS_ZmS_imm(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Zn.s, z'u1816.s['u2019]";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::DisassembleSVEFlogb(const Instruction *instr) {
|
|
const char *form = "'Zd.'tf, 'Pgl/m, 'Zn.'tf";
|
|
if (instr->GetSVEVectorFormat(17) == kFormatVnB) {
|
|
Format(instr, "unimplemented", "(SVEFlogb)");
|
|
} else {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdT_PgM_ZnT(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Pgl/m, 'Zn.'t";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdT_PgZ_ZnT_ZmT(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Pgl/z, 'Zn.'t, 'Zm.'t";
|
|
VectorFormat vform = instr->GetSVEVectorFormat();
|
|
if ((vform == kFormatVnS) || (vform == kFormatVnD)) {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
} else {
|
|
Format(instr, "unimplemented", "(ZdT_PgZ_ZnT_ZmT)");
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdT_Pg_Zn1T_Zn2T(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Pgl, {'Zn.'t, 'Zn2.'t}";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdT_Zn1T_Zn2T_ZmT(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, {'Zn.'t, 'Zn2.'t}, 'Zm.'t";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdT_ZnT_ZmT(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Zn.'t, 'Zm.'t";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdT_ZnT_ZmTb(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Zn.'t, 'Zm.'th";
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
Format(instr, "unimplemented", "(ZdT_ZnT_ZmTb)");
|
|
} else {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdT_ZnTb(const Instruction *instr) {
|
|
const char *form = "'Zd.'tszs, 'Zn.'tszd";
|
|
std::pair<int, int> shift_and_lane_size =
|
|
instr->GetSVEImmShiftAndLaneSizeLog2(/* is_predicated = */ false);
|
|
int shift_dist = shift_and_lane_size.first;
|
|
int lane_size = shift_and_lane_size.second;
|
|
// Convert shift_dist from a right to left shift. Valid xtn instructions
|
|
// must have a left shift_dist equivalent of zero.
|
|
shift_dist = (8 << lane_size) - shift_dist;
|
|
if ((lane_size >= static_cast<int>(kBRegSizeInBytesLog2)) &&
|
|
(lane_size <= static_cast<int>(kSRegSizeInBytesLog2)) &&
|
|
(shift_dist == 0)) {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
} else {
|
|
Format(instr, "unimplemented", "(ZdT_ZnTb)");
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdT_ZnTb_ZmTb(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Zn.'th, 'Zm.'th";
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
// TODO: This is correct for saddlbt, ssublbt, subltb, which don't have
|
|
// b-lane sized form, and for pmull[b|t] as feature `SVEPmull128` isn't
|
|
// supported, but may need changes for other instructions reaching here.
|
|
Format(instr, "unimplemented", "(ZdT_ZnTb_ZmTb)");
|
|
} else {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::DisassembleSVEAddSubHigh(const Instruction *instr) {
|
|
const char *form = "'Zd.'th, 'Zn.'t, 'Zm.'t";
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
Format(instr, "unimplemented", "(SVEAddSubHigh)");
|
|
} else {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::DisassembleSVEShiftLeftImm(const Instruction *instr) {
|
|
const char *form = "'Zd.'tszd, 'Zn.'tszs, 'ITriSver";
|
|
std::pair<int, int> shift_and_lane_size =
|
|
instr->GetSVEImmShiftAndLaneSizeLog2(/* is_predicated = */ false);
|
|
int lane_size = shift_and_lane_size.second;
|
|
if ((lane_size >= static_cast<int>(kBRegSizeInBytesLog2)) &&
|
|
(lane_size <= static_cast<int>(kSRegSizeInBytesLog2))) {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
} else {
|
|
Format(instr, "unimplemented", "(SVEShiftLeftImm)");
|
|
}
|
|
}
|
|
|
|
void Disassembler::DisassembleSVEShiftRightImm(const Instruction *instr) {
|
|
const char *form = "'Zd.'tszs, 'Zn.'tszd, 'ITriSves";
|
|
std::pair<int, int> shift_and_lane_size =
|
|
instr->GetSVEImmShiftAndLaneSizeLog2(/* is_predicated = */ false);
|
|
int lane_size = shift_and_lane_size.second;
|
|
if ((lane_size >= static_cast<int>(kBRegSizeInBytesLog2)) &&
|
|
(lane_size <= static_cast<int>(kSRegSizeInBytesLog2))) {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
} else {
|
|
Format(instr, "unimplemented", "(SVEShiftRightImm)");
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaD_ZnD_ZmD_imm(const Instruction *instr) {
|
|
const char *form = "'Zd.d, 'Zn.d, z'u1916.d['u2020]";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaD_ZnH_ZmH_imm_const(
|
|
const Instruction *instr) {
|
|
const char *form = "'Zd.d, 'Zn.h, z'u1916.h['u2020], #'u1110*90";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaD_ZnS_ZmS_imm(const Instruction *instr) {
|
|
const char *form = "'Zd.d, 'Zn.s, z'u1916.s['u2020:1111]";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaH_ZnH_ZmH_imm(const Instruction *instr) {
|
|
const char *form = "'Zd.h, 'Zn.h, z'u1816.h['u2222:2019]";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaH_ZnH_ZmH_imm_const(
|
|
const Instruction *instr) {
|
|
const char *form = "'Zd.h, 'Zn.h, z'u1816.h['u2019], #'u1110*90";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaS_ZnB_ZmB(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Zn.b, 'Zm.b";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaS_ZnB_ZmB_imm_const(
|
|
const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Zn.b, z'u1816.b['u2019], #'u1110*90";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaS_ZnH_ZmH(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Zn.h, 'Zm.h";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaS_ZnH_ZmH_imm(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Zn.h, z'u1816.h['u2019:1111]";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaS_ZnS_ZmS_imm(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Zn.s, z'u1816.s['u2019]";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaS_ZnS_ZmS_imm_const(
|
|
const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Zn.s, z'u1916.s['u2020], #'u1110*90";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaT_PgM_ZnTb(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Pgl/m, 'Zn.'th";
|
|
|
|
if (instr->GetSVESize() == 0) {
|
|
// The lowest lane size of the destination vector is H-sized lane.
|
|
Format(instr, "unimplemented", "(Disassemble_ZdaT_PgM_ZnTb)");
|
|
return;
|
|
}
|
|
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::DisassembleSVEAddSubCarry(const Instruction *instr) {
|
|
const char *form = "'Zd.'?22:ds, 'Zn.'?22:ds, 'Zm.'?22:ds";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaT_ZnT_ZmT(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Zn.'t, 'Zm.'t";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaT_ZnT_ZmT_const(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Zn.'t, 'Zm.'t, #'u1110*90";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaT_ZnTb_ZmTb(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Zn.'th, 'Zm.'th";
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
Format(instr, "unimplemented", "(ZdaT_ZnTb_ZmTb)");
|
|
} else {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdaT_ZnTb_ZmTb_const(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Zn.'tq, 'Zm.'tq, #'u1110*90";
|
|
VectorFormat vform = instr->GetSVEVectorFormat();
|
|
|
|
if ((vform == kFormatVnB) || (vform == kFormatVnH)) {
|
|
Format(instr, "unimplemented", "(ZdaT_ZnTb_ZmTb_const)");
|
|
} else {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdnB_ZdnB(const Instruction *instr) {
|
|
const char *form = "'Zd.b, 'Zd.b";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdnB_ZdnB_ZmB(const Instruction *instr) {
|
|
const char *form = "'Zd.b, 'Zd.b, 'Zn.b";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::DisassembleSVEBitwiseTernary(const Instruction *instr) {
|
|
const char *form = "'Zd.d, 'Zd.d, 'Zm.d, 'Zn.d";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdnS_ZdnS_ZmS(const Instruction *instr) {
|
|
const char *form = "'Zd.s, 'Zd.s, 'Zn.s";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::DisassembleSVEFPPair(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Pgl/m, 'Zd.'t, 'Zn.'t";
|
|
if (instr->GetSVEVectorFormat() == kFormatVnB) {
|
|
Format(instr, "unimplemented", "(SVEFPPair)");
|
|
} else {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdnT_PgM_ZdnT_ZmT(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Pgl/m, 'Zd.'t, 'Zn.'t";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::DisassembleSVEComplexIntAddition(const Instruction *instr) {
|
|
const char *form = "'Zd.'t, 'Zd.'t, 'Zn.'t, #";
|
|
const char *suffix = (instr->ExtractBit(10) == 0) ? "90" : "270";
|
|
Format(instr, mnemonic_.c_str(), form, suffix);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZdnT_ZdnT_ZmT_const(const Instruction *instr) {
|
|
const char *form = "'Zd.'tszs, 'Zd.'tszs, 'Zn.'tszs, 'ITriSves";
|
|
unsigned tsize =
|
|
(instr->ExtractBits(23, 22) << 2) | instr->ExtractBits(20, 19);
|
|
|
|
if (tsize == 0) {
|
|
Format(instr, "unimplemented", "(ZdnT_ZdnT_ZmT_const)");
|
|
} else {
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZtD_PgZ_ZnD_Xm(const Instruction *instr) {
|
|
const char *form = "{'Zt.d}, 'Pgl/z, ['Zn.d";
|
|
const char *suffix = instr->GetRm() == 31 ? "]" : ", 'Xm]";
|
|
Format(instr, mnemonic_.c_str(), form, suffix);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZtD_Pg_ZnD_Xm(const Instruction *instr) {
|
|
const char *form = "{'Zt.d}, 'Pgl, ['Zn.d";
|
|
const char *suffix = instr->GetRm() == 31 ? "]" : ", 'Xm]";
|
|
Format(instr, mnemonic_.c_str(), form, suffix);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZtS_PgZ_ZnS_Xm(const Instruction *instr) {
|
|
const char *form = "{'Zt.s}, 'Pgl/z, ['Zn.s";
|
|
const char *suffix = instr->GetRm() == 31 ? "]" : ", 'Xm]";
|
|
Format(instr, mnemonic_.c_str(), form, suffix);
|
|
}
|
|
|
|
void Disassembler::Disassemble_ZtS_Pg_ZnS_Xm(const Instruction *instr) {
|
|
const char *form = "{'Zt.s}, 'Pgl, ['Zn.s";
|
|
const char *suffix = instr->GetRm() == 31 ? "]" : ", 'Xm]";
|
|
Format(instr, mnemonic_.c_str(), form, suffix);
|
|
}
|
|
|
|
void Disassembler::Disassemble_XdSP_XnSP_Xm(const Instruction *instr) {
|
|
const char *form = "'Xds, 'Xns";
|
|
const char *suffix = instr->GetRm() == 31 ? "" : ", 'Xm";
|
|
Format(instr, mnemonic_.c_str(), form, suffix);
|
|
}
|
|
|
|
void Disassembler::Disassemble_XdSP_XnSP_uimm6_uimm4(const Instruction *instr) {
|
|
VIXL_STATIC_ASSERT(kMTETagGranuleInBytes == 16);
|
|
const char *form = "'Xds, 'Xns, #'u2116*16, #'u1310";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_Xd_XnSP_Xm(const Instruction *instr) {
|
|
const char *form = "'Rd, 'Xns, 'Rm";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::Disassemble_Xd_XnSP_XmSP(const Instruction *instr) {
|
|
if ((form_hash_ == Hash("subps_64s_dp_2src")) && (instr->GetRd() == 31)) {
|
|
Format(instr, "cmpp", "'Xns, 'Xms");
|
|
} else {
|
|
const char *form = "'Xd, 'Xns, 'Xms";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
}
|
|
|
|
void Disassembler::DisassembleMTEStoreTagPair(const Instruction *instr) {
|
|
const char *form = "'Xt, 'Xt2, ['Xns";
|
|
const char *suffix = NULL;
|
|
switch (form_hash_) {
|
|
case Hash("stgp_64_ldstpair_off"):
|
|
suffix = ", #'s2115*16]";
|
|
break;
|
|
case Hash("stgp_64_ldstpair_post"):
|
|
suffix = "], #'s2115*16";
|
|
break;
|
|
case Hash("stgp_64_ldstpair_pre"):
|
|
suffix = ", #'s2115*16]!";
|
|
break;
|
|
default:
|
|
mnemonic_ = "unimplemented";
|
|
break;
|
|
}
|
|
|
|
if (instr->GetImmLSPair() == 0) {
|
|
suffix = "]";
|
|
}
|
|
|
|
Format(instr, mnemonic_.c_str(), form, suffix);
|
|
}
|
|
|
|
void Disassembler::DisassembleMTEStoreTag(const Instruction *instr) {
|
|
const char *form = "'Xds, ['Xns";
|
|
const char *suffix = NULL;
|
|
switch (form_hash_) {
|
|
case Hash("st2g_64soffset_ldsttags"):
|
|
case Hash("stg_64soffset_ldsttags"):
|
|
case Hash("stz2g_64soffset_ldsttags"):
|
|
case Hash("stzg_64soffset_ldsttags"):
|
|
suffix = ", #'s2012*16]";
|
|
break;
|
|
case Hash("st2g_64spost_ldsttags"):
|
|
case Hash("stg_64spost_ldsttags"):
|
|
case Hash("stz2g_64spost_ldsttags"):
|
|
case Hash("stzg_64spost_ldsttags"):
|
|
suffix = "], #'s2012*16";
|
|
break;
|
|
case Hash("st2g_64spre_ldsttags"):
|
|
case Hash("stg_64spre_ldsttags"):
|
|
case Hash("stz2g_64spre_ldsttags"):
|
|
case Hash("stzg_64spre_ldsttags"):
|
|
suffix = ", #'s2012*16]!";
|
|
break;
|
|
default:
|
|
mnemonic_ = "unimplemented";
|
|
break;
|
|
}
|
|
|
|
if (instr->GetImmLS() == 0) {
|
|
suffix = "]";
|
|
}
|
|
|
|
Format(instr, mnemonic_.c_str(), form, suffix);
|
|
}
|
|
|
|
void Disassembler::DisassembleMTELoadTag(const Instruction *instr) {
|
|
const char *form =
|
|
(instr->GetImmLS() == 0) ? "'Xt, ['Xns]" : "'Xt, ['Xns, #'s2012*16]";
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::DisassembleCpy(const Instruction *instr) {
|
|
const char *form = "['Xd]!, ['Xs]!, 'Xn!";
|
|
|
|
int d = instr->GetRd();
|
|
int n = instr->GetRn();
|
|
int s = instr->GetRs();
|
|
|
|
// Aliased registers and sp/zr are disallowed.
|
|
if ((d == n) || (d == s) || (n == s) || (d == 31) || (n == 31) || (s == 31)) {
|
|
form = NULL;
|
|
}
|
|
|
|
// Bits 31 and 30 must be zero.
|
|
if (instr->ExtractBits(31, 30)) {
|
|
form = NULL;
|
|
}
|
|
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::DisassembleSet(const Instruction *instr) {
|
|
const char *form = "['Xd]!, 'Xn!, 'Xs";
|
|
|
|
int d = instr->GetRd();
|
|
int n = instr->GetRn();
|
|
int s = instr->GetRs();
|
|
|
|
// Aliased registers are disallowed. Only Xs may be xzr.
|
|
if ((d == n) || (d == s) || (n == s) || (d == 31) || (n == 31)) {
|
|
form = NULL;
|
|
}
|
|
|
|
// Bits 31 and 30 must be zero.
|
|
if (instr->ExtractBits(31, 30)) {
|
|
form = NULL;
|
|
}
|
|
|
|
Format(instr, mnemonic_.c_str(), form);
|
|
}
|
|
|
|
void Disassembler::ProcessOutput(const Instruction * /*instr*/) {
|
|
// The base disasm does nothing more than disassembling into a buffer.
|
|
}
|
|
|
|
|
|
void Disassembler::AppendRegisterNameToOutput(const Instruction *instr,
|
|
const CPURegister ®) {
|
|
USE(instr);
|
|
VIXL_ASSERT(reg.IsValid());
|
|
char reg_char;
|
|
|
|
if (reg.IsRegister()) {
|
|
reg_char = reg.Is64Bits() ? 'x' : 'w';
|
|
} else {
|
|
VIXL_ASSERT(reg.IsVRegister());
|
|
switch (reg.GetSizeInBits()) {
|
|
case kBRegSize:
|
|
reg_char = 'b';
|
|
break;
|
|
case kHRegSize:
|
|
reg_char = 'h';
|
|
break;
|
|
case kSRegSize:
|
|
reg_char = 's';
|
|
break;
|
|
case kDRegSize:
|
|
reg_char = 'd';
|
|
break;
|
|
default:
|
|
VIXL_ASSERT(reg.Is128Bits());
|
|
reg_char = 'q';
|
|
}
|
|
}
|
|
|
|
if (reg.IsVRegister() || !(reg.Aliases(sp) || reg.Aliases(xzr))) {
|
|
// A core or scalar/vector register: [wx]0 - 30, [bhsdq]0 - 31.
|
|
AppendToOutput("%c%d", reg_char, reg.GetCode());
|
|
} else if (reg.Aliases(sp)) {
|
|
// Disassemble w31/x31 as stack pointer wsp/sp.
|
|
AppendToOutput("%s", reg.Is64Bits() ? "sp" : "wsp");
|
|
} else {
|
|
// Disassemble w31/x31 as zero register wzr/xzr.
|
|
AppendToOutput("%czr", reg_char);
|
|
}
|
|
}
|
|
|
|
|
|
void Disassembler::AppendPCRelativeOffsetToOutput(const Instruction *instr,
|
|
int64_t offset) {
|
|
USE(instr);
|
|
if (offset < 0) {
|
|
// Cast to uint64_t so that INT64_MIN is handled in a well-defined way.
|
|
uint64_t abs_offset = UnsignedNegate(static_cast<uint64_t>(offset));
|
|
AppendToOutput("#-0x%" PRIx64, abs_offset);
|
|
} else {
|
|
AppendToOutput("#+0x%" PRIx64, offset);
|
|
}
|
|
}
|
|
|
|
|
|
void Disassembler::AppendAddressToOutput(const Instruction *instr,
|
|
const void *addr) {
|
|
USE(instr);
|
|
AppendToOutput("(addr 0x%" PRIxPTR ")", reinterpret_cast<uintptr_t>(addr));
|
|
}
|
|
|
|
|
|
void Disassembler::AppendCodeAddressToOutput(const Instruction *instr,
|
|
const void *addr) {
|
|
AppendAddressToOutput(instr, addr);
|
|
}
|
|
|
|
|
|
void Disassembler::AppendDataAddressToOutput(const Instruction *instr,
|
|
const void *addr) {
|
|
AppendAddressToOutput(instr, addr);
|
|
}
|
|
|
|
|
|
void Disassembler::AppendCodeRelativeAddressToOutput(const Instruction *instr,
|
|
const void *addr) {
|
|
USE(instr);
|
|
int64_t rel_addr = CodeRelativeAddress(addr);
|
|
if (rel_addr >= 0) {
|
|
AppendToOutput("(addr 0x%" PRIx64 ")", rel_addr);
|
|
} else {
|
|
AppendToOutput("(addr -0x%" PRIx64 ")", -rel_addr);
|
|
}
|
|
}
|
|
|
|
|
|
void Disassembler::AppendCodeRelativeCodeAddressToOutput(
|
|
const Instruction *instr, const void *addr) {
|
|
AppendCodeRelativeAddressToOutput(instr, addr);
|
|
}
|
|
|
|
|
|
void Disassembler::AppendCodeRelativeDataAddressToOutput(
|
|
const Instruction *instr, const void *addr) {
|
|
AppendCodeRelativeAddressToOutput(instr, addr);
|
|
}
|
|
|
|
|
|
void Disassembler::MapCodeAddress(int64_t base_address,
|
|
const Instruction *instr_address) {
|
|
set_code_address_offset(base_address -
|
|
reinterpret_cast<intptr_t>(instr_address));
|
|
}
|
|
int64_t Disassembler::CodeRelativeAddress(const void *addr) {
|
|
return reinterpret_cast<intptr_t>(addr) + code_address_offset();
|
|
}
|
|
|
|
|
|
void Disassembler::Format(const Instruction *instr,
|
|
const char *mnemonic,
|
|
const char *format0,
|
|
const char *format1) {
|
|
if ((mnemonic == NULL) || (format0 == NULL)) {
|
|
VisitUnallocated(instr);
|
|
} else {
|
|
ResetOutput();
|
|
Substitute(instr, mnemonic);
|
|
if (format0[0] != 0) { // Not a zero-length string.
|
|
VIXL_ASSERT(buffer_pos_ < buffer_size_);
|
|
buffer_[buffer_pos_++] = ' ';
|
|
Substitute(instr, format0);
|
|
// TODO: consider using a zero-length string here, too.
|
|
if (format1 != NULL) {
|
|
Substitute(instr, format1);
|
|
}
|
|
}
|
|
VIXL_ASSERT(buffer_pos_ < buffer_size_);
|
|
buffer_[buffer_pos_] = 0;
|
|
ProcessOutput(instr);
|
|
}
|
|
}
|
|
|
|
void Disassembler::FormatWithDecodedMnemonic(const Instruction *instr,
|
|
const char *format0,
|
|
const char *format1) {
|
|
Format(instr, mnemonic_.c_str(), format0, format1);
|
|
}
|
|
|
|
void Disassembler::Substitute(const Instruction *instr, const char *string) {
|
|
char chr = *string++;
|
|
while (chr != '\0') {
|
|
if (chr == '\'') {
|
|
string += SubstituteField(instr, string);
|
|
} else {
|
|
VIXL_ASSERT(buffer_pos_ < buffer_size_);
|
|
buffer_[buffer_pos_++] = chr;
|
|
}
|
|
chr = *string++;
|
|
}
|
|
}
|
|
|
|
|
|
int Disassembler::SubstituteField(const Instruction *instr,
|
|
const char *format) {
|
|
switch (format[0]) {
|
|
// NB. The remaining substitution prefix upper-case characters are: JU.
|
|
case 'R': // Register. X or W, selected by sf (or alternative) bit.
|
|
case 'F': // FP register. S or D, selected by type field.
|
|
case 'V': // Vector register, V, vector format.
|
|
case 'Z': // Scalable vector register.
|
|
case 'W':
|
|
case 'X':
|
|
case 'B':
|
|
case 'H':
|
|
case 'S':
|
|
case 'D':
|
|
case 'Q':
|
|
return SubstituteRegisterField(instr, format);
|
|
case 'P':
|
|
return SubstitutePredicateRegisterField(instr, format);
|
|
case 'I':
|
|
return SubstituteImmediateField(instr, format);
|
|
case 'L':
|
|
return SubstituteLiteralField(instr, format);
|
|
case 'N':
|
|
return SubstituteShiftField(instr, format);
|
|
case 'C':
|
|
return SubstituteConditionField(instr, format);
|
|
case 'E':
|
|
return SubstituteExtendField(instr, format);
|
|
case 'A':
|
|
return SubstitutePCRelAddressField(instr, format);
|
|
case 'T':
|
|
return SubstituteBranchTargetField(instr, format);
|
|
case 'O':
|
|
return SubstituteLSRegOffsetField(instr, format);
|
|
case 'M':
|
|
return SubstituteBarrierField(instr, format);
|
|
case 'K':
|
|
return SubstituteCrField(instr, format);
|
|
case 'G':
|
|
return SubstituteSysOpField(instr, format);
|
|
case 'p':
|
|
return SubstitutePrefetchField(instr, format);
|
|
case 'u':
|
|
case 's':
|
|
return SubstituteIntField(instr, format);
|
|
case 't':
|
|
return SubstituteSVESize(instr, format);
|
|
case '?':
|
|
return SubstituteTernary(instr, format);
|
|
default: {
|
|
VIXL_UNREACHABLE();
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::pair<unsigned, unsigned> Disassembler::GetRegNumForField(
|
|
const Instruction *instr, char reg_prefix, const char *field) {
|
|
unsigned reg_num = UINT_MAX;
|
|
unsigned field_len = 1;
|
|
|
|
switch (field[0]) {
|
|
case 'd':
|
|
reg_num = instr->GetRd();
|
|
break;
|
|
case 'n':
|
|
reg_num = instr->GetRn();
|
|
break;
|
|
case 'm':
|
|
reg_num = instr->GetRm();
|
|
break;
|
|
case 'e':
|
|
// This is register Rm, but using a 4-bit specifier. Used in NEON
|
|
// by-element instructions.
|
|
reg_num = instr->GetRmLow16();
|
|
break;
|
|
case 'f':
|
|
// This is register Rm, but using an element size dependent number of bits
|
|
// in the register specifier.
|
|
reg_num =
|
|
(instr->GetNEONSize() < 2) ? instr->GetRmLow16() : instr->GetRm();
|
|
break;
|
|
case 'a':
|
|
reg_num = instr->GetRa();
|
|
break;
|
|
case 's':
|
|
reg_num = instr->GetRs();
|
|
break;
|
|
case 't':
|
|
reg_num = instr->GetRt();
|
|
break;
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
|
|
switch (field[1]) {
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
if ((reg_prefix == 'V') || (reg_prefix == 'Z')) { // t2/3/4, n2/3/4
|
|
VIXL_ASSERT((field[0] == 't') || (field[0] == 'n'));
|
|
reg_num = (reg_num + field[1] - '1') % 32;
|
|
field_len++;
|
|
} else {
|
|
VIXL_ASSERT((field[0] == 't') && (field[1] == '2'));
|
|
reg_num = instr->GetRt2();
|
|
field_len++;
|
|
}
|
|
break;
|
|
case '+': // Rt+, Rs+ (ie. Rt + 1, Rs + 1)
|
|
VIXL_ASSERT((reg_prefix == 'W') || (reg_prefix == 'X'));
|
|
VIXL_ASSERT((field[0] == 's') || (field[0] == 't'));
|
|
reg_num++;
|
|
field_len++;
|
|
break;
|
|
case 's': // Core registers that are (w)sp rather than zr.
|
|
VIXL_ASSERT((reg_prefix == 'W') || (reg_prefix == 'X'));
|
|
reg_num = (reg_num == kZeroRegCode) ? kSPRegInternalCode : reg_num;
|
|
field_len++;
|
|
break;
|
|
}
|
|
|
|
VIXL_ASSERT(reg_num != UINT_MAX);
|
|
return std::make_pair(reg_num, field_len);
|
|
}
|
|
|
|
int Disassembler::SubstituteRegisterField(const Instruction *instr,
|
|
const char *format) {
|
|
unsigned field_len = 1; // Initially, count only the first character.
|
|
|
|
// The first character of the register format field, eg R, X, S, etc.
|
|
char reg_prefix = format[0];
|
|
|
|
// Pointer to the character after the prefix. This may be one of the standard
|
|
// symbols representing a register encoding, or a two digit bit position,
|
|
// handled by the following code.
|
|
const char *reg_field = &format[1];
|
|
|
|
if (reg_prefix == 'R') {
|
|
bool is_x = instr->GetSixtyFourBits() == 1;
|
|
if (strspn(reg_field, "0123456789") == 2) { // r20d, r31n, etc.
|
|
// Core W or X registers where the type is determined by a specified bit
|
|
// position, eg. 'R20d, 'R05n. This is like the 'Rd syntax, where bit 31
|
|
// is implicitly used to select between W and X.
|
|
int bitpos = ((reg_field[0] - '0') * 10) + (reg_field[1] - '0');
|
|
VIXL_ASSERT(bitpos <= 31);
|
|
is_x = (instr->ExtractBit(bitpos) == 1);
|
|
reg_field = &format[3];
|
|
field_len += 2;
|
|
}
|
|
reg_prefix = is_x ? 'X' : 'W';
|
|
}
|
|
|
|
std::pair<unsigned, unsigned> rn =
|
|
GetRegNumForField(instr, reg_prefix, reg_field);
|
|
unsigned reg_num = rn.first;
|
|
field_len += rn.second;
|
|
|
|
if (reg_field[0] == 'm') {
|
|
switch (reg_field[1]) {
|
|
// Handle registers tagged with b (bytes), z (instruction), or
|
|
// r (registers), used for address updates in NEON load/store
|
|
// instructions.
|
|
case 'r':
|
|
case 'b':
|
|
case 'z': {
|
|
VIXL_ASSERT(reg_prefix == 'X');
|
|
field_len = 3;
|
|
char *eimm;
|
|
int imm = static_cast<int>(strtol(®_field[2], &eimm, 10));
|
|
field_len += static_cast<unsigned>(eimm - ®_field[2]);
|
|
if (reg_num == 31) {
|
|
switch (reg_field[1]) {
|
|
case 'z':
|
|
imm *= (1 << instr->GetNEONLSSize());
|
|
break;
|
|
case 'r':
|
|
imm *= (instr->GetNEONQ() == 0) ? kDRegSizeInBytes
|
|
: kQRegSizeInBytes;
|
|
break;
|
|
case 'b':
|
|
break;
|
|
}
|
|
AppendToOutput("#%d", imm);
|
|
return field_len;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CPURegister::RegisterType reg_type = CPURegister::kRegister;
|
|
unsigned reg_size = kXRegSize;
|
|
|
|
if (reg_prefix == 'F') {
|
|
switch (instr->GetFPType()) {
|
|
case 3:
|
|
reg_prefix = 'H';
|
|
break;
|
|
case 0:
|
|
reg_prefix = 'S';
|
|
break;
|
|
default:
|
|
reg_prefix = 'D';
|
|
}
|
|
}
|
|
|
|
switch (reg_prefix) {
|
|
case 'W':
|
|
reg_type = CPURegister::kRegister;
|
|
reg_size = kWRegSize;
|
|
break;
|
|
case 'X':
|
|
reg_type = CPURegister::kRegister;
|
|
reg_size = kXRegSize;
|
|
break;
|
|
case 'B':
|
|
reg_type = CPURegister::kVRegister;
|
|
reg_size = kBRegSize;
|
|
break;
|
|
case 'H':
|
|
reg_type = CPURegister::kVRegister;
|
|
reg_size = kHRegSize;
|
|
break;
|
|
case 'S':
|
|
reg_type = CPURegister::kVRegister;
|
|
reg_size = kSRegSize;
|
|
break;
|
|
case 'D':
|
|
reg_type = CPURegister::kVRegister;
|
|
reg_size = kDRegSize;
|
|
break;
|
|
case 'Q':
|
|
reg_type = CPURegister::kVRegister;
|
|
reg_size = kQRegSize;
|
|
break;
|
|
case 'V':
|
|
if (reg_field[1] == 'v') {
|
|
reg_type = CPURegister::kVRegister;
|
|
reg_size = 1 << (instr->GetSVESize() + 3);
|
|
field_len++;
|
|
break;
|
|
}
|
|
AppendToOutput("v%d", reg_num);
|
|
return field_len;
|
|
case 'Z':
|
|
AppendToOutput("z%d", reg_num);
|
|
return field_len;
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
|
|
AppendRegisterNameToOutput(instr, CPURegister(reg_num, reg_size, reg_type));
|
|
|
|
return field_len;
|
|
}
|
|
|
|
int Disassembler::SubstitutePredicateRegisterField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(format[0] == 'P');
|
|
switch (format[1]) {
|
|
// This field only supports P register that are always encoded in the same
|
|
// position.
|
|
case 'd':
|
|
case 't':
|
|
AppendToOutput("p%u", instr->GetPt());
|
|
break;
|
|
case 'n':
|
|
AppendToOutput("p%u", instr->GetPn());
|
|
break;
|
|
case 'm':
|
|
AppendToOutput("p%u", instr->GetPm());
|
|
break;
|
|
case 'g':
|
|
VIXL_ASSERT(format[2] == 'l');
|
|
AppendToOutput("p%u", instr->GetPgLow8());
|
|
return 3;
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
return 2;
|
|
}
|
|
|
|
int Disassembler::SubstituteImmediateField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(format[0] == 'I');
|
|
|
|
switch (format[1]) {
|
|
case 'M': { // IMoveImm, IMoveNeg or IMoveLSL.
|
|
if (format[5] == 'L') {
|
|
AppendToOutput("#0x%" PRIx32, instr->GetImmMoveWide());
|
|
if (instr->GetShiftMoveWide() > 0) {
|
|
AppendToOutput(", lsl #%" PRId32, 16 * instr->GetShiftMoveWide());
|
|
}
|
|
} else {
|
|
VIXL_ASSERT((format[5] == 'I') || (format[5] == 'N'));
|
|
uint64_t imm = static_cast<uint64_t>(instr->GetImmMoveWide())
|
|
<< (16 * instr->GetShiftMoveWide());
|
|
if (format[5] == 'N') imm = ~imm;
|
|
if (!instr->GetSixtyFourBits()) imm &= UINT64_C(0xffffffff);
|
|
AppendToOutput("#0x%" PRIx64, imm);
|
|
}
|
|
return 8;
|
|
}
|
|
case 'L': {
|
|
switch (format[2]) {
|
|
case 'L': { // ILLiteral - Immediate Load Literal.
|
|
AppendToOutput("pc%+" PRId32,
|
|
instr->GetImmLLiteral() *
|
|
static_cast<int>(kLiteralEntrySize));
|
|
return 9;
|
|
}
|
|
case 'S': { // ILS - Immediate Load/Store.
|
|
// ILSi - As above, but an index field which must not be
|
|
// omitted even if it is zero.
|
|
bool is_index = format[3] == 'i';
|
|
if (is_index || (instr->GetImmLS() != 0)) {
|
|
AppendToOutput(", #%" PRId32, instr->GetImmLS());
|
|
}
|
|
return is_index ? 4 : 3;
|
|
}
|
|
case 'P': { // ILPx - Immediate Load/Store Pair, x = access size.
|
|
// ILPxi - As above, but an index field which must not be
|
|
// omitted even if it is zero.
|
|
VIXL_ASSERT((format[3] >= '0') && (format[3] <= '9'));
|
|
bool is_index = format[4] == 'i';
|
|
if (is_index || (instr->GetImmLSPair() != 0)) {
|
|
// format[3] is the scale value. Convert to a number.
|
|
int scale = 1 << (format[3] - '0');
|
|
AppendToOutput(", #%" PRId32, instr->GetImmLSPair() * scale);
|
|
}
|
|
return is_index ? 5 : 4;
|
|
}
|
|
case 'U': { // ILU - Immediate Load/Store Unsigned.
|
|
if (instr->GetImmLSUnsigned() != 0) {
|
|
int shift = instr->GetSizeLS();
|
|
AppendToOutput(", #%" PRId32, instr->GetImmLSUnsigned() << shift);
|
|
}
|
|
return 3;
|
|
}
|
|
case 'A': { // ILA - Immediate Load with pointer authentication.
|
|
if (instr->GetImmLSPAC() != 0) {
|
|
AppendToOutput(", #%" PRId32, instr->GetImmLSPAC());
|
|
}
|
|
return 3;
|
|
}
|
|
default: {
|
|
VIXL_UNIMPLEMENTED();
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
case 'C': { // ICondB - Immediate Conditional Branch.
|
|
int64_t offset = instr->GetImmCondBranch() << 2;
|
|
AppendPCRelativeOffsetToOutput(instr, offset);
|
|
return 6;
|
|
}
|
|
case 'A': { // IAddSub.
|
|
int64_t imm = instr->GetImmAddSub() << (12 * instr->GetImmAddSubShift());
|
|
AppendToOutput("#0x%" PRIx64 " (%" PRId64 ")", imm, imm);
|
|
return 7;
|
|
}
|
|
case 'F': { // IFP, IFPNeon, IFPSve or IFPFBits.
|
|
int imm8 = 0;
|
|
size_t len = strlen("IFP");
|
|
switch (format[3]) {
|
|
case 'F':
|
|
VIXL_ASSERT(strncmp(format, "IFPFBits", strlen("IFPFBits")) == 0);
|
|
AppendToOutput("#%" PRId32, 64 - instr->GetFPScale());
|
|
return static_cast<int>(strlen("IFPFBits"));
|
|
case 'N':
|
|
VIXL_ASSERT(strncmp(format, "IFPNeon", strlen("IFPNeon")) == 0);
|
|
imm8 = instr->GetImmNEONabcdefgh();
|
|
len += strlen("Neon");
|
|
break;
|
|
case 'S':
|
|
VIXL_ASSERT(strncmp(format, "IFPSve", strlen("IFPSve")) == 0);
|
|
imm8 = instr->ExtractBits(12, 5);
|
|
len += strlen("Sve");
|
|
break;
|
|
default:
|
|
VIXL_ASSERT(strncmp(format, "IFP", strlen("IFP")) == 0);
|
|
imm8 = instr->GetImmFP();
|
|
break;
|
|
}
|
|
AppendToOutput("#0x%" PRIx32 " (%.4f)",
|
|
imm8,
|
|
Instruction::Imm8ToFP32(imm8));
|
|
return static_cast<int>(len);
|
|
}
|
|
case 'H': { // IH - ImmHint
|
|
AppendToOutput("#%" PRId32, instr->GetImmHint());
|
|
return 2;
|
|
}
|
|
case 'T': { // ITri - Immediate Triangular Encoded.
|
|
if (format[4] == 'S') {
|
|
VIXL_ASSERT((format[5] == 'v') && (format[6] == 'e'));
|
|
switch (format[7]) {
|
|
case 'l':
|
|
// SVE logical immediate encoding.
|
|
AppendToOutput("#0x%" PRIx64, instr->GetSVEImmLogical());
|
|
return 8;
|
|
case 'p': {
|
|
// SVE predicated shift immediate encoding, lsl.
|
|
std::pair<int, int> shift_and_lane_size =
|
|
instr->GetSVEImmShiftAndLaneSizeLog2(
|
|
/* is_predicated = */ true);
|
|
int lane_bits = 8 << shift_and_lane_size.second;
|
|
AppendToOutput("#%" PRId32, lane_bits - shift_and_lane_size.first);
|
|
return 8;
|
|
}
|
|
case 'q': {
|
|
// SVE predicated shift immediate encoding, asr and lsr.
|
|
std::pair<int, int> shift_and_lane_size =
|
|
instr->GetSVEImmShiftAndLaneSizeLog2(
|
|
/* is_predicated = */ true);
|
|
AppendToOutput("#%" PRId32, shift_and_lane_size.first);
|
|
return 8;
|
|
}
|
|
case 'r': {
|
|
// SVE unpredicated shift immediate encoding, left shifts.
|
|
std::pair<int, int> shift_and_lane_size =
|
|
instr->GetSVEImmShiftAndLaneSizeLog2(
|
|
/* is_predicated = */ false);
|
|
int lane_bits = 8 << shift_and_lane_size.second;
|
|
AppendToOutput("#%" PRId32, lane_bits - shift_and_lane_size.first);
|
|
return 8;
|
|
}
|
|
case 's': {
|
|
// SVE unpredicated shift immediate encoding, right shifts.
|
|
std::pair<int, int> shift_and_lane_size =
|
|
instr->GetSVEImmShiftAndLaneSizeLog2(
|
|
/* is_predicated = */ false);
|
|
AppendToOutput("#%" PRId32, shift_and_lane_size.first);
|
|
return 8;
|
|
}
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
return 0;
|
|
}
|
|
} else {
|
|
AppendToOutput("#0x%" PRIx64, instr->GetImmLogical());
|
|
return 4;
|
|
}
|
|
}
|
|
case 'N': { // INzcv.
|
|
int nzcv = (instr->GetNzcv() << Flags_offset);
|
|
AppendToOutput("#%c%c%c%c",
|
|
((nzcv & NFlag) == 0) ? 'n' : 'N',
|
|
((nzcv & ZFlag) == 0) ? 'z' : 'Z',
|
|
((nzcv & CFlag) == 0) ? 'c' : 'C',
|
|
((nzcv & VFlag) == 0) ? 'v' : 'V');
|
|
return 5;
|
|
}
|
|
case 'P': { // IP - Conditional compare.
|
|
AppendToOutput("#%" PRId32, instr->GetImmCondCmp());
|
|
return 2;
|
|
}
|
|
case 'B': { // Bitfields.
|
|
return SubstituteBitfieldImmediateField(instr, format);
|
|
}
|
|
case 'E': { // IExtract.
|
|
AppendToOutput("#%" PRId32, instr->GetImmS());
|
|
return 8;
|
|
}
|
|
case 't': { // It - Test and branch bit.
|
|
AppendToOutput("#%" PRId32,
|
|
(instr->GetImmTestBranchBit5() << 5) |
|
|
instr->GetImmTestBranchBit40());
|
|
return 2;
|
|
}
|
|
case 'S': { // ISveSvl - SVE 'mul vl' immediate for structured ld/st.
|
|
VIXL_ASSERT(strncmp(format, "ISveSvl", 7) == 0);
|
|
int imm = instr->ExtractSignedBits(19, 16);
|
|
if (imm != 0) {
|
|
int reg_count = instr->ExtractBits(22, 21) + 1;
|
|
AppendToOutput(", #%d, mul vl", imm * reg_count);
|
|
}
|
|
return 7;
|
|
}
|
|
case 's': { // Is - Shift (immediate).
|
|
switch (format[2]) {
|
|
case 'R': { // IsR - right shifts.
|
|
int shift = 16 << HighestSetBitPosition(instr->GetImmNEONImmh());
|
|
shift -= instr->GetImmNEONImmhImmb();
|
|
AppendToOutput("#%d", shift);
|
|
return 3;
|
|
}
|
|
case 'L': { // IsL - left shifts.
|
|
int shift = instr->GetImmNEONImmhImmb();
|
|
shift -= 8 << HighestSetBitPosition(instr->GetImmNEONImmh());
|
|
AppendToOutput("#%d", shift);
|
|
return 3;
|
|
}
|
|
default: {
|
|
VIXL_UNIMPLEMENTED();
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
case 'D': { // IDebug - HLT and BRK instructions.
|
|
AppendToOutput("#0x%" PRIx32, instr->GetImmException());
|
|
return 6;
|
|
}
|
|
case 'U': { // IUdf - UDF immediate.
|
|
AppendToOutput("#0x%" PRIx32, instr->GetImmUdf());
|
|
return 4;
|
|
}
|
|
case 'V': { // Immediate Vector.
|
|
switch (format[2]) {
|
|
case 'E': { // IVExtract.
|
|
AppendToOutput("#%" PRId32, instr->GetImmNEONExt());
|
|
return 9;
|
|
}
|
|
case 'B': { // IVByElemIndex.
|
|
int ret = static_cast<int>(strlen("IVByElemIndex"));
|
|
uint32_t vm_index = instr->GetNEONH() << 2;
|
|
vm_index |= instr->GetNEONL() << 1;
|
|
vm_index |= instr->GetNEONM();
|
|
|
|
static const char *format_rot = "IVByElemIndexRot";
|
|
static const char *format_fhm = "IVByElemIndexFHM";
|
|
if (strncmp(format, format_rot, strlen(format_rot)) == 0) {
|
|
// FCMLA uses 'H' bit index when SIZE is 2, else H:L
|
|
VIXL_ASSERT((instr->GetNEONSize() == 1) ||
|
|
(instr->GetNEONSize() == 2));
|
|
vm_index >>= instr->GetNEONSize();
|
|
ret = static_cast<int>(strlen(format_rot));
|
|
} else if (strncmp(format, format_fhm, strlen(format_fhm)) == 0) {
|
|
// Nothing to do - FMLAL and FMLSL use H:L:M.
|
|
ret = static_cast<int>(strlen(format_fhm));
|
|
} else {
|
|
if (instr->GetNEONSize() == 2) {
|
|
// S-sized elements use H:L.
|
|
vm_index >>= 1;
|
|
} else if (instr->GetNEONSize() == 3) {
|
|
// D-sized elements use H.
|
|
vm_index >>= 2;
|
|
}
|
|
}
|
|
AppendToOutput("%d", vm_index);
|
|
return ret;
|
|
}
|
|
case 'I': { // INS element.
|
|
if (strncmp(format, "IVInsIndex", strlen("IVInsIndex")) == 0) {
|
|
unsigned rd_index, rn_index;
|
|
unsigned imm5 = instr->GetImmNEON5();
|
|
unsigned imm4 = instr->GetImmNEON4();
|
|
int tz = CountTrailingZeros(imm5, 32);
|
|
if (tz <= 3) { // Defined for tz = 0 to 3 only.
|
|
rd_index = imm5 >> (tz + 1);
|
|
rn_index = imm4 >> tz;
|
|
if (strncmp(format, "IVInsIndex1", strlen("IVInsIndex1")) == 0) {
|
|
AppendToOutput("%d", rd_index);
|
|
return static_cast<int>(strlen("IVInsIndex1"));
|
|
} else if (strncmp(format,
|
|
"IVInsIndex2",
|
|
strlen("IVInsIndex2")) == 0) {
|
|
AppendToOutput("%d", rn_index);
|
|
return static_cast<int>(strlen("IVInsIndex2"));
|
|
}
|
|
}
|
|
return 0;
|
|
} else if (strncmp(format,
|
|
"IVInsSVEIndex",
|
|
strlen("IVInsSVEIndex")) == 0) {
|
|
std::pair<int, int> index_and_lane_size =
|
|
instr->GetSVEPermuteIndexAndLaneSizeLog2();
|
|
AppendToOutput("%d", index_and_lane_size.first);
|
|
return static_cast<int>(strlen("IVInsSVEIndex"));
|
|
}
|
|
VIXL_FALLTHROUGH();
|
|
}
|
|
case 'L': { // IVLSLane[0123] - suffix indicates access size shift.
|
|
AppendToOutput("%d", instr->GetNEONLSIndex(format[8] - '0'));
|
|
return 9;
|
|
}
|
|
case 'M': { // Modified Immediate cases.
|
|
if (strncmp(format, "IVMIImm8", strlen("IVMIImm8")) == 0) {
|
|
uint64_t imm8 = instr->GetImmNEONabcdefgh();
|
|
AppendToOutput("#0x%" PRIx64, imm8);
|
|
return static_cast<int>(strlen("IVMIImm8"));
|
|
} else if (strncmp(format, "IVMIImm", strlen("IVMIImm")) == 0) {
|
|
uint64_t imm8 = instr->GetImmNEONabcdefgh();
|
|
uint64_t imm = 0;
|
|
for (int i = 0; i < 8; ++i) {
|
|
if (imm8 & (UINT64_C(1) << i)) {
|
|
imm |= (UINT64_C(0xff) << (8 * i));
|
|
}
|
|
}
|
|
AppendToOutput("#0x%" PRIx64, imm);
|
|
return static_cast<int>(strlen("IVMIImm"));
|
|
} else if (strncmp(format,
|
|
"IVMIShiftAmt1",
|
|
strlen("IVMIShiftAmt1")) == 0) {
|
|
int cmode = instr->GetNEONCmode();
|
|
int shift_amount = 8 * ((cmode >> 1) & 3);
|
|
AppendToOutput("#%d", shift_amount);
|
|
return static_cast<int>(strlen("IVMIShiftAmt1"));
|
|
} else if (strncmp(format,
|
|
"IVMIShiftAmt2",
|
|
strlen("IVMIShiftAmt2")) == 0) {
|
|
int cmode = instr->GetNEONCmode();
|
|
int shift_amount = 8 << (cmode & 1);
|
|
AppendToOutput("#%d", shift_amount);
|
|
return static_cast<int>(strlen("IVMIShiftAmt2"));
|
|
} else {
|
|
VIXL_UNIMPLEMENTED();
|
|
return 0;
|
|
}
|
|
}
|
|
default: {
|
|
VIXL_UNIMPLEMENTED();
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
case 'X': { // IX - CLREX instruction.
|
|
AppendToOutput("#0x%" PRIx32, instr->GetCRm());
|
|
return 2;
|
|
}
|
|
case 'Y': { // IY - system register immediate.
|
|
switch (instr->GetImmSystemRegister()) {
|
|
case NZCV:
|
|
AppendToOutput("nzcv");
|
|
break;
|
|
case FPCR:
|
|
AppendToOutput("fpcr");
|
|
break;
|
|
case RNDR:
|
|
AppendToOutput("rndr");
|
|
break;
|
|
case RNDRRS:
|
|
AppendToOutput("rndrrs");
|
|
break;
|
|
default:
|
|
AppendToOutput("S%d_%d_c%d_c%d_%d",
|
|
instr->GetSysOp0(),
|
|
instr->GetSysOp1(),
|
|
instr->GetCRn(),
|
|
instr->GetCRm(),
|
|
instr->GetSysOp2());
|
|
break;
|
|
}
|
|
return 2;
|
|
}
|
|
case 'R': { // IR - Rotate right into flags.
|
|
switch (format[2]) {
|
|
case 'r': { // IRr - Rotate amount.
|
|
AppendToOutput("#%d", instr->GetImmRMIFRotation());
|
|
return 3;
|
|
}
|
|
default: {
|
|
VIXL_UNIMPLEMENTED();
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
case 'p': { // Ipc - SVE predicate constraint specifier.
|
|
VIXL_ASSERT(format[2] == 'c');
|
|
unsigned pattern = instr->GetImmSVEPredicateConstraint();
|
|
switch (pattern) {
|
|
// VL1-VL8 are encoded directly.
|
|
case SVE_VL1:
|
|
case SVE_VL2:
|
|
case SVE_VL3:
|
|
case SVE_VL4:
|
|
case SVE_VL5:
|
|
case SVE_VL6:
|
|
case SVE_VL7:
|
|
case SVE_VL8:
|
|
AppendToOutput("vl%u", pattern);
|
|
break;
|
|
// VL16-VL256 are encoded as log2(N) + c.
|
|
case SVE_VL16:
|
|
case SVE_VL32:
|
|
case SVE_VL64:
|
|
case SVE_VL128:
|
|
case SVE_VL256:
|
|
AppendToOutput("vl%u", 16 << (pattern - SVE_VL16));
|
|
break;
|
|
// Special cases.
|
|
case SVE_POW2:
|
|
AppendToOutput("pow2");
|
|
break;
|
|
case SVE_MUL4:
|
|
AppendToOutput("mul4");
|
|
break;
|
|
case SVE_MUL3:
|
|
AppendToOutput("mul3");
|
|
break;
|
|
case SVE_ALL:
|
|
AppendToOutput("all");
|
|
break;
|
|
default:
|
|
AppendToOutput("#0x%x", pattern);
|
|
break;
|
|
}
|
|
return 3;
|
|
}
|
|
default: {
|
|
VIXL_UNIMPLEMENTED();
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int Disassembler::SubstituteBitfieldImmediateField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT((format[0] == 'I') && (format[1] == 'B'));
|
|
unsigned r = instr->GetImmR();
|
|
unsigned s = instr->GetImmS();
|
|
|
|
switch (format[2]) {
|
|
case 'r': { // IBr.
|
|
AppendToOutput("#%d", r);
|
|
return 3;
|
|
}
|
|
case 's': { // IBs+1 or IBs-r+1.
|
|
if (format[3] == '+') {
|
|
AppendToOutput("#%d", s + 1);
|
|
return 5;
|
|
} else {
|
|
VIXL_ASSERT(format[3] == '-');
|
|
AppendToOutput("#%d", s - r + 1);
|
|
return 7;
|
|
}
|
|
}
|
|
case 'Z': { // IBZ-r.
|
|
VIXL_ASSERT((format[3] == '-') && (format[4] == 'r'));
|
|
unsigned reg_size =
|
|
(instr->GetSixtyFourBits() == 1) ? kXRegSize : kWRegSize;
|
|
AppendToOutput("#%d", reg_size - r);
|
|
return 5;
|
|
}
|
|
default: {
|
|
VIXL_UNREACHABLE();
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int Disassembler::SubstituteLiteralField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(strncmp(format, "LValue", 6) == 0);
|
|
USE(format);
|
|
|
|
const void *address = instr->GetLiteralAddress<const void *>();
|
|
switch (instr->Mask(LoadLiteralMask)) {
|
|
case LDR_w_lit:
|
|
case LDR_x_lit:
|
|
case LDRSW_x_lit:
|
|
case LDR_s_lit:
|
|
case LDR_d_lit:
|
|
case LDR_q_lit:
|
|
AppendCodeRelativeDataAddressToOutput(instr, address);
|
|
break;
|
|
case PRFM_lit: {
|
|
// Use the prefetch hint to decide how to print the address.
|
|
switch (instr->GetPrefetchHint()) {
|
|
case 0x0: // PLD: prefetch for load.
|
|
case 0x2: // PST: prepare for store.
|
|
AppendCodeRelativeDataAddressToOutput(instr, address);
|
|
break;
|
|
case 0x1: // PLI: preload instructions.
|
|
AppendCodeRelativeCodeAddressToOutput(instr, address);
|
|
break;
|
|
case 0x3: // Unallocated hint.
|
|
AppendCodeRelativeAddressToOutput(instr, address);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
|
|
return 6;
|
|
}
|
|
|
|
|
|
int Disassembler::SubstituteShiftField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(format[0] == 'N');
|
|
VIXL_ASSERT(instr->GetShiftDP() <= 0x3);
|
|
|
|
switch (format[1]) {
|
|
case 'D': { // NDP.
|
|
VIXL_ASSERT(instr->GetShiftDP() != ROR);
|
|
VIXL_FALLTHROUGH();
|
|
}
|
|
case 'L': { // NLo.
|
|
if (instr->GetImmDPShift() != 0) {
|
|
const char *shift_type[] = {"lsl", "lsr", "asr", "ror"};
|
|
AppendToOutput(", %s #%" PRId32,
|
|
shift_type[instr->GetShiftDP()],
|
|
instr->GetImmDPShift());
|
|
}
|
|
return 3;
|
|
}
|
|
case 'S': { // NSveS (SVE structured load/store indexing shift).
|
|
VIXL_ASSERT(strncmp(format, "NSveS", 5) == 0);
|
|
int msz = instr->ExtractBits(24, 23);
|
|
if (msz > 0) {
|
|
AppendToOutput(", lsl #%d", msz);
|
|
}
|
|
return 5;
|
|
}
|
|
default:
|
|
VIXL_UNIMPLEMENTED();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
int Disassembler::SubstituteConditionField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(format[0] == 'C');
|
|
const char *condition_code[] = {"eq",
|
|
"ne",
|
|
"hs",
|
|
"lo",
|
|
"mi",
|
|
"pl",
|
|
"vs",
|
|
"vc",
|
|
"hi",
|
|
"ls",
|
|
"ge",
|
|
"lt",
|
|
"gt",
|
|
"le",
|
|
"al",
|
|
"nv"};
|
|
int cond;
|
|
switch (format[1]) {
|
|
case 'B':
|
|
cond = instr->GetConditionBranch();
|
|
break;
|
|
case 'I': {
|
|
cond = InvertCondition(static_cast<Condition>(instr->GetCondition()));
|
|
break;
|
|
}
|
|
default:
|
|
cond = instr->GetCondition();
|
|
}
|
|
AppendToOutput("%s", condition_code[cond]);
|
|
return 4;
|
|
}
|
|
|
|
|
|
int Disassembler::SubstitutePCRelAddressField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT((strcmp(format, "AddrPCRelByte") == 0) || // Used by `adr`.
|
|
(strcmp(format, "AddrPCRelPage") == 0)); // Used by `adrp`.
|
|
|
|
int64_t offset = instr->GetImmPCRel();
|
|
|
|
// Compute the target address based on the effective address (after applying
|
|
// code_address_offset). This is required for correct behaviour of adrp.
|
|
const Instruction *base = instr + code_address_offset();
|
|
if (format[9] == 'P') {
|
|
offset *= kPageSize;
|
|
base = AlignDown(base, kPageSize);
|
|
}
|
|
// Strip code_address_offset before printing, so we can use the
|
|
// semantically-correct AppendCodeRelativeAddressToOutput.
|
|
const void *target =
|
|
reinterpret_cast<const void *>(base + offset - code_address_offset());
|
|
|
|
AppendPCRelativeOffsetToOutput(instr, offset);
|
|
AppendToOutput(" ");
|
|
AppendCodeRelativeAddressToOutput(instr, target);
|
|
return 13;
|
|
}
|
|
|
|
|
|
int Disassembler::SubstituteBranchTargetField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(strncmp(format, "TImm", 4) == 0);
|
|
|
|
int64_t offset = 0;
|
|
switch (format[5]) {
|
|
// BImmUncn - unconditional branch immediate.
|
|
case 'n':
|
|
offset = instr->GetImmUncondBranch();
|
|
break;
|
|
// BImmCond - conditional branch immediate.
|
|
case 'o':
|
|
offset = instr->GetImmCondBranch();
|
|
break;
|
|
// BImmCmpa - compare and branch immediate.
|
|
case 'm':
|
|
offset = instr->GetImmCmpBranch();
|
|
break;
|
|
// BImmTest - test and branch immediate.
|
|
case 'e':
|
|
offset = instr->GetImmTestBranch();
|
|
break;
|
|
default:
|
|
VIXL_UNIMPLEMENTED();
|
|
}
|
|
offset *= static_cast<int>(kInstructionSize);
|
|
const void *target_address = reinterpret_cast<const void *>(instr + offset);
|
|
VIXL_STATIC_ASSERT(sizeof(*instr) == 1);
|
|
|
|
AppendPCRelativeOffsetToOutput(instr, offset);
|
|
AppendToOutput(" ");
|
|
AppendCodeRelativeCodeAddressToOutput(instr, target_address);
|
|
|
|
return 8;
|
|
}
|
|
|
|
|
|
int Disassembler::SubstituteExtendField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(strncmp(format, "Ext", 3) == 0);
|
|
VIXL_ASSERT(instr->GetExtendMode() <= 7);
|
|
USE(format);
|
|
|
|
const char *extend_mode[] =
|
|
{"uxtb", "uxth", "uxtw", "uxtx", "sxtb", "sxth", "sxtw", "sxtx"};
|
|
|
|
// If rd or rn is SP, uxtw on 32-bit registers and uxtx on 64-bit
|
|
// registers becomes lsl.
|
|
if (((instr->GetRd() == kZeroRegCode) || (instr->GetRn() == kZeroRegCode)) &&
|
|
(((instr->GetExtendMode() == UXTW) && (instr->GetSixtyFourBits() == 0)) ||
|
|
(instr->GetExtendMode() == UXTX))) {
|
|
if (instr->GetImmExtendShift() > 0) {
|
|
AppendToOutput(", lsl #%" PRId32, instr->GetImmExtendShift());
|
|
}
|
|
} else {
|
|
AppendToOutput(", %s", extend_mode[instr->GetExtendMode()]);
|
|
if (instr->GetImmExtendShift() > 0) {
|
|
AppendToOutput(" #%" PRId32, instr->GetImmExtendShift());
|
|
}
|
|
}
|
|
return 3;
|
|
}
|
|
|
|
|
|
int Disassembler::SubstituteLSRegOffsetField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(strncmp(format, "Offsetreg", 9) == 0);
|
|
const char *extend_mode[] = {"undefined",
|
|
"undefined",
|
|
"uxtw",
|
|
"lsl",
|
|
"undefined",
|
|
"undefined",
|
|
"sxtw",
|
|
"sxtx"};
|
|
USE(format);
|
|
|
|
unsigned shift = instr->GetImmShiftLS();
|
|
Extend ext = static_cast<Extend>(instr->GetExtendMode());
|
|
char reg_type = ((ext == UXTW) || (ext == SXTW)) ? 'w' : 'x';
|
|
|
|
unsigned rm = instr->GetRm();
|
|
if (rm == kZeroRegCode) {
|
|
AppendToOutput("%czr", reg_type);
|
|
} else {
|
|
AppendToOutput("%c%d", reg_type, rm);
|
|
}
|
|
|
|
// Extend mode UXTX is an alias for shift mode LSL here.
|
|
if (!((ext == UXTX) && (shift == 0))) {
|
|
AppendToOutput(", %s", extend_mode[ext]);
|
|
if (shift != 0) {
|
|
AppendToOutput(" #%d", instr->GetSizeLS());
|
|
}
|
|
}
|
|
return 9;
|
|
}
|
|
|
|
|
|
int Disassembler::SubstitutePrefetchField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(format[0] == 'p');
|
|
USE(format);
|
|
|
|
bool is_sve =
|
|
(strncmp(format, "prefSVEOp", strlen("prefSVEOp")) == 0) ? true : false;
|
|
int placeholder_length = is_sve ? 9 : 6;
|
|
static const char *stream_options[] = {"keep", "strm"};
|
|
|
|
auto get_hints = [](bool want_sve_hint) -> std::vector<std::string> {
|
|
static const std::vector<std::string> sve_hints = {"ld", "st"};
|
|
static const std::vector<std::string> core_hints = {"ld", "li", "st"};
|
|
return (want_sve_hint) ? sve_hints : core_hints;
|
|
};
|
|
|
|
std::vector<std::string> hints = get_hints(is_sve);
|
|
unsigned hint =
|
|
is_sve ? instr->GetSVEPrefetchHint() : instr->GetPrefetchHint();
|
|
unsigned target = instr->GetPrefetchTarget() + 1;
|
|
unsigned stream = instr->GetPrefetchStream();
|
|
|
|
if ((hint >= hints.size()) || (target > 3)) {
|
|
// Unallocated prefetch operations.
|
|
if (is_sve) {
|
|
std::bitset<4> prefetch_mode(instr->GetSVEImmPrefetchOperation());
|
|
AppendToOutput("#0b%s", prefetch_mode.to_string().c_str());
|
|
} else {
|
|
std::bitset<5> prefetch_mode(instr->GetImmPrefetchOperation());
|
|
AppendToOutput("#0b%s", prefetch_mode.to_string().c_str());
|
|
}
|
|
} else {
|
|
VIXL_ASSERT(stream < ArrayLength(stream_options));
|
|
AppendToOutput("p%sl%d%s",
|
|
hints[hint].c_str(),
|
|
target,
|
|
stream_options[stream]);
|
|
}
|
|
return placeholder_length;
|
|
}
|
|
|
|
int Disassembler::SubstituteBarrierField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(format[0] == 'M');
|
|
USE(format);
|
|
|
|
static const char *options[4][4] = {{"sy (0b0000)", "oshld", "oshst", "osh"},
|
|
{"sy (0b0100)", "nshld", "nshst", "nsh"},
|
|
{"sy (0b1000)", "ishld", "ishst", "ish"},
|
|
{"sy (0b1100)", "ld", "st", "sy"}};
|
|
int domain = instr->GetImmBarrierDomain();
|
|
int type = instr->GetImmBarrierType();
|
|
|
|
AppendToOutput("%s", options[domain][type]);
|
|
return 1;
|
|
}
|
|
|
|
int Disassembler::SubstituteSysOpField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(format[0] == 'G');
|
|
int op = -1;
|
|
switch (format[1]) {
|
|
case '1':
|
|
op = instr->GetSysOp1();
|
|
break;
|
|
case '2':
|
|
op = instr->GetSysOp2();
|
|
break;
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
AppendToOutput("#%d", op);
|
|
return 2;
|
|
}
|
|
|
|
int Disassembler::SubstituteCrField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT(format[0] == 'K');
|
|
int cr = -1;
|
|
switch (format[1]) {
|
|
case 'n':
|
|
cr = instr->GetCRn();
|
|
break;
|
|
case 'm':
|
|
cr = instr->GetCRm();
|
|
break;
|
|
default:
|
|
VIXL_UNREACHABLE();
|
|
}
|
|
AppendToOutput("C%d", cr);
|
|
return 2;
|
|
}
|
|
|
|
int Disassembler::SubstituteIntField(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT((format[0] == 'u') || (format[0] == 's'));
|
|
|
|
// A generic signed or unsigned int field uses a placeholder of the form
|
|
// 'sAABB and 'uAABB respectively where AA and BB are two digit bit positions
|
|
// between 00 and 31, and AA >= BB. The placeholder is substituted with the
|
|
// decimal integer represented by the bits in the instruction between
|
|
// positions AA and BB inclusive.
|
|
//
|
|
// In addition, split fields can be represented using 'sAABB:CCDD, where CCDD
|
|
// become the least-significant bits of the result, and bit AA is the sign bit
|
|
// (if 's is used).
|
|
int32_t bits = 0;
|
|
int width = 0;
|
|
const char *c = format;
|
|
do {
|
|
c++; // Skip the 'u', 's' or ':'.
|
|
VIXL_ASSERT(strspn(c, "0123456789") == 4);
|
|
int msb = ((c[0] - '0') * 10) + (c[1] - '0');
|
|
int lsb = ((c[2] - '0') * 10) + (c[3] - '0');
|
|
c += 4; // Skip the characters we just read.
|
|
int chunk_width = msb - lsb + 1;
|
|
VIXL_ASSERT((chunk_width > 0) && (chunk_width < 32));
|
|
bits = (bits << chunk_width) | (instr->ExtractBits(msb, lsb));
|
|
width += chunk_width;
|
|
} while (*c == ':');
|
|
VIXL_ASSERT(IsUintN(width, bits));
|
|
|
|
if (format[0] == 's') {
|
|
bits = ExtractSignedBitfield32(width - 1, 0, bits);
|
|
}
|
|
|
|
if (*c == '+') {
|
|
// A "+n" trailing the format specifier indicates the extracted value should
|
|
// be incremented by n. This is for cases where the encoding is zero-based,
|
|
// but range of values is not, eg. values [1, 16] encoded as [0, 15]
|
|
char *new_c;
|
|
uint64_t value = strtoul(c + 1, &new_c, 10);
|
|
c = new_c;
|
|
VIXL_ASSERT(IsInt32(value));
|
|
bits = static_cast<int32_t>(bits + value);
|
|
} else if (*c == '*') {
|
|
// Similarly, a "*n" trailing the format specifier indicates the extracted
|
|
// value should be multiplied by n. This is for cases where the encoded
|
|
// immediate is scaled, for example by access size.
|
|
char *new_c;
|
|
uint64_t value = strtoul(c + 1, &new_c, 10);
|
|
c = new_c;
|
|
VIXL_ASSERT(IsInt32(value));
|
|
bits = static_cast<int32_t>(bits * value);
|
|
}
|
|
|
|
AppendToOutput("%d", bits);
|
|
|
|
return static_cast<int>(c - format);
|
|
}
|
|
|
|
int Disassembler::SubstituteSVESize(const Instruction *instr,
|
|
const char *format) {
|
|
USE(format);
|
|
VIXL_ASSERT(format[0] == 't');
|
|
|
|
static const char sizes[] = {'b', 'h', 's', 'd', 'q'};
|
|
unsigned size_in_bytes_log2 = instr->GetSVESize();
|
|
int placeholder_length = 1;
|
|
switch (format[1]) {
|
|
case 'f': // 'tf - FP size encoded in <18:17>
|
|
placeholder_length++;
|
|
size_in_bytes_log2 = instr->ExtractBits(18, 17);
|
|
break;
|
|
case 'l':
|
|
placeholder_length++;
|
|
if (format[2] == 's') {
|
|
// 'tls: Loads and stores
|
|
size_in_bytes_log2 = instr->ExtractBits(22, 21);
|
|
placeholder_length++;
|
|
if (format[3] == 's') {
|
|
// Sign extension load.
|
|
unsigned msize = instr->ExtractBits(24, 23);
|
|
if (msize > size_in_bytes_log2) size_in_bytes_log2 ^= 0x3;
|
|
placeholder_length++;
|
|
}
|
|
} else {
|
|
// 'tl: Logical operations
|
|
size_in_bytes_log2 = instr->GetSVEBitwiseImmLaneSizeInBytesLog2();
|
|
}
|
|
break;
|
|
case 'm': // 'tmsz
|
|
VIXL_ASSERT(strncmp(format, "tmsz", 4) == 0);
|
|
placeholder_length += 3;
|
|
size_in_bytes_log2 = instr->ExtractBits(24, 23);
|
|
break;
|
|
case 'i': { // 'ti: indices.
|
|
std::pair<int, int> index_and_lane_size =
|
|
instr->GetSVEPermuteIndexAndLaneSizeLog2();
|
|
placeholder_length++;
|
|
size_in_bytes_log2 = index_and_lane_size.second;
|
|
break;
|
|
}
|
|
case 's':
|
|
if (format[2] == 'z') {
|
|
VIXL_ASSERT((format[3] == 'p') || (format[3] == 's') ||
|
|
(format[3] == 'd'));
|
|
bool is_predicated = (format[3] == 'p');
|
|
std::pair<int, int> shift_and_lane_size =
|
|
instr->GetSVEImmShiftAndLaneSizeLog2(is_predicated);
|
|
size_in_bytes_log2 = shift_and_lane_size.second;
|
|
if (format[3] == 'd') { // Double size lanes.
|
|
size_in_bytes_log2++;
|
|
}
|
|
placeholder_length += 3; // skip "sz(p|s|d)"
|
|
}
|
|
break;
|
|
case 'h':
|
|
// Half size of the lane size field.
|
|
size_in_bytes_log2 -= 1;
|
|
placeholder_length++;
|
|
break;
|
|
case 'q':
|
|
// Quarter size of the lane size field.
|
|
size_in_bytes_log2 -= 2;
|
|
placeholder_length++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
VIXL_ASSERT(size_in_bytes_log2 < ArrayLength(sizes));
|
|
AppendToOutput("%c", sizes[size_in_bytes_log2]);
|
|
|
|
return placeholder_length;
|
|
}
|
|
|
|
int Disassembler::SubstituteTernary(const Instruction *instr,
|
|
const char *format) {
|
|
VIXL_ASSERT((format[0] == '?') && (format[3] == ':'));
|
|
|
|
// The ternary substitution of the format "'?bb:TF" is replaced by a single
|
|
// character, either T or F, depending on the value of the bit at position
|
|
// bb in the instruction. For example, "'?31:xw" is substituted with "x" if
|
|
// bit 31 is true, and "w" otherwise.
|
|
VIXL_ASSERT(strspn(&format[1], "0123456789") == 2);
|
|
char *c;
|
|
uint64_t value = strtoul(&format[1], &c, 10);
|
|
VIXL_ASSERT(value < (kInstructionSize * kBitsPerByte));
|
|
VIXL_ASSERT((*c == ':') && (strlen(c) >= 3)); // Minimum of ":TF"
|
|
c++;
|
|
AppendToOutput("%c", c[1 - instr->ExtractBit(static_cast<int>(value))]);
|
|
return 6;
|
|
}
|
|
|
|
void Disassembler::ResetOutput() {
|
|
buffer_pos_ = 0;
|
|
buffer_[buffer_pos_] = 0;
|
|
}
|
|
|
|
|
|
void Disassembler::AppendToOutput(const char *format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
buffer_pos_ += vsnprintf(&buffer_[buffer_pos_],
|
|
buffer_size_ - buffer_pos_,
|
|
format,
|
|
args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
void PrintDisassembler::Disassemble(const Instruction *instr) {
|
|
Decoder decoder;
|
|
if (cpu_features_auditor_ != NULL) {
|
|
decoder.AppendVisitor(cpu_features_auditor_);
|
|
}
|
|
decoder.AppendVisitor(this);
|
|
decoder.Decode(instr);
|
|
}
|
|
|
|
void PrintDisassembler::DisassembleBuffer(const Instruction *start,
|
|
const Instruction *end) {
|
|
Decoder decoder;
|
|
if (cpu_features_auditor_ != NULL) {
|
|
decoder.AppendVisitor(cpu_features_auditor_);
|
|
}
|
|
decoder.AppendVisitor(this);
|
|
decoder.Decode(start, end);
|
|
}
|
|
|
|
void PrintDisassembler::DisassembleBuffer(const Instruction *start,
|
|
uint64_t size) {
|
|
DisassembleBuffer(start, start + size);
|
|
}
|
|
|
|
|
|
void PrintDisassembler::ProcessOutput(const Instruction *instr) {
|
|
int64_t address = CodeRelativeAddress(instr);
|
|
|
|
uint64_t abs_address;
|
|
const char *sign;
|
|
if (signed_addresses_) {
|
|
if (address < 0) {
|
|
sign = "-";
|
|
abs_address = UnsignedNegate(static_cast<uint64_t>(address));
|
|
} else {
|
|
// Leave a leading space, to maintain alignment.
|
|
sign = " ";
|
|
abs_address = address;
|
|
}
|
|
} else {
|
|
sign = "";
|
|
abs_address = address;
|
|
}
|
|
|
|
int bytes_printed = fprintf(stream_,
|
|
"%s0x%016" PRIx64 " %08" PRIx32 "\t\t%s",
|
|
sign,
|
|
abs_address,
|
|
instr->GetInstructionBits(),
|
|
GetOutput());
|
|
if (cpu_features_auditor_ != NULL) {
|
|
CPUFeatures needs = cpu_features_auditor_->GetInstructionFeatures();
|
|
needs.Remove(cpu_features_auditor_->GetAvailableFeatures());
|
|
if (needs != CPUFeatures::None()) {
|
|
// Try to align annotations. This value is arbitrary, but based on looking
|
|
// good with most instructions. Note that, for historical reasons, the
|
|
// disassembly itself is printed with tab characters, so bytes_printed is
|
|
// _not_ equivalent to the number of occupied screen columns. However, the
|
|
// prefix before the tabs is always the same length, so the annotation
|
|
// indentation does not change from one line to the next.
|
|
const int indent_to = 70;
|
|
// Always allow some space between the instruction and the annotation.
|
|
const int min_pad = 2;
|
|
|
|
int pad = std::max(min_pad, (indent_to - bytes_printed));
|
|
fprintf(stream_, "%*s", pad, "");
|
|
|
|
std::stringstream features;
|
|
features << needs;
|
|
fprintf(stream_,
|
|
"%s%s%s",
|
|
cpu_features_prefix_,
|
|
features.str().c_str(),
|
|
cpu_features_suffix_);
|
|
}
|
|
}
|
|
fprintf(stream_, "\n");
|
|
}
|
|
|
|
} // namespace aarch64
|
|
} // namespace vixl
|