blob: c1695dacb43a6ad62ea18a62019760b0c364235e [file] [log] [blame]
//==- 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<MCParsedAsmOperand*> &Operands);
bool ParseDirective(AsmToken DirectiveID);
bool ParseDirectiveTLSDescCall(SMLoc L);
bool ParseDirectiveWord(unsigned Size, SMLoc L);
bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
SmallVectorImpl<MCParsedAsmOperand*> &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<MCParsedAsmOperand*> &Operands,
StringRef Mnemonic);
OperandMatchResultTy ParseImmediate(const MCExpr *&ExprVal);
OperandMatchResultTy ParseRelocPrefix(AArch64MCExpr::VariantKind &RefKind);
OperandMatchResultTy
ParseNEONLane(SmallVectorImpl<MCParsedAsmOperand*> &Operands,
uint32_t NumLanes);
OperandMatchResultTy
ParseRegister(SmallVectorImpl<MCParsedAsmOperand*> &Operands,
uint32_t &NumLanes);
OperandMatchResultTy
ParseImmWithLSLOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands);
OperandMatchResultTy
ParseCondCodeOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands);
OperandMatchResultTy
ParseCRxOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands);
OperandMatchResultTy
ParseFPImmOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands);
template<typename SomeNamedImmMapper> OperandMatchResultTy
ParseNamedImmOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands) {
return ParseNamedImmOperand(SomeNamedImmMapper(), Operands);
}
OperandMatchResultTy
ParseNamedImmOperand(const NamedImmMapper &Mapper,
SmallVectorImpl<MCParsedAsmOperand*> &Operands);
OperandMatchResultTy
ParseLSXAddressOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands);
OperandMatchResultTy
ParseShiftExtend(SmallVectorImpl<MCParsedAsmOperand*> &Operands);
OperandMatchResultTy
ParseSysRegOperand(SmallVectorImpl<MCParsedAsmOperand*> &Operands);
bool validateInstruction(MCInst &Inst,
const SmallVectorImpl<MCParsedAsmOperand*> &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;
union {
struct {
const MCExpr *Val;
unsigned ShiftAmount;
bool ImplicitAmount;
} ImmWithLSL;
struct {
A64CC::CondCodes Code;
} CondCode;
struct {
double Val;
} FPImm;
struct {
const MCExpr *Val;
} Imm;
struct {
unsigned RegNum;
} Reg;
struct {
A64SE::ShiftExtSpecifiers ShiftType;
unsigned Amount;
bool ImplicitAmount;
} ShiftExtend;
struct {
const char *Data;
unsigned Length;
} SysReg;
struct {
const char *Data;
unsigned Length;
} 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<AArch64MCExpr>(E)) {
Variant = A64E->getKind();
return true;
} else if (!isa<MCConstantExpr>(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<MCConstantExpr>(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<MCConstantExpr>(ImmWithLSL.Val);
return CE->getValue() >= 0 && CE->getValue() <= 0xfff;
}
template<unsigned MemSize, unsigned RmSize> 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<unsigned RegWidth> bool isBitfieldWidth() const {
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
if (!CE) return false;
return CE->getValue() >= 1 && CE->getValue() <= RegWidth;
}
template<int RegWidth>
bool isCVTFixedPos() const {
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(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<unsigned field_width, unsigned scale>
bool isLabel() const {
if (!isImm()) return false;
if (dyn_cast<MCSymbolRefExpr>(Imm.Val)) {
return true;
} else if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(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<MCConstantExpr>(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<unsigned RegWidth> bool isLogicalImm() const {
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(Imm.Val);
if (!CE) return false;
uint32_t Bits;
return A64Imms::isLogicalImm(RegWidth, CE->getValue(), Bits);
}
template<unsigned RegWidth> bool isLogicalImmMOV() const {
if (!isLogicalImm<RegWidth>()) return false;
const MCConstantExpr *CE = cast<MCConstantExpr>(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<int MemSize>
bool isOffsetUImm12() const {
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(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<A64SE::ShiftExtSpecifiers SHKind, bool is64Bit>
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<MCConstantExpr>(ImmWithLSL.Val);
return CE && CE->getValue() >= 0 && CE->getValue() <= 0xffff;
}
template<int RegWidth, bool (*isValidImm)(int, uint64_t, int&, int&)>
bool isMoveWideMovAlias() const {
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(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<MCConstantExpr>(getImm());
if (!CE)
return false;
return CE->getValue() >= 0 && CE->getValue() <= 31;
}
template<A64SE::ShiftExtSpecifiers SHKind> 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<int MemSize> bool isSImm7Scaled() const {
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
if (!CE) return false;
int64_t Val = CE->getValue();
if (Val % MemSize != 0) return false;
Val /= MemSize;
return Val >= -64 && Val < 64;
}
template<int BitWidth>
bool isSImm() const {
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
if (!CE) return false;
return CE->getValue() >= -(1LL << (BitWidth - 1))
&& CE->getValue() < (1LL << (BitWidth - 1));
}
template<int bitWidth>
bool isUImm() const {
if (!isImm()) return false;
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm());
if (!CE) return false;
return CE->getValue() >= 0 && CE->getValue() < (1LL << bitWidth);
}
bool isUImm() const {
if (!isImm()) return false;
return isa<MCConstantExpr>(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<MCConstantExpr>(Expr))
Inst.addOperand(MCOperand::CreateImm(CE->getValue()));
else
Inst.addOperand(MCOperand::CreateExpr(Expr));
}
template<unsigned RegWidth>
void addBFILSBOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
const MCConstantExpr *CE = cast<MCConstantExpr>(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<MCConstantExpr>(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<MCConstantExpr>(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<MCConstantExpr>(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<int MemSize>
void addSImm7ScaledOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
const MCConstantExpr *CE = cast<MCConstantExpr>(getImm());
uint64_t Val = CE->getValue() / MemSize;
Inst.addOperand(MCOperand::CreateImm(Val & 0x7f));
}
template<int BitWidth>
void addSImmOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
const MCConstantExpr *CE = cast<MCConstantExpr>(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<unsigned field_width, unsigned scale>
void addLabelOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(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<int MemSize>
void addOffsetUImm12Operands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
if (const MCConstantExpr *CE = dyn_cast<MCConstantExpr>(getImm())) {
Inst.addOperand(MCOperand::CreateImm(CE->getValue() / MemSize));
} else {
Inst.addOperand(MCOperand::CreateExpr(getImm()));
}
}
template<unsigned RegWidth>
void addLogicalImmOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands");
const MCConstantExpr *CE = cast<MCConstantExpr>(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<int RegWidth, bool isValidImm(int, uint64_t, int&, int&)>
void addMoveWideMovAliasOperands(MCInst &Inst, unsigned N) const {
assert(N == 2 && "Invalid number of operands!");
int UImm16, Shift;
const MCConstantExpr *CE = cast<MCConstantExpr>(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<MCConstantExpr>(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<unsigned MemSize>
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<MCParsedAsmOperand*> &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<MCParsedAsmOperand*> &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<AArch64MCExpr::VariantKind>(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<MCParsedAsmOperand*> &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<MCParsedAsmOperand*> &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<MCParsedAsmOperand*> &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<MCParsedAsmOperand*> &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<unsigned>(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<const char *>(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<MCParsedAsmOperand*> &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<MCParsedAsmOperand*> &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<MCConstantExpr>(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<MCParsedAsmOperand*> &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<MCParsedAsmOperand*> &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<MCParsedAsmOperand*> &Operands) {
StringRef IDVal = Parser.getTok().getIdentifier();
std::string LowerID = IDVal.lower();
A64SE::ShiftExtSpecifiers Spec =
StringSwitch<A64SE::ShiftExtSpecifiers>(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<MCParsedAsmOperand*> &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<A64IC::ICValues>(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<A64IC::ICValues>(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<A64TLBI::TLBIValues>(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<A64TLBI::TLBIValues>(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<MCParsedAsmOperand*> &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<MCParsedAsmOperand*> &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 [<lsb>, 31]");
case Match_Width64:
return Error(((AArch64Operand*)Operands[ErrorInfo])->getStartLoc(),
"expected integer in range [<lsb>, 63]");
}
llvm_unreachable("Implement any new match types added!");
return true;
}
void AArch64Operand::print(raw_ostream &OS) const {
switch (Kind) {
case k_CondCode:
OS << "<CondCode: " << CondCode.Code << ">";
break;
case k_FPImmediate:
OS << "<fpimm: " << FPImm.Val << ">";
break;
case k_ImmWithLSL:
OS << "<immwithlsl: imm=" << ImmWithLSL.Val
<< ", shift=" << ImmWithLSL.ShiftAmount << ">";
break;
case k_Immediate:
getImm()->print(OS);
break;
case k_Register:
OS << "<register " << getReg() << '>';
break;
case k_Token:
OS << '\'' << getToken() << '\'';
break;
case k_ShiftExtend:
OS << "<shift: type=" << ShiftExtend.ShiftType
<< ", amount=" << ShiftExtend.Amount << ">";
break;
case k_SysReg: {
StringRef Name(SysReg.Data, SysReg.Length);
OS << "<sysreg: " << Name << '>';
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<AArch64AsmParser> X(TheAArch64Target);
}
#define GET_REGISTER_MATCHER
#define GET_MATCHER_IMPLEMENTATION
#include "AArch64GenAsmMatcher.inc"