blob: 9d6d981bb9082399a3968f54011e5450b3414e3e [file] [log] [blame]
// LoongArchAsmParser.cpp - Parse LoongArch assembly to MCInst instructions -=//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "MCTargetDesc/LoongArchInstPrinter.h"
#include "MCTargetDesc/LoongArchMCExpr.h"
#include "MCTargetDesc/LoongArchMCTargetDesc.h"
#include "MCTargetDesc/LoongArchMatInt.h"
#include "TargetInfo/LoongArchTargetInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInstBuilder.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCValue.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/Casting.h"
using namespace llvm;
#define DEBUG_TYPE "loongarch-asm-parser"
namespace {
class LoongArchAsmParser : public MCTargetAsmParser {
SMLoc getLoc() const { return getParser().getTok().getLoc(); }
bool is64Bit() const { return getSTI().hasFeature(LoongArch::Feature64Bit); }
struct Inst {
unsigned Opc;
LoongArchMCExpr::VariantKind VK;
Inst(unsigned Opc,
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None)
: Opc(Opc), VK(VK) {}
};
using InstSeq = SmallVector<Inst>;
/// Parse a register as used in CFI directives.
bool parseRegister(MCRegister &RegNo, SMLoc &StartLoc,
SMLoc &EndLoc) override;
OperandMatchResultTy tryParseRegister(MCRegister &RegNo, SMLoc &StartLoc,
SMLoc &EndLoc) override;
bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name,
SMLoc NameLoc, OperandVector &Operands) override;
bool ParseDirective(AsmToken DirectiveID) override { return true; }
bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
OperandVector &Operands, MCStreamer &Out,
uint64_t &ErrorInfo,
bool MatchingInlineAsm) override;
unsigned checkTargetMatchPredicate(MCInst &Inst) override;
unsigned validateTargetOperandClass(MCParsedAsmOperand &Op,
unsigned Kind) override;
bool generateImmOutOfRangeError(OperandVector &Operands, uint64_t ErrorInfo,
int64_t Lower, int64_t Upper, Twine Msg);
/// Helper for processing MC instructions that have been successfully matched
/// by MatchAndEmitInstruction.
bool processInstruction(MCInst &Inst, SMLoc IDLoc, OperandVector &Operands,
MCStreamer &Out);
// Auto-generated instruction matching functions.
#define GET_ASSEMBLER_HEADER
#include "LoongArchGenAsmMatcher.inc"
OperandMatchResultTy parseRegister(OperandVector &Operands);
OperandMatchResultTy parseImmediate(OperandVector &Operands);
OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands);
OperandMatchResultTy parseSImm26Operand(OperandVector &Operands);
OperandMatchResultTy parseAtomicMemOp(OperandVector &Operands);
bool parseOperand(OperandVector &Operands, StringRef Mnemonic);
// Helper to emit the sequence of instructions generated by the
// "emitLoadAddress*" functions.
void emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg,
const MCExpr *Symbol, SmallVectorImpl<Inst> &Insts,
SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.abs $rd, sym".
void emitLoadAddressAbs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.pcrel $rd, sym".
void emitLoadAddressPcrel(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.pcrel $rd, $rj, sym".
void emitLoadAddressPcrelLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.got $rd, sym".
void emitLoadAddressGot(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.got $rd, $rj, sym".
void emitLoadAddressGotLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.tls.le $rd, sym".
void emitLoadAddressTLSLE(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.tls.ie $rd, sym".
void emitLoadAddressTLSIE(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.tls.ie $rd, $rj, sym".
void emitLoadAddressTLSIELarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.tls.ld $rd, sym".
void emitLoadAddressTLSLD(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.tls.ld $rd, $rj, sym".
void emitLoadAddressTLSLDLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.tls.gd $rd, sym".
void emitLoadAddressTLSGD(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "la.tls.gd $rd, $rj, sym".
void emitLoadAddressTLSGDLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
// Helper to emit pseudo instruction "li.w/d $rd, $imm".
void emitLoadImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
public:
enum LoongArchMatchResultTy {
Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY,
Match_RequiresMsbNotLessThanLsb,
Match_RequiresOpnd2NotR0R1,
Match_RequiresAMORdDifferRkRj,
Match_RequiresLAORdDifferRj,
#define GET_OPERAND_DIAGNOSTIC_TYPES
#include "LoongArchGenAsmMatcher.inc"
#undef GET_OPERAND_DIAGNOSTIC_TYPES
};
static bool classifySymbolRef(const MCExpr *Expr,
LoongArchMCExpr::VariantKind &Kind);
LoongArchAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
const MCInstrInfo &MII, const MCTargetOptions &Options)
: MCTargetAsmParser(Options, STI, MII) {
Parser.addAliasForDirective(".half", ".2byte");
Parser.addAliasForDirective(".hword", ".2byte");
Parser.addAliasForDirective(".word", ".4byte");
Parser.addAliasForDirective(".dword", ".8byte");
// Initialize the set of available features.
setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits()));
}
};
// Instances of this class represent a parsed LoongArch machine instruction.
class LoongArchOperand : public MCParsedAsmOperand {
enum class KindTy {
Token,
Register,
Immediate,
} Kind;
struct RegOp {
MCRegister RegNum;
};
struct ImmOp {
const MCExpr *Val;
};
SMLoc StartLoc, EndLoc;
union {
StringRef Tok;
struct RegOp Reg;
struct ImmOp Imm;
};
public:
LoongArchOperand(KindTy K) : MCParsedAsmOperand(), Kind(K) {}
bool isToken() const override { return Kind == KindTy::Token; }
bool isReg() const override { return Kind == KindTy::Register; }
bool isImm() const override { return Kind == KindTy::Immediate; }
bool isMem() const override { return false; }
void setReg(MCRegister PhysReg) { Reg.RegNum = PhysReg; }
bool isGPR() const {
return Kind == KindTy::Register &&
LoongArchMCRegisterClasses[LoongArch::GPRRegClassID].contains(
Reg.RegNum);
}
static bool evaluateConstantImm(const MCExpr *Expr, int64_t &Imm,
LoongArchMCExpr::VariantKind &VK) {
if (auto *LE = dyn_cast<LoongArchMCExpr>(Expr)) {
VK = LE->getKind();
return false;
}
if (auto CE = dyn_cast<MCConstantExpr>(Expr)) {
Imm = CE->getValue();
return true;
}
return false;
}
template <unsigned N, int P = 0> bool isUImm() const {
if (!isImm())
return false;
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
return IsConstantImm && isUInt<N>(Imm - P) &&
VK == LoongArchMCExpr::VK_LoongArch_None;
}
template <unsigned N, unsigned S = 0> bool isSImm() const {
if (!isImm())
return false;
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
return IsConstantImm && isShiftedInt<N, S>(Imm) &&
VK == LoongArchMCExpr::VK_LoongArch_None;
}
bool isBareSymbol() const {
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
// Must be of 'immediate' type but not a constant.
if (!isImm() || evaluateConstantImm(getImm(), Imm, VK))
return false;
return LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
VK == LoongArchMCExpr::VK_LoongArch_None;
}
bool isUImm2() const { return isUImm<2>(); }
bool isUImm2plus1() const { return isUImm<2, 1>(); }
bool isUImm3() const { return isUImm<3>(); }
bool isUImm5() const { return isUImm<5>(); }
bool isUImm6() const { return isUImm<6>(); }
bool isUImm8() const { return isUImm<8>(); }
bool isSImm12() const { return isSImm<12>(); }
bool isSImm12addlike() const {
if (!isImm())
return false;
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
VK == LoongArchMCExpr::VK_LoongArch_PCALA_LO12 ||
VK == LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12;
return IsConstantImm
? isInt<12>(Imm) && IsValidKind
: LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
IsValidKind;
}
bool isSImm12lu52id() const {
if (!isImm())
return false;
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
VK == LoongArchMCExpr::VK_LoongArch_ABS64_HI12 ||
VK == LoongArchMCExpr::VK_LoongArch_PCALA64_HI12 ||
VK == LoongArchMCExpr::VK_LoongArch_GOT64_HI12 ||
VK == LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_LE64_HI12 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_HI12 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_HI12;
return IsConstantImm
? isInt<12>(Imm) && IsValidKind
: LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
IsValidKind;
}
bool isUImm12() const { return isUImm<12>(); }
bool isUImm12ori() const {
if (!isImm())
return false;
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
VK == LoongArchMCExpr::VK_LoongArch_ABS_LO12 ||
VK == LoongArchMCExpr::VK_LoongArch_PCALA_LO12 ||
VK == LoongArchMCExpr::VK_LoongArch_GOT_LO12 ||
VK == LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_LE_LO12 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_LO12 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12;
return IsConstantImm
? isUInt<12>(Imm) && IsValidKind
: LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
IsValidKind;
}
bool isUImm14() const { return isUImm<14>(); }
bool isUImm15() const { return isUImm<15>(); }
bool isSImm14lsl2() const { return isSImm<14, 2>(); }
bool isSImm16() const { return isSImm<16>(); }
bool isSImm16lsl2() const {
if (!isImm())
return false;
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
VK == LoongArchMCExpr::VK_LoongArch_B16 ||
VK == LoongArchMCExpr::VK_LoongArch_PCALA_LO12;
return IsConstantImm
? isShiftedInt<16, 2>(Imm) && IsValidKind
: LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
IsValidKind;
}
bool isSImm20() const { return isSImm<20>(); }
bool isSImm20pcalau12i() const {
if (!isImm())
return false;
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
VK == LoongArchMCExpr::VK_LoongArch_PCALA_HI20 ||
VK == LoongArchMCExpr::VK_LoongArch_GOT_PC_HI20 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_HI20 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_LD_PC_HI20 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20;
return IsConstantImm
? isInt<20>(Imm) && IsValidKind
: LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
IsValidKind;
}
bool isSImm20lu12iw() const {
if (!isImm())
return false;
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
VK == LoongArchMCExpr::VK_LoongArch_ABS_HI20 ||
VK == LoongArchMCExpr::VK_LoongArch_GOT_HI20 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_GD_HI20 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_LD_HI20 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_IE_HI20 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_LE_HI20;
return IsConstantImm
? isInt<20>(Imm) && IsValidKind
: LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
IsValidKind;
}
bool isSImm20lu32id() const {
if (!isImm())
return false;
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
VK == LoongArchMCExpr::VK_LoongArch_ABS64_LO20 ||
VK == LoongArchMCExpr::VK_LoongArch_PCALA64_LO20 ||
VK == LoongArchMCExpr::VK_LoongArch_GOT64_LO20 ||
VK == LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_LO20 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_LO20 ||
VK == LoongArchMCExpr::VK_LoongArch_TLS_LE64_LO20;
return IsConstantImm
? isInt<20>(Imm) && IsValidKind
: LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
IsValidKind;
}
bool isSImm21lsl2() const {
if (!isImm())
return false;
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
VK == LoongArchMCExpr::VK_LoongArch_B21;
return IsConstantImm
? isShiftedInt<21, 2>(Imm) && IsValidKind
: LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
IsValidKind;
}
bool isSImm26Operand() const {
if (!isImm())
return false;
int64_t Imm;
LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_None ||
VK == LoongArchMCExpr::VK_LoongArch_CALL ||
VK == LoongArchMCExpr::VK_LoongArch_CALL_PLT ||
VK == LoongArchMCExpr::VK_LoongArch_B26;
return IsConstantImm
? isShiftedInt<26, 2>(Imm) && IsValidKind
: LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
IsValidKind;
}
bool isImm32() const { return isSImm<32>() || isUImm<32>(); }
/// Gets location of the first token of this operand.
SMLoc getStartLoc() const override { return StartLoc; }
/// Gets location of the last token of this operand.
SMLoc getEndLoc() const override { return EndLoc; }
unsigned getReg() const override {
assert(Kind == KindTy::Register && "Invalid type access!");
return Reg.RegNum.id();
}
const MCExpr *getImm() const {
assert(Kind == KindTy::Immediate && "Invalid type access!");
return Imm.Val;
}
StringRef getToken() const {
assert(Kind == KindTy::Token && "Invalid type access!");
return Tok;
}
void print(raw_ostream &OS) const override {
auto RegName = [](MCRegister Reg) {
if (Reg)
return LoongArchInstPrinter::getRegisterName(Reg);
else
return "noreg";
};
switch (Kind) {
case KindTy::Immediate:
OS << *getImm();
break;
case KindTy::Register:
OS << "<register " << RegName(getReg()) << ">";
break;
case KindTy::Token:
OS << "'" << getToken() << "'";
break;
}
}
static std::unique_ptr<LoongArchOperand> createToken(StringRef Str, SMLoc S) {
auto Op = std::make_unique<LoongArchOperand>(KindTy::Token);
Op->Tok = Str;
Op->StartLoc = S;
Op->EndLoc = S;
return Op;
}
static std::unique_ptr<LoongArchOperand> createReg(unsigned RegNo, SMLoc S,
SMLoc E) {
auto Op = std::make_unique<LoongArchOperand>(KindTy::Register);
Op->Reg.RegNum = RegNo;
Op->StartLoc = S;
Op->EndLoc = E;
return Op;
}
static std::unique_ptr<LoongArchOperand> createImm(const MCExpr *Val, SMLoc S,
SMLoc E) {
auto Op = std::make_unique<LoongArchOperand>(KindTy::Immediate);
Op->Imm.Val = Val;
Op->StartLoc = S;
Op->EndLoc = E;
return Op;
}
void addExpr(MCInst &Inst, const MCExpr *Expr) const {
if (auto CE = dyn_cast<MCConstantExpr>(Expr))
Inst.addOperand(MCOperand::createImm(CE->getValue()));
else
Inst.addOperand(MCOperand::createExpr(Expr));
}
// Used by the TableGen Code.
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());
}
};
} // end namespace
#define GET_REGISTER_MATCHER
#define GET_SUBTARGET_FEATURE_NAME
#define GET_MATCHER_IMPLEMENTATION
#define GET_MNEMONIC_SPELL_CHECKER
#include "LoongArchGenAsmMatcher.inc"
static MCRegister convertFPR32ToFPR64(MCRegister Reg) {
assert(Reg >= LoongArch::F0 && Reg <= LoongArch::F31 && "Invalid register");
return Reg - LoongArch::F0 + LoongArch::F0_64;
}
// Attempts to match Name as a register (either using the default name or
// alternative ABI names), setting RegNo to the matching register. Upon
// failure, returns true and sets RegNo to 0.
static bool matchRegisterNameHelper(MCRegister &RegNo, StringRef Name) {
RegNo = MatchRegisterName(Name);
// The 32-bit and 64-bit FPRs have the same asm name. Check that the initial
// match always matches the 32-bit variant, and not the 64-bit one.
assert(!(RegNo >= LoongArch::F0_64 && RegNo <= LoongArch::F31_64));
// The default FPR register class is based on the tablegen enum ordering.
static_assert(LoongArch::F0 < LoongArch::F0_64,
"FPR matching must be updated");
if (RegNo == LoongArch::NoRegister)
RegNo = MatchRegisterAltName(Name);
return RegNo == LoongArch::NoRegister;
}
bool LoongArchAsmParser::parseRegister(MCRegister &RegNo, SMLoc &StartLoc,
SMLoc &EndLoc) {
return Error(getLoc(), "invalid register number");
}
OperandMatchResultTy LoongArchAsmParser::tryParseRegister(MCRegister &RegNo,
SMLoc &StartLoc,
SMLoc &EndLoc) {
llvm_unreachable("Unimplemented function.");
}
bool LoongArchAsmParser::classifySymbolRef(const MCExpr *Expr,
LoongArchMCExpr::VariantKind &Kind) {
Kind = LoongArchMCExpr::VK_LoongArch_None;
if (const LoongArchMCExpr *RE = dyn_cast<LoongArchMCExpr>(Expr)) {
Kind = RE->getKind();
Expr = RE->getSubExpr();
}
MCValue Res;
if (Expr->evaluateAsRelocatable(Res, nullptr, nullptr))
return Res.getRefKind() == LoongArchMCExpr::VK_LoongArch_None;
return false;
}
OperandMatchResultTy
LoongArchAsmParser::parseRegister(OperandVector &Operands) {
if (getLexer().getTok().isNot(AsmToken::Dollar))
return MatchOperand_NoMatch;
// Eat the $ prefix.
getLexer().Lex();
if (getLexer().getKind() != AsmToken::Identifier)
return MatchOperand_NoMatch;
StringRef Name = getLexer().getTok().getIdentifier();
MCRegister RegNo;
matchRegisterNameHelper(RegNo, Name);
if (RegNo == LoongArch::NoRegister)
return MatchOperand_NoMatch;
SMLoc S = getLoc();
SMLoc E = SMLoc::getFromPointer(S.getPointer() + Name.size());
getLexer().Lex();
Operands.push_back(LoongArchOperand::createReg(RegNo, S, E));
return MatchOperand_Success;
}
OperandMatchResultTy
LoongArchAsmParser::parseImmediate(OperandVector &Operands) {
SMLoc S = getLoc();
SMLoc E;
const MCExpr *Res;
switch (getLexer().getKind()) {
default:
return MatchOperand_NoMatch;
case AsmToken::LParen:
case AsmToken::Dot:
case AsmToken::Minus:
case AsmToken::Plus:
case AsmToken::Exclaim:
case AsmToken::Tilde:
case AsmToken::Integer:
case AsmToken::String:
case AsmToken::Identifier:
if (getParser().parseExpression(Res, E))
return MatchOperand_ParseFail;
break;
case AsmToken::Percent:
return parseOperandWithModifier(Operands);
}
Operands.push_back(LoongArchOperand::createImm(Res, S, E));
return MatchOperand_Success;
}
OperandMatchResultTy
LoongArchAsmParser::parseOperandWithModifier(OperandVector &Operands) {
SMLoc S = getLoc();
SMLoc E;
if (getLexer().getKind() != AsmToken::Percent) {
Error(getLoc(), "expected '%' for operand modifier");
return MatchOperand_ParseFail;
}
getParser().Lex(); // Eat '%'
if (getLexer().getKind() != AsmToken::Identifier) {
Error(getLoc(), "expected valid identifier for operand modifier");
return MatchOperand_ParseFail;
}
StringRef Identifier = getParser().getTok().getIdentifier();
LoongArchMCExpr::VariantKind VK =
LoongArchMCExpr::getVariantKindForName(Identifier);
if (VK == LoongArchMCExpr::VK_LoongArch_Invalid) {
Error(getLoc(), "unrecognized operand modifier");
return MatchOperand_ParseFail;
}
getParser().Lex(); // Eat the identifier
if (getLexer().getKind() != AsmToken::LParen) {
Error(getLoc(), "expected '('");
return MatchOperand_ParseFail;
}
getParser().Lex(); // Eat '('
const MCExpr *SubExpr;
if (getParser().parseParenExpression(SubExpr, E)) {
return MatchOperand_ParseFail;
}
const MCExpr *ModExpr = LoongArchMCExpr::create(SubExpr, VK, getContext());
Operands.push_back(LoongArchOperand::createImm(ModExpr, S, E));
return MatchOperand_Success;
}
OperandMatchResultTy
LoongArchAsmParser::parseSImm26Operand(OperandVector &Operands) {
SMLoc S = getLoc();
const MCExpr *Res;
if (getLexer().getKind() == AsmToken::Percent)
return parseOperandWithModifier(Operands);
if (getLexer().getKind() != AsmToken::Identifier)
return MatchOperand_NoMatch;
StringRef Identifier;
if (getParser().parseIdentifier(Identifier))
return MatchOperand_ParseFail;
SMLoc E = SMLoc::getFromPointer(S.getPointer() + Identifier.size());
MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier);
Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext());
Res = LoongArchMCExpr::create(Res, LoongArchMCExpr::VK_LoongArch_CALL,
getContext());
Operands.push_back(LoongArchOperand::createImm(Res, S, E));
return MatchOperand_Success;
}
OperandMatchResultTy
LoongArchAsmParser::parseAtomicMemOp(OperandVector &Operands) {
// Parse "$r*".
if (parseRegister(Operands) != MatchOperand_Success)
return MatchOperand_NoMatch;
// If there is a next operand and it is 0, ignore it. Otherwise print a
// diagnostic message.
if (getLexer().is(AsmToken::Comma)) {
getLexer().Lex(); // Consume comma token.
int64_t ImmVal;
SMLoc ImmStart = getLoc();
if (getParser().parseIntToken(ImmVal, "expected optional integer offset"))
return MatchOperand_ParseFail;
if (ImmVal) {
Error(ImmStart, "optional integer offset must be 0");
return MatchOperand_ParseFail;
}
}
return MatchOperand_Success;
}
/// Looks at a token type and creates the relevant operand from this
/// information, adding to Operands. Return true upon an error.
bool LoongArchAsmParser::parseOperand(OperandVector &Operands,
StringRef Mnemonic) {
// Check if the current operand has a custom associated parser, if so, try to
// custom parse the operand, or fallback to the general approach.
OperandMatchResultTy Result =
MatchOperandParserImpl(Operands, Mnemonic, /*ParseForAllFeatures=*/true);
if (Result == MatchOperand_Success)
return false;
if (Result == MatchOperand_ParseFail)
return true;
if (parseRegister(Operands) == MatchOperand_Success ||
parseImmediate(Operands) == MatchOperand_Success)
return false;
// Finally we have exhausted all options and must declare defeat.
Error(getLoc(), "unknown operand");
return true;
}
bool LoongArchAsmParser::ParseInstruction(ParseInstructionInfo &Info,
StringRef Name, SMLoc NameLoc,
OperandVector &Operands) {
// First operand in MCInst is instruction mnemonic.
Operands.push_back(LoongArchOperand::createToken(Name, NameLoc));
// If there are no more operands, then finish.
if (parseOptionalToken(AsmToken::EndOfStatement))
return false;
// Parse first operand.
if (parseOperand(Operands, Name))
return true;
// Parse until end of statement, consuming commas between operands.
while (parseOptionalToken(AsmToken::Comma))
if (parseOperand(Operands, Name))
return true;
// Parse end of statement and return successfully.
if (parseOptionalToken(AsmToken::EndOfStatement))
return false;
SMLoc Loc = getLexer().getLoc();
getParser().eatToEndOfStatement();
return Error(Loc, "unexpected token");
}
void LoongArchAsmParser::emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg,
const MCExpr *Symbol,
SmallVectorImpl<Inst> &Insts,
SMLoc IDLoc, MCStreamer &Out) {
MCContext &Ctx = getContext();
for (LoongArchAsmParser::Inst &Inst : Insts) {
unsigned Opc = Inst.Opc;
LoongArchMCExpr::VariantKind VK = Inst.VK;
const LoongArchMCExpr *LE = LoongArchMCExpr::create(Symbol, VK, Ctx);
switch (Opc) {
default:
llvm_unreachable("unexpected opcode");
case LoongArch::PCALAU12I:
case LoongArch::LU12I_W:
Out.emitInstruction(MCInstBuilder(Opc).addReg(DestReg).addExpr(LE),
getSTI());
break;
case LoongArch::ORI:
case LoongArch::ADDI_W:
case LoongArch::LD_W:
case LoongArch::LD_D: {
if (VK == LoongArchMCExpr::VK_LoongArch_None) {
Out.emitInstruction(
MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addImm(0),
getSTI());
continue;
}
Out.emitInstruction(
MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addExpr(LE),
getSTI());
break;
}
case LoongArch::LU32I_D:
Out.emitInstruction(MCInstBuilder(Opc)
.addReg(DestReg == TmpReg ? DestReg : TmpReg)
.addReg(DestReg == TmpReg ? DestReg : TmpReg)
.addExpr(LE),
getSTI());
break;
case LoongArch::LU52I_D:
Out.emitInstruction(
MCInstBuilder(Opc).addReg(TmpReg).addReg(TmpReg).addExpr(LE),
getSTI());
break;
case LoongArch::ADDI_D:
Out.emitInstruction(
MCInstBuilder(Opc)
.addReg(TmpReg)
.addReg(DestReg == TmpReg ? TmpReg : LoongArch::R0)
.addExpr(LE),
getSTI());
break;
case LoongArch::ADD_D:
case LoongArch::LDX_D:
Out.emitInstruction(
MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addReg(TmpReg),
getSTI());
break;
}
}
}
void LoongArchAsmParser::emitLoadAddressAbs(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.abs $rd, sym
// expands to:
// lu12i.w $rd, %abs_hi20(sym)
// ori $rd, $rd, %abs_lo12(sym)
//
// for 64bit appends:
// lu32i.d $rd, %abs64_lo20(sym)
// lu52i.d $rd, $rd, %abs64_hi12(sym)
MCRegister DestReg = Inst.getOperand(0).getReg();
const MCExpr *Symbol = Inst.getOpcode() == LoongArch::PseudoLA_ABS
? Inst.getOperand(1).getExpr()
: Inst.getOperand(2).getExpr();
InstSeq Insts;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU12I_W, LoongArchMCExpr::VK_LoongArch_ABS_HI20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::ORI, LoongArchMCExpr::VK_LoongArch_ABS_LO12));
if (is64Bit()) {
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_ABS64_LO20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_ABS64_HI12));
}
emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadAddressPcrel(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.pcrel $rd, sym
// expands to:
// pcalau12i $rd, %pc_hi20(sym)
// addi.w/d $rd, rd, %pc_lo12(sym)
MCRegister DestReg = Inst.getOperand(0).getReg();
const MCExpr *Symbol = Inst.getOperand(1).getExpr();
InstSeq Insts;
unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_PCALA_HI20));
Insts.push_back(
LoongArchAsmParser::Inst(ADDI, LoongArchMCExpr::VK_LoongArch_PCALA_LO12));
emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadAddressPcrelLarge(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.pcrel $rd, $rj, sym
// expands to:
// pcalau12i $rd, %pc_hi20(sym)
// addi.d $rj, $r0, %pc_lo12(sym)
// lu32i.d $rj, %pc64_lo20(sym)
// lu52i.d $rj, $rj, %pc64_hi12(sym)
// add.d $rd, $rd, $rj
MCRegister DestReg = Inst.getOperand(0).getReg();
MCRegister TmpReg = Inst.getOperand(1).getReg();
const MCExpr *Symbol = Inst.getOperand(2).getExpr();
InstSeq Insts;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_PCALA_HI20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_PCALA_LO12));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_PCALA64_LO20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_PCALA64_HI12));
Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D));
emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadAddressGot(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.got $rd, sym
// expands to:
// pcalau12i $rd, %got_pc_hi20(sym)
// ld.w/d $rd, $rd, %got_pc_lo12(sym)
MCRegister DestReg = Inst.getOperand(0).getReg();
const MCExpr *Symbol = Inst.getOperand(1).getExpr();
InstSeq Insts;
unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_GOT_PC_HI20));
Insts.push_back(
LoongArchAsmParser::Inst(LD, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadAddressGotLarge(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.got $rd, $rj, sym
// expands to:
// pcalau12i $rd, %got_pc_hi20(sym)
// addi.d $rj, $r0, %got_pc_lo12(sym)
// lu32i.d $rj, %got64_pc_lo20(sym)
// lu52i.d $rj, $rj, %got64_pc_hi12(sym)
// ldx.d $rd, $rd, $rj
MCRegister DestReg = Inst.getOperand(0).getReg();
MCRegister TmpReg = Inst.getOperand(1).getReg();
const MCExpr *Symbol = Inst.getOperand(2).getExpr();
InstSeq Insts;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_GOT_PC_HI20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12));
Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LDX_D));
emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadAddressTLSLE(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.tls.le $rd, sym
// expands to:
// lu12i.w $rd, %le_hi20(sym)
// ori $rd, $rd, %le_lo12(sym)
MCRegister DestReg = Inst.getOperand(0).getReg();
const MCExpr *Symbol = Inst.getOperand(1).getExpr();
InstSeq Insts;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU12I_W, LoongArchMCExpr::VK_LoongArch_TLS_LE_HI20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::ORI, LoongArchMCExpr::VK_LoongArch_TLS_LE_LO12));
emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadAddressTLSIE(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.tls.ie $rd, sym
// expands to:
// pcalau12i $rd, %ie_pc_hi20(sym)
// ld.w/d $rd, $rd, %ie_pc_lo12(sym)
MCRegister DestReg = Inst.getOperand(0).getReg();
const MCExpr *Symbol = Inst.getOperand(1).getExpr();
InstSeq Insts;
unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_HI20));
Insts.push_back(LoongArchAsmParser::Inst(
LD, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12));
emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadAddressTLSIELarge(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.tls.ie $rd, $rj, sym
// expands to:
// pcalau12i $rd, %ie_pc_hi20(sym)
// addi.d $rj, $r0, %ie_pc_lo12(sym)
// lu32i.d $rj, %ie64_pc_lo20(sym)
// lu52i.d $rj, $rj, %ie64_pc_hi12(sym)
// ldx.d $rd, $rd, $rj
MCRegister DestReg = Inst.getOperand(0).getReg();
MCRegister TmpReg = Inst.getOperand(1).getReg();
const MCExpr *Symbol = Inst.getOperand(2).getExpr();
InstSeq Insts;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_HI20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_LO20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_HI12));
Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LDX_D));
emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadAddressTLSLD(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.tls.ld $rd, sym
// expands to:
// pcalau12i $rd, %ld_pc_hi20(sym)
// addi.w/d $rd, $rd, %got_pc_lo12(sym)
MCRegister DestReg = Inst.getOperand(0).getReg();
const MCExpr *Symbol = Inst.getOperand(1).getExpr();
InstSeq Insts;
unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_LD_PC_HI20));
Insts.push_back(LoongArchAsmParser::Inst(
ADDI, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadAddressTLSLDLarge(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.tls.ld $rd, $rj, sym
// expands to:
// pcalau12i $rd, %ld_pc_hi20(sym)
// addi.d $rj, $r0, %got_pc_lo12(sym)
// lu32i.d $rj, %got64_pc_lo20(sym)
// lu52i.d $rj, $rj, %got64_pc_hi12(sym)
// add.d $rd, $rd, $rj
MCRegister DestReg = Inst.getOperand(0).getReg();
MCRegister TmpReg = Inst.getOperand(1).getReg();
const MCExpr *Symbol = Inst.getOperand(2).getExpr();
InstSeq Insts;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_LD_PC_HI20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12));
Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D));
emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadAddressTLSGD(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.tls.gd $rd, sym
// expands to:
// pcalau12i $rd, %gd_pc_hi20(sym)
// addi.w/d $rd, $rd, %got_pc_lo12(sym)
MCRegister DestReg = Inst.getOperand(0).getReg();
const MCExpr *Symbol = Inst.getOperand(1).getExpr();
InstSeq Insts;
unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20));
Insts.push_back(LoongArchAsmParser::Inst(
ADDI, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadAddressTLSGDLarge(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
// la.tls.gd $rd, $rj, sym
// expands to:
// pcalau12i $rd, %gd_pc_hi20(sym)
// addi.d $rj, $r0, %got_pc_lo12(sym)
// lu32i.d $rj, %got64_pc_lo20(sym)
// lu52i.d $rj, $rj, %got64_pc_hi12(sym)
// add.d $rd, $rd, $rj
MCRegister DestReg = Inst.getOperand(0).getReg();
MCRegister TmpReg = Inst.getOperand(1).getReg();
const MCExpr *Symbol = Inst.getOperand(2).getExpr();
InstSeq Insts;
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20));
Insts.push_back(LoongArchAsmParser::Inst(
LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12));
Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D));
emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
}
void LoongArchAsmParser::emitLoadImm(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
MCRegister DestReg = Inst.getOperand(0).getReg();
int64_t Imm = Inst.getOperand(1).getImm();
MCRegister SrcReg = LoongArch::R0;
if (Inst.getOpcode() == LoongArch::PseudoLI_W)
Imm = SignExtend64<32>(Imm);
for (LoongArchMatInt::Inst &Inst : LoongArchMatInt::generateInstSeq(Imm)) {
unsigned Opc = Inst.Opc;
if (Opc == LoongArch::LU12I_W)
Out.emitInstruction(MCInstBuilder(Opc).addReg(DestReg).addImm(Inst.Imm),
getSTI());
else
Out.emitInstruction(
MCInstBuilder(Opc).addReg(DestReg).addReg(SrcReg).addImm(Inst.Imm),
getSTI());
SrcReg = DestReg;
}
}
bool LoongArchAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
OperandVector &Operands,
MCStreamer &Out) {
Inst.setLoc(IDLoc);
switch (Inst.getOpcode()) {
default:
break;
case LoongArch::PseudoLA_ABS:
case LoongArch::PseudoLA_ABS_LARGE:
emitLoadAddressAbs(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLA_PCREL:
emitLoadAddressPcrel(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLA_PCREL_LARGE:
emitLoadAddressPcrelLarge(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLA_GOT:
emitLoadAddressGot(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLA_GOT_LARGE:
emitLoadAddressGotLarge(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLA_TLS_LE:
emitLoadAddressTLSLE(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLA_TLS_IE:
emitLoadAddressTLSIE(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLA_TLS_IE_LARGE:
emitLoadAddressTLSIELarge(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLA_TLS_LD:
emitLoadAddressTLSLD(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLA_TLS_LD_LARGE:
emitLoadAddressTLSLDLarge(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLA_TLS_GD:
emitLoadAddressTLSGD(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLA_TLS_GD_LARGE:
emitLoadAddressTLSGDLarge(Inst, IDLoc, Out);
return false;
case LoongArch::PseudoLI_W:
case LoongArch::PseudoLI_D:
emitLoadImm(Inst, IDLoc, Out);
return false;
}
Out.emitInstruction(Inst, getSTI());
return false;
}
unsigned LoongArchAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
unsigned Opc = Inst.getOpcode();
switch (Opc) {
default:
if (Opc >= LoongArch::AMADD_D && Opc <= LoongArch::AMXOR_W) {
unsigned Rd = Inst.getOperand(0).getReg();
unsigned Rk = Inst.getOperand(1).getReg();
unsigned Rj = Inst.getOperand(2).getReg();
if ((Rd == Rk || Rd == Rj) && Rd != LoongArch::R0)
return Match_RequiresAMORdDifferRkRj;
}
break;
case LoongArch::PseudoLA_PCREL_LARGE:
case LoongArch::PseudoLA_GOT_LARGE:
case LoongArch::PseudoLA_TLS_IE_LARGE:
case LoongArch::PseudoLA_TLS_LD_LARGE:
case LoongArch::PseudoLA_TLS_GD_LARGE: {
unsigned Rd = Inst.getOperand(0).getReg();
unsigned Rj = Inst.getOperand(1).getReg();
if (Rd == Rj)
return Match_RequiresLAORdDifferRj;
break;
}
case LoongArch::CSRXCHG: {
unsigned Rj = Inst.getOperand(2).getReg();
if (Rj == LoongArch::R0 || Rj == LoongArch::R1)
return Match_RequiresOpnd2NotR0R1;
return Match_Success;
}
case LoongArch::BSTRINS_W:
case LoongArch::BSTRINS_D:
case LoongArch::BSTRPICK_W:
case LoongArch::BSTRPICK_D: {
unsigned Opc = Inst.getOpcode();
const signed Msb =
(Opc == LoongArch::BSTRINS_W || Opc == LoongArch::BSTRINS_D)
? Inst.getOperand(3).getImm()
: Inst.getOperand(2).getImm();
const signed Lsb =
(Opc == LoongArch::BSTRINS_W || Opc == LoongArch::BSTRINS_D)
? Inst.getOperand(4).getImm()
: Inst.getOperand(3).getImm();
if (Msb < Lsb)
return Match_RequiresMsbNotLessThanLsb;
return Match_Success;
}
}
return Match_Success;
}
unsigned
LoongArchAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp,
unsigned Kind) {
LoongArchOperand &Op = static_cast<LoongArchOperand &>(AsmOp);
if (!Op.isReg())
return Match_InvalidOperand;
MCRegister Reg = Op.getReg();
// As the parser couldn't differentiate an FPR32 from an FPR64, coerce the
// register from FPR32 to FPR64 if necessary.
if (LoongArchMCRegisterClasses[LoongArch::FPR32RegClassID].contains(Reg) &&
Kind == MCK_FPR64) {
Op.setReg(convertFPR32ToFPR64(Reg));
return Match_Success;
}
return Match_InvalidOperand;
}
bool LoongArchAsmParser::generateImmOutOfRangeError(
OperandVector &Operands, uint64_t ErrorInfo, int64_t Lower, int64_t Upper,
Twine Msg = "immediate must be an integer in the range") {
SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc();
return Error(ErrorLoc, Msg + " [" + Twine(Lower) + ", " + Twine(Upper) + "]");
}
bool LoongArchAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
OperandVector &Operands,
MCStreamer &Out,
uint64_t &ErrorInfo,
bool MatchingInlineAsm) {
MCInst Inst;
FeatureBitset MissingFeatures;
auto Result = MatchInstructionImpl(Operands, Inst, ErrorInfo, MissingFeatures,
MatchingInlineAsm);
switch (Result) {
default:
break;
case Match_Success:
return processInstruction(Inst, IDLoc, Operands, Out);
case Match_MissingFeature: {
assert(MissingFeatures.any() && "Unknown missing features!");
bool FirstFeature = true;
std::string Msg = "instruction requires the following:";
for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i) {
if (MissingFeatures[i]) {
Msg += FirstFeature ? " " : ", ";
Msg += getSubtargetFeatureName(i);
FirstFeature = false;
}
}
return Error(IDLoc, Msg);
}
case Match_MnemonicFail: {
FeatureBitset FBS = ComputeAvailableFeatures(getSTI().getFeatureBits());
std::string Suggestion = LoongArchMnemonicSpellCheck(
((LoongArchOperand &)*Operands[0]).getToken(), FBS, 0);
return Error(IDLoc, "unrecognized instruction mnemonic" + Suggestion);
}
case Match_InvalidOperand: {
SMLoc ErrorLoc = IDLoc;
if (ErrorInfo != ~0ULL) {
if (ErrorInfo >= Operands.size())
return Error(ErrorLoc, "too few operands for instruction");
ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc();
if (ErrorLoc == SMLoc())
ErrorLoc = IDLoc;
}
return Error(ErrorLoc, "invalid operand for instruction");
}
}
// Handle the case when the error message is of specific type
// other than the generic Match_InvalidOperand, and the
// corresponding operand is missing.
if (Result > FIRST_TARGET_MATCH_RESULT_TY) {
SMLoc ErrorLoc = IDLoc;
if (ErrorInfo != ~0ULL && ErrorInfo >= Operands.size())
return Error(ErrorLoc, "too few operands for instruction");
}
switch (Result) {
default:
break;
case Match_RequiresMsbNotLessThanLsb: {
SMLoc ErrorStart = Operands[3]->getStartLoc();
return Error(ErrorStart, "msb is less than lsb",
SMRange(ErrorStart, Operands[4]->getEndLoc()));
}
case Match_RequiresOpnd2NotR0R1:
return Error(Operands[2]->getStartLoc(), "must not be $r0 or $r1");
case Match_RequiresAMORdDifferRkRj:
return Error(Operands[1]->getStartLoc(),
"$rd must be different from both $rk and $rj");
case Match_RequiresLAORdDifferRj:
return Error(Operands[1]->getStartLoc(), "$rd must be different from $rj");
case Match_InvalidUImm2:
return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
/*Upper=*/(1 << 2) - 1);
case Match_InvalidUImm2plus1:
return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/1,
/*Upper=*/(1 << 2));
case Match_InvalidUImm3:
return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
/*Upper=*/(1 << 3) - 1);
case Match_InvalidUImm5:
return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
/*Upper=*/(1 << 5) - 1);
case Match_InvalidUImm6:
return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
/*Upper=*/(1 << 6) - 1);
case Match_InvalidUImm12:
return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
/*Upper=*/(1 << 12) - 1);
case Match_InvalidUImm12ori:
return generateImmOutOfRangeError(
Operands, ErrorInfo, /*Lower=*/0,
/*Upper=*/(1 << 12) - 1,
"operand must be a symbol with modifier (e.g. %abs_lo12) or an "
"integer in the range");
case Match_InvalidUImm15:
return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
/*Upper=*/(1 << 15) - 1);
case Match_InvalidSImm12:
return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 11),
/*Upper=*/(1 << 11) - 1);
case Match_InvalidSImm12addlike:
return generateImmOutOfRangeError(
Operands, ErrorInfo, /*Lower=*/-(1 << 11),
/*Upper=*/(1 << 11) - 1,
"operand must be a symbol with modifier (e.g. %pc_lo12) or an integer "
"in the range");
case Match_InvalidSImm12lu52id:
return generateImmOutOfRangeError(
Operands, ErrorInfo, /*Lower=*/-(1 << 11),
/*Upper=*/(1 << 11) - 1,
"operand must be a symbol with modifier (e.g. %pc64_hi12) or an "
"integer in the range");
case Match_InvalidSImm14lsl2:
return generateImmOutOfRangeError(
Operands, ErrorInfo, /*Lower=*/-(1 << 15), /*Upper=*/(1 << 15) - 4,
"immediate must be a multiple of 4 in the range");
case Match_InvalidSImm16:
return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 15),
/*Upper=*/(1 << 15) - 1);
case Match_InvalidSImm16lsl2:
return generateImmOutOfRangeError(
Operands, ErrorInfo, /*Lower=*/-(1 << 17), /*Upper=*/(1 << 17) - 4,
"operand must be a symbol with modifier (e.g. %b16) or an integer "
"in the range");
case Match_InvalidSImm20:
return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 19),
/*Upper=*/(1 << 19) - 1);
case Match_InvalidSImm20lu12iw:
return generateImmOutOfRangeError(
Operands, ErrorInfo, /*Lower=*/-(1 << 19),
/*Upper=*/(1 << 19) - 1,
"operand must be a symbol with modifier (e.g. %abs_hi20) or an integer "
"in the range");
case Match_InvalidSImm20lu32id:
return generateImmOutOfRangeError(
Operands, ErrorInfo, /*Lower=*/-(1 << 19),
/*Upper=*/(1 << 19) - 1,
"operand must be a symbol with modifier (e.g. %abs64_lo20) or an "
"integer in the range");
case Match_InvalidSImm20pcalau12i:
return generateImmOutOfRangeError(
Operands, ErrorInfo, /*Lower=*/-(1 << 19),
/*Upper=*/(1 << 19) - 1,
"operand must be a symbol with modifier (e.g. %pc_hi20) or an integer "
"in the range");
case Match_InvalidSImm21lsl2:
return generateImmOutOfRangeError(
Operands, ErrorInfo, /*Lower=*/-(1 << 22), /*Upper=*/(1 << 22) - 4,
"operand must be a symbol with modifier (e.g. %b21) or an integer "
"in the range");
case Match_InvalidSImm26Operand:
return generateImmOutOfRangeError(
Operands, ErrorInfo, /*Lower=*/-(1 << 27), /*Upper=*/(1 << 27) - 4,
"operand must be a bare symbol name or an immediate must be a multiple "
"of 4 in the range");
case Match_InvalidImm32: {
SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc();
return Error(ErrorLoc, "operand must be a 32 bit immediate");
}
case Match_InvalidBareSymbol: {
SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc();
return Error(ErrorLoc, "operand must be a bare symbol name");
}
}
llvm_unreachable("Unknown match type detected!");
}
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchAsmParser() {
RegisterMCAsmParser<LoongArchAsmParser> X(getTheLoongArch32Target());
RegisterMCAsmParser<LoongArchAsmParser> Y(getTheLoongArch64Target());
}