// Copyright 2018, 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 "cpu-features.h" #include "globals-vixl.h" #include "utils-vixl.h" #include "decoder-aarch64.h" #include "cpu-features-auditor-aarch64.h" namespace vixl { namespace aarch64 { const CPUFeaturesAuditor::FormToVisitorFnMap* CPUFeaturesAuditor::GetFormToVisitorFnMap() { static const FormToVisitorFnMap form_to_visitor = { DEFAULT_FORM_TO_VISITOR_MAP(CPUFeaturesAuditor), SIM_AUD_VISITOR_MAP(CPUFeaturesAuditor), {"fcmla_asimdelem_c_h"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fcmla_asimdelem_c_s"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmlal2_asimdelem_lh"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmlal_asimdelem_lh"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmla_asimdelem_rh_h"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmla_asimdelem_r_sd"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmlsl2_asimdelem_lh"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmlsl_asimdelem_lh"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmls_asimdelem_rh_h"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmls_asimdelem_r_sd"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmulx_asimdelem_rh_h"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmulx_asimdelem_r_sd"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmul_asimdelem_rh_h"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"fmul_asimdelem_r_sd"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"sdot_asimdelem_d"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"smlal_asimdelem_l"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"smlsl_asimdelem_l"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"smull_asimdelem_l"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"sqdmlal_asimdelem_l"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"sqdmlsl_asimdelem_l"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"sqdmull_asimdelem_l"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"udot_asimdelem_d"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"umlal_asimdelem_l"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"umlsl_asimdelem_l"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, {"umull_asimdelem_l"_h, &CPUFeaturesAuditor::VisitNEONByIndexedElement}, }; return &form_to_visitor; } // Every instruction must update last_instruction_, even if only to clear it, // and every instruction must also update seen_ once it has been fully handled. // This scope makes that simple, and allows early returns in the decode logic. class CPUFeaturesAuditor::RecordInstructionFeaturesScope { public: explicit RecordInstructionFeaturesScope(CPUFeaturesAuditor* auditor) : auditor_(auditor) { auditor_->last_instruction_ = CPUFeatures::None(); } ~RecordInstructionFeaturesScope() { auditor_->seen_.Combine(auditor_->last_instruction_); } void Record(const CPUFeatures& features) { auditor_->last_instruction_.Combine(features); } void Record(CPUFeatures::Feature feature0, CPUFeatures::Feature feature1 = CPUFeatures::kNone, CPUFeatures::Feature feature2 = CPUFeatures::kNone, CPUFeatures::Feature feature3 = CPUFeatures::kNone) { auditor_->last_instruction_.Combine(feature0, feature1, feature2, feature3); } // If exactly one of a or b is known to be available, record it. Otherwise, // record both. This is intended for encodings that can be provided by two // different features. void RecordOneOrBothOf(CPUFeatures::Feature a, CPUFeatures::Feature b) { bool hint_a = auditor_->available_.Has(a); bool hint_b = auditor_->available_.Has(b); if (hint_a && !hint_b) { Record(a); } else if (hint_b && !hint_a) { Record(b); } else { Record(a, b); } } private: CPUFeaturesAuditor* auditor_; }; void CPUFeaturesAuditor::LoadStoreHelper(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); switch (instr->Mask(LoadStoreMask)) { case LDR_b: case LDR_q: case STR_b: case STR_q: scope.Record(CPUFeatures::kNEON); return; case LDR_h: case LDR_s: case LDR_d: case STR_h: case STR_s: case STR_d: scope.RecordOneOrBothOf(CPUFeatures::kFP, CPUFeatures::kNEON); return; default: // No special CPU features. return; } } void CPUFeaturesAuditor::LoadStorePairHelper(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); switch (instr->Mask(LoadStorePairMask)) { case LDP_q: case STP_q: scope.Record(CPUFeatures::kNEON); return; case LDP_s: case LDP_d: case STP_s: case STP_d: { scope.RecordOneOrBothOf(CPUFeatures::kFP, CPUFeatures::kNEON); return; } default: // No special CPU features. return; } } void CPUFeaturesAuditor::VisitAddSubExtended(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitAddSubImmediate(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitAddSubShifted(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitAddSubWithCarry(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitRotateRightIntoFlags(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); switch (instr->Mask(RotateRightIntoFlagsMask)) { case RMIF: scope.Record(CPUFeatures::kFlagM); return; } } void CPUFeaturesAuditor::VisitEvaluateIntoFlags(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); switch (instr->Mask(EvaluateIntoFlagsMask)) { case SETF8: case SETF16: scope.Record(CPUFeatures::kFlagM); return; } } void CPUFeaturesAuditor::VisitAtomicMemory(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); switch (instr->Mask(AtomicMemoryMask)) { case LDAPRB: case LDAPRH: case LDAPR_w: case LDAPR_x: scope.Record(CPUFeatures::kRCpc); return; default: // Everything else belongs to the Atomics extension. scope.Record(CPUFeatures::kAtomics); return; } } void CPUFeaturesAuditor::VisitBitfield(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitCompareBranch(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitConditionalBranch(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitConditionalCompareImmediate( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitConditionalCompareRegister( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitConditionalSelect(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitCrypto2RegSHA(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitCrypto3RegSHA(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitCryptoAES(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitDataProcessing1Source(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); switch (instr->Mask(DataProcessing1SourceMask)) { case PACIA: case PACIB: case PACDA: case PACDB: case AUTIA: case AUTIB: case AUTDA: case AUTDB: case PACIZA: case PACIZB: case PACDZA: case PACDZB: case AUTIZA: case AUTIZB: case AUTDZA: case AUTDZB: case XPACI: case XPACD: scope.Record(CPUFeatures::kPAuth); return; default: // No special CPU features. return; } } void CPUFeaturesAuditor::VisitDataProcessing2Source(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); switch (instr->Mask(DataProcessing2SourceMask)) { case CRC32B: case CRC32H: case CRC32W: case CRC32X: case CRC32CB: case CRC32CH: case CRC32CW: case CRC32CX: scope.Record(CPUFeatures::kCRC32); return; case PACGA: scope.Record(CPUFeatures::kPAuth, CPUFeatures::kPAuthGeneric); return; default: // No special CPU features. return; } } void CPUFeaturesAuditor::VisitLoadStoreRCpcUnscaledOffset( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); switch (instr->Mask(LoadStoreRCpcUnscaledOffsetMask)) { case LDAPURB: case LDAPURSB_w: case LDAPURSB_x: case LDAPURH: case LDAPURSH_w: case LDAPURSH_x: case LDAPUR_w: case LDAPURSW: case LDAPUR_x: // These stores don't actually have RCpc semantics but they're included with // the RCpc extensions. case STLURB: case STLURH: case STLUR_w: case STLUR_x: scope.Record(CPUFeatures::kRCpc, CPUFeatures::kRCpcImm); return; } } void CPUFeaturesAuditor::VisitLoadStorePAC(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); scope.Record(CPUFeatures::kPAuth); } void CPUFeaturesAuditor::VisitDataProcessing3Source(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitException(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitExtract(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitFPCompare(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require FP. scope.Record(CPUFeatures::kFP); switch (instr->Mask(FPCompareMask)) { case FCMP_h: case FCMP_h_zero: case FCMPE_h: case FCMPE_h_zero: scope.Record(CPUFeatures::kFPHalf); return; default: // No special CPU features. return; } } void CPUFeaturesAuditor::VisitFPConditionalCompare(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require FP. scope.Record(CPUFeatures::kFP); switch (instr->Mask(FPConditionalCompareMask)) { case FCCMP_h: case FCCMPE_h: scope.Record(CPUFeatures::kFPHalf); return; default: // No special CPU features. return; } } void CPUFeaturesAuditor::VisitFPConditionalSelect(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require FP. scope.Record(CPUFeatures::kFP); if (instr->Mask(FPConditionalSelectMask) == FCSEL_h) { scope.Record(CPUFeatures::kFPHalf); } } void CPUFeaturesAuditor::VisitFPDataProcessing1Source( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require FP. scope.Record(CPUFeatures::kFP); switch (instr->Mask(FPDataProcessing1SourceMask)) { case FMOV_h: case FABS_h: case FNEG_h: case FSQRT_h: case FRINTN_h: case FRINTP_h: case FRINTM_h: case FRINTZ_h: case FRINTA_h: case FRINTX_h: case FRINTI_h: scope.Record(CPUFeatures::kFPHalf); return; case FRINT32X_s: case FRINT32X_d: case FRINT32Z_s: case FRINT32Z_d: case FRINT64X_s: case FRINT64X_d: case FRINT64Z_s: case FRINT64Z_d: scope.Record(CPUFeatures::kFrintToFixedSizedInt); return; default: // No special CPU features. // This category includes some half-precision FCVT instructions that do // not require FPHalf. return; } } void CPUFeaturesAuditor::VisitFPDataProcessing2Source( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require FP. scope.Record(CPUFeatures::kFP); switch (instr->Mask(FPDataProcessing2SourceMask)) { case FMUL_h: case FDIV_h: case FADD_h: case FSUB_h: case FMAX_h: case FMIN_h: case FMAXNM_h: case FMINNM_h: case FNMUL_h: scope.Record(CPUFeatures::kFPHalf); return; default: // No special CPU features. return; } } void CPUFeaturesAuditor::VisitFPDataProcessing3Source( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require FP. scope.Record(CPUFeatures::kFP); switch (instr->Mask(FPDataProcessing3SourceMask)) { case FMADD_h: case FMSUB_h: case FNMADD_h: case FNMSUB_h: scope.Record(CPUFeatures::kFPHalf); return; default: // No special CPU features. return; } } void CPUFeaturesAuditor::VisitFPFixedPointConvert(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require FP. scope.Record(CPUFeatures::kFP); switch (instr->Mask(FPFixedPointConvertMask)) { case FCVTZS_wh_fixed: case FCVTZS_xh_fixed: case FCVTZU_wh_fixed: case FCVTZU_xh_fixed: case SCVTF_hw_fixed: case SCVTF_hx_fixed: case UCVTF_hw_fixed: case UCVTF_hx_fixed: scope.Record(CPUFeatures::kFPHalf); return; default: // No special CPU features. return; } } void CPUFeaturesAuditor::VisitFPImmediate(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require FP. scope.Record(CPUFeatures::kFP); if (instr->Mask(FPImmediateMask) == FMOV_h_imm) { scope.Record(CPUFeatures::kFPHalf); } } void CPUFeaturesAuditor::VisitFPIntegerConvert(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require FP. scope.Record(CPUFeatures::kFP); switch (instr->Mask(FPIntegerConvertMask)) { case FCVTAS_wh: case FCVTAS_xh: case FCVTAU_wh: case FCVTAU_xh: case FCVTMS_wh: case FCVTMS_xh: case FCVTMU_wh: case FCVTMU_xh: case FCVTNS_wh: case FCVTNS_xh: case FCVTNU_wh: case FCVTNU_xh: case FCVTPS_wh: case FCVTPS_xh: case FCVTPU_wh: case FCVTPU_xh: case FCVTZS_wh: case FCVTZS_xh: case FCVTZU_wh: case FCVTZU_xh: case FMOV_hw: case FMOV_hx: case FMOV_wh: case FMOV_xh: case SCVTF_hw: case SCVTF_hx: case UCVTF_hw: case UCVTF_hx: scope.Record(CPUFeatures::kFPHalf); return; case FMOV_d1_x: case FMOV_x_d1: scope.Record(CPUFeatures::kNEON); return; case FJCVTZS: scope.Record(CPUFeatures::kJSCVT); return; default: // No special CPU features. return; } } void CPUFeaturesAuditor::VisitLoadLiteral(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); switch (instr->Mask(LoadLiteralMask)) { case LDR_s_lit: case LDR_d_lit: scope.RecordOneOrBothOf(CPUFeatures::kFP, CPUFeatures::kNEON); return; case LDR_q_lit: scope.Record(CPUFeatures::kNEON); return; default: // No special CPU features. return; } } void CPUFeaturesAuditor::VisitLoadStoreExclusive(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); switch (instr->Mask(LoadStoreExclusiveMask)) { case CAS_w: case CASA_w: case CASL_w: case CASAL_w: case CAS_x: case CASA_x: case CASL_x: case CASAL_x: case CASB: case CASAB: case CASLB: case CASALB: case CASH: case CASAH: case CASLH: case CASALH: case CASP_w: case CASPA_w: case CASPL_w: case CASPAL_w: case CASP_x: case CASPA_x: case CASPL_x: case CASPAL_x: scope.Record(CPUFeatures::kAtomics); return; case STLLRB: case LDLARB: case STLLRH: case LDLARH: case STLLR_w: case LDLAR_w: case STLLR_x: case LDLAR_x: scope.Record(CPUFeatures::kLORegions); return; default: // No special CPU features. return; } } void CPUFeaturesAuditor::VisitLoadStorePairNonTemporal( const Instruction* instr) { LoadStorePairHelper(instr); } void CPUFeaturesAuditor::VisitLoadStorePairOffset(const Instruction* instr) { LoadStorePairHelper(instr); } void CPUFeaturesAuditor::VisitLoadStorePairPostIndex(const Instruction* instr) { LoadStorePairHelper(instr); } void CPUFeaturesAuditor::VisitLoadStorePairPreIndex(const Instruction* instr) { LoadStorePairHelper(instr); } void CPUFeaturesAuditor::VisitLoadStorePostIndex(const Instruction* instr) { LoadStoreHelper(instr); } void CPUFeaturesAuditor::VisitLoadStorePreIndex(const Instruction* instr) { LoadStoreHelper(instr); } void CPUFeaturesAuditor::VisitLoadStoreRegisterOffset( const Instruction* instr) { LoadStoreHelper(instr); } void CPUFeaturesAuditor::VisitLoadStoreUnscaledOffset( const Instruction* instr) { LoadStoreHelper(instr); } void CPUFeaturesAuditor::VisitLoadStoreUnsignedOffset( const Instruction* instr) { LoadStoreHelper(instr); } void CPUFeaturesAuditor::VisitLogicalImmediate(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitLogicalShifted(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitMoveWideImmediate(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitNEON2RegMisc(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); switch (instr->Mask(NEON2RegMiscFPMask)) { case NEON_FABS: case NEON_FNEG: case NEON_FSQRT: case NEON_FCVTL: case NEON_FCVTN: case NEON_FCVTXN: case NEON_FRINTI: case NEON_FRINTX: case NEON_FRINTA: case NEON_FRINTM: case NEON_FRINTN: case NEON_FRINTP: case NEON_FRINTZ: case NEON_FCVTNS: case NEON_FCVTNU: case NEON_FCVTPS: case NEON_FCVTPU: case NEON_FCVTMS: case NEON_FCVTMU: case NEON_FCVTZS: case NEON_FCVTZU: case NEON_FCVTAS: case NEON_FCVTAU: case NEON_SCVTF: case NEON_UCVTF: case NEON_FRSQRTE: case NEON_FRECPE: case NEON_FCMGT_zero: case NEON_FCMGE_zero: case NEON_FCMEQ_zero: case NEON_FCMLE_zero: case NEON_FCMLT_zero: scope.Record(CPUFeatures::kFP); return; case NEON_FRINT32X: case NEON_FRINT32Z: case NEON_FRINT64X: case NEON_FRINT64Z: scope.Record(CPUFeatures::kFP, CPUFeatures::kFrintToFixedSizedInt); return; default: // No additional features. return; } } void CPUFeaturesAuditor::VisitNEON2RegMiscFP16(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEONHalf. scope.Record(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kNEONHalf); USE(instr); } void CPUFeaturesAuditor::VisitNEON3Different(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); USE(instr); } void CPUFeaturesAuditor::VisitNEON3Same(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); if (instr->Mask(NEON3SameFPFMask) == NEON3SameFPFixed) { scope.Record(CPUFeatures::kFP); } switch (instr->Mask(NEON3SameFHMMask)) { case NEON_FMLAL: case NEON_FMLAL2: case NEON_FMLSL: case NEON_FMLSL2: scope.Record(CPUFeatures::kFP, CPUFeatures::kNEONHalf, CPUFeatures::kFHM); return; default: // No additional features. return; } } void CPUFeaturesAuditor::VisitNEON3SameExtra(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); if ((instr->Mask(NEON3SameExtraFCMLAMask) == NEON_FCMLA) || (instr->Mask(NEON3SameExtraFCADDMask) == NEON_FCADD)) { scope.Record(CPUFeatures::kFP, CPUFeatures::kFcma); if (instr->GetNEONSize() == 1) scope.Record(CPUFeatures::kNEONHalf); } else { switch (instr->Mask(NEON3SameExtraMask)) { case NEON_SDOT: case NEON_UDOT: scope.Record(CPUFeatures::kDotProduct); return; case NEON_SQRDMLAH: case NEON_SQRDMLSH: scope.Record(CPUFeatures::kRDM); return; default: // No additional features. return; } } } void CPUFeaturesAuditor::VisitNEON3SameFP16(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON FP16 support. scope.Record(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kNEONHalf); USE(instr); } void CPUFeaturesAuditor::VisitNEONAcrossLanes(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); if (instr->Mask(NEONAcrossLanesFP16FMask) == NEONAcrossLanesFP16Fixed) { // FMAXV_H, FMINV_H, FMAXNMV_H, FMINNMV_H scope.Record(CPUFeatures::kFP, CPUFeatures::kNEONHalf); } else if (instr->Mask(NEONAcrossLanesFPFMask) == NEONAcrossLanesFPFixed) { // FMAXV, FMINV, FMAXNMV, FMINNMV scope.Record(CPUFeatures::kFP); } } void CPUFeaturesAuditor::VisitNEONByIndexedElement(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); switch (instr->Mask(NEONByIndexedElementMask)) { case NEON_SDOT_byelement: case NEON_UDOT_byelement: scope.Record(CPUFeatures::kDotProduct); return; case NEON_SQRDMLAH_byelement: case NEON_SQRDMLSH_byelement: scope.Record(CPUFeatures::kRDM); return; default: // Fall through to check other instructions. break; } switch (instr->Mask(NEONByIndexedElementFPLongMask)) { case NEON_FMLAL_H_byelement: case NEON_FMLAL2_H_byelement: case NEON_FMLSL_H_byelement: case NEON_FMLSL2_H_byelement: scope.Record(CPUFeatures::kFP, CPUFeatures::kNEONHalf, CPUFeatures::kFHM); return; default: // Fall through to check other instructions. break; } switch (instr->Mask(NEONByIndexedElementFPMask)) { case NEON_FMLA_H_byelement: case NEON_FMLS_H_byelement: case NEON_FMUL_H_byelement: case NEON_FMULX_H_byelement: scope.Record(CPUFeatures::kNEONHalf); VIXL_FALLTHROUGH(); case NEON_FMLA_byelement: case NEON_FMLS_byelement: case NEON_FMUL_byelement: case NEON_FMULX_byelement: scope.Record(CPUFeatures::kFP); return; default: switch (instr->Mask(NEONByIndexedElementFPComplexMask)) { case NEON_FCMLA_byelement: scope.Record(CPUFeatures::kFP, CPUFeatures::kFcma); if (instr->GetNEONSize() == 1) scope.Record(CPUFeatures::kNEONHalf); return; } // No additional features. return; } } void CPUFeaturesAuditor::VisitNEONCopy(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); USE(instr); } void CPUFeaturesAuditor::VisitNEONExtract(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); USE(instr); } void CPUFeaturesAuditor::VisitNEONLoadStoreMultiStruct( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); USE(instr); } void CPUFeaturesAuditor::VisitNEONLoadStoreMultiStructPostIndex( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); USE(instr); } void CPUFeaturesAuditor::VisitNEONLoadStoreSingleStruct( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); USE(instr); } void CPUFeaturesAuditor::VisitNEONLoadStoreSingleStructPostIndex( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); USE(instr); } void CPUFeaturesAuditor::VisitNEONModifiedImmediate(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); if (instr->GetNEONCmode() == 0xf) { // FMOV (vector, immediate), double-, single- or half-precision. scope.Record(CPUFeatures::kFP); if (instr->ExtractBit(11)) scope.Record(CPUFeatures::kNEONHalf); } } void CPUFeaturesAuditor::VisitNEONPerm(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); USE(instr); } void CPUFeaturesAuditor::VisitNEONScalar2RegMisc(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); switch (instr->Mask(NEONScalar2RegMiscFPMask)) { case NEON_FRECPE_scalar: case NEON_FRECPX_scalar: case NEON_FRSQRTE_scalar: case NEON_FCMGT_zero_scalar: case NEON_FCMGE_zero_scalar: case NEON_FCMEQ_zero_scalar: case NEON_FCMLE_zero_scalar: case NEON_FCMLT_zero_scalar: case NEON_SCVTF_scalar: case NEON_UCVTF_scalar: case NEON_FCVTNS_scalar: case NEON_FCVTNU_scalar: case NEON_FCVTPS_scalar: case NEON_FCVTPU_scalar: case NEON_FCVTMS_scalar: case NEON_FCVTMU_scalar: case NEON_FCVTZS_scalar: case NEON_FCVTZU_scalar: case NEON_FCVTAS_scalar: case NEON_FCVTAU_scalar: case NEON_FCVTXN_scalar: scope.Record(CPUFeatures::kFP); return; default: // No additional features. return; } } void CPUFeaturesAuditor::VisitNEONScalar2RegMiscFP16(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEONHalf. scope.Record(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kNEONHalf); USE(instr); } void CPUFeaturesAuditor::VisitNEONScalar3Diff(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); USE(instr); } void CPUFeaturesAuditor::VisitNEONScalar3Same(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); if (instr->Mask(NEONScalar3SameFPFMask) == NEONScalar3SameFPFixed) { scope.Record(CPUFeatures::kFP); } } void CPUFeaturesAuditor::VisitNEONScalar3SameExtra(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON and RDM. scope.Record(CPUFeatures::kNEON, CPUFeatures::kRDM); USE(instr); } void CPUFeaturesAuditor::VisitNEONScalar3SameFP16(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEONHalf. scope.Record(CPUFeatures::kFP, CPUFeatures::kNEON, CPUFeatures::kNEONHalf); USE(instr); } void CPUFeaturesAuditor::VisitNEONScalarByIndexedElement( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); switch (instr->Mask(NEONScalarByIndexedElementMask)) { case NEON_SQRDMLAH_byelement_scalar: case NEON_SQRDMLSH_byelement_scalar: scope.Record(CPUFeatures::kRDM); return; default: switch (instr->Mask(NEONScalarByIndexedElementFPMask)) { case NEON_FMLA_H_byelement_scalar: case NEON_FMLS_H_byelement_scalar: case NEON_FMUL_H_byelement_scalar: case NEON_FMULX_H_byelement_scalar: scope.Record(CPUFeatures::kNEONHalf); VIXL_FALLTHROUGH(); case NEON_FMLA_byelement_scalar: case NEON_FMLS_byelement_scalar: case NEON_FMUL_byelement_scalar: case NEON_FMULX_byelement_scalar: scope.Record(CPUFeatures::kFP); return; } // No additional features. return; } } void CPUFeaturesAuditor::VisitNEONScalarCopy(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); USE(instr); } void CPUFeaturesAuditor::VisitNEONScalarPairwise(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); switch (instr->Mask(NEONScalarPairwiseMask)) { case NEON_FMAXNMP_h_scalar: case NEON_FADDP_h_scalar: case NEON_FMAXP_h_scalar: case NEON_FMINNMP_h_scalar: case NEON_FMINP_h_scalar: scope.Record(CPUFeatures::kNEONHalf); VIXL_FALLTHROUGH(); case NEON_FADDP_scalar: case NEON_FMAXP_scalar: case NEON_FMAXNMP_scalar: case NEON_FMINP_scalar: case NEON_FMINNMP_scalar: scope.Record(CPUFeatures::kFP); return; default: // No additional features. return; } } void CPUFeaturesAuditor::VisitNEONScalarShiftImmediate( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); switch (instr->Mask(NEONScalarShiftImmediateMask)) { case NEON_FCVTZS_imm_scalar: case NEON_FCVTZU_imm_scalar: case NEON_SCVTF_imm_scalar: case NEON_UCVTF_imm_scalar: scope.Record(CPUFeatures::kFP); // If immh is 0b001x then the data type is FP16, and requires kNEONHalf. if ((instr->GetImmNEONImmh() & 0xe) == 0x2) { scope.Record(CPUFeatures::kNEONHalf); } return; default: // No additional features. return; } } void CPUFeaturesAuditor::VisitNEONShiftImmediate(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); switch (instr->Mask(NEONShiftImmediateMask)) { case NEON_SCVTF_imm: case NEON_UCVTF_imm: case NEON_FCVTZS_imm: case NEON_FCVTZU_imm: scope.Record(CPUFeatures::kFP); // If immh is 0b001x then the data type is FP16, and requires kNEONHalf. if ((instr->GetImmNEONImmh() & 0xe) == 0x2) { scope.Record(CPUFeatures::kNEONHalf); } return; default: // No additional features. return; } } void CPUFeaturesAuditor::VisitNEONTable(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); // All of these instructions require NEON. scope.Record(CPUFeatures::kNEON); USE(instr); } void CPUFeaturesAuditor::VisitPCRelAddressing(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } // Most SVE visitors require only SVE. #define VIXL_SIMPLE_SVE_VISITOR_LIST(V) \ V(SVE32BitGatherLoad_ScalarPlus32BitUnscaledOffsets) \ V(SVE32BitGatherLoad_VectorPlusImm) \ V(SVE32BitGatherLoadHalfwords_ScalarPlus32BitScaledOffsets) \ V(SVE32BitGatherLoadWords_ScalarPlus32BitScaledOffsets) \ V(SVE32BitGatherPrefetch_ScalarPlus32BitScaledOffsets) \ V(SVE32BitGatherPrefetch_VectorPlusImm) \ V(SVE32BitScatterStore_ScalarPlus32BitScaledOffsets) \ V(SVE32BitScatterStore_ScalarPlus32BitUnscaledOffsets) \ V(SVE32BitScatterStore_VectorPlusImm) \ V(SVE64BitGatherLoad_ScalarPlus32BitUnpackedScaledOffsets) \ V(SVE64BitGatherLoad_ScalarPlus64BitScaledOffsets) \ V(SVE64BitGatherLoad_ScalarPlus64BitUnscaledOffsets) \ V(SVE64BitGatherLoad_ScalarPlusUnpacked32BitUnscaledOffsets) \ V(SVE64BitGatherLoad_VectorPlusImm) \ V(SVE64BitGatherPrefetch_ScalarPlus64BitScaledOffsets) \ V(SVE64BitGatherPrefetch_ScalarPlusUnpacked32BitScaledOffsets) \ V(SVE64BitGatherPrefetch_VectorPlusImm) \ V(SVE64BitScatterStore_ScalarPlus64BitScaledOffsets) \ V(SVE64BitScatterStore_ScalarPlus64BitUnscaledOffsets) \ V(SVE64BitScatterStore_ScalarPlusUnpacked32BitScaledOffsets) \ V(SVE64BitScatterStore_ScalarPlusUnpacked32BitUnscaledOffsets) \ V(SVE64BitScatterStore_VectorPlusImm) \ V(SVEAddressGeneration) \ V(SVEBitwiseLogicalUnpredicated) \ V(SVEBitwiseShiftUnpredicated) \ V(SVEFFRInitialise) \ V(SVEFFRWriteFromPredicate) \ V(SVEFPAccumulatingReduction) \ V(SVEFPArithmeticUnpredicated) \ V(SVEFPCompareVectors) \ V(SVEFPCompareWithZero) \ V(SVEFPComplexAddition) \ V(SVEFPComplexMulAdd) \ V(SVEFPComplexMulAddIndex) \ V(SVEFPFastReduction) \ V(SVEFPMulIndex) \ V(SVEFPMulAdd) \ V(SVEFPMulAddIndex) \ V(SVEFPUnaryOpUnpredicated) \ V(SVEIncDecByPredicateCount) \ V(SVEIndexGeneration) \ V(SVEIntArithmeticUnpredicated) \ V(SVEIntCompareSignedImm) \ V(SVEIntCompareUnsignedImm) \ V(SVEIntCompareVectors) \ V(SVEIntMulAddPredicated) \ V(SVEIntMulAddUnpredicated) \ V(SVEIntReduction) \ V(SVEIntUnaryArithmeticPredicated) \ V(SVEMovprfx) \ V(SVEMulIndex) \ V(SVEPermuteVectorExtract) \ V(SVEPermuteVectorInterleaving) \ V(SVEPredicateCount) \ V(SVEPredicateLogical) \ V(SVEPropagateBreak) \ V(SVEStackFrameAdjustment) \ V(SVEStackFrameSize) \ V(SVEVectorSelect) \ V(SVEBitwiseLogical_Predicated) \ V(SVEBitwiseLogicalWithImm_Unpredicated) \ V(SVEBitwiseShiftByImm_Predicated) \ V(SVEBitwiseShiftByVector_Predicated) \ V(SVEBitwiseShiftByWideElements_Predicated) \ V(SVEBroadcastBitmaskImm) \ V(SVEBroadcastFPImm_Unpredicated) \ V(SVEBroadcastGeneralRegister) \ V(SVEBroadcastIndexElement) \ V(SVEBroadcastIntImm_Unpredicated) \ V(SVECompressActiveElements) \ V(SVEConditionallyBroadcastElementToVector) \ V(SVEConditionallyExtractElementToSIMDFPScalar) \ V(SVEConditionallyExtractElementToGeneralRegister) \ V(SVEConditionallyTerminateScalars) \ V(SVEConstructivePrefix_Unpredicated) \ V(SVEContiguousFirstFaultLoad_ScalarPlusScalar) \ V(SVEContiguousLoad_ScalarPlusImm) \ V(SVEContiguousLoad_ScalarPlusScalar) \ V(SVEContiguousNonFaultLoad_ScalarPlusImm) \ V(SVEContiguousNonTemporalLoad_ScalarPlusImm) \ V(SVEContiguousNonTemporalLoad_ScalarPlusScalar) \ V(SVEContiguousNonTemporalStore_ScalarPlusImm) \ V(SVEContiguousNonTemporalStore_ScalarPlusScalar) \ V(SVEContiguousPrefetch_ScalarPlusImm) \ V(SVEContiguousPrefetch_ScalarPlusScalar) \ V(SVEContiguousStore_ScalarPlusImm) \ V(SVEContiguousStore_ScalarPlusScalar) \ V(SVECopySIMDFPScalarRegisterToVector_Predicated) \ V(SVECopyFPImm_Predicated) \ V(SVECopyGeneralRegisterToVector_Predicated) \ V(SVECopyIntImm_Predicated) \ V(SVEElementCount) \ V(SVEExtractElementToSIMDFPScalarRegister) \ V(SVEExtractElementToGeneralRegister) \ V(SVEFPArithmetic_Predicated) \ V(SVEFPArithmeticWithImm_Predicated) \ V(SVEFPConvertPrecision) \ V(SVEFPConvertToInt) \ V(SVEFPExponentialAccelerator) \ V(SVEFPRoundToIntegralValue) \ V(SVEFPTrigMulAddCoefficient) \ V(SVEFPTrigSelectCoefficient) \ V(SVEFPUnaryOp) \ V(SVEIncDecRegisterByElementCount) \ V(SVEIncDecVectorByElementCount) \ V(SVEInsertSIMDFPScalarRegister) \ V(SVEInsertGeneralRegister) \ V(SVEIntAddSubtractImm_Unpredicated) \ V(SVEIntAddSubtractVectors_Predicated) \ V(SVEIntCompareScalarCountAndLimit) \ V(SVEIntConvertToFP) \ V(SVEIntDivideVectors_Predicated) \ V(SVEIntMinMaxImm_Unpredicated) \ V(SVEIntMinMaxDifference_Predicated) \ V(SVEIntMulImm_Unpredicated) \ V(SVEIntMulVectors_Predicated) \ V(SVELoadAndBroadcastElement) \ V(SVELoadAndBroadcastQOWord_ScalarPlusImm) \ V(SVELoadAndBroadcastQOWord_ScalarPlusScalar) \ V(SVELoadMultipleStructures_ScalarPlusImm) \ V(SVELoadMultipleStructures_ScalarPlusScalar) \ V(SVELoadPredicateRegister) \ V(SVELoadVectorRegister) \ V(SVEPartitionBreakCondition) \ V(SVEPermutePredicateElements) \ V(SVEPredicateFirstActive) \ V(SVEPredicateInitialize) \ V(SVEPredicateNextActive) \ V(SVEPredicateReadFromFFR_Predicated) \ V(SVEPredicateReadFromFFR_Unpredicated) \ V(SVEPredicateTest) \ V(SVEPredicateZero) \ V(SVEPropagateBreakToNextPartition) \ V(SVEReversePredicateElements) \ V(SVEReverseVectorElements) \ V(SVEReverseWithinElements) \ V(SVESaturatingIncDecRegisterByElementCount) \ V(SVESaturatingIncDecVectorByElementCount) \ V(SVEStoreMultipleStructures_ScalarPlusImm) \ V(SVEStoreMultipleStructures_ScalarPlusScalar) \ V(SVEStorePredicateRegister) \ V(SVEStoreVectorRegister) \ V(SVETableLookup) \ V(SVEUnpackPredicateElements) \ V(SVEUnpackVectorElements) \ V(SVEVectorSplice) #define VIXL_DEFINE_SIMPLE_SVE_VISITOR(NAME) \ void CPUFeaturesAuditor::Visit##NAME(const Instruction* instr) { \ RecordInstructionFeaturesScope scope(this); \ scope.Record(CPUFeatures::kSVE); \ USE(instr); \ } VIXL_SIMPLE_SVE_VISITOR_LIST(VIXL_DEFINE_SIMPLE_SVE_VISITOR) #undef VIXL_DEFINE_SIMPLE_SVE_VISITOR #undef VIXL_SIMPLE_SVE_VISITOR_LIST void CPUFeaturesAuditor::VisitSystem(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); if (instr->Mask(SystemHintFMask) == SystemHintFixed) { CPUFeatures required; switch (instr->GetInstructionBits()) { case PACIA1716: case PACIB1716: case AUTIA1716: case AUTIB1716: case PACIAZ: case PACIASP: case PACIBZ: case PACIBSP: case AUTIAZ: case AUTIASP: case AUTIBZ: case AUTIBSP: case XPACLRI: required.Combine(CPUFeatures::kPAuth); break; default: switch (instr->GetImmHint()) { case ESB: required.Combine(CPUFeatures::kRAS); break; case BTI: case BTI_j: case BTI_c: case BTI_jc: required.Combine(CPUFeatures::kBTI); break; default: break; } break; } // These are all HINT instructions, and behave as NOPs if the corresponding // features are not implemented, so we record the corresponding features // only if they are available. if (available_.Has(required)) scope.Record(required); } else if (instr->Mask(SystemSysMask) == SYS) { switch (instr->GetSysOp()) { // DC instruction variants. case CVAP: scope.Record(CPUFeatures::kDCPoP); break; case CVADP: scope.Record(CPUFeatures::kDCCVADP); break; case IVAU: case CVAC: case CVAU: case CIVAC: // No special CPU features. break; } } else if (instr->Mask(SystemPStateFMask) == SystemPStateFixed) { switch (instr->Mask(SystemPStateMask)) { case CFINV: scope.Record(CPUFeatures::kFlagM); break; case AXFLAG: case XAFLAG: scope.Record(CPUFeatures::kAXFlag); break; } } else if (instr->Mask(SystemSysRegFMask) == SystemSysRegFixed) { if (instr->Mask(SystemSysRegMask) == MRS) { switch (instr->GetImmSystemRegister()) { case RNDR: case RNDRRS: scope.Record(CPUFeatures::kRNG); break; } } } } void CPUFeaturesAuditor::VisitTestBranch(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitUnallocated(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitUnconditionalBranch(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitUnconditionalBranchToRegister( const Instruction* instr) { RecordInstructionFeaturesScope scope(this); switch (instr->Mask(UnconditionalBranchToRegisterMask)) { case BRAAZ: case BRABZ: case BLRAAZ: case BLRABZ: case RETAA: case RETAB: case BRAA: case BRAB: case BLRAA: case BLRAB: scope.Record(CPUFeatures::kPAuth); return; default: // No additional features. return; } } void CPUFeaturesAuditor::VisitReserved(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::VisitUnimplemented(const Instruction* instr) { RecordInstructionFeaturesScope scope(this); USE(instr); } void CPUFeaturesAuditor::Visit(Metadata* metadata, const Instruction* instr) { VIXL_ASSERT(metadata->count("form") > 0); const std::string& form = (*metadata)["form"]; uint32_t form_hash = Hash(form.c_str()); const FormToVisitorFnMap* fv = CPUFeaturesAuditor::GetFormToVisitorFnMap(); FormToVisitorFnMap::const_iterator it = fv->find(form_hash); if (it == fv->end()) { RecordInstructionFeaturesScope scope(this); std::map features = { {"adclb_z_zzz"_h, CPUFeatures::kSVE2}, {"adclt_z_zzz"_h, CPUFeatures::kSVE2}, {"addhnb_z_zz"_h, CPUFeatures::kSVE2}, {"addhnt_z_zz"_h, CPUFeatures::kSVE2}, {"addp_z_p_zz"_h, CPUFeatures::kSVE2}, {"bcax_z_zzz"_h, CPUFeatures::kSVE2}, {"bdep_z_zz"_h, CPUFeatures(CPUFeatures::kSVE2, CPUFeatures::kSVEBitPerm)}, {"bext_z_zz"_h, CPUFeatures(CPUFeatures::kSVE2, CPUFeatures::kSVEBitPerm)}, {"bgrp_z_zz"_h, CPUFeatures(CPUFeatures::kSVE2, CPUFeatures::kSVEBitPerm)}, {"bsl1n_z_zzz"_h, CPUFeatures::kSVE2}, {"bsl2n_z_zzz"_h, CPUFeatures::kSVE2}, {"bsl_z_zzz"_h, CPUFeatures::kSVE2}, {"cadd_z_zz"_h, CPUFeatures::kSVE2}, {"cdot_z_zzz"_h, CPUFeatures::kSVE2}, {"cdot_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"cdot_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"cmla_z_zzz"_h, CPUFeatures::kSVE2}, {"cmla_z_zzzi_h"_h, CPUFeatures::kSVE2}, {"cmla_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"eor3_z_zzz"_h, CPUFeatures::kSVE2}, {"eorbt_z_zz"_h, CPUFeatures::kSVE2}, {"eortb_z_zz"_h, CPUFeatures::kSVE2}, {"ext_z_zi_con"_h, CPUFeatures::kSVE2}, {"faddp_z_p_zz"_h, CPUFeatures::kSVE2}, {"fcvtlt_z_p_z_h2s"_h, CPUFeatures::kSVE2}, {"fcvtlt_z_p_z_s2d"_h, CPUFeatures::kSVE2}, {"fcvtnt_z_p_z_d2s"_h, CPUFeatures::kSVE2}, {"fcvtnt_z_p_z_s2h"_h, CPUFeatures::kSVE2}, {"fcvtx_z_p_z_d2s"_h, CPUFeatures::kSVE2}, {"fcvtxnt_z_p_z_d2s"_h, CPUFeatures::kSVE2}, {"flogb_z_p_z"_h, CPUFeatures::kSVE2}, {"fmaxnmp_z_p_zz"_h, CPUFeatures::kSVE2}, {"fmaxp_z_p_zz"_h, CPUFeatures::kSVE2}, {"fminnmp_z_p_zz"_h, CPUFeatures::kSVE2}, {"fminp_z_p_zz"_h, CPUFeatures::kSVE2}, {"fmlalb_z_zzz"_h, CPUFeatures::kSVE2}, {"fmlalb_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"fmlalt_z_zzz"_h, CPUFeatures::kSVE2}, {"fmlalt_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"fmlslb_z_zzz"_h, CPUFeatures::kSVE2}, {"fmlslb_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"fmlslt_z_zzz"_h, CPUFeatures::kSVE2}, {"fmlslt_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"histcnt_z_p_zz"_h, CPUFeatures::kSVE2}, {"histseg_z_zz"_h, CPUFeatures::kSVE2}, {"ldnt1b_z_p_ar_d_64_unscaled"_h, CPUFeatures::kSVE2}, {"ldnt1b_z_p_ar_s_x32_unscaled"_h, CPUFeatures::kSVE2}, {"ldnt1d_z_p_ar_d_64_unscaled"_h, CPUFeatures::kSVE2}, {"ldnt1h_z_p_ar_d_64_unscaled"_h, CPUFeatures::kSVE2}, {"ldnt1h_z_p_ar_s_x32_unscaled"_h, CPUFeatures::kSVE2}, {"ldnt1sb_z_p_ar_d_64_unscaled"_h, CPUFeatures::kSVE2}, {"ldnt1sb_z_p_ar_s_x32_unscaled"_h, CPUFeatures::kSVE2}, {"ldnt1sh_z_p_ar_d_64_unscaled"_h, CPUFeatures::kSVE2}, {"ldnt1sh_z_p_ar_s_x32_unscaled"_h, CPUFeatures::kSVE2}, {"ldnt1sw_z_p_ar_d_64_unscaled"_h, CPUFeatures::kSVE2}, {"ldnt1w_z_p_ar_d_64_unscaled"_h, CPUFeatures::kSVE2}, {"ldnt1w_z_p_ar_s_x32_unscaled"_h, CPUFeatures::kSVE2}, {"match_p_p_zz"_h, CPUFeatures::kSVE2}, {"mla_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"mla_z_zzzi_h"_h, CPUFeatures::kSVE2}, {"mla_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"mls_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"mls_z_zzzi_h"_h, CPUFeatures::kSVE2}, {"mls_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"mul_z_zz"_h, CPUFeatures::kSVE2}, {"mul_z_zzi_d"_h, CPUFeatures::kSVE2}, {"mul_z_zzi_h"_h, CPUFeatures::kSVE2}, {"mul_z_zzi_s"_h, CPUFeatures::kSVE2}, {"nbsl_z_zzz"_h, CPUFeatures::kSVE2}, {"nmatch_p_p_zz"_h, CPUFeatures::kSVE2}, {"pmul_z_zz"_h, CPUFeatures::kSVE2}, {"pmullb_z_zz"_h, CPUFeatures::kSVE2}, {"pmullt_z_zz"_h, CPUFeatures::kSVE2}, {"raddhnb_z_zz"_h, CPUFeatures::kSVE2}, {"raddhnt_z_zz"_h, CPUFeatures::kSVE2}, {"rshrnb_z_zi"_h, CPUFeatures::kSVE2}, {"rshrnt_z_zi"_h, CPUFeatures::kSVE2}, {"rsubhnb_z_zz"_h, CPUFeatures::kSVE2}, {"rsubhnt_z_zz"_h, CPUFeatures::kSVE2}, {"saba_z_zzz"_h, CPUFeatures::kSVE2}, {"sabalb_z_zzz"_h, CPUFeatures::kSVE2}, {"sabalt_z_zzz"_h, CPUFeatures::kSVE2}, {"sabdlb_z_zz"_h, CPUFeatures::kSVE2}, {"sabdlt_z_zz"_h, CPUFeatures::kSVE2}, {"sadalp_z_p_z"_h, CPUFeatures::kSVE2}, {"saddlb_z_zz"_h, CPUFeatures::kSVE2}, {"saddlbt_z_zz"_h, CPUFeatures::kSVE2}, {"saddlt_z_zz"_h, CPUFeatures::kSVE2}, {"saddwb_z_zz"_h, CPUFeatures::kSVE2}, {"saddwt_z_zz"_h, CPUFeatures::kSVE2}, {"sbclb_z_zzz"_h, CPUFeatures::kSVE2}, {"sbclt_z_zzz"_h, CPUFeatures::kSVE2}, {"shadd_z_p_zz"_h, CPUFeatures::kSVE2}, {"shrnb_z_zi"_h, CPUFeatures::kSVE2}, {"shrnt_z_zi"_h, CPUFeatures::kSVE2}, {"shsub_z_p_zz"_h, CPUFeatures::kSVE2}, {"shsubr_z_p_zz"_h, CPUFeatures::kSVE2}, {"sli_z_zzi"_h, CPUFeatures::kSVE2}, {"smaxp_z_p_zz"_h, CPUFeatures::kSVE2}, {"sminp_z_p_zz"_h, CPUFeatures::kSVE2}, {"smlalb_z_zzz"_h, CPUFeatures::kSVE2}, {"smlalb_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"smlalb_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"smlalt_z_zzz"_h, CPUFeatures::kSVE2}, {"smlalt_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"smlalt_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"smlslb_z_zzz"_h, CPUFeatures::kSVE2}, {"smlslb_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"smlslb_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"smlslt_z_zzz"_h, CPUFeatures::kSVE2}, {"smlslt_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"smlslt_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"smulh_z_zz"_h, CPUFeatures::kSVE2}, {"smullb_z_zz"_h, CPUFeatures::kSVE2}, {"smullb_z_zzi_d"_h, CPUFeatures::kSVE2}, {"smullb_z_zzi_s"_h, CPUFeatures::kSVE2}, {"smullt_z_zz"_h, CPUFeatures::kSVE2}, {"smullt_z_zzi_d"_h, CPUFeatures::kSVE2}, {"smullt_z_zzi_s"_h, CPUFeatures::kSVE2}, {"splice_z_p_zz_con"_h, CPUFeatures::kSVE2}, {"sqabs_z_p_z"_h, CPUFeatures::kSVE2}, {"sqadd_z_p_zz"_h, CPUFeatures::kSVE2}, {"sqcadd_z_zz"_h, CPUFeatures::kSVE2}, {"sqdmlalb_z_zzz"_h, CPUFeatures::kSVE2}, {"sqdmlalb_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"sqdmlalb_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"sqdmlalbt_z_zzz"_h, CPUFeatures::kSVE2}, {"sqdmlalt_z_zzz"_h, CPUFeatures::kSVE2}, {"sqdmlalt_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"sqdmlalt_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"sqdmlslb_z_zzz"_h, CPUFeatures::kSVE2}, {"sqdmlslb_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"sqdmlslb_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"sqdmlslbt_z_zzz"_h, CPUFeatures::kSVE2}, {"sqdmlslt_z_zzz"_h, CPUFeatures::kSVE2}, {"sqdmlslt_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"sqdmlslt_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"sqdmulh_z_zz"_h, CPUFeatures::kSVE2}, {"sqdmulh_z_zzi_d"_h, CPUFeatures::kSVE2}, {"sqdmulh_z_zzi_h"_h, CPUFeatures::kSVE2}, {"sqdmulh_z_zzi_s"_h, CPUFeatures::kSVE2}, {"sqdmullb_z_zz"_h, CPUFeatures::kSVE2}, {"sqdmullb_z_zzi_d"_h, CPUFeatures::kSVE2}, {"sqdmullb_z_zzi_s"_h, CPUFeatures::kSVE2}, {"sqdmullt_z_zz"_h, CPUFeatures::kSVE2}, {"sqdmullt_z_zzi_d"_h, CPUFeatures::kSVE2}, {"sqdmullt_z_zzi_s"_h, CPUFeatures::kSVE2}, {"sqneg_z_p_z"_h, CPUFeatures::kSVE2}, {"sqrdcmlah_z_zzz"_h, CPUFeatures::kSVE2}, {"sqrdcmlah_z_zzzi_h"_h, CPUFeatures::kSVE2}, {"sqrdcmlah_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"sqrdmlah_z_zzz"_h, CPUFeatures::kSVE2}, {"sqrdmlah_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"sqrdmlah_z_zzzi_h"_h, CPUFeatures::kSVE2}, {"sqrdmlah_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"sqrdmlsh_z_zzz"_h, CPUFeatures::kSVE2}, {"sqrdmlsh_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"sqrdmlsh_z_zzzi_h"_h, CPUFeatures::kSVE2}, {"sqrdmlsh_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"sqrdmulh_z_zz"_h, CPUFeatures::kSVE2}, {"sqrdmulh_z_zzi_d"_h, CPUFeatures::kSVE2}, {"sqrdmulh_z_zzi_h"_h, CPUFeatures::kSVE2}, {"sqrdmulh_z_zzi_s"_h, CPUFeatures::kSVE2}, {"sqrshl_z_p_zz"_h, CPUFeatures::kSVE2}, {"sqrshlr_z_p_zz"_h, CPUFeatures::kSVE2}, {"sqrshrnb_z_zi"_h, CPUFeatures::kSVE2}, {"sqrshrnt_z_zi"_h, CPUFeatures::kSVE2}, {"sqrshrunb_z_zi"_h, CPUFeatures::kSVE2}, {"sqrshrunt_z_zi"_h, CPUFeatures::kSVE2}, {"sqshl_z_p_zi"_h, CPUFeatures::kSVE2}, {"sqshl_z_p_zz"_h, CPUFeatures::kSVE2}, {"sqshlr_z_p_zz"_h, CPUFeatures::kSVE2}, {"sqshlu_z_p_zi"_h, CPUFeatures::kSVE2}, {"sqshrnb_z_zi"_h, CPUFeatures::kSVE2}, {"sqshrnt_z_zi"_h, CPUFeatures::kSVE2}, {"sqshrunb_z_zi"_h, CPUFeatures::kSVE2}, {"sqshrunt_z_zi"_h, CPUFeatures::kSVE2}, {"sqsub_z_p_zz"_h, CPUFeatures::kSVE2}, {"sqsubr_z_p_zz"_h, CPUFeatures::kSVE2}, {"sqxtnb_z_zz"_h, CPUFeatures::kSVE2}, {"sqxtnt_z_zz"_h, CPUFeatures::kSVE2}, {"sqxtunb_z_zz"_h, CPUFeatures::kSVE2}, {"sqxtunt_z_zz"_h, CPUFeatures::kSVE2}, {"srhadd_z_p_zz"_h, CPUFeatures::kSVE2}, {"sri_z_zzi"_h, CPUFeatures::kSVE2}, {"srshl_z_p_zz"_h, CPUFeatures::kSVE2}, {"srshlr_z_p_zz"_h, CPUFeatures::kSVE2}, {"srshr_z_p_zi"_h, CPUFeatures::kSVE2}, {"srsra_z_zi"_h, CPUFeatures::kSVE2}, {"sshllb_z_zi"_h, CPUFeatures::kSVE2}, {"sshllt_z_zi"_h, CPUFeatures::kSVE2}, {"ssra_z_zi"_h, CPUFeatures::kSVE2}, {"ssublb_z_zz"_h, CPUFeatures::kSVE2}, {"ssublbt_z_zz"_h, CPUFeatures::kSVE2}, {"ssublt_z_zz"_h, CPUFeatures::kSVE2}, {"ssubltb_z_zz"_h, CPUFeatures::kSVE2}, {"ssubwb_z_zz"_h, CPUFeatures::kSVE2}, {"ssubwt_z_zz"_h, CPUFeatures::kSVE2}, {"stnt1b_z_p_ar_d_64_unscaled"_h, CPUFeatures::kSVE2}, {"stnt1b_z_p_ar_s_x32_unscaled"_h, CPUFeatures::kSVE2}, {"stnt1d_z_p_ar_d_64_unscaled"_h, CPUFeatures::kSVE2}, {"stnt1h_z_p_ar_d_64_unscaled"_h, CPUFeatures::kSVE2}, {"stnt1h_z_p_ar_s_x32_unscaled"_h, CPUFeatures::kSVE2}, {"stnt1w_z_p_ar_d_64_unscaled"_h, CPUFeatures::kSVE2}, {"stnt1w_z_p_ar_s_x32_unscaled"_h, CPUFeatures::kSVE2}, {"subhnb_z_zz"_h, CPUFeatures::kSVE2}, {"subhnt_z_zz"_h, CPUFeatures::kSVE2}, {"suqadd_z_p_zz"_h, CPUFeatures::kSVE2}, {"tbl_z_zz_2"_h, CPUFeatures::kSVE2}, {"tbx_z_zz"_h, CPUFeatures::kSVE2}, {"uaba_z_zzz"_h, CPUFeatures::kSVE2}, {"uabalb_z_zzz"_h, CPUFeatures::kSVE2}, {"uabalt_z_zzz"_h, CPUFeatures::kSVE2}, {"uabdlb_z_zz"_h, CPUFeatures::kSVE2}, {"uabdlt_z_zz"_h, CPUFeatures::kSVE2}, {"uadalp_z_p_z"_h, CPUFeatures::kSVE2}, {"uaddlb_z_zz"_h, CPUFeatures::kSVE2}, {"uaddlt_z_zz"_h, CPUFeatures::kSVE2}, {"uaddwb_z_zz"_h, CPUFeatures::kSVE2}, {"uaddwt_z_zz"_h, CPUFeatures::kSVE2}, {"uhadd_z_p_zz"_h, CPUFeatures::kSVE2}, {"uhsub_z_p_zz"_h, CPUFeatures::kSVE2}, {"uhsubr_z_p_zz"_h, CPUFeatures::kSVE2}, {"umaxp_z_p_zz"_h, CPUFeatures::kSVE2}, {"uminp_z_p_zz"_h, CPUFeatures::kSVE2}, {"umlalb_z_zzz"_h, CPUFeatures::kSVE2}, {"umlalb_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"umlalb_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"umlalt_z_zzz"_h, CPUFeatures::kSVE2}, {"umlalt_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"umlalt_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"umlslb_z_zzz"_h, CPUFeatures::kSVE2}, {"umlslb_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"umlslb_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"umlslt_z_zzz"_h, CPUFeatures::kSVE2}, {"umlslt_z_zzzi_d"_h, CPUFeatures::kSVE2}, {"umlslt_z_zzzi_s"_h, CPUFeatures::kSVE2}, {"umulh_z_zz"_h, CPUFeatures::kSVE2}, {"umullb_z_zz"_h, CPUFeatures::kSVE2}, {"umullb_z_zzi_d"_h, CPUFeatures::kSVE2}, {"umullb_z_zzi_s"_h, CPUFeatures::kSVE2}, {"umullt_z_zz"_h, CPUFeatures::kSVE2}, {"umullt_z_zzi_d"_h, CPUFeatures::kSVE2}, {"umullt_z_zzi_s"_h, CPUFeatures::kSVE2}, {"uqadd_z_p_zz"_h, CPUFeatures::kSVE2}, {"uqrshl_z_p_zz"_h, CPUFeatures::kSVE2}, {"uqrshlr_z_p_zz"_h, CPUFeatures::kSVE2}, {"uqrshrnb_z_zi"_h, CPUFeatures::kSVE2}, {"uqrshrnt_z_zi"_h, CPUFeatures::kSVE2}, {"uqshl_z_p_zi"_h, CPUFeatures::kSVE2}, {"uqshl_z_p_zz"_h, CPUFeatures::kSVE2}, {"uqshlr_z_p_zz"_h, CPUFeatures::kSVE2}, {"uqshrnb_z_zi"_h, CPUFeatures::kSVE2}, {"uqshrnt_z_zi"_h, CPUFeatures::kSVE2}, {"uqsub_z_p_zz"_h, CPUFeatures::kSVE2}, {"uqsubr_z_p_zz"_h, CPUFeatures::kSVE2}, {"uqxtnb_z_zz"_h, CPUFeatures::kSVE2}, {"uqxtnt_z_zz"_h, CPUFeatures::kSVE2}, {"urecpe_z_p_z"_h, CPUFeatures::kSVE2}, {"urhadd_z_p_zz"_h, CPUFeatures::kSVE2}, {"urshl_z_p_zz"_h, CPUFeatures::kSVE2}, {"urshlr_z_p_zz"_h, CPUFeatures::kSVE2}, {"urshr_z_p_zi"_h, CPUFeatures::kSVE2}, {"ursqrte_z_p_z"_h, CPUFeatures::kSVE2}, {"ursra_z_zi"_h, CPUFeatures::kSVE2}, {"ushllb_z_zi"_h, CPUFeatures::kSVE2}, {"ushllt_z_zi"_h, CPUFeatures::kSVE2}, {"usqadd_z_p_zz"_h, CPUFeatures::kSVE2}, {"usra_z_zi"_h, CPUFeatures::kSVE2}, {"usublb_z_zz"_h, CPUFeatures::kSVE2}, {"usublt_z_zz"_h, CPUFeatures::kSVE2}, {"usubwb_z_zz"_h, CPUFeatures::kSVE2}, {"usubwt_z_zz"_h, CPUFeatures::kSVE2}, {"whilege_p_p_rr"_h, CPUFeatures::kSVE2}, {"whilegt_p_p_rr"_h, CPUFeatures::kSVE2}, {"whilehi_p_p_rr"_h, CPUFeatures::kSVE2}, {"whilehs_p_p_rr"_h, CPUFeatures::kSVE2}, {"whilerw_p_rr"_h, CPUFeatures::kSVE2}, {"whilewr_p_rr"_h, CPUFeatures::kSVE2}, {"xar_z_zzi"_h, CPUFeatures::kSVE2}, {"smmla_z_zzz"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEI8MM)}, {"ummla_z_zzz"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEI8MM)}, {"usmmla_z_zzz"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEI8MM)}, {"fmmla_z_zzz_s"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEF32MM)}, {"fmmla_z_zzz_d"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEF64MM)}, {"smmla_asimdsame2_g"_h, CPUFeatures(CPUFeatures::kNEON, CPUFeatures::kI8MM)}, {"ummla_asimdsame2_g"_h, CPUFeatures(CPUFeatures::kNEON, CPUFeatures::kI8MM)}, {"usmmla_asimdsame2_g"_h, CPUFeatures(CPUFeatures::kNEON, CPUFeatures::kI8MM)}, {"ld1row_z_p_bi_u32"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEF64MM)}, {"ld1row_z_p_br_contiguous"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEF64MM)}, {"ld1rod_z_p_bi_u64"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEF64MM)}, {"ld1rod_z_p_br_contiguous"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEF64MM)}, {"ld1rob_z_p_bi_u8"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEF64MM)}, {"ld1rob_z_p_br_contiguous"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEF64MM)}, {"ld1roh_z_p_bi_u16"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEF64MM)}, {"ld1roh_z_p_br_contiguous"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEF64MM)}, {"usdot_asimdsame2_d"_h, CPUFeatures(CPUFeatures::kNEON, CPUFeatures::kI8MM)}, {"sudot_asimdelem_d"_h, CPUFeatures(CPUFeatures::kNEON, CPUFeatures::kI8MM)}, {"usdot_asimdelem_d"_h, CPUFeatures(CPUFeatures::kNEON, CPUFeatures::kI8MM)}, {"usdot_z_zzz_s"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEI8MM)}, {"usdot_z_zzzi_s"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEI8MM)}, {"sudot_z_zzzi_s"_h, CPUFeatures(CPUFeatures::kSVE, CPUFeatures::kSVEI8MM)}, }; if (features.count(form_hash) > 0) { scope.Record(features[form_hash]); } } else { (it->second)(this, instr); } } } // namespace aarch64 } // namespace vixl