// 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; /// 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 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, const 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" ParseStatus parseRegister(OperandVector &Operands); ParseStatus parseImmediate(OperandVector &Operands); ParseStatus parseOperandWithModifier(OperandVector &Operands); ParseStatus parseSImm26Operand(OperandVector &Operands); ParseStatus 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 &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(Expr)) { VK = LE->getKind(); return false; } if (auto CE = dyn_cast(Expr)) { Imm = CE->getValue(); return true; } return false; } template 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(Imm - P) && VK == LoongArchMCExpr::VK_LoongArch_None; } template 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(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 isUImm1() const { return isUImm<1>(); } bool isUImm2() const { return isUImm<2>(); } bool isUImm2plus1() const { return isUImm<2, 1>(); } bool isUImm3() const { return isUImm<3>(); } bool isUImm4() const { return isUImm<4>(); } bool isSImm5() const { return isSImm<5>(); } bool isUImm5() const { return isUImm<5>(); } bool isUImm6() const { return isUImm<6>(); } bool isUImm7() const { return isUImm<7>(); } bool isSImm8() const { return isSImm<8>(); } bool isSImm8lsl1() const { return isSImm<8, 1>(); } bool isSImm8lsl2() const { return isSImm<8, 2>(); } bool isSImm8lsl3() const { return isSImm<8, 3>(); } bool isUImm8() const { return isUImm<8>(); } bool isSImm9lsl3() const { return isSImm<9, 3>(); } bool isSImm10() const { return isSImm<10>(); } bool isSImm10lsl2() const { return isSImm<10, 2>(); } bool isSImm11lsl1() const { return isSImm<11, 1>(); } 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 isSImm13() const { return isSImm<13>(); } 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 << ""; break; case KindTy::Token: OS << "'" << getToken() << "'"; break; } } static std::unique_ptr createToken(StringRef Str, SMLoc S) { auto Op = std::make_unique(KindTy::Token); Op->Tok = Str; Op->StartLoc = S; Op->EndLoc = S; return Op; } static std::unique_ptr createReg(unsigned RegNo, SMLoc S, SMLoc E) { auto Op = std::make_unique(KindTy::Register); Op->Reg.RegNum = RegNo; Op->StartLoc = S; Op->EndLoc = E; return Op; } static std::unique_ptr createImm(const MCExpr *Val, SMLoc S, SMLoc E) { auto Op = std::make_unique(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(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(Expr)) { Kind = RE->getKind(); Expr = RE->getSubExpr(); } MCValue Res; if (Expr->evaluateAsRelocatable(Res, nullptr, nullptr)) return Res.getRefKind() == LoongArchMCExpr::VK_LoongArch_None; return false; } ParseStatus LoongArchAsmParser::parseRegister(OperandVector &Operands) { if (!parseOptionalToken(AsmToken::Dollar)) return ParseStatus::NoMatch; if (getLexer().getKind() != AsmToken::Identifier) return ParseStatus::NoMatch; StringRef Name = getLexer().getTok().getIdentifier(); MCRegister RegNo; matchRegisterNameHelper(RegNo, Name); if (RegNo == LoongArch::NoRegister) return ParseStatus::NoMatch; SMLoc S = getLoc(); SMLoc E = SMLoc::getFromPointer(S.getPointer() + Name.size()); getLexer().Lex(); Operands.push_back(LoongArchOperand::createReg(RegNo, S, E)); return ParseStatus::Success; } ParseStatus LoongArchAsmParser::parseImmediate(OperandVector &Operands) { SMLoc S = getLoc(); SMLoc E; const MCExpr *Res; switch (getLexer().getKind()) { default: return ParseStatus::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 ParseStatus::Failure; break; case AsmToken::Percent: return parseOperandWithModifier(Operands); } Operands.push_back(LoongArchOperand::createImm(Res, S, E)); return ParseStatus::Success; } ParseStatus LoongArchAsmParser::parseOperandWithModifier(OperandVector &Operands) { SMLoc S = getLoc(); SMLoc E; if (getLexer().getKind() != AsmToken::Percent) return Error(getLoc(), "expected '%' for operand modifier"); getParser().Lex(); // Eat '%' if (getLexer().getKind() != AsmToken::Identifier) return Error(getLoc(), "expected valid identifier for operand modifier"); StringRef Identifier = getParser().getTok().getIdentifier(); LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::getVariantKindForName(Identifier); if (VK == LoongArchMCExpr::VK_LoongArch_Invalid) return Error(getLoc(), "unrecognized operand modifier"); getParser().Lex(); // Eat the identifier if (getLexer().getKind() != AsmToken::LParen) return Error(getLoc(), "expected '('"); getParser().Lex(); // Eat '(' const MCExpr *SubExpr; if (getParser().parseParenExpression(SubExpr, E)) return ParseStatus::Failure; const MCExpr *ModExpr = LoongArchMCExpr::create(SubExpr, VK, getContext()); Operands.push_back(LoongArchOperand::createImm(ModExpr, S, E)); return ParseStatus::Success; } ParseStatus LoongArchAsmParser::parseSImm26Operand(OperandVector &Operands) { SMLoc S = getLoc(); const MCExpr *Res; if (getLexer().getKind() == AsmToken::Percent) return parseOperandWithModifier(Operands); if (getLexer().getKind() != AsmToken::Identifier) return ParseStatus::NoMatch; StringRef Identifier; if (getParser().parseIdentifier(Identifier)) return ParseStatus::Failure; 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 ParseStatus::Success; } ParseStatus LoongArchAsmParser::parseAtomicMemOp(OperandVector &Operands) { // Parse "$r*". if (!parseRegister(Operands).isSuccess()) return ParseStatus::NoMatch; // If there is a next operand and it is 0, ignore it. Otherwise print a // diagnostic message. if (parseOptionalToken(AsmToken::Comma)) { int64_t ImmVal; SMLoc ImmStart = getLoc(); if (getParser().parseIntToken(ImmVal, "expected optional integer offset")) return ParseStatus::Failure; if (ImmVal) return Error(ImmStart, "optional integer offset must be 0"); } return ParseStatus::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. ParseStatus Result = MatchOperandParserImpl(Operands, Mnemonic, /*ParseForAllFeatures=*/true); if (Result.isSuccess()) return false; if (Result.isFailure()) return true; if (parseRegister(Operands).isSuccess() || parseImmediate(Operands).isSuccess()) return false; // Finally we have exhausted all options and must declare defeat. return Error(getLoc(), "unknown operand"); } 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 &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: case LoongArch::GCSRXCHG: { 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(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, const 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_InvalidUImm1: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0, /*Upper=*/(1 << 1) - 1); 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_InvalidUImm4: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0, /*Upper=*/(1 << 4) - 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_InvalidUImm7: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0, /*Upper=*/(1 << 7) - 1); case Match_InvalidUImm8: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0, /*Upper=*/(1 << 8) - 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_InvalidUImm14: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0, /*Upper=*/(1 << 14) - 1); case Match_InvalidUImm15: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0, /*Upper=*/(1 << 15) - 1); case Match_InvalidSImm5: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 4), /*Upper=*/(1 << 4) - 1); case Match_InvalidSImm8: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 7), /*Upper=*/(1 << 7) - 1); case Match_InvalidSImm8lsl1: return generateImmOutOfRangeError( Operands, ErrorInfo, /*Lower=*/-(1 << 8), /*Upper=*/(1 << 8) - 2, "immediate must be a multiple of 2 in the range"); case Match_InvalidSImm8lsl2: return generateImmOutOfRangeError( Operands, ErrorInfo, /*Lower=*/-(1 << 9), /*Upper=*/(1 << 9) - 4, "immediate must be a multiple of 4 in the range"); case Match_InvalidSImm10: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 9), /*Upper=*/(1 << 9) - 1); case Match_InvalidSImm8lsl3: return generateImmOutOfRangeError( Operands, ErrorInfo, /*Lower=*/-(1 << 10), /*Upper=*/(1 << 10) - 8, "immediate must be a multiple of 8 in the range"); case Match_InvalidSImm9lsl3: return generateImmOutOfRangeError( Operands, ErrorInfo, /*Lower=*/-(1 << 11), /*Upper=*/(1 << 11) - 8, "immediate must be a multiple of 8 in the range"); case Match_InvalidSImm10lsl2: return generateImmOutOfRangeError( Operands, ErrorInfo, /*Lower=*/-(1 << 11), /*Upper=*/(1 << 11) - 4, "immediate must be a multiple of 4 in the range"); case Match_InvalidSImm11lsl1: return generateImmOutOfRangeError( Operands, ErrorInfo, /*Lower=*/-(1 << 11), /*Upper=*/(1 << 11) - 2, "immediate must be a multiple of 2 in the range"); 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_InvalidSImm13: return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 12), /*Upper=*/(1 << 12) - 1); 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 X(getTheLoongArch32Target()); RegisterMCAsmParser Y(getTheLoongArch64Target()); }