| // 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/LoongArchBaseInfo.h" |
| #include "MCTargetDesc/LoongArchInstPrinter.h" |
| #include "MCTargetDesc/LoongArchMCExpr.h" |
| #include "MCTargetDesc/LoongArchMCTargetDesc.h" |
| #include "MCTargetDesc/LoongArchMatInt.h" |
| #include "MCTargetDesc/LoongArchTargetStreamer.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 { |
| SmallVector<FeatureBitset, 4> FeatureBitStack; |
| |
| SMLoc getLoc() const { return getParser().getTok().getLoc(); } |
| bool is64Bit() const { return getSTI().hasFeature(LoongArch::Feature64Bit); } |
| LoongArchTargetStreamer &getTargetStreamer() { |
| assert(getParser().getStreamer().getTargetStreamer() && |
| "do not have a target streamer"); |
| MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); |
| return static_cast<LoongArchTargetStreamer &>(TS); |
| } |
| |
| struct Inst { |
| unsigned Opc; |
| LoongArchMCExpr::Specifier VK; |
| Inst(unsigned Opc, LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_None) |
| : Opc(Opc), VK(VK) {} |
| }; |
| using InstSeq = SmallVector<Inst>; |
| |
| /// Parse a register as used in CFI directives. |
| bool parseRegister(MCRegister &Reg, SMLoc &StartLoc, SMLoc &EndLoc) override; |
| ParseStatus tryParseRegister(MCRegister &Reg, 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; |
| |
| ParseStatus parseDirective(AsmToken DirectiveID) 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); |
| |
| bool parseDirectiveOption(); |
| |
| void setFeatureBits(uint64_t Feature, StringRef FeatureString) { |
| if (!(getSTI().hasFeature(Feature))) { |
| MCSubtargetInfo &STI = copySTI(); |
| setAvailableFeatures( |
| ComputeAvailableFeatures(STI.ToggleFeature(FeatureString))); |
| } |
| } |
| |
| void clearFeatureBits(uint64_t Feature, StringRef FeatureString) { |
| if (getSTI().hasFeature(Feature)) { |
| MCSubtargetInfo &STI = copySTI(); |
| setAvailableFeatures( |
| ComputeAvailableFeatures(STI.ToggleFeature(FeatureString))); |
| } |
| } |
| |
| void pushFeatureBits() { |
| FeatureBitStack.push_back(getSTI().getFeatureBits()); |
| } |
| |
| bool popFeatureBits() { |
| if (FeatureBitStack.empty()) |
| return true; |
| |
| FeatureBitset FeatureBits = FeatureBitStack.pop_back_val(); |
| copySTI().setFeatureBits(FeatureBits); |
| setAvailableFeatures(ComputeAvailableFeatures(FeatureBits)); |
| |
| return false; |
| } |
| |
| // 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, bool RelaxHint = false); |
| |
| // 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 "la.tls.desc $rd, sym". |
| void emitLoadAddressTLSDesc(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); |
| // Helper to emit pseudo instruction "la.tls.desc $rd, $rj, sym". |
| void emitLoadAddressTLSDescLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); |
| |
| // Helper to emit pseudo instruction "li.w/d $rd, $imm". |
| void emitLoadImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); |
| |
| // Helper to emit pseudo instruction "call36 sym" or "tail36 $rj, sym". |
| void emitFuncCall36(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, |
| bool IsTailCall); |
| |
| public: |
| enum LoongArchMatchResultTy { |
| Match_Dummy = FIRST_TARGET_MATCH_RESULT_TY, |
| Match_RequiresMsbNotLessThanLsb, |
| Match_RequiresOpnd2NotR0R1, |
| Match_RequiresAMORdDifferRkRj, |
| Match_RequiresLAORdDifferRj, |
| Match_RequiresLAORdR4, |
| #define GET_OPERAND_DIAGNOSTIC_TYPES |
| #include "LoongArchGenAsmMatcher.inc" |
| #undef GET_OPERAND_DIAGNOSTIC_TYPES |
| }; |
| |
| static bool classifySymbolRef(const MCExpr *Expr, |
| LoongArchMCExpr::Specifier &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::Specifier &VK) { |
| if (auto *LE = dyn_cast<LoongArchMCExpr>(Expr)) { |
| VK = LE->getSpecifier(); |
| 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::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| return IsConstantImm && isUInt<N>(Imm - P) && |
| VK == LoongArchMCExpr::VK_None; |
| } |
| |
| template <unsigned N, unsigned S = 0> bool isSImm() const { |
| if (!isImm()) |
| return false; |
| |
| int64_t Imm; |
| LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| return IsConstantImm && isShiftedInt<N, S>(Imm) && |
| VK == LoongArchMCExpr::VK_None; |
| } |
| |
| bool isBareSymbol() const { |
| int64_t Imm; |
| LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_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_None; |
| } |
| |
| bool isTPRelAddSymbol() const { |
| int64_t Imm; |
| LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_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_TLS_LE_ADD_R; |
| } |
| |
| 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::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| bool IsValidKind = VK == LoongArchMCExpr::VK_None || |
| VK == LoongArchMCExpr::VK_PCALA_LO12 || |
| VK == LoongArchMCExpr::VK_GOT_PC_LO12 || |
| VK == LoongArchMCExpr::VK_TLS_IE_PC_LO12 || |
| VK == LoongArchMCExpr::VK_TLS_LE_LO12_R || |
| VK == LoongArchMCExpr::VK_TLS_DESC_PC_LO12 || |
| VK == LoongArchMCExpr::VK_TLS_DESC_LD; |
| return IsConstantImm |
| ? isInt<12>(Imm) && IsValidKind |
| : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && |
| IsValidKind; |
| } |
| |
| bool isSImm12lu52id() const { |
| if (!isImm()) |
| return false; |
| |
| int64_t Imm; |
| LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| bool IsValidKind = VK == LoongArchMCExpr::VK_None || |
| VK == LoongArchMCExpr::VK_ABS64_HI12 || |
| VK == LoongArchMCExpr::VK_PCALA64_HI12 || |
| VK == LoongArchMCExpr::VK_GOT64_HI12 || |
| VK == LoongArchMCExpr::VK_GOT64_PC_HI12 || |
| VK == LoongArchMCExpr::VK_TLS_LE64_HI12 || |
| VK == LoongArchMCExpr::VK_TLS_IE64_HI12 || |
| VK == LoongArchMCExpr::VK_TLS_IE64_PC_HI12 || |
| VK == LoongArchMCExpr::VK_TLS_DESC64_HI12 || |
| VK == LoongArchMCExpr::VK_TLS_DESC64_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::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| bool IsValidKind = VK == LoongArchMCExpr::VK_None || |
| VK == LoongArchMCExpr::VK_ABS_LO12 || |
| VK == LoongArchMCExpr::VK_PCALA_LO12 || |
| VK == LoongArchMCExpr::VK_GOT_LO12 || |
| VK == LoongArchMCExpr::VK_GOT_PC_LO12 || |
| VK == LoongArchMCExpr::VK_TLS_LE_LO12 || |
| VK == LoongArchMCExpr::VK_TLS_IE_LO12 || |
| VK == LoongArchMCExpr::VK_TLS_IE_PC_LO12 || |
| VK == LoongArchMCExpr::VK_TLS_DESC_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::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| bool IsValidKind = VK == LoongArchMCExpr::VK_None || |
| VK == LoongArchMCExpr::VK_B16 || |
| VK == LoongArchMCExpr::VK_PCALA_LO12 || |
| VK == LoongArchMCExpr::VK_TLS_DESC_CALL; |
| 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::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| bool IsValidKind = VK == LoongArchMCExpr::VK_None || |
| VK == LoongArchMCExpr::VK_PCALA_HI20 || |
| VK == LoongArchMCExpr::VK_GOT_PC_HI20 || |
| VK == LoongArchMCExpr::VK_TLS_IE_PC_HI20 || |
| VK == LoongArchMCExpr::VK_TLS_LD_PC_HI20 || |
| VK == LoongArchMCExpr::VK_TLS_GD_PC_HI20 || |
| VK == LoongArchMCExpr::VK_TLS_DESC_PC_HI20; |
| return IsConstantImm |
| ? isInt<20>(Imm) && IsValidKind |
| : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && |
| IsValidKind; |
| } |
| |
| bool isSImm20lu12iw() const { |
| if (!isImm()) |
| return false; |
| |
| int64_t Imm; |
| LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| bool IsValidKind = VK == LoongArchMCExpr::VK_None || |
| VK == LoongArchMCExpr::VK_ABS_HI20 || |
| VK == LoongArchMCExpr::VK_GOT_HI20 || |
| VK == LoongArchMCExpr::VK_TLS_GD_HI20 || |
| VK == LoongArchMCExpr::VK_TLS_LD_HI20 || |
| VK == LoongArchMCExpr::VK_TLS_IE_HI20 || |
| VK == LoongArchMCExpr::VK_TLS_LE_HI20 || |
| VK == LoongArchMCExpr::VK_TLS_LE_HI20_R || |
| VK == LoongArchMCExpr::VK_TLS_DESC_HI20; |
| return IsConstantImm |
| ? isInt<20>(Imm) && IsValidKind |
| : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && |
| IsValidKind; |
| } |
| |
| bool isSImm20lu32id() const { |
| if (!isImm()) |
| return false; |
| |
| int64_t Imm; |
| LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| bool IsValidKind = VK == LoongArchMCExpr::VK_None || |
| VK == LoongArchMCExpr::VK_ABS64_LO20 || |
| VK == LoongArchMCExpr::VK_PCALA64_LO20 || |
| VK == LoongArchMCExpr::VK_GOT64_LO20 || |
| VK == LoongArchMCExpr::VK_GOT64_PC_LO20 || |
| VK == LoongArchMCExpr::VK_TLS_IE64_LO20 || |
| VK == LoongArchMCExpr::VK_TLS_IE64_PC_LO20 || |
| VK == LoongArchMCExpr::VK_TLS_LE64_LO20 || |
| VK == LoongArchMCExpr::VK_TLS_DESC64_PC_LO20 || |
| VK == LoongArchMCExpr::VK_TLS_DESC64_LO20; |
| |
| return IsConstantImm |
| ? isInt<20>(Imm) && IsValidKind |
| : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && |
| IsValidKind; |
| } |
| |
| bool isSImm20pcaddu18i() const { |
| if (!isImm()) |
| return false; |
| |
| int64_t Imm; |
| LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| bool IsValidKind = |
| VK == LoongArchMCExpr::VK_None || VK == LoongArchMCExpr::VK_CALL36; |
| |
| return IsConstantImm |
| ? isInt<20>(Imm) && IsValidKind |
| : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && |
| IsValidKind; |
| } |
| |
| bool isSImm20pcaddi() const { |
| if (!isImm()) |
| return false; |
| |
| int64_t Imm; |
| LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| bool IsValidKind = VK == LoongArchMCExpr::VK_None || |
| VK == LoongArchMCExpr::VK_PCREL20_S2 || |
| VK == LoongArchMCExpr::VK_TLS_LD_PCREL20_S2 || |
| VK == LoongArchMCExpr::VK_TLS_GD_PCREL20_S2 || |
| VK == LoongArchMCExpr::VK_TLS_DESC_PCREL20_S2; |
| return IsConstantImm |
| ? isInt<20>(Imm) && IsValidKind |
| : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && |
| IsValidKind; |
| } |
| |
| bool isSImm21lsl2() const { |
| if (!isImm()) |
| return false; |
| |
| int64_t Imm; |
| LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| bool IsValidKind = |
| VK == LoongArchMCExpr::VK_None || VK == LoongArchMCExpr::VK_B21; |
| return IsConstantImm |
| ? isShiftedInt<21, 2>(Imm) && IsValidKind |
| : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && |
| IsValidKind; |
| } |
| |
| bool isSImm26Operand() const { |
| if (!isImm()) |
| return false; |
| |
| int64_t Imm; |
| LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| bool IsValidKind = |
| VK == LoongArchMCExpr::VK_None || VK == LoongArchMCExpr::VK_CALL || |
| VK == LoongArchMCExpr::VK_CALL_PLT || VK == LoongArchMCExpr::VK_B26; |
| return IsConstantImm |
| ? isShiftedInt<26, 2>(Imm) && IsValidKind |
| : LoongArchAsmParser::classifySymbolRef(getImm(), VK) && |
| IsValidKind; |
| } |
| |
| bool isImm32() const { return isSImm<32>() || isUImm<32>(); } |
| bool isImm64() const { |
| if (!isImm()) |
| return false; |
| int64_t Imm; |
| LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_None; |
| bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK); |
| return IsConstantImm && VK == LoongArchMCExpr::VK_None; |
| } |
| |
| /// 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; } |
| |
| MCRegister getReg() const override { |
| assert(Kind == KindTy::Register && "Invalid type access!"); |
| return Reg.RegNum; |
| } |
| |
| const MCExpr *getImm() const { |
| assert(Kind == KindTy::Immediate && "Invalid type access!"); |
| return Imm.Val; |
| } |
| |
| StringRef getToken() const { |
| assert(Kind == KindTy::Token && "Invalid type access!"); |
| return Tok; |
| } |
| |
| void print(raw_ostream &OS) const override { |
| auto RegName = [](MCRegister Reg) { |
| if (Reg) |
| return LoongArchInstPrinter::getRegisterName(Reg); |
| else |
| return "noreg"; |
| }; |
| |
| switch (Kind) { |
| case KindTy::Immediate: |
| OS << *getImm(); |
| break; |
| case KindTy::Register: |
| OS << "<register " << RegName(getReg()) << ">"; |
| break; |
| case KindTy::Token: |
| OS << "'" << getToken() << "'"; |
| break; |
| } |
| } |
| |
| static std::unique_ptr<LoongArchOperand> createToken(StringRef Str, SMLoc S) { |
| auto Op = std::make_unique<LoongArchOperand>(KindTy::Token); |
| Op->Tok = Str; |
| Op->StartLoc = S; |
| Op->EndLoc = S; |
| return Op; |
| } |
| |
| static std::unique_ptr<LoongArchOperand> createReg(MCRegister Reg, SMLoc S, |
| SMLoc E) { |
| auto Op = std::make_unique<LoongArchOperand>(KindTy::Register); |
| Op->Reg.RegNum = Reg; |
| Op->StartLoc = S; |
| Op->EndLoc = E; |
| return Op; |
| } |
| |
| static std::unique_ptr<LoongArchOperand> createImm(const MCExpr *Val, SMLoc S, |
| SMLoc E) { |
| auto Op = std::make_unique<LoongArchOperand>(KindTy::Immediate); |
| Op->Imm.Val = Val; |
| Op->StartLoc = S; |
| Op->EndLoc = E; |
| return Op; |
| } |
| |
| void addExpr(MCInst &Inst, const MCExpr *Expr) const { |
| if (auto CE = dyn_cast<MCConstantExpr>(Expr)) |
| Inst.addOperand(MCOperand::createImm(CE->getValue())); |
| else |
| Inst.addOperand(MCOperand::createExpr(Expr)); |
| } |
| |
| // Used by the TableGen Code. |
| void addRegOperands(MCInst &Inst, unsigned N) const { |
| assert(N == 1 && "Invalid number of operands!"); |
| Inst.addOperand(MCOperand::createReg(getReg())); |
| } |
| void addImmOperands(MCInst &Inst, unsigned N) const { |
| assert(N == 1 && "Invalid number of operands!"); |
| addExpr(Inst, getImm()); |
| } |
| }; |
| } // end namespace |
| |
| #define GET_REGISTER_MATCHER |
| #define GET_SUBTARGET_FEATURE_NAME |
| #define GET_MATCHER_IMPLEMENTATION |
| #define GET_MNEMONIC_SPELL_CHECKER |
| #include "LoongArchGenAsmMatcher.inc" |
| |
| static MCRegister convertFPR32ToFPR64(MCRegister Reg) { |
| assert(Reg >= LoongArch::F0 && Reg <= LoongArch::F31 && "Invalid register"); |
| return Reg - LoongArch::F0 + LoongArch::F0_64; |
| } |
| |
| // Attempts to match Name as a register (either using the default name or |
| // alternative ABI names), setting RegNo to the matching register. Upon |
| // failure, returns true and sets RegNo to 0. |
| static bool matchRegisterNameHelper(MCRegister &RegNo, StringRef Name) { |
| RegNo = MatchRegisterName(Name); |
| // The 32-bit and 64-bit FPRs have the same asm name. Check that the initial |
| // match always matches the 32-bit variant, and not the 64-bit one. |
| assert(!(RegNo >= LoongArch::F0_64 && RegNo <= LoongArch::F31_64)); |
| // The default FPR register class is based on the tablegen enum ordering. |
| static_assert(LoongArch::F0 < LoongArch::F0_64, |
| "FPR matching must be updated"); |
| if (RegNo == LoongArch::NoRegister) |
| RegNo = MatchRegisterAltName(Name); |
| |
| return RegNo == LoongArch::NoRegister; |
| } |
| |
| bool LoongArchAsmParser::parseRegister(MCRegister &Reg, SMLoc &StartLoc, |
| SMLoc &EndLoc) { |
| if (!tryParseRegister(Reg, StartLoc, EndLoc).isSuccess()) |
| return Error(getLoc(), "invalid register name"); |
| |
| if (!LoongArchMCRegisterClasses[LoongArch::GPRRegClassID].contains(Reg) && |
| !LoongArchMCRegisterClasses[LoongArch::FPR32RegClassID].contains(Reg)) |
| return Error(getLoc(), "invalid register name"); |
| |
| return false; |
| } |
| |
| ParseStatus LoongArchAsmParser::tryParseRegister(MCRegister &Reg, |
| SMLoc &StartLoc, |
| SMLoc &EndLoc) { |
| const AsmToken &Tok = getParser().getTok(); |
| StartLoc = Tok.getLoc(); |
| EndLoc = Tok.getEndLoc(); |
| |
| parseOptionalToken(AsmToken::Dollar); |
| if (getLexer().getKind() != AsmToken::Identifier) |
| return ParseStatus::NoMatch; |
| |
| StringRef Name = Tok.getIdentifier(); |
| if (matchRegisterNameHelper(Reg, Name)) |
| return ParseStatus::NoMatch; |
| |
| getParser().Lex(); // Eat identifier token. |
| return ParseStatus::Success; |
| } |
| |
| bool LoongArchAsmParser::classifySymbolRef(const MCExpr *Expr, |
| LoongArchMCExpr::Specifier &Kind) { |
| Kind = LoongArchMCExpr::VK_None; |
| |
| if (const LoongArchMCExpr *RE = dyn_cast<LoongArchMCExpr>(Expr)) { |
| Kind = RE->getSpecifier(); |
| Expr = RE->getSubExpr(); |
| } |
| |
| MCValue Res; |
| if (Expr->evaluateAsRelocatable(Res, nullptr)) |
| return Res.getSpecifier() == LoongArchMCExpr::VK_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::Specifier VK = LoongArchMCExpr::parseSpecifier(Identifier); |
| if (VK == LoongArchMCExpr::VK_None) |
| return Error(getLoc(), "invalid relocation specifier"); |
| |
| 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, getContext()); |
| Res = LoongArchMCExpr::create(Res, LoongArchMCExpr::VK_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<Inst> &Insts, |
| SMLoc IDLoc, MCStreamer &Out, |
| bool RelaxHint) { |
| MCContext &Ctx = getContext(); |
| for (LoongArchAsmParser::Inst &Inst : Insts) { |
| unsigned Opc = Inst.Opc; |
| LoongArchMCExpr::Specifier VK = Inst.VK; |
| const LoongArchMCExpr *LE = |
| LoongArchMCExpr::create(Symbol, VK, Ctx, RelaxHint); |
| 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_None) { |
| Out.emitInstruction( |
| MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addImm(0), |
| getSTI()); |
| continue; |
| } else if (VK == LoongArchMCExpr::VK_TLS_DESC_LD) { |
| Out.emitInstruction(MCInstBuilder(Opc) |
| .addReg(LoongArch::R1) |
| .addReg(DestReg) |
| .addExpr(LE), |
| 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; |
| case LoongArch::JIRL: |
| Out.emitInstruction(MCInstBuilder(Opc) |
| .addReg(LoongArch::R1) |
| .addReg(LoongArch::R1) |
| .addExpr(LE), |
| 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_ABS_HI20)); |
| Insts.push_back( |
| LoongArchAsmParser::Inst(LoongArch::ORI, LoongArchMCExpr::VK_ABS_LO12)); |
| |
| if (is64Bit()) { |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU32I_D, |
| LoongArchMCExpr::VK_ABS64_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU52I_D, |
| LoongArchMCExpr::VK_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_PCALA_HI20)); |
| Insts.push_back( |
| LoongArchAsmParser::Inst(ADDI, LoongArchMCExpr::VK_PCALA_LO12)); |
| |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out, |
| /*RelaxHint=*/true); |
| } |
| |
| 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_PCALA_HI20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADDI_D, |
| LoongArchMCExpr::VK_PCALA_LO12)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU32I_D, |
| LoongArchMCExpr::VK_PCALA64_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU52I_D, |
| LoongArchMCExpr::VK_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 |
| MCRegister DestReg = Inst.getOperand(0).getReg(); |
| const MCExpr *Symbol = Inst.getOperand(1).getExpr(); |
| InstSeq Insts; |
| unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W; |
| |
| if (getSTI().hasFeature(LoongArch::LaGlobalWithAbs)) { |
| // with feature: +la-glabal-with-abs |
| // for 32bit: |
| // lu12i.w $rd, %got_hi20(sym) |
| // ori $rd, $rd, %got_lo12(sym) |
| // ld.w $rd, $rd, 0 |
| // |
| // for 64bit: |
| // lu12i.w $rd, %got_hi20(sym) |
| // ori $rd, $rd, %got_lo12(sym) |
| // lu32i.d $rd, %got64_lo20(sym) |
| // lu52i.d $rd, $rd, %got64_hi12(sym) |
| // ld.d $rd, $rd, 0 |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU12I_W, |
| LoongArchMCExpr::VK_GOT_HI20)); |
| Insts.push_back( |
| LoongArchAsmParser::Inst(LoongArch::ORI, LoongArchMCExpr::VK_GOT_LO12)); |
| |
| if (is64Bit()) { |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU32I_D, |
| LoongArchMCExpr::VK_GOT64_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU52I_D, |
| LoongArchMCExpr::VK_GOT64_HI12)); |
| } |
| Insts.push_back(LoongArchAsmParser::Inst(LD)); |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); |
| return; |
| } |
| // expands to: |
| // pcalau12i $rd, %got_pc_hi20(sym) |
| // ld.w/d $rd, $rd, %got_pc_lo12(sym) |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::PCALAU12I, |
| LoongArchMCExpr::VK_GOT_PC_HI20)); |
| Insts.push_back( |
| LoongArchAsmParser::Inst(LD, LoongArchMCExpr::VK_GOT_PC_LO12)); |
| |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out, |
| /*RelaxHint=*/true); |
| } |
| |
| 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_GOT_PC_HI20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADDI_D, |
| LoongArchMCExpr::VK_GOT_PC_LO12)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU32I_D, |
| LoongArchMCExpr::VK_GOT64_PC_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU52I_D, |
| LoongArchMCExpr::VK_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_TLS_LE_HI20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ORI, |
| LoongArchMCExpr::VK_TLS_LE_LO12)); |
| |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); |
| } |
| |
| void LoongArchAsmParser::emitLoadAddressTLSIE(MCInst &Inst, SMLoc IDLoc, |
| MCStreamer &Out) { |
| // la.tls.ie $rd, 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; |
| |
| if (getSTI().hasFeature(LoongArch::LaGlobalWithAbs)) { |
| // with feature: +la-glabal-with-abs |
| // for 32bit: |
| // lu12i.w $rd, %ie_hi20(sym) |
| // ori $rd, $rd, %ie_lo12(sym) |
| // ld.w $rd, $rd, 0 |
| // |
| // for 64bit: |
| // lu12i.w $rd, %ie_hi20(sym) |
| // ori $rd, $rd, %ie_lo12(sym) |
| // lu32i.d $rd, %ie64_lo20(sym) |
| // lu52i.d $rd, $rd, %ie64_hi12(sym) |
| // ld.d $rd, $rd, 0 |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU12I_W, |
| LoongArchMCExpr::VK_TLS_IE_HI20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ORI, |
| LoongArchMCExpr::VK_TLS_IE_LO12)); |
| |
| if (is64Bit()) { |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::LU32I_D, LoongArchMCExpr::VK_TLS_IE64_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::LU52I_D, LoongArchMCExpr::VK_TLS_IE64_HI12)); |
| } |
| Insts.push_back(LoongArchAsmParser::Inst(LD)); |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); |
| return; |
| } |
| |
| // expands to: |
| // pcalau12i $rd, %ie_pc_hi20(sym) |
| // ld.w/d $rd, $rd, %ie_pc_lo12(sym) |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::PCALAU12I, |
| LoongArchMCExpr::VK_TLS_IE_PC_HI20)); |
| Insts.push_back( |
| LoongArchAsmParser::Inst(LD, LoongArchMCExpr::VK_TLS_IE_PC_LO12)); |
| |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out, |
| /*RelaxHint=*/true); |
| } |
| |
| 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_TLS_IE_PC_HI20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADDI_D, |
| LoongArchMCExpr::VK_TLS_IE_PC_LO12)); |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::LU32I_D, LoongArchMCExpr::VK_TLS_IE64_PC_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::LU52I_D, LoongArchMCExpr::VK_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 |
| MCRegister DestReg = Inst.getOperand(0).getReg(); |
| const MCExpr *Symbol = Inst.getOperand(1).getExpr(); |
| InstSeq Insts; |
| unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W; |
| |
| if (getSTI().hasFeature(LoongArch::LaGlobalWithAbs)) { |
| // with feature: +la-glabal-with-abs |
| // for 32bit: |
| // lu12i.w $rd, %ld_hi20(sym) |
| // ori $rd, $rd, %got_lo12(sym) |
| // |
| // for 64bit: |
| // lu12i.w $rd, %ld_hi20(sym) |
| // ori $rd, $rd, %got_lo12(sym) |
| // lu32i.d $rd, %got64_lo20(sym) |
| // lu52i.d $rd, $rd, %got64_hi12(sym) |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU12I_W, |
| LoongArchMCExpr::VK_TLS_LD_HI20)); |
| Insts.push_back( |
| LoongArchAsmParser::Inst(LoongArch::ORI, LoongArchMCExpr::VK_GOT_LO12)); |
| |
| if (is64Bit()) { |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU32I_D, |
| LoongArchMCExpr::VK_GOT64_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU52I_D, |
| LoongArchMCExpr::VK_GOT64_HI12)); |
| } |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); |
| return; |
| } |
| |
| // expands to: |
| // pcalau12i $rd, %ld_pc_hi20(sym) |
| // addi.w/d $rd, $rd, %got_pc_lo12(sym) |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::PCALAU12I, |
| LoongArchMCExpr::VK_TLS_LD_PC_HI20)); |
| Insts.push_back( |
| LoongArchAsmParser::Inst(ADDI, LoongArchMCExpr::VK_GOT_PC_LO12)); |
| |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out, |
| /*RelaxHint=*/true); |
| } |
| |
| 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_TLS_LD_PC_HI20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADDI_D, |
| LoongArchMCExpr::VK_GOT_PC_LO12)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU32I_D, |
| LoongArchMCExpr::VK_GOT64_PC_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU52I_D, |
| LoongArchMCExpr::VK_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 |
| MCRegister DestReg = Inst.getOperand(0).getReg(); |
| const MCExpr *Symbol = Inst.getOperand(1).getExpr(); |
| InstSeq Insts; |
| unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W; |
| |
| if (getSTI().hasFeature(LoongArch::LaGlobalWithAbs)) { |
| // with feature: +la-glabal-with-abs |
| // for 32bit: |
| // lu12i.w $rd, %gd_hi20(sym) |
| // ori $rd, $rd, %got_lo12(sym) |
| // |
| // for 64bit: |
| // lu12i.w $rd, %gd_hi20(sym) |
| // ori $rd, $rd, %got_lo12(sym) |
| // lu32i.d $rd, %got64_lo20(sym) |
| // lu52i.d $rd, $rd, %got64_hi12(sym) |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU12I_W, |
| LoongArchMCExpr::VK_TLS_GD_HI20)); |
| Insts.push_back( |
| LoongArchAsmParser::Inst(LoongArch::ORI, LoongArchMCExpr::VK_GOT_LO12)); |
| |
| if (is64Bit()) { |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU32I_D, |
| LoongArchMCExpr::VK_GOT64_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU52I_D, |
| LoongArchMCExpr::VK_GOT64_HI12)); |
| } |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); |
| return; |
| } |
| |
| // expands to: |
| // pcalau12i $rd, %gd_pc_hi20(sym) |
| // addi.w/d $rd, $rd, %got_pc_lo12(sym) |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::PCALAU12I, |
| LoongArchMCExpr::VK_TLS_GD_PC_HI20)); |
| Insts.push_back( |
| LoongArchAsmParser::Inst(ADDI, LoongArchMCExpr::VK_GOT_PC_LO12)); |
| |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out, |
| /*RelaxHint=*/true); |
| } |
| |
| 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_TLS_GD_PC_HI20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADDI_D, |
| LoongArchMCExpr::VK_GOT_PC_LO12)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU32I_D, |
| LoongArchMCExpr::VK_GOT64_PC_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LU52I_D, |
| LoongArchMCExpr::VK_GOT64_PC_HI12)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D)); |
| |
| emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out); |
| } |
| |
| void LoongArchAsmParser::emitLoadAddressTLSDesc(MCInst &Inst, SMLoc IDLoc, |
| MCStreamer &Out) { |
| // la.tls.desc $rd, sym |
| MCRegister DestReg = Inst.getOperand(0).getReg(); |
| const MCExpr *Symbol = Inst.getOperand(1).getExpr(); |
| unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W; |
| unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W; |
| InstSeq Insts; |
| |
| if (getSTI().hasFeature(LoongArch::LaGlobalWithAbs)) { |
| // with feature: +la-glabal-with-abs |
| // for la32 expands to: |
| // lu12i.w $rd, %desc_hi20(sym) |
| // ori $rd, $rd, %desc_lo12(sym) |
| // ld.w $ra, $rd, %desc_ld(sym) |
| // jirl $ra, $ra, %desc_call(sym) |
| // |
| // for la64 expands to: |
| // lu12i.w $rd, %desc_hi20(sym) |
| // ori $rd, $rd, %desc_lo12(sym) |
| // lu32i.d $rd, %desc64_lo20(sym) |
| // lu52i.d $rd, $rd, %desc64_hi12(sym) |
| // ld.d $ra, $rd, %desc_ld(sym) |
| // jirl $ra, $ra, %desc_call(sym) |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::LU12I_W, LoongArchMCExpr::VK_TLS_DESC_HI20)); |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::ORI, LoongArchMCExpr::VK_TLS_DESC_LO12)); |
| |
| if (is64Bit()) { |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::LU32I_D, LoongArchMCExpr::VK_TLS_DESC64_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::LU52I_D, LoongArchMCExpr::VK_TLS_DESC64_HI12)); |
| } |
| |
| Insts.push_back( |
| LoongArchAsmParser::Inst(LD, LoongArchMCExpr::VK_TLS_DESC_LD)); |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::JIRL, LoongArchMCExpr::VK_TLS_DESC_CALL)); |
| |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out); |
| return; |
| } |
| |
| // expands to: |
| // pcalau12i $rd, %desc_pc_hi20(sym) |
| // addi.w/d $rd, $rd, %desc_pc_lo12(sym) |
| // ld.w/d $ra, $rd, %desc_ld(sym) |
| // jirl $ra, $ra, %desc_call(sym) |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::PCALAU12I, LoongArchMCExpr::VK_TLS_DESC_PC_HI20)); |
| Insts.push_back( |
| LoongArchAsmParser::Inst(ADDI, LoongArchMCExpr::VK_TLS_DESC_PC_LO12)); |
| Insts.push_back( |
| LoongArchAsmParser::Inst(LD, LoongArchMCExpr::VK_TLS_DESC_LD)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::JIRL, |
| LoongArchMCExpr::VK_TLS_DESC_CALL)); |
| |
| emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out, |
| /*RelaxHint=*/true); |
| } |
| |
| void LoongArchAsmParser::emitLoadAddressTLSDescLarge(MCInst &Inst, SMLoc IDLoc, |
| MCStreamer &Out) { |
| // la.tls.desc $rd, $rj, sym |
| // expands to: |
| // pcalau12i $rd, %desc_pc_hi20(sym) |
| // addi.d $rj, $r0, %desc_pc_lo12(sym) |
| // lu32i.d $rj, %desc64_pc_lo20(sym) |
| // lu52i.d $rj, $rj, %desc64_pc_hi12(sym) |
| // add.d $rd, $rd, $rj |
| // ld.w/d $ra, $rd, %desc_ld(sym) |
| // jirl $ra, $ra, %desc_call(sym) |
| 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_TLS_DESC_PC_HI20)); |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::ADDI_D, LoongArchMCExpr::VK_TLS_DESC_PC_LO12)); |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::LU32I_D, LoongArchMCExpr::VK_TLS_DESC64_PC_LO20)); |
| Insts.push_back(LoongArchAsmParser::Inst( |
| LoongArch::LU52I_D, LoongArchMCExpr::VK_TLS_DESC64_PC_HI12)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LD_D, |
| LoongArchMCExpr::VK_TLS_DESC_LD)); |
| Insts.push_back(LoongArchAsmParser::Inst(LoongArch::JIRL, |
| LoongArchMCExpr::VK_TLS_DESC_CALL)); |
| |
| 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)) { |
| switch (Inst.Opc) { |
| case LoongArch::LU12I_W: |
| Out.emitInstruction( |
| MCInstBuilder(Inst.Opc).addReg(DestReg).addImm(Inst.Imm), getSTI()); |
| break; |
| case LoongArch::ADDI_W: |
| case LoongArch::ORI: |
| case LoongArch::LU32I_D: |
| case LoongArch::LU52I_D: |
| Out.emitInstruction( |
| MCInstBuilder(Inst.Opc).addReg(DestReg).addReg(SrcReg).addImm( |
| Inst.Imm), |
| getSTI()); |
| break; |
| case LoongArch::BSTRINS_D: |
| Out.emitInstruction(MCInstBuilder(Inst.Opc) |
| .addReg(DestReg) |
| .addReg(SrcReg) |
| .addReg(SrcReg) |
| .addImm(Inst.Imm >> 32) |
| .addImm(Inst.Imm & 0xFF), |
| getSTI()); |
| break; |
| default: |
| llvm_unreachable("unexpected opcode generated by LoongArchMatInt"); |
| } |
| SrcReg = DestReg; |
| } |
| } |
| |
| void LoongArchAsmParser::emitFuncCall36(MCInst &Inst, SMLoc IDLoc, |
| MCStreamer &Out, bool IsTailCall) { |
| // call36 sym |
| // expands to: |
| // pcaddu18i $ra, %call36(sym) |
| // jirl $ra, $ra, 0 |
| // |
| // tail36 $rj, sym |
| // expands to: |
| // pcaddu18i $rj, %call36(sym) |
| // jirl $r0, $rj, 0 |
| MCRegister ScratchReg = |
| IsTailCall ? Inst.getOperand(0).getReg() : MCRegister(LoongArch::R1); |
| const MCExpr *Sym = |
| IsTailCall ? Inst.getOperand(1).getExpr() : Inst.getOperand(0).getExpr(); |
| const LoongArchMCExpr *LE = LoongArchMCExpr::create( |
| Sym, LoongArchMCExpr::VK_CALL36, getContext(), /*RelaxHint=*/true); |
| |
| Out.emitInstruction( |
| MCInstBuilder(LoongArch::PCADDU18I).addReg(ScratchReg).addExpr(LE), |
| getSTI()); |
| Out.emitInstruction( |
| MCInstBuilder(LoongArch::JIRL) |
| .addReg(IsTailCall ? MCRegister(LoongArch::R0) : ScratchReg) |
| .addReg(ScratchReg) |
| .addImm(0), |
| getSTI()); |
| } |
| |
| 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::PseudoLA_TLS_DESC: |
| emitLoadAddressTLSDesc(Inst, IDLoc, Out); |
| return false; |
| case LoongArch::PseudoLA_TLS_DESC_LARGE: |
| emitLoadAddressTLSDescLarge(Inst, IDLoc, Out); |
| return false; |
| case LoongArch::PseudoLI_W: |
| case LoongArch::PseudoLI_D: |
| emitLoadImm(Inst, IDLoc, Out); |
| return false; |
| case LoongArch::PseudoCALL36: |
| emitFuncCall36(Inst, IDLoc, Out, /*IsTailCall=*/false); |
| return false; |
| case LoongArch::PseudoTAIL36: |
| emitFuncCall36(Inst, IDLoc, Out, /*IsTailCall=*/true); |
| return false; |
| } |
| Out.emitInstruction(Inst, getSTI()); |
| return false; |
| } |
| |
| unsigned LoongArchAsmParser::checkTargetMatchPredicate(MCInst &Inst) { |
| unsigned Opc = Inst.getOpcode(); |
| const MCInstrDesc &MCID = MII.get(Opc); |
| switch (Opc) { |
| default: |
| if (LoongArchII::isSubjectToAMORdConstraint(MCID.TSFlags)) { |
| const bool IsAMCAS = LoongArchII::isAMCAS(MCID.TSFlags); |
| MCRegister Rd = Inst.getOperand(0).getReg(); |
| MCRegister Rk = Inst.getOperand(IsAMCAS ? 2 : 1).getReg(); |
| MCRegister Rj = Inst.getOperand(IsAMCAS ? 3 : 2).getReg(); |
| if ((Rd == Rk || Rd == Rj) && Rd != LoongArch::R0) |
| return Match_RequiresAMORdDifferRkRj; |
| } |
| break; |
| case LoongArch::PseudoLA_TLS_DESC: |
| case LoongArch::PseudoLA_TLS_DESC_LARGE: { |
| MCRegister Rd = Inst.getOperand(0).getReg(); |
| if (Rd != LoongArch::R4) |
| return Match_RequiresLAORdR4; |
| 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: { |
| MCRegister Rd = Inst.getOperand(0).getReg(); |
| MCRegister Rj = Inst.getOperand(1).getReg(); |
| if (Rd == Rj) |
| return Match_RequiresLAORdDifferRj; |
| break; |
| } |
| case LoongArch::CSRXCHG: |
| case LoongArch::GCSRXCHG: { |
| MCRegister 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, |
| 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_RequiresLAORdR4: |
| return Error(Operands[1]->getStartLoc(), "$rd must be $r4"); |
| 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_InvalidSImm20pcaddu18i: |
| return generateImmOutOfRangeError( |
| Operands, ErrorInfo, /*Lower=*/-(1 << 19), |
| /*Upper=*/(1 << 19) - 1, |
| "operand must be a symbol with modifier (e.g. %call36) or an integer " |
| "in the range"); |
| case Match_InvalidSImm20pcaddi: |
| return generateImmOutOfRangeError( |
| Operands, ErrorInfo, /*Lower=*/-(1 << 19), |
| /*Upper=*/(1 << 19) - 1, |
| "operand must be a symbol with modifier (e.g. %pcrel_20) 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_InvalidImm64: { |
| SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc(); |
| return Error(ErrorLoc, "operand must be a 64 bit immediate"); |
| } |
| case Match_InvalidBareSymbol: { |
| SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc(); |
| return Error(ErrorLoc, "operand must be a bare symbol name"); |
| } |
| case Match_InvalidTPRelAddSymbol: { |
| SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc(); |
| return Error(ErrorLoc, "operand must be a symbol with %le_add_r modifier"); |
| } |
| } |
| llvm_unreachable("Unknown match type detected!"); |
| } |
| |
| bool LoongArchAsmParser::parseDirectiveOption() { |
| MCAsmParser &Parser = getParser(); |
| // Get the option token. |
| AsmToken Tok = Parser.getTok(); |
| |
| // At the moment only identifiers are supported. |
| if (parseToken(AsmToken::Identifier, "expected identifier")) |
| return true; |
| |
| StringRef Option = Tok.getIdentifier(); |
| |
| if (Option == "push") { |
| if (Parser.parseEOL()) |
| return true; |
| |
| getTargetStreamer().emitDirectiveOptionPush(); |
| pushFeatureBits(); |
| return false; |
| } |
| |
| if (Option == "pop") { |
| SMLoc StartLoc = Parser.getTok().getLoc(); |
| if (Parser.parseEOL()) |
| return true; |
| |
| getTargetStreamer().emitDirectiveOptionPop(); |
| if (popFeatureBits()) |
| return Error(StartLoc, ".option pop with no .option push"); |
| |
| return false; |
| } |
| |
| if (Option == "relax") { |
| if (Parser.parseEOL()) |
| return true; |
| |
| getTargetStreamer().emitDirectiveOptionRelax(); |
| setFeatureBits(LoongArch::FeatureRelax, "relax"); |
| return false; |
| } |
| |
| if (Option == "norelax") { |
| if (Parser.parseEOL()) |
| return true; |
| |
| getTargetStreamer().emitDirectiveOptionNoRelax(); |
| clearFeatureBits(LoongArch::FeatureRelax, "relax"); |
| return false; |
| } |
| |
| // Unknown option. |
| Warning(Parser.getTok().getLoc(), |
| "unknown option, expected 'push', 'pop', 'relax' or 'norelax'"); |
| Parser.eatToEndOfStatement(); |
| return false; |
| } |
| |
| ParseStatus LoongArchAsmParser::parseDirective(AsmToken DirectiveID) { |
| if (DirectiveID.getString() == ".option") |
| return parseDirectiveOption(); |
| |
| return ParseStatus::NoMatch; |
| } |
| |
| extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchAsmParser() { |
| RegisterMCAsmParser<LoongArchAsmParser> X(getTheLoongArch32Target()); |
| RegisterMCAsmParser<LoongArchAsmParser> Y(getTheLoongArch64Target()); |
| } |