//==- AArch64AsmParser.cpp - Parse AArch64 assembly to MCInst instructions -==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file contains the (GNU-style) assembly parser for the AArch64 // architecture. // //===----------------------------------------------------------------------===// #include "MCTargetDesc/AArch64MCTargetDesc.h" #include "MCTargetDesc/AArch64MCExpr.h" #include "Utils/AArch64BaseInfo.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/STLExtras.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCTargetAsmParser.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCAsmParser.h" #include "llvm/MC/MCParser/MCParsedAsmOperand.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/TargetRegistry.h" using namespace llvm; namespace { class AArch64Operand; class AArch64AsmParser : public MCTargetAsmParser { MCSubtargetInfo &STI; MCAsmParser &Parser; #define GET_ASSEMBLER_HEADER #include "AArch64GenAsmMatcher.inc" public: enum AArch64MatchResultTy { Match_FirstAArch64 = FIRST_TARGET_MATCH_RESULT_TY, #define GET_OPERAND_DIAGNOSTIC_TYPES #include "AArch64GenAsmMatcher.inc" }; AArch64AsmParser(MCSubtargetInfo &_STI, MCAsmParser &_Parser) : MCTargetAsmParser(), STI(_STI), Parser(_Parser) { MCAsmParserExtension::Initialize(_Parser); // Initialize the set of available features. setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); } // These are the public interface of the MCTargetAsmParser bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc); bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, SmallVectorImpl &Operands); bool ParseDirective(AsmToken DirectiveID); bool ParseDirectiveTLSDescCall(SMLoc L); bool ParseDirectiveWord(unsigned Size, SMLoc L); bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, SmallVectorImpl &Operands, MCStreamer&Out, unsigned &ErrorInfo, bool MatchingInlineAsm); // The rest of the sub-parsers have more freedom over interface: they return // an OperandMatchResultTy because it's less ambiguous than true/false or // -1/0/1 even if it is more verbose OperandMatchResultTy ParseOperand(SmallVectorImpl &Operands, StringRef Mnemonic); OperandMatchResultTy ParseImmediate(const MCExpr *&ExprVal); OperandMatchResultTy ParseRelocPrefix(AArch64MCExpr::VariantKind &RefKind); OperandMatchResultTy ParseNEONLane(SmallVectorImpl &Operands, uint32_t NumLanes); OperandMatchResultTy ParseRegister(SmallVectorImpl &Operands, uint32_t &NumLanes); OperandMatchResultTy ParseImmWithLSLOperand(SmallVectorImpl &Operands); OperandMatchResultTy ParseCondCodeOperand(SmallVectorImpl &Operands); OperandMatchResultTy ParseCRxOperand(SmallVectorImpl &Operands); OperandMatchResultTy ParseFPImmOperand(SmallVectorImpl &Operands); template OperandMatchResultTy ParseNamedImmOperand(SmallVectorImpl &Operands) { return ParseNamedImmOperand(SomeNamedImmMapper(), Operands); } OperandMatchResultTy ParseNamedImmOperand(const NamedImmMapper &Mapper, SmallVectorImpl &Operands); OperandMatchResultTy ParseLSXAddressOperand(SmallVectorImpl &Operands); OperandMatchResultTy ParseShiftExtend(SmallVectorImpl &Operands); OperandMatchResultTy ParseSysRegOperand(SmallVectorImpl &Operands); bool validateInstruction(MCInst &Inst, const SmallVectorImpl &Operands); /// Scan the next token (which had better be an identifier) and determine /// whether it represents a general-purpose or vector register. It returns /// true if an identifier was found and populates its reference arguments. It /// does not consume the token. bool IdentifyRegister(unsigned &RegNum, SMLoc &RegEndLoc, StringRef &LayoutSpec, SMLoc &LayoutLoc) const; }; } namespace { /// Instances of this class represent a parsed AArch64 machine instruction. class AArch64Operand : public MCParsedAsmOperand { private: enum KindTy { k_ImmWithLSL, // #uimm {, LSL #amt } k_CondCode, // eq/ne/... k_FPImmediate, // Limited-precision floating-point imm k_Immediate, // Including expressions referencing symbols k_Register, k_ShiftExtend, k_SysReg, // The register operand of MRS and MSR instructions k_Token, // The mnemonic; other raw tokens the auto-generated k_WrappedRegister // Load/store exclusive permit a wrapped register. } Kind; SMLoc StartLoc, EndLoc; struct ImmWithLSLOp { const MCExpr *Val; unsigned ShiftAmount; bool ImplicitAmount; }; struct CondCodeOp { A64CC::CondCodes Code; }; struct FPImmOp { double Val; }; struct ImmOp { const MCExpr *Val; }; struct RegOp { unsigned RegNum; }; struct ShiftExtendOp { A64SE::ShiftExtSpecifiers ShiftType; unsigned Amount; bool ImplicitAmount; }; struct SysRegOp { const char *Data; unsigned Length; }; struct TokOp { const char *Data; unsigned Length; }; union { struct ImmWithLSLOp ImmWithLSL; struct CondCodeOp CondCode; struct FPImmOp FPImm; struct ImmOp Imm; struct RegOp Reg; struct ShiftExtendOp ShiftExtend; struct SysRegOp SysReg; struct TokOp Tok; }; AArch64Operand(KindTy K, SMLoc S, SMLoc E) : MCParsedAsmOperand(), Kind(K), StartLoc(S), EndLoc(E) {} public: AArch64Operand(const AArch64Operand &o) : MCParsedAsmOperand() { } SMLoc getStartLoc() const { return StartLoc; } SMLoc getEndLoc() const { return EndLoc; } void print(raw_ostream&) const; void dump() const; StringRef getToken() const { assert(Kind == k_Token && "Invalid access!"); return StringRef(Tok.Data, Tok.Length); } unsigned getReg() const { assert((Kind == k_Register || Kind == k_WrappedRegister) && "Invalid access!"); return Reg.RegNum; } const MCExpr *getImm() const { assert(Kind == k_Immediate && "Invalid access!"); return Imm.Val; } A64CC::CondCodes getCondCode() const { assert(Kind == k_CondCode && "Invalid access!"); return CondCode.Code; } static bool isNonConstantExpr(const MCExpr *E, AArch64MCExpr::VariantKind &Variant) { if (const AArch64MCExpr *A64E = dyn_cast(E)) { Variant = A64E->getKind(); return true; } else if (!isa(E)) { Variant = AArch64MCExpr::VK_AARCH64_None; return true; } return false; } bool isCondCode() const { return Kind == k_CondCode; } bool isToken() const { return Kind == k_Token; } bool isReg() const { return Kind == k_Register; } bool isImm() const { return Kind == k_Immediate; } bool isMem() const { return false; } bool isFPImm() const { return Kind == k_FPImmediate; } bool isShiftOrExtend() const { return Kind == k_ShiftExtend; } bool isSysReg() const { return Kind == k_SysReg; } bool isImmWithLSL() const { return Kind == k_ImmWithLSL; } bool isWrappedReg() const { return Kind == k_WrappedRegister; } bool isAddSubImmLSL0() const { if (!isImmWithLSL()) return false; if (ImmWithLSL.ShiftAmount != 0) return false; AArch64MCExpr::VariantKind Variant; if (isNonConstantExpr(ImmWithLSL.Val, Variant)) { return Variant == AArch64MCExpr::VK_AARCH64_LO12 || Variant == AArch64MCExpr::VK_AARCH64_DTPREL_LO12 || Variant == AArch64MCExpr::VK_AARCH64_DTPREL_LO12_NC || Variant == AArch64MCExpr::VK_AARCH64_TPREL_LO12 || Variant == AArch64MCExpr::VK_AARCH64_TPREL_LO12_NC || Variant == AArch64MCExpr::VK_AARCH64_TLSDESC_LO12; } // Otherwise it should be a real immediate in range: const MCConstantExpr *CE = cast(ImmWithLSL.Val); return CE->getValue() >= 0 && CE->getValue() <= 0xfff; } bool isAddSubImmLSL12() const { if (!isImmWithLSL()) return false; if (ImmWithLSL.ShiftAmount != 12) return false; AArch64MCExpr::VariantKind Variant; if (isNonConstantExpr(ImmWithLSL.Val, Variant)) { return Variant == AArch64MCExpr::VK_AARCH64_DTPREL_HI12 || Variant == AArch64MCExpr::VK_AARCH64_TPREL_HI12; } // Otherwise it should be a real immediate in range: const MCConstantExpr *CE = cast(ImmWithLSL.Val); return CE->getValue() >= 0 && CE->getValue() <= 0xfff; } template bool isAddrRegExtend() const { if (!isShiftOrExtend()) return false; A64SE::ShiftExtSpecifiers Ext = ShiftExtend.ShiftType; if (RmSize == 32 && !(Ext == A64SE::UXTW || Ext == A64SE::SXTW)) return false; if (RmSize == 64 && !(Ext == A64SE::LSL || Ext == A64SE::SXTX)) return false; return ShiftExtend.Amount == Log2_32(MemSize) || ShiftExtend.Amount == 0; } bool isAdrpLabel() const { if (!isImm()) return false; AArch64MCExpr::VariantKind Variant; if (isNonConstantExpr(getImm(), Variant)) { return Variant == AArch64MCExpr::VK_AARCH64_None || Variant == AArch64MCExpr::VK_AARCH64_GOT || Variant == AArch64MCExpr::VK_AARCH64_GOTTPREL || Variant == AArch64MCExpr::VK_AARCH64_TLSDESC; } return isLabel<21, 4096>(); } template bool isBitfieldWidth() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); if (!CE) return false; return CE->getValue() >= 1 && CE->getValue() <= RegWidth; } template bool isCVTFixedPos() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); if (!CE) return false; return CE->getValue() >= 1 && CE->getValue() <= RegWidth; } bool isFMOVImm() const { if (!isFPImm()) return false; APFloat RealVal(FPImm.Val); uint32_t ImmVal; return A64Imms::isFPImm(RealVal, ImmVal); } bool isFPZero() const { if (!isFPImm()) return false; APFloat RealVal(FPImm.Val); return RealVal.isPosZero(); } template bool isLabel() const { if (!isImm()) return false; if (dyn_cast(Imm.Val)) { return true; } else if (const MCConstantExpr *CE = dyn_cast(Imm.Val)) { int64_t Val = CE->getValue(); int64_t Min = - (scale * (1LL << (field_width - 1))); int64_t Max = scale * ((1LL << (field_width - 1)) - 1); return (Val % scale) == 0 && Val >= Min && Val <= Max; } // N.b. this disallows explicit relocation specifications via an // AArch64MCExpr. Users needing that behaviour return false; } bool isLane1() const { if (!isImm()) return false; // Because it's come through custom assembly parsing, it must always be a // constant expression. return cast(getImm())->getValue() == 1; } bool isLoadLitLabel() const { if (!isImm()) return false; AArch64MCExpr::VariantKind Variant; if (isNonConstantExpr(getImm(), Variant)) { return Variant == AArch64MCExpr::VK_AARCH64_None || Variant == AArch64MCExpr::VK_AARCH64_GOTTPREL; } return isLabel<19, 4>(); } template bool isLogicalImm() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(Imm.Val); if (!CE) return false; uint32_t Bits; return A64Imms::isLogicalImm(RegWidth, CE->getValue(), Bits); } template bool isLogicalImmMOV() const { if (!isLogicalImm()) return false; const MCConstantExpr *CE = cast(Imm.Val); // The move alias for ORR is only valid if the immediate cannot be // represented with a move (immediate) instruction; they take priority. int UImm16, Shift; return !A64Imms::isMOVZImm(RegWidth, CE->getValue(), UImm16, Shift) && !A64Imms::isMOVNImm(RegWidth, CE->getValue(), UImm16, Shift); } template bool isOffsetUImm12() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); // Assume they know what they're doing for now if they've given us a // non-constant expression. In principle we could check for ridiculous // things that can't possibly work or relocations that would almost // certainly break resulting code. if (!CE) return true; int64_t Val = CE->getValue(); // Must be a multiple of the access size in bytes. if ((Val & (MemSize - 1)) != 0) return false; // Must be 12-bit unsigned return Val >= 0 && Val <= 0xfff * MemSize; } template bool isShift() const { if (!isShiftOrExtend()) return false; if (ShiftExtend.ShiftType != SHKind) return false; return is64Bit ? ShiftExtend.Amount <= 63 : ShiftExtend.Amount <= 31; } bool isMOVN32Imm() const { static AArch64MCExpr::VariantKind PermittedModifiers[] = { AArch64MCExpr::VK_AARCH64_SABS_G0, AArch64MCExpr::VK_AARCH64_SABS_G1, AArch64MCExpr::VK_AARCH64_DTPREL_G1, AArch64MCExpr::VK_AARCH64_DTPREL_G0, AArch64MCExpr::VK_AARCH64_GOTTPREL_G1, AArch64MCExpr::VK_AARCH64_TPREL_G1, AArch64MCExpr::VK_AARCH64_TPREL_G0, }; unsigned NumModifiers = llvm::array_lengthof(PermittedModifiers); return isMoveWideImm(32, PermittedModifiers, NumModifiers); } bool isMOVN64Imm() const { static AArch64MCExpr::VariantKind PermittedModifiers[] = { AArch64MCExpr::VK_AARCH64_SABS_G0, AArch64MCExpr::VK_AARCH64_SABS_G1, AArch64MCExpr::VK_AARCH64_SABS_G2, AArch64MCExpr::VK_AARCH64_DTPREL_G2, AArch64MCExpr::VK_AARCH64_DTPREL_G1, AArch64MCExpr::VK_AARCH64_DTPREL_G0, AArch64MCExpr::VK_AARCH64_GOTTPREL_G1, AArch64MCExpr::VK_AARCH64_TPREL_G2, AArch64MCExpr::VK_AARCH64_TPREL_G1, AArch64MCExpr::VK_AARCH64_TPREL_G0, }; unsigned NumModifiers = llvm::array_lengthof(PermittedModifiers); return isMoveWideImm(64, PermittedModifiers, NumModifiers); } bool isMOVZ32Imm() const { static AArch64MCExpr::VariantKind PermittedModifiers[] = { AArch64MCExpr::VK_AARCH64_ABS_G0, AArch64MCExpr::VK_AARCH64_ABS_G1, AArch64MCExpr::VK_AARCH64_SABS_G0, AArch64MCExpr::VK_AARCH64_SABS_G1, AArch64MCExpr::VK_AARCH64_DTPREL_G1, AArch64MCExpr::VK_AARCH64_DTPREL_G0, AArch64MCExpr::VK_AARCH64_GOTTPREL_G1, AArch64MCExpr::VK_AARCH64_TPREL_G1, AArch64MCExpr::VK_AARCH64_TPREL_G0, }; unsigned NumModifiers = llvm::array_lengthof(PermittedModifiers); return isMoveWideImm(32, PermittedModifiers, NumModifiers); } bool isMOVZ64Imm() const { static AArch64MCExpr::VariantKind PermittedModifiers[] = { AArch64MCExpr::VK_AARCH64_ABS_G0, AArch64MCExpr::VK_AARCH64_ABS_G1, AArch64MCExpr::VK_AARCH64_ABS_G2, AArch64MCExpr::VK_AARCH64_ABS_G3, AArch64MCExpr::VK_AARCH64_SABS_G0, AArch64MCExpr::VK_AARCH64_SABS_G1, AArch64MCExpr::VK_AARCH64_SABS_G2, AArch64MCExpr::VK_AARCH64_DTPREL_G2, AArch64MCExpr::VK_AARCH64_DTPREL_G1, AArch64MCExpr::VK_AARCH64_DTPREL_G0, AArch64MCExpr::VK_AARCH64_GOTTPREL_G1, AArch64MCExpr::VK_AARCH64_TPREL_G2, AArch64MCExpr::VK_AARCH64_TPREL_G1, AArch64MCExpr::VK_AARCH64_TPREL_G0, }; unsigned NumModifiers = llvm::array_lengthof(PermittedModifiers); return isMoveWideImm(64, PermittedModifiers, NumModifiers); } bool isMOVK32Imm() const { static AArch64MCExpr::VariantKind PermittedModifiers[] = { AArch64MCExpr::VK_AARCH64_ABS_G0_NC, AArch64MCExpr::VK_AARCH64_ABS_G1_NC, AArch64MCExpr::VK_AARCH64_DTPREL_G1_NC, AArch64MCExpr::VK_AARCH64_DTPREL_G0_NC, AArch64MCExpr::VK_AARCH64_GOTTPREL_G0_NC, AArch64MCExpr::VK_AARCH64_TPREL_G1_NC, AArch64MCExpr::VK_AARCH64_TPREL_G0_NC, }; unsigned NumModifiers = llvm::array_lengthof(PermittedModifiers); return isMoveWideImm(32, PermittedModifiers, NumModifiers); } bool isMOVK64Imm() const { static AArch64MCExpr::VariantKind PermittedModifiers[] = { AArch64MCExpr::VK_AARCH64_ABS_G0_NC, AArch64MCExpr::VK_AARCH64_ABS_G1_NC, AArch64MCExpr::VK_AARCH64_ABS_G2_NC, AArch64MCExpr::VK_AARCH64_ABS_G3, AArch64MCExpr::VK_AARCH64_DTPREL_G1_NC, AArch64MCExpr::VK_AARCH64_DTPREL_G0_NC, AArch64MCExpr::VK_AARCH64_GOTTPREL_G0_NC, AArch64MCExpr::VK_AARCH64_TPREL_G1_NC, AArch64MCExpr::VK_AARCH64_TPREL_G0_NC, }; unsigned NumModifiers = llvm::array_lengthof(PermittedModifiers); return isMoveWideImm(64, PermittedModifiers, NumModifiers); } bool isMoveWideImm(unsigned RegWidth, AArch64MCExpr::VariantKind *PermittedModifiers, unsigned NumModifiers) const { if (!isImmWithLSL()) return false; if (ImmWithLSL.ShiftAmount % 16 != 0) return false; if (ImmWithLSL.ShiftAmount >= RegWidth) return false; AArch64MCExpr::VariantKind Modifier; if (isNonConstantExpr(ImmWithLSL.Val, Modifier)) { // E.g. "#:abs_g0:sym, lsl #16" makes no sense. if (!ImmWithLSL.ImplicitAmount) return false; for (unsigned i = 0; i < NumModifiers; ++i) if (PermittedModifiers[i] == Modifier) return true; return false; } const MCConstantExpr *CE = dyn_cast(ImmWithLSL.Val); return CE && CE->getValue() >= 0 && CE->getValue() <= 0xffff; } template bool isMoveWideMovAlias() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); if (!CE) return false; int UImm16, Shift; uint64_t Value = CE->getValue(); // If this is a 32-bit instruction then all bits above 32 should be the // same: either of these is fine because signed/unsigned values should be // permitted. if (RegWidth == 32) { if ((Value >> 32) != 0 && (Value >> 32) != 0xffffffff) return false; Value &= 0xffffffffULL; } return isValidImm(RegWidth, Value, UImm16, Shift); } bool isMSRWithReg() const { if (!isSysReg()) return false; bool IsKnownRegister; StringRef Name(SysReg.Data, SysReg.Length); A64SysReg::MSRMapper().fromString(Name, IsKnownRegister); return IsKnownRegister; } bool isMSRPState() const { if (!isSysReg()) return false; bool IsKnownRegister; StringRef Name(SysReg.Data, SysReg.Length); A64PState::PStateMapper().fromString(Name, IsKnownRegister); return IsKnownRegister; } bool isMRS() const { if (!isSysReg()) return false; // First check against specific MSR-only (write-only) registers bool IsKnownRegister; StringRef Name(SysReg.Data, SysReg.Length); A64SysReg::MRSMapper().fromString(Name, IsKnownRegister); return IsKnownRegister; } bool isPRFM() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); if (!CE) return false; return CE->getValue() >= 0 && CE->getValue() <= 31; } template bool isRegExtend() const { if (!isShiftOrExtend()) return false; if (ShiftExtend.ShiftType != SHKind) return false; return ShiftExtend.Amount <= 4; } bool isRegExtendLSL() const { if (!isShiftOrExtend()) return false; if (ShiftExtend.ShiftType != A64SE::LSL) return false; return !ShiftExtend.ImplicitAmount && ShiftExtend.Amount <= 4; } template bool isSImm7Scaled() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); if (!CE) return false; int64_t Val = CE->getValue(); if (Val % MemSize != 0) return false; Val /= MemSize; return Val >= -64 && Val < 64; } template bool isSImm() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); if (!CE) return false; return CE->getValue() >= -(1LL << (BitWidth - 1)) && CE->getValue() < (1LL << (BitWidth - 1)); } template bool isUImm() const { if (!isImm()) return false; const MCConstantExpr *CE = dyn_cast(getImm()); if (!CE) return false; return CE->getValue() >= 0 && CE->getValue() < (1LL << bitWidth); } bool isUImm() const { if (!isImm()) return false; return isa(getImm()); } static AArch64Operand *CreateImmWithLSL(const MCExpr *Val, unsigned ShiftAmount, bool ImplicitAmount, SMLoc S, SMLoc E) { AArch64Operand *Op = new AArch64Operand(k_ImmWithLSL, S, E); Op->ImmWithLSL.Val = Val; Op->ImmWithLSL.ShiftAmount = ShiftAmount; Op->ImmWithLSL.ImplicitAmount = ImplicitAmount; return Op; } static AArch64Operand *CreateCondCode(A64CC::CondCodes Code, SMLoc S, SMLoc E) { AArch64Operand *Op = new AArch64Operand(k_CondCode, S, E); Op->CondCode.Code = Code; return Op; } static AArch64Operand *CreateFPImm(double Val, SMLoc S, SMLoc E) { AArch64Operand *Op = new AArch64Operand(k_FPImmediate, S, E); Op->FPImm.Val = Val; return Op; } static AArch64Operand *CreateImm(const MCExpr *Val, SMLoc S, SMLoc E) { AArch64Operand *Op = new AArch64Operand(k_Immediate, S, E); Op->Imm.Val = Val; return Op; } static AArch64Operand *CreateReg(unsigned RegNum, SMLoc S, SMLoc E) { AArch64Operand *Op = new AArch64Operand(k_Register, S, E); Op->Reg.RegNum = RegNum; return Op; } static AArch64Operand *CreateWrappedReg(unsigned RegNum, SMLoc S, SMLoc E) { AArch64Operand *Op = new AArch64Operand(k_WrappedRegister, S, E); Op->Reg.RegNum = RegNum; return Op; } static AArch64Operand *CreateShiftExtend(A64SE::ShiftExtSpecifiers ShiftTyp, unsigned Amount, bool ImplicitAmount, SMLoc S, SMLoc E) { AArch64Operand *Op = new AArch64Operand(k_ShiftExtend, S, E); Op->ShiftExtend.ShiftType = ShiftTyp; Op->ShiftExtend.Amount = Amount; Op->ShiftExtend.ImplicitAmount = ImplicitAmount; return Op; } static AArch64Operand *CreateSysReg(StringRef Str, SMLoc S) { AArch64Operand *Op = new AArch64Operand(k_SysReg, S, S); Op->Tok.Data = Str.data(); Op->Tok.Length = Str.size(); return Op; } static AArch64Operand *CreateToken(StringRef Str, SMLoc S) { AArch64Operand *Op = new AArch64Operand(k_Token, S, S); Op->Tok.Data = Str.data(); Op->Tok.Length = Str.size(); return Op; } void addExpr(MCInst &Inst, const MCExpr *Expr) const { // Add as immediates when possible. if (const MCConstantExpr *CE = dyn_cast(Expr)) Inst.addOperand(MCOperand::CreateImm(CE->getValue())); else Inst.addOperand(MCOperand::CreateExpr(Expr)); } template void addBFILSBOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); const MCConstantExpr *CE = cast(getImm()); unsigned EncodedVal = (RegWidth - CE->getValue()) % RegWidth; Inst.addOperand(MCOperand::CreateImm(EncodedVal)); } void addBFIWidthOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); const MCConstantExpr *CE = cast(getImm()); Inst.addOperand(MCOperand::CreateImm(CE->getValue() - 1)); } void addBFXWidthOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); uint64_t LSB = Inst.getOperand(Inst.getNumOperands()-1).getImm(); const MCConstantExpr *CE = cast(getImm()); Inst.addOperand(MCOperand::CreateImm(LSB + CE->getValue() - 1)); } void addCondCodeOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::CreateImm(getCondCode())); } void addCVTFixedPosOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); const MCConstantExpr *CE = cast(getImm()); Inst.addOperand(MCOperand::CreateImm(64 - CE->getValue())); } void addFMOVImmOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); APFloat RealVal(FPImm.Val); uint32_t ImmVal; A64Imms::isFPImm(RealVal, ImmVal); Inst.addOperand(MCOperand::CreateImm(ImmVal)); } void addFPZeroOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands"); Inst.addOperand(MCOperand::CreateImm(0)); } void addInvCondCodeOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); unsigned Encoded = A64InvertCondCode(getCondCode()); Inst.addOperand(MCOperand::CreateImm(Encoded)); } void addRegOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::CreateReg(getReg())); } void addImmOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); addExpr(Inst, getImm()); } template void addSImm7ScaledOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); const MCConstantExpr *CE = cast(getImm()); uint64_t Val = CE->getValue() / MemSize; Inst.addOperand(MCOperand::CreateImm(Val & 0x7f)); } template void addSImmOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); const MCConstantExpr *CE = cast(getImm()); uint64_t Val = CE->getValue(); Inst.addOperand(MCOperand::CreateImm(Val & ((1ULL << BitWidth) - 1))); } void addImmWithLSLOperands(MCInst &Inst, unsigned N) const { assert (N == 1 && "Invalid number of operands!"); addExpr(Inst, ImmWithLSL.Val); } template void addLabelOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); const MCConstantExpr *CE = dyn_cast(Imm.Val); if (!CE) { addExpr(Inst, Imm.Val); return; } int64_t Val = CE->getValue(); assert(Val % scale == 0 && "Unaligned immediate in instruction"); Val /= scale; Inst.addOperand(MCOperand::CreateImm(Val & ((1LL << field_width) - 1))); } template void addOffsetUImm12Operands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); if (const MCConstantExpr *CE = dyn_cast(getImm())) { Inst.addOperand(MCOperand::CreateImm(CE->getValue() / MemSize)); } else { Inst.addOperand(MCOperand::CreateExpr(getImm())); } } template void addLogicalImmOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands"); const MCConstantExpr *CE = cast(Imm.Val); uint32_t Bits; A64Imms::isLogicalImm(RegWidth, CE->getValue(), Bits); Inst.addOperand(MCOperand::CreateImm(Bits)); } void addMRSOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); bool Valid; StringRef Name(SysReg.Data, SysReg.Length); uint32_t Bits = A64SysReg::MRSMapper().fromString(Name, Valid); Inst.addOperand(MCOperand::CreateImm(Bits)); } void addMSRWithRegOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); bool Valid; StringRef Name(SysReg.Data, SysReg.Length); uint32_t Bits = A64SysReg::MSRMapper().fromString(Name, Valid); Inst.addOperand(MCOperand::CreateImm(Bits)); } void addMSRPStateOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); bool Valid; StringRef Name(SysReg.Data, SysReg.Length); uint32_t Bits = A64PState::PStateMapper().fromString(Name, Valid); Inst.addOperand(MCOperand::CreateImm(Bits)); } void addMoveWideImmOperands(MCInst &Inst, unsigned N) const { assert(N == 2 && "Invalid number of operands!"); addExpr(Inst, ImmWithLSL.Val); AArch64MCExpr::VariantKind Variant; if (!isNonConstantExpr(ImmWithLSL.Val, Variant)) { Inst.addOperand(MCOperand::CreateImm(ImmWithLSL.ShiftAmount / 16)); return; } // We know it's relocated switch (Variant) { case AArch64MCExpr::VK_AARCH64_ABS_G0: case AArch64MCExpr::VK_AARCH64_ABS_G0_NC: case AArch64MCExpr::VK_AARCH64_SABS_G0: case AArch64MCExpr::VK_AARCH64_DTPREL_G0: case AArch64MCExpr::VK_AARCH64_DTPREL_G0_NC: case AArch64MCExpr::VK_AARCH64_GOTTPREL_G0_NC: case AArch64MCExpr::VK_AARCH64_TPREL_G0: case AArch64MCExpr::VK_AARCH64_TPREL_G0_NC: Inst.addOperand(MCOperand::CreateImm(0)); break; case AArch64MCExpr::VK_AARCH64_ABS_G1: case AArch64MCExpr::VK_AARCH64_ABS_G1_NC: case AArch64MCExpr::VK_AARCH64_SABS_G1: case AArch64MCExpr::VK_AARCH64_DTPREL_G1: case AArch64MCExpr::VK_AARCH64_DTPREL_G1_NC: case AArch64MCExpr::VK_AARCH64_GOTTPREL_G1: case AArch64MCExpr::VK_AARCH64_TPREL_G1: case AArch64MCExpr::VK_AARCH64_TPREL_G1_NC: Inst.addOperand(MCOperand::CreateImm(1)); break; case AArch64MCExpr::VK_AARCH64_ABS_G2: case AArch64MCExpr::VK_AARCH64_ABS_G2_NC: case AArch64MCExpr::VK_AARCH64_SABS_G2: case AArch64MCExpr::VK_AARCH64_DTPREL_G2: case AArch64MCExpr::VK_AARCH64_TPREL_G2: Inst.addOperand(MCOperand::CreateImm(2)); break; case AArch64MCExpr::VK_AARCH64_ABS_G3: Inst.addOperand(MCOperand::CreateImm(3)); break; default: llvm_unreachable("Inappropriate move wide relocation"); } } template void addMoveWideMovAliasOperands(MCInst &Inst, unsigned N) const { assert(N == 2 && "Invalid number of operands!"); int UImm16, Shift; const MCConstantExpr *CE = cast(getImm()); uint64_t Value = CE->getValue(); if (RegWidth == 32) { Value &= 0xffffffffULL; } bool Valid = isValidImm(RegWidth, Value, UImm16, Shift); (void)Valid; assert(Valid && "Invalid immediates should have been weeded out by now"); Inst.addOperand(MCOperand::CreateImm(UImm16)); Inst.addOperand(MCOperand::CreateImm(Shift)); } void addPRFMOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); const MCConstantExpr *CE = cast(getImm()); assert(CE->getValue() >= 0 && CE->getValue() <= 31 && "PRFM operand should be 5-bits"); Inst.addOperand(MCOperand::CreateImm(CE->getValue())); } // For Add-sub (extended register) operands. void addRegExtendOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::CreateImm(ShiftExtend.Amount)); } // For the extend in load-store (register offset) instructions. template void addAddrRegExtendOperands(MCInst &Inst, unsigned N) const { addAddrRegExtendOperands(Inst, N, MemSize); } void addAddrRegExtendOperands(MCInst &Inst, unsigned N, unsigned MemSize) const { assert(N == 1 && "Invalid number of operands!"); // First bit of Option is set in instruction classes, the high two bits are // as follows: unsigned OptionHi = 0; switch (ShiftExtend.ShiftType) { case A64SE::UXTW: case A64SE::LSL: OptionHi = 1; break; case A64SE::SXTW: case A64SE::SXTX: OptionHi = 3; break; default: llvm_unreachable("Invalid extend type for register offset"); } unsigned S = 0; if (MemSize == 1 && !ShiftExtend.ImplicitAmount) S = 1; else if (MemSize != 1 && ShiftExtend.Amount != 0) S = 1; Inst.addOperand(MCOperand::CreateImm((OptionHi << 1) | S)); } void addShiftOperands(MCInst &Inst, unsigned N) const { assert(N == 1 && "Invalid number of operands!"); Inst.addOperand(MCOperand::CreateImm(ShiftExtend.Amount)); } }; } // end anonymous namespace. AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseOperand(SmallVectorImpl &Operands, StringRef Mnemonic) { // See if the operand has a custom parser OperandMatchResultTy ResTy = MatchOperandParserImpl(Operands, Mnemonic); // It could either succeed, fail or just not care. if (ResTy != MatchOperand_NoMatch) return ResTy; switch (getLexer().getKind()) { default: Error(Parser.getTok().getLoc(), "unexpected token in operand"); return MatchOperand_ParseFail; case AsmToken::Identifier: { // It might be in the LSL/UXTB family ... OperandMatchResultTy GotShift = ParseShiftExtend(Operands); // We can only continue if no tokens were eaten. if (GotShift != MatchOperand_NoMatch) return GotShift; // ... or it might be a register ... uint32_t NumLanes = 0; OperandMatchResultTy GotReg = ParseRegister(Operands, NumLanes); assert(GotReg != MatchOperand_ParseFail && "register parsing shouldn't partially succeed"); if (GotReg == MatchOperand_Success) { if (Parser.getTok().is(AsmToken::LBrac)) return ParseNEONLane(Operands, NumLanes); else return MatchOperand_Success; } // ... or it might be a symbolish thing } // Fall through case AsmToken::LParen: // E.g. (strcmp-4) case AsmToken::Integer: // 1f, 2b labels case AsmToken::String: // quoted labels case AsmToken::Dot: // . is Current location case AsmToken::Dollar: // $ is PC case AsmToken::Colon: { SMLoc StartLoc = Parser.getTok().getLoc(); SMLoc EndLoc; const MCExpr *ImmVal = 0; if (ParseImmediate(ImmVal) != MatchOperand_Success) return MatchOperand_ParseFail; EndLoc = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); Operands.push_back(AArch64Operand::CreateImm(ImmVal, StartLoc, EndLoc)); return MatchOperand_Success; } case AsmToken::Hash: { // Immediates SMLoc StartLoc = Parser.getTok().getLoc(); SMLoc EndLoc; const MCExpr *ImmVal = 0; Parser.Lex(); if (ParseImmediate(ImmVal) != MatchOperand_Success) return MatchOperand_ParseFail; EndLoc = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); Operands.push_back(AArch64Operand::CreateImm(ImmVal, StartLoc, EndLoc)); return MatchOperand_Success; } case AsmToken::LBrac: { SMLoc Loc = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateToken("[", Loc)); Parser.Lex(); // Eat '[' // There's no comma after a '[', so we can parse the next operand // immediately. return ParseOperand(Operands, Mnemonic); } // The following will likely be useful later, but not in very early cases case AsmToken::LCurly: // Weird SIMD lists llvm_unreachable("Don't know how to deal with '{' in operand"); return MatchOperand_ParseFail; } } AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseImmediate(const MCExpr *&ExprVal) { if (getLexer().is(AsmToken::Colon)) { AArch64MCExpr::VariantKind RefKind; OperandMatchResultTy ResTy = ParseRelocPrefix(RefKind); if (ResTy != MatchOperand_Success) return ResTy; const MCExpr *SubExprVal; if (getParser().parseExpression(SubExprVal)) return MatchOperand_ParseFail; ExprVal = AArch64MCExpr::Create(RefKind, SubExprVal, getContext()); return MatchOperand_Success; } // No weird AArch64MCExpr prefix return getParser().parseExpression(ExprVal) ? MatchOperand_ParseFail : MatchOperand_Success; } // A lane attached to a NEON register. "[N]", which should yield three tokens: // '[', N, ']'. A hash is not allowed to precede the immediate here. AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseNEONLane(SmallVectorImpl &Operands, uint32_t NumLanes) { SMLoc Loc = Parser.getTok().getLoc(); assert(Parser.getTok().is(AsmToken::LBrac) && "inappropriate operand"); Operands.push_back(AArch64Operand::CreateToken("[", Loc)); Parser.Lex(); // Eat '[' if (Parser.getTok().isNot(AsmToken::Integer)) { Error(Parser.getTok().getLoc(), "expected lane number"); return MatchOperand_ParseFail; } if (Parser.getTok().getIntVal() >= NumLanes) { Error(Parser.getTok().getLoc(), "lane number incompatible with layout"); return MatchOperand_ParseFail; } const MCExpr *Lane = MCConstantExpr::Create(Parser.getTok().getIntVal(), getContext()); SMLoc S = Parser.getTok().getLoc(); Parser.Lex(); // Eat actual lane SMLoc E = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateImm(Lane, S, E)); if (Parser.getTok().isNot(AsmToken::RBrac)) { Error(Parser.getTok().getLoc(), "expected ']' after lane"); return MatchOperand_ParseFail; } Operands.push_back(AArch64Operand::CreateToken("]", Loc)); Parser.Lex(); // Eat ']' return MatchOperand_Success; } AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseRelocPrefix(AArch64MCExpr::VariantKind &RefKind) { assert(getLexer().is(AsmToken::Colon) && "expected a ':'"); Parser.Lex(); if (getLexer().isNot(AsmToken::Identifier)) { Error(Parser.getTok().getLoc(), "expected relocation specifier in operand after ':'"); return MatchOperand_ParseFail; } std::string LowerCase = Parser.getTok().getIdentifier().lower(); RefKind = StringSwitch(LowerCase) .Case("got", AArch64MCExpr::VK_AARCH64_GOT) .Case("got_lo12", AArch64MCExpr::VK_AARCH64_GOT_LO12) .Case("lo12", AArch64MCExpr::VK_AARCH64_LO12) .Case("abs_g0", AArch64MCExpr::VK_AARCH64_ABS_G0) .Case("abs_g0_nc", AArch64MCExpr::VK_AARCH64_ABS_G0_NC) .Case("abs_g1", AArch64MCExpr::VK_AARCH64_ABS_G1) .Case("abs_g1_nc", AArch64MCExpr::VK_AARCH64_ABS_G1_NC) .Case("abs_g2", AArch64MCExpr::VK_AARCH64_ABS_G2) .Case("abs_g2_nc", AArch64MCExpr::VK_AARCH64_ABS_G2_NC) .Case("abs_g3", AArch64MCExpr::VK_AARCH64_ABS_G3) .Case("abs_g0_s", AArch64MCExpr::VK_AARCH64_SABS_G0) .Case("abs_g1_s", AArch64MCExpr::VK_AARCH64_SABS_G1) .Case("abs_g2_s", AArch64MCExpr::VK_AARCH64_SABS_G2) .Case("dtprel_g2", AArch64MCExpr::VK_AARCH64_DTPREL_G2) .Case("dtprel_g1", AArch64MCExpr::VK_AARCH64_DTPREL_G1) .Case("dtprel_g1_nc", AArch64MCExpr::VK_AARCH64_DTPREL_G1_NC) .Case("dtprel_g0", AArch64MCExpr::VK_AARCH64_DTPREL_G0) .Case("dtprel_g0_nc", AArch64MCExpr::VK_AARCH64_DTPREL_G0_NC) .Case("dtprel_hi12", AArch64MCExpr::VK_AARCH64_DTPREL_HI12) .Case("dtprel_lo12", AArch64MCExpr::VK_AARCH64_DTPREL_LO12) .Case("dtprel_lo12_nc", AArch64MCExpr::VK_AARCH64_DTPREL_LO12_NC) .Case("gottprel_g1", AArch64MCExpr::VK_AARCH64_GOTTPREL_G1) .Case("gottprel_g0_nc", AArch64MCExpr::VK_AARCH64_GOTTPREL_G0_NC) .Case("gottprel", AArch64MCExpr::VK_AARCH64_GOTTPREL) .Case("gottprel_lo12", AArch64MCExpr::VK_AARCH64_GOTTPREL_LO12) .Case("tprel_g2", AArch64MCExpr::VK_AARCH64_TPREL_G2) .Case("tprel_g1", AArch64MCExpr::VK_AARCH64_TPREL_G1) .Case("tprel_g1_nc", AArch64MCExpr::VK_AARCH64_TPREL_G1_NC) .Case("tprel_g0", AArch64MCExpr::VK_AARCH64_TPREL_G0) .Case("tprel_g0_nc", AArch64MCExpr::VK_AARCH64_TPREL_G0_NC) .Case("tprel_hi12", AArch64MCExpr::VK_AARCH64_TPREL_HI12) .Case("tprel_lo12", AArch64MCExpr::VK_AARCH64_TPREL_LO12) .Case("tprel_lo12_nc", AArch64MCExpr::VK_AARCH64_TPREL_LO12_NC) .Case("tlsdesc", AArch64MCExpr::VK_AARCH64_TLSDESC) .Case("tlsdesc_lo12", AArch64MCExpr::VK_AARCH64_TLSDESC_LO12) .Default(AArch64MCExpr::VK_AARCH64_None); if (RefKind == AArch64MCExpr::VK_AARCH64_None) { Error(Parser.getTok().getLoc(), "expected relocation specifier in operand after ':'"); return MatchOperand_ParseFail; } Parser.Lex(); // Eat identifier if (getLexer().isNot(AsmToken::Colon)) { Error(Parser.getTok().getLoc(), "expected ':' after relocation specifier"); return MatchOperand_ParseFail; } Parser.Lex(); return MatchOperand_Success; } AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseImmWithLSLOperand( SmallVectorImpl &Operands) { // FIXME?: I want to live in a world where immediates must start with // #. Please don't dash my hopes (well, do if you have a good reason). if (Parser.getTok().isNot(AsmToken::Hash)) return MatchOperand_NoMatch; SMLoc S = Parser.getTok().getLoc(); Parser.Lex(); // Eat '#' const MCExpr *Imm; if (ParseImmediate(Imm) != MatchOperand_Success) return MatchOperand_ParseFail; else if (Parser.getTok().isNot(AsmToken::Comma)) { SMLoc E = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateImmWithLSL(Imm, 0, true, S, E)); return MatchOperand_Success; } // Eat ',' Parser.Lex(); // The optional operand must be "lsl #N" where N is non-negative. if (Parser.getTok().is(AsmToken::Identifier) && Parser.getTok().getIdentifier().lower() == "lsl") { Parser.Lex(); if (Parser.getTok().is(AsmToken::Hash)) { Parser.Lex(); if (Parser.getTok().isNot(AsmToken::Integer)) { Error(Parser.getTok().getLoc(), "only 'lsl #+N' valid after immediate"); return MatchOperand_ParseFail; } } } int64_t ShiftAmount = Parser.getTok().getIntVal(); if (ShiftAmount < 0) { Error(Parser.getTok().getLoc(), "positive shift amount required"); return MatchOperand_ParseFail; } Parser.Lex(); // Eat the number SMLoc E = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateImmWithLSL(Imm, ShiftAmount, false, S, E)); return MatchOperand_Success; } AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseCondCodeOperand( SmallVectorImpl &Operands) { if (Parser.getTok().isNot(AsmToken::Identifier)) return MatchOperand_NoMatch; StringRef Tok = Parser.getTok().getIdentifier(); A64CC::CondCodes CondCode = A64StringToCondCode(Tok); if (CondCode == A64CC::Invalid) return MatchOperand_NoMatch; SMLoc S = Parser.getTok().getLoc(); Parser.Lex(); // Eat condition code SMLoc E = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateCondCode(CondCode, S, E)); return MatchOperand_Success; } AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseCRxOperand( SmallVectorImpl &Operands) { SMLoc S = Parser.getTok().getLoc(); if (Parser.getTok().isNot(AsmToken::Identifier)) { Error(S, "Expected cN operand where 0 <= N <= 15"); return MatchOperand_ParseFail; } std::string LowerTok = Parser.getTok().getIdentifier().lower(); StringRef Tok(LowerTok); if (Tok[0] != 'c') { Error(S, "Expected cN operand where 0 <= N <= 15"); return MatchOperand_ParseFail; } uint32_t CRNum; bool BadNum = Tok.drop_front().getAsInteger(10, CRNum); if (BadNum || CRNum > 15) { Error(S, "Expected cN operand where 0 <= N <= 15"); return MatchOperand_ParseFail; } const MCExpr *CRImm = MCConstantExpr::Create(CRNum, getContext()); Parser.Lex(); SMLoc E = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateImm(CRImm, S, E)); return MatchOperand_Success; } AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseFPImmOperand( SmallVectorImpl &Operands) { // FIXME?: I want to live in a world where immediates must start with // #. Please don't dash my hopes (well, do if you have a good reason). if (Parser.getTok().isNot(AsmToken::Hash)) return MatchOperand_NoMatch; SMLoc S = Parser.getTok().getLoc(); Parser.Lex(); // Eat '#' bool Negative = false; if (Parser.getTok().is(AsmToken::Minus)) { Negative = true; Parser.Lex(); // Eat '-' } else if (Parser.getTok().is(AsmToken::Plus)) { Parser.Lex(); // Eat '+' } if (Parser.getTok().isNot(AsmToken::Real)) { Error(S, "Expected floating-point immediate"); return MatchOperand_ParseFail; } APFloat RealVal(APFloat::IEEEdouble, Parser.getTok().getString()); if (Negative) RealVal.changeSign(); double DblVal = RealVal.convertToDouble(); Parser.Lex(); // Eat real number SMLoc E = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateFPImm(DblVal, S, E)); return MatchOperand_Success; } // Automatically generated static unsigned MatchRegisterName(StringRef Name); bool AArch64AsmParser::IdentifyRegister(unsigned &RegNum, SMLoc &RegEndLoc, StringRef &Layout, SMLoc &LayoutLoc) const { const AsmToken &Tok = Parser.getTok(); if (Tok.isNot(AsmToken::Identifier)) return false; std::string LowerReg = Tok.getString().lower(); size_t DotPos = LowerReg.find('.'); RegNum = MatchRegisterName(LowerReg.substr(0, DotPos)); if (RegNum == AArch64::NoRegister) { RegNum = StringSwitch(LowerReg.substr(0, DotPos)) .Case("ip0", AArch64::X16) .Case("ip1", AArch64::X17) .Case("fp", AArch64::X29) .Case("lr", AArch64::X30) .Default(AArch64::NoRegister); } if (RegNum == AArch64::NoRegister) return false; SMLoc S = Tok.getLoc(); RegEndLoc = SMLoc::getFromPointer(S.getPointer() + DotPos); if (DotPos == StringRef::npos) { Layout = StringRef(); } else { // Everything afterwards needs to be a literal token, expected to be // '.2d','.b' etc for vector registers. // This StringSwitch validates the input and (perhaps more importantly) // gives us a permanent string to use in the token (a pointer into LowerReg // would go out of scope when we return). LayoutLoc = SMLoc::getFromPointer(S.getPointer() + DotPos + 1); std::string LayoutText = LowerReg.substr(DotPos, StringRef::npos); Layout = StringSwitch(LayoutText) .Case(".d", ".d").Case(".1d", ".1d").Case(".2d", ".2d") .Case(".s", ".s").Case(".2s", ".2s").Case(".4s", ".4s") .Case(".h", ".h").Case(".4h", ".4h").Case(".8h", ".8h") .Case(".b", ".b").Case(".8b", ".8b").Case(".16b", ".16b") .Default(""); if (Layout.size() == 0) { // Malformed register return false; } } return true; } AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseRegister(SmallVectorImpl &Operands, uint32_t &NumLanes) { unsigned RegNum; StringRef Layout; SMLoc RegEndLoc, LayoutLoc; SMLoc S = Parser.getTok().getLoc(); if (!IdentifyRegister(RegNum, RegEndLoc, Layout, LayoutLoc)) return MatchOperand_NoMatch; Operands.push_back(AArch64Operand::CreateReg(RegNum, S, RegEndLoc)); if (Layout.size() != 0) { unsigned long long TmpLanes = 0; llvm::getAsUnsignedInteger(Layout.substr(1), 10, TmpLanes); if (TmpLanes != 0) { NumLanes = TmpLanes; } else { // If the number of lanes isn't specified explicitly, a valid instruction // will have an element specifier and be capable of acting on the entire // vector register. switch (Layout.back()) { default: llvm_unreachable("Invalid layout specifier"); case 'b': NumLanes = 16; break; case 'h': NumLanes = 8; break; case 's': NumLanes = 4; break; case 'd': NumLanes = 2; break; } } Operands.push_back(AArch64Operand::CreateToken(Layout, LayoutLoc)); } Parser.Lex(); return MatchOperand_Success; } bool AArch64AsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) { // This callback is used for things like DWARF frame directives in // assembly. They don't care about things like NEON layouts or lanes, they // just want to be able to produce the DWARF register number. StringRef LayoutSpec; SMLoc RegEndLoc, LayoutLoc; StartLoc = Parser.getTok().getLoc(); if (!IdentifyRegister(RegNo, RegEndLoc, LayoutSpec, LayoutLoc)) return true; Parser.Lex(); EndLoc = Parser.getTok().getLoc(); return false; } AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseNamedImmOperand(const NamedImmMapper &Mapper, SmallVectorImpl &Operands) { // Since these operands occur in very limited circumstances, without // alternatives, we actually signal an error if there is no match. If relaxing // this, beware of unintended consequences: an immediate will be accepted // during matching, no matter how it gets into the AArch64Operand. const AsmToken &Tok = Parser.getTok(); SMLoc S = Tok.getLoc(); if (Tok.is(AsmToken::Identifier)) { bool ValidName; uint32_t Code = Mapper.fromString(Tok.getString().lower(), ValidName); if (!ValidName) { Error(S, "operand specifier not recognised"); return MatchOperand_ParseFail; } Parser.Lex(); // We're done with the identifier. Eat it SMLoc E = Parser.getTok().getLoc(); const MCExpr *Imm = MCConstantExpr::Create(Code, getContext()); Operands.push_back(AArch64Operand::CreateImm(Imm, S, E)); return MatchOperand_Success; } else if (Tok.is(AsmToken::Hash)) { Parser.Lex(); const MCExpr *ImmVal; if (ParseImmediate(ImmVal) != MatchOperand_Success) return MatchOperand_ParseFail; const MCConstantExpr *CE = dyn_cast(ImmVal); if (!CE || CE->getValue() < 0 || !Mapper.validImm(CE->getValue())) { Error(S, "Invalid immediate for instruction"); return MatchOperand_ParseFail; } SMLoc E = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateImm(ImmVal, S, E)); return MatchOperand_Success; } Error(S, "unexpected operand for instruction"); return MatchOperand_ParseFail; } AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseSysRegOperand( SmallVectorImpl &Operands) { const AsmToken &Tok = Parser.getTok(); // Any MSR/MRS operand will be an identifier, and we want to store it as some // kind of string: SPSel is valid for two different forms of MSR with two // different encodings. There's no collision at the moment, but the potential // is there. if (!Tok.is(AsmToken::Identifier)) { return MatchOperand_NoMatch; } SMLoc S = Tok.getLoc(); Operands.push_back(AArch64Operand::CreateSysReg(Tok.getString(), S)); Parser.Lex(); // Eat identifier return MatchOperand_Success; } AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseLSXAddressOperand( SmallVectorImpl &Operands) { SMLoc S = Parser.getTok().getLoc(); unsigned RegNum; SMLoc RegEndLoc, LayoutLoc; StringRef Layout; if(!IdentifyRegister(RegNum, RegEndLoc, Layout, LayoutLoc) || !AArch64MCRegisterClasses[AArch64::GPR64xspRegClassID].contains(RegNum) || Layout.size() != 0) { // Check Layout.size because we don't want to let "x3.4s" or similar // through. return MatchOperand_NoMatch; } Parser.Lex(); // Eat register if (Parser.getTok().is(AsmToken::RBrac)) { // We're done SMLoc E = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateWrappedReg(RegNum, S, E)); return MatchOperand_Success; } // Otherwise, only ", #0" is valid if (Parser.getTok().isNot(AsmToken::Comma)) { Error(Parser.getTok().getLoc(), "expected ',' or ']' after register"); return MatchOperand_ParseFail; } Parser.Lex(); // Eat ',' if (Parser.getTok().isNot(AsmToken::Hash)) { Error(Parser.getTok().getLoc(), "expected '#0'"); return MatchOperand_ParseFail; } Parser.Lex(); // Eat '#' if (Parser.getTok().isNot(AsmToken::Integer) || Parser.getTok().getIntVal() != 0 ) { Error(Parser.getTok().getLoc(), "expected '#0'"); return MatchOperand_ParseFail; } Parser.Lex(); // Eat '0' SMLoc E = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateWrappedReg(RegNum, S, E)); return MatchOperand_Success; } AArch64AsmParser::OperandMatchResultTy AArch64AsmParser::ParseShiftExtend( SmallVectorImpl &Operands) { StringRef IDVal = Parser.getTok().getIdentifier(); std::string LowerID = IDVal.lower(); A64SE::ShiftExtSpecifiers Spec = StringSwitch(LowerID) .Case("lsl", A64SE::LSL) .Case("lsr", A64SE::LSR) .Case("asr", A64SE::ASR) .Case("ror", A64SE::ROR) .Case("uxtb", A64SE::UXTB) .Case("uxth", A64SE::UXTH) .Case("uxtw", A64SE::UXTW) .Case("uxtx", A64SE::UXTX) .Case("sxtb", A64SE::SXTB) .Case("sxth", A64SE::SXTH) .Case("sxtw", A64SE::SXTW) .Case("sxtx", A64SE::SXTX) .Default(A64SE::Invalid); if (Spec == A64SE::Invalid) return MatchOperand_NoMatch; // Eat the shift SMLoc S, E; S = Parser.getTok().getLoc(); Parser.Lex(); if (Spec != A64SE::LSL && Spec != A64SE::LSR && Spec != A64SE::ASR && Spec != A64SE::ROR) { // The shift amount can be omitted for the extending versions, but not real // shifts: // add x0, x0, x0, uxtb // is valid, and equivalent to // add x0, x0, x0, uxtb #0 if (Parser.getTok().is(AsmToken::Comma) || Parser.getTok().is(AsmToken::EndOfStatement) || Parser.getTok().is(AsmToken::RBrac)) { Operands.push_back(AArch64Operand::CreateShiftExtend(Spec, 0, true, S, E)); return MatchOperand_Success; } } // Eat # at beginning of immediate if (!Parser.getTok().is(AsmToken::Hash)) { Error(Parser.getTok().getLoc(), "expected #imm after shift specifier"); return MatchOperand_ParseFail; } Parser.Lex(); // Make sure we do actually have a number if (!Parser.getTok().is(AsmToken::Integer)) { Error(Parser.getTok().getLoc(), "expected integer shift amount"); return MatchOperand_ParseFail; } unsigned Amount = Parser.getTok().getIntVal(); Parser.Lex(); E = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateShiftExtend(Spec, Amount, false, S, E)); return MatchOperand_Success; } // FIXME: We would really like to be able to tablegen'erate this. bool AArch64AsmParser:: validateInstruction(MCInst &Inst, const SmallVectorImpl &Operands) { switch (Inst.getOpcode()) { case AArch64::BFIwwii: case AArch64::BFIxxii: case AArch64::SBFIZwwii: case AArch64::SBFIZxxii: case AArch64::UBFIZwwii: case AArch64::UBFIZxxii: { unsigned ImmOps = Inst.getNumOperands() - 2; int64_t ImmR = Inst.getOperand(ImmOps).getImm(); int64_t ImmS = Inst.getOperand(ImmOps+1).getImm(); if (ImmR != 0 && ImmS >= ImmR) { return Error(Operands[4]->getStartLoc(), "requested insert overflows register"); } return false; } case AArch64::BFXILwwii: case AArch64::BFXILxxii: case AArch64::SBFXwwii: case AArch64::SBFXxxii: case AArch64::UBFXwwii: case AArch64::UBFXxxii: { unsigned ImmOps = Inst.getNumOperands() - 2; int64_t ImmR = Inst.getOperand(ImmOps).getImm(); int64_t ImmS = Inst.getOperand(ImmOps+1).getImm(); int64_t RegWidth = 0; switch (Inst.getOpcode()) { case AArch64::SBFXxxii: case AArch64::UBFXxxii: case AArch64::BFXILxxii: RegWidth = 64; break; case AArch64::SBFXwwii: case AArch64::UBFXwwii: case AArch64::BFXILwwii: RegWidth = 32; break; } if (ImmS >= RegWidth || ImmS < ImmR) { return Error(Operands[4]->getStartLoc(), "requested extract overflows register"); } return false; } case AArch64::ICix: { int64_t ImmVal = Inst.getOperand(0).getImm(); A64IC::ICValues ICOp = static_cast(ImmVal); if (!A64IC::NeedsRegister(ICOp)) { return Error(Operands[1]->getStartLoc(), "specified IC op does not use a register"); } return false; } case AArch64::ICi: { int64_t ImmVal = Inst.getOperand(0).getImm(); A64IC::ICValues ICOp = static_cast(ImmVal); if (A64IC::NeedsRegister(ICOp)) { return Error(Operands[1]->getStartLoc(), "specified IC op requires a register"); } return false; } case AArch64::TLBIix: { int64_t ImmVal = Inst.getOperand(0).getImm(); A64TLBI::TLBIValues TLBIOp = static_cast(ImmVal); if (!A64TLBI::NeedsRegister(TLBIOp)) { return Error(Operands[1]->getStartLoc(), "specified TLBI op does not use a register"); } return false; } case AArch64::TLBIi: { int64_t ImmVal = Inst.getOperand(0).getImm(); A64TLBI::TLBIValues TLBIOp = static_cast(ImmVal); if (A64TLBI::NeedsRegister(TLBIOp)) { return Error(Operands[1]->getStartLoc(), "specified TLBI op requires a register"); } return false; } } return false; } // Parses the instruction *together with* all operands, appending each parsed // operand to the "Operands" list bool AArch64AsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, SMLoc NameLoc, SmallVectorImpl &Operands) { size_t CondCodePos = Name.find('.'); StringRef Mnemonic = Name.substr(0, CondCodePos); Operands.push_back(AArch64Operand::CreateToken(Mnemonic, NameLoc)); if (CondCodePos != StringRef::npos) { // We have a condition code SMLoc S = SMLoc::getFromPointer(NameLoc.getPointer() + CondCodePos + 1); StringRef CondStr = Name.substr(CondCodePos + 1, StringRef::npos); A64CC::CondCodes Code; Code = A64StringToCondCode(CondStr); if (Code == A64CC::Invalid) { Error(S, "invalid condition code"); Parser.eatToEndOfStatement(); return true; } SMLoc DotL = SMLoc::getFromPointer(NameLoc.getPointer() + CondCodePos); Operands.push_back(AArch64Operand::CreateToken(".", DotL)); SMLoc E = SMLoc::getFromPointer(NameLoc.getPointer() + CondCodePos + 3); Operands.push_back(AArch64Operand::CreateCondCode(Code, S, E)); } // Now we parse the operands of this instruction if (getLexer().isNot(AsmToken::EndOfStatement)) { // Read the first operand. if (ParseOperand(Operands, Mnemonic)) { Parser.eatToEndOfStatement(); return true; } while (getLexer().is(AsmToken::Comma)) { Parser.Lex(); // Eat the comma. // Parse and remember the operand. if (ParseOperand(Operands, Mnemonic)) { Parser.eatToEndOfStatement(); return true; } // After successfully parsing some operands there are two special cases to // consider (i.e. notional operands not separated by commas). Both are due // to memory specifiers: // + An RBrac will end an address for load/store/prefetch // + An '!' will indicate a pre-indexed operation. // // It's someone else's responsibility to make sure these tokens are sane // in the given context! if (Parser.getTok().is(AsmToken::RBrac)) { SMLoc Loc = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateToken("]", Loc)); Parser.Lex(); } if (Parser.getTok().is(AsmToken::Exclaim)) { SMLoc Loc = Parser.getTok().getLoc(); Operands.push_back(AArch64Operand::CreateToken("!", Loc)); Parser.Lex(); } } } if (getLexer().isNot(AsmToken::EndOfStatement)) { SMLoc Loc = getLexer().getLoc(); Parser.eatToEndOfStatement(); return Error(Loc, "expected comma before next operand"); } // Eat the EndOfStatement Parser.Lex(); return false; } bool AArch64AsmParser::ParseDirective(AsmToken DirectiveID) { StringRef IDVal = DirectiveID.getIdentifier(); if (IDVal == ".hword") return ParseDirectiveWord(2, DirectiveID.getLoc()); else if (IDVal == ".word") return ParseDirectiveWord(4, DirectiveID.getLoc()); else if (IDVal == ".xword") return ParseDirectiveWord(8, DirectiveID.getLoc()); else if (IDVal == ".tlsdesccall") return ParseDirectiveTLSDescCall(DirectiveID.getLoc()); return true; } /// parseDirectiveWord /// ::= .word [ expression (, expression)* ] bool AArch64AsmParser::ParseDirectiveWord(unsigned Size, SMLoc L) { if (getLexer().isNot(AsmToken::EndOfStatement)) { for (;;) { const MCExpr *Value; if (getParser().parseExpression(Value)) return true; getParser().getStreamer().EmitValue(Value, Size, 0/*addrspace*/); if (getLexer().is(AsmToken::EndOfStatement)) break; // FIXME: Improve diagnostic. if (getLexer().isNot(AsmToken::Comma)) return Error(L, "unexpected token in directive"); Parser.Lex(); } } Parser.Lex(); return false; } // parseDirectiveTLSDescCall: // ::= .tlsdesccall symbol bool AArch64AsmParser::ParseDirectiveTLSDescCall(SMLoc L) { StringRef Name; if (getParser().parseIdentifier(Name)) return Error(L, "expected symbol after directive"); MCSymbol *Sym = getContext().GetOrCreateSymbol(Name); const MCSymbolRefExpr *Expr = MCSymbolRefExpr::Create(Sym, getContext()); MCInst Inst; Inst.setOpcode(AArch64::TLSDESCCALL); Inst.addOperand(MCOperand::CreateExpr(Expr)); getParser().getStreamer().EmitInstruction(Inst); return false; } bool AArch64AsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, SmallVectorImpl &Operands, MCStreamer &Out, unsigned &ErrorInfo, bool MatchingInlineAsm) { MCInst Inst; unsigned MatchResult; MatchResult = MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); if (ErrorInfo != ~0U && ErrorInfo >= Operands.size()) return Error(IDLoc, "too few operands for instruction"); switch (MatchResult) { default: break; case Match_Success: if (validateInstruction(Inst, Operands)) return true; Out.EmitInstruction(Inst); return false; case Match_MissingFeature: Error(IDLoc, "instruction requires a CPU feature not currently enabled"); return true; case Match_InvalidOperand: { SMLoc ErrorLoc = IDLoc; if (ErrorInfo != ~0U) { ErrorLoc = ((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(); if (ErrorLoc == SMLoc()) ErrorLoc = IDLoc; } return Error(ErrorLoc, "invalid operand for instruction"); } case Match_MnemonicFail: return Error(IDLoc, "invalid instruction"); case Match_AddSubRegExtendSmall: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected '[su]xt[bhw]' or 'lsl' with optional integer in range [0, 4]"); case Match_AddSubRegExtendLarge: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'sxtx' 'uxtx' or 'lsl' with optional integer in range [0, 4]"); case Match_AddSubRegShift32: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'lsl', 'lsr' or 'asr' with optional integer in range [0, 31]"); case Match_AddSubRegShift64: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'lsl', 'lsr' or 'asr' with optional integer in range [0, 63]"); case Match_AddSubSecondSource: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected compatible register, symbol or integer in range [0, 4095]"); case Match_CVTFixedPos32: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [1, 32]"); case Match_CVTFixedPos64: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [1, 64]"); case Match_CondCode: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected AArch64 condition code"); case Match_FPImm: // Any situation which allows a nontrivial floating-point constant also // allows a register. return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected compatible register or floating-point constant"); case Match_FPZero: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected floating-point constant #0.0"); case Match_Label: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected label or encodable integer pc offset"); case Match_Lane1: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected lane specifier '[1]'"); case Match_LoadStoreExtend32_1: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'uxtw' or 'sxtw' with optional shift of #0"); case Match_LoadStoreExtend32_2: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'uxtw' or 'sxtw' with optional shift of #0 or #1"); case Match_LoadStoreExtend32_4: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'uxtw' or 'sxtw' with optional shift of #0 or #2"); case Match_LoadStoreExtend32_8: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'uxtw' or 'sxtw' with optional shift of #0 or #3"); case Match_LoadStoreExtend32_16: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'lsl' or 'sxtw' with optional shift of #0 or #4"); case Match_LoadStoreExtend64_1: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'lsl' or 'sxtx' with optional shift of #0"); case Match_LoadStoreExtend64_2: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'lsl' or 'sxtx' with optional shift of #0 or #1"); case Match_LoadStoreExtend64_4: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'lsl' or 'sxtx' with optional shift of #0 or #2"); case Match_LoadStoreExtend64_8: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'lsl' or 'sxtx' with optional shift of #0 or #3"); case Match_LoadStoreExtend64_16: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'lsl' or 'sxtx' with optional shift of #0 or #4"); case Match_LoadStoreSImm7_4: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer multiple of 4 in range [-256, 252]"); case Match_LoadStoreSImm7_8: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer multiple of 8 in range [-512, 508]"); case Match_LoadStoreSImm7_16: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer multiple of 16 in range [-1024, 1016]"); case Match_LoadStoreSImm9: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [-256, 255]"); case Match_LoadStoreUImm12_1: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected symbolic reference or integer in range [0, 4095]"); case Match_LoadStoreUImm12_2: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected symbolic reference or integer in range [0, 8190]"); case Match_LoadStoreUImm12_4: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected symbolic reference or integer in range [0, 16380]"); case Match_LoadStoreUImm12_8: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected symbolic reference or integer in range [0, 32760]"); case Match_LoadStoreUImm12_16: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected symbolic reference or integer in range [0, 65520]"); case Match_LogicalSecondSource: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected compatible register or logical immediate"); case Match_MOVWUImm16: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected relocated symbol or integer in range [0, 65535]"); case Match_MRS: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected readable system register"); case Match_MSR: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected writable system register or pstate"); case Match_NamedImm_at: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected symbolic 'at' operand: s1e[0-3][rw] or s12e[01][rw]"); case Match_NamedImm_dbarrier: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [0, 15] or symbolic barrier operand"); case Match_NamedImm_dc: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected symbolic 'dc' operand"); case Match_NamedImm_ic: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected 'ic' operand: 'ialluis', 'iallu' or 'ivau'"); case Match_NamedImm_isb: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [0, 15] or 'sy'"); case Match_NamedImm_prefetch: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected prefetch hint: p(ld|st|i)l[123](strm|keep)"); case Match_NamedImm_tlbi: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected translation buffer invalidation operand"); case Match_UImm16: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [0, 65535]"); case Match_UImm3: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [0, 7]"); case Match_UImm4: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [0, 15]"); case Match_UImm5: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [0, 31]"); case Match_UImm6: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [0, 63]"); case Match_UImm7: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [0, 127]"); case Match_Width32: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [, 31]"); case Match_Width64: return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(), "expected integer in range [, 63]"); } llvm_unreachable("Implement any new match types added!"); return true; } void AArch64Operand::print(raw_ostream &OS) const { switch (Kind) { case k_CondCode: OS << ""; break; case k_FPImmediate: OS << ""; break; case k_ImmWithLSL: OS << ""; break; case k_Immediate: getImm()->print(OS); break; case k_Register: OS << "'; break; case k_Token: OS << '\'' << getToken() << '\''; break; case k_ShiftExtend: OS << ""; break; case k_SysReg: { StringRef Name(SysReg.Data, SysReg.Length); OS << "'; break; } default: llvm_unreachable("No idea how to print this kind of operand"); break; } } void AArch64Operand::dump() const { print(errs()); } /// Force static initialization. extern "C" void LLVMInitializeAArch64AsmParser() { RegisterMCAsmParser X(TheAArch64Target); } #define GET_REGISTER_MATCHER #define GET_MATCHER_IMPLEMENTATION #include "AArch64GenAsmMatcher.inc"