1393 lines
52 KiB
C++
1393 lines
52 KiB
C++
// 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(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override;
|
|
OperandMatchResultTy tryParseRegister(unsigned &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);
|
|
|
|
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; }
|
|
|
|
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;
|
|
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 = [](unsigned 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(unsigned &RegNo, SMLoc &StartLoc,
|
|
SMLoc &EndLoc) {
|
|
return Error(getLoc(), "invalid register number");
|
|
}
|
|
|
|
OperandMatchResultTy LoongArchAsmParser::tryParseRegister(unsigned &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;
|
|
}
|
|
|
|
/// 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)
|
|
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());
|
|
}
|