[llvm] a7ec7f6 - [LoongArch] Support parsing la* pseudo instructions
via llvm-commits
llvm-commits at lists.llvm.org
Sun Nov 20 23:34:29 PST 2022
Author: wanglei
Date: 2022-11-21T15:33:30+08:00
New Revision: a7ec7f6d773218f2fef81130f1acc700dcdb3966
URL: https://github.com/llvm/llvm-project/commit/a7ec7f6d773218f2fef81130f1acc700dcdb3966
DIFF: https://github.com/llvm/llvm-project/commit/a7ec7f6d773218f2fef81130f1acc700dcdb3966.diff
LOG: [LoongArch] Support parsing la* pseudo instructions
This patch makes `IAS` compatible with `GAS`. It accepts `la*` pseudo
instructions, and expands `la{,.local,.global}` into different
instructions according to different features.
```
Default:
la = la.global = la.got
la.local = la.pcrel
With feature "+la-global-with-pcrel":
la = la.global = la.pcrel
With feature "+la-global-with-abs":
la = la.global = la.abs
With feature "+la-local-with-abs":
la.local = la.abs
With features "+la-global-with-pcrel,+la-global-with-abs"(disorder):
la = la.global = la.pcrel
```
Note: To keep consistent with `GAS` behavior, the "la" can only have
one register operand.
Reviewed By: SixWeining
Differential Revision: https://reviews.llvm.org/D138021
Added:
llvm/test/MC/LoongArch/Macros/aliases-la-bad.s
llvm/test/MC/LoongArch/Macros/aliases-la.s
llvm/test/MC/LoongArch/Macros/macros-la-bad.s
llvm/test/MC/LoongArch/Macros/macros-la.s
Modified:
llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
llvm/lib/Target/LoongArch/LoongArch.td
llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
llvm/lib/Target/LoongArch/LoongArchSubtarget.h
Removed:
################################################################################
diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
index 77f431bafa2b2..b33be3ee32acc 100644
--- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
+++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
@@ -11,6 +11,7 @@
#include "MCTargetDesc/LoongArchMCTargetDesc.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"
@@ -29,6 +30,16 @@ using namespace llvm;
namespace {
class LoongArchAsmParser : public MCTargetAsmParser {
SMLoc getLoc() const { return getParser().getTok().getLoc(); }
+ bool is64Bit() const { return getSTI().hasFeature(LoongArch::Feature64Bit); }
+
+ struct Inst {
+ unsigned Opc;
+ LoongArchMCExpr::VariantKind VK;
+ Inst(unsigned Opc,
+ LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None)
+ : Opc(Opc), VK(VK) {}
+ };
+ using InstSeq = SmallVector<Inst>;
/// Parse a register as used in CFI directives.
bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override;
@@ -69,12 +80,50 @@ class LoongArchAsmParser : public MCTargetAsmParser {
bool parseOperand(OperandVector &Operands, StringRef Mnemonic);
+ // Helper to emit the sequence of instructions generated by the
+ // "emitLoadAddress*" functions.
+ void emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg,
+ const MCExpr *Symbol, SmallVectorImpl<Inst> &Insts,
+ SMLoc IDLoc, MCStreamer &Out);
+
+ // Helper to emit pseudo instruction "la.abs $rd, sym".
+ void emitLoadAddressAbs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+
+ // Helper to emit pseudo instruction "la.pcrel $rd, sym".
+ void emitLoadAddressPcrel(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+ // Helper to emit pseudo instruction "la.pcrel $rd, $rj, sym".
+ void emitLoadAddressPcrelLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+
+ // Helper to emit pseudo instruction "la.got $rd, sym".
+ void emitLoadAddressGot(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+ // Helper to emit pseudo instruction "la.got $rd, $rj, sym".
+ void emitLoadAddressGotLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+
+ // Helper to emit pseudo instruction "la.tls.le $rd, sym".
+ void emitLoadAddressTLSLE(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+
+ // Helper to emit pseudo instruction "la.tls.ie $rd, sym".
+ void emitLoadAddressTLSIE(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+ // Helper to emit pseudo instruction "la.tls.ie $rd, $rj, sym".
+ void emitLoadAddressTLSIELarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+
+ // Helper to emit pseudo instruction "la.tls.ld $rd, sym".
+ void emitLoadAddressTLSLD(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+ // Helper to emit pseudo instruction "la.tls.ld $rd, $rj, sym".
+ void emitLoadAddressTLSLDLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+
+ // Helper to emit pseudo instruction "la.tls.gd $rd, sym".
+ void emitLoadAddressTLSGD(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+ // Helper to emit pseudo instruction "la.tls.gd $rd, $rj, sym".
+ void emitLoadAddressTLSGDLarge(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+
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
@@ -165,6 +214,16 @@ class LoongArchOperand : public MCParsedAsmOperand {
VK == LoongArchMCExpr::VK_LoongArch_None;
}
+ bool isBareSymbol() const {
+ int64_t Imm;
+ LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
+ // Must be of 'immediate' type but not a constant.
+ if (!isImm() || evaluateConstantImm(getImm(), Imm, VK))
+ return false;
+ return LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
+ VK == LoongArchMCExpr::VK_LoongArch_None;
+ }
+
bool isUImm2() const { return isUImm<2>(); }
bool isUImm2plus1() const { return isUImm<2, 1>(); }
bool isUImm3() const { return isUImm<3>(); }
@@ -659,10 +718,392 @@ bool LoongArchAsmParser::ParseInstruction(ParseInstructionInfo &Info,
return Error(Loc, "unexpected token");
}
+void LoongArchAsmParser::emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg,
+ const MCExpr *Symbol,
+ SmallVectorImpl<Inst> &Insts,
+ SMLoc IDLoc, MCStreamer &Out) {
+ MCContext &Ctx = getContext();
+ for (LoongArchAsmParser::Inst &Inst : Insts) {
+ unsigned Opc = Inst.Opc;
+ LoongArchMCExpr::VariantKind VK = Inst.VK;
+ const LoongArchMCExpr *LE = LoongArchMCExpr::create(Symbol, VK, Ctx);
+ switch (Opc) {
+ default:
+ llvm_unreachable("unexpected opcode");
+ case LoongArch::PCALAU12I:
+ case LoongArch::LU12I_W:
+ Out.emitInstruction(MCInstBuilder(Opc).addReg(DestReg).addExpr(LE),
+ getSTI());
+ break;
+ case LoongArch::ORI:
+ case LoongArch::ADDI_W:
+ case LoongArch::LD_W:
+ case LoongArch::LD_D: {
+ if (VK == LoongArchMCExpr::VK_LoongArch_None) {
+ Out.emitInstruction(
+ MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addImm(0),
+ getSTI());
+ continue;
+ }
+ Out.emitInstruction(
+ MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addExpr(LE),
+ getSTI());
+ break;
+ }
+ case LoongArch::LU32I_D:
+ Out.emitInstruction(MCInstBuilder(Opc)
+ .addReg(DestReg == TmpReg ? DestReg : TmpReg)
+ .addReg(DestReg == TmpReg ? DestReg : TmpReg)
+ .addExpr(LE),
+ getSTI());
+ break;
+ case LoongArch::LU52I_D:
+ Out.emitInstruction(
+ MCInstBuilder(Opc).addReg(TmpReg).addReg(TmpReg).addExpr(LE),
+ getSTI());
+ break;
+ case LoongArch::ADDI_D:
+ Out.emitInstruction(
+ MCInstBuilder(Opc)
+ .addReg(TmpReg)
+ .addReg(DestReg == TmpReg ? TmpReg : LoongArch::R0)
+ .addExpr(LE),
+ getSTI());
+ break;
+ case LoongArch::ADD_D:
+ case LoongArch::LDX_D:
+ Out.emitInstruction(
+ MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addReg(TmpReg),
+ getSTI());
+ break;
+ }
+ }
+}
+
+void LoongArchAsmParser::emitLoadAddressAbs(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.abs $rd, sym
+ // expands to:
+ // lu12i.w $rd, %abs_hi20(sym)
+ // ori $rd, $rd, %abs_lo12(sym)
+ //
+ // for 64bit appends:
+ // lu32i.d $rd, %abs64_lo20(sym)
+ // lu52i.d $rd, $rd, %abs64_hi12(sym)
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ const MCExpr *Symbol = Inst.getOpcode() == LoongArch::PseudoLA_ABS
+ ? Inst.getOperand(1).getExpr()
+ : Inst.getOperand(2).getExpr();
+ InstSeq Insts;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU12I_W, LoongArchMCExpr::VK_LoongArch_ABS_HI20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::ORI, LoongArchMCExpr::VK_LoongArch_ABS_LO12));
+
+ if (is64Bit()) {
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_ABS64_LO20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_ABS64_HI12));
+ }
+
+ emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
+}
+
+void LoongArchAsmParser::emitLoadAddressPcrel(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.pcrel $rd, sym
+ // expands to:
+ // pcalau12i $rd, %pc_hi20(sym)
+ // addi.w/d $rd, rd, %pc_lo12(sym)
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ const MCExpr *Symbol = Inst.getOperand(1).getExpr();
+ InstSeq Insts;
+ unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_PCALA_HI20));
+ Insts.push_back(
+ LoongArchAsmParser::Inst(ADDI, LoongArchMCExpr::VK_LoongArch_PCALA_LO12));
+
+ emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
+}
+
+void LoongArchAsmParser::emitLoadAddressPcrelLarge(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.pcrel $rd, $rj, sym
+ // expands to:
+ // pcalau12i $rd, %pc_hi20(sym)
+ // addi.d $rj, $r0, %pc_lo12(sym)
+ // lu32i.d $rj, %pc64_lo20(sym)
+ // lu52i.d $rj, $rj, %pc64_hi12(sym)
+ // add.d $rd, $rd, $rj
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ MCRegister TmpReg = Inst.getOperand(1).getReg();
+ const MCExpr *Symbol = Inst.getOperand(2).getExpr();
+ InstSeq Insts;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_PCALA_HI20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_PCALA_LO12));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_PCALA64_LO20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_PCALA64_HI12));
+ Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D));
+
+ emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
+}
+
+void LoongArchAsmParser::emitLoadAddressGot(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.got $rd, sym
+ // expands to:
+ // pcalau12i $rd, %got_pc_hi20(sym)
+ // ld.w/d $rd, $rd, %got_pc_lo12(sym)
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ const MCExpr *Symbol = Inst.getOperand(1).getExpr();
+ InstSeq Insts;
+ unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_GOT_PC_HI20));
+ Insts.push_back(
+ LoongArchAsmParser::Inst(LD, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
+
+ emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
+}
+
+void LoongArchAsmParser::emitLoadAddressGotLarge(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.got $rd, $rj, sym
+ // expands to:
+ // pcalau12i $rd, %got_pc_hi20(sym)
+ // addi.d $rj, $r0, %got_pc_lo12(sym)
+ // lu32i.d $rj, %got64_pc_lo20(sym)
+ // lu52i.d $rj, $rj, %got64_pc_hi12(sym)
+ // ldx.d $rd, $rd, $rj
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ MCRegister TmpReg = Inst.getOperand(1).getReg();
+ const MCExpr *Symbol = Inst.getOperand(2).getExpr();
+ InstSeq Insts;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_GOT_PC_HI20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12));
+ Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LDX_D));
+
+ emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
+}
+
+void LoongArchAsmParser::emitLoadAddressTLSLE(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.tls.le $rd, sym
+ // expands to:
+ // lu12i.w $rd, %le_hi20(sym)
+ // ori $rd, $rd, %le_lo12(sym)
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ const MCExpr *Symbol = Inst.getOperand(1).getExpr();
+ InstSeq Insts;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU12I_W, LoongArchMCExpr::VK_LoongArch_TLS_LE_HI20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::ORI, LoongArchMCExpr::VK_LoongArch_TLS_LE_LO12));
+
+ emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
+}
+
+void LoongArchAsmParser::emitLoadAddressTLSIE(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.tls.ie $rd, sym
+ // expands to:
+ // pcalau12i $rd, %ie_pc_hi20(sym)
+ // ld.w/d $rd, $rd, %ie_pc_lo12(sym)
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ const MCExpr *Symbol = Inst.getOperand(1).getExpr();
+ InstSeq Insts;
+ unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_HI20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LD, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12));
+
+ emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
+}
+
+void LoongArchAsmParser::emitLoadAddressTLSIELarge(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.tls.ie $rd, $rj, sym
+ // expands to:
+ // pcalau12i $rd, %ie_pc_hi20(sym)
+ // addi.d $rj, $r0, %ie_pc_lo12(sym)
+ // lu32i.d $rj, %ie64_pc_lo20(sym)
+ // lu52i.d $rj, $rj, %ie64_pc_hi12(sym)
+ // ldx.d $rd, $rd, $rj
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ MCRegister TmpReg = Inst.getOperand(1).getReg();
+ const MCExpr *Symbol = Inst.getOperand(2).getExpr();
+ InstSeq Insts;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_HI20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_TLS_IE_PC_LO12));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_LO20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_TLS_IE64_PC_HI12));
+ Insts.push_back(LoongArchAsmParser::Inst(LoongArch::LDX_D));
+
+ emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
+}
+
+void LoongArchAsmParser::emitLoadAddressTLSLD(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.tls.ld $rd, sym
+ // expands to:
+ // pcalau12i $rd, %ld_pc_hi20(sym)
+ // addi.w/d $rd, $rd, %got_pc_lo12(sym)
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ const MCExpr *Symbol = Inst.getOperand(1).getExpr();
+ InstSeq Insts;
+ unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_LD_PC_HI20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ ADDI, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
+
+ emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
+}
+
+void LoongArchAsmParser::emitLoadAddressTLSLDLarge(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.tls.ld $rd, $rj, sym
+ // expands to:
+ // pcalau12i $rd, %ld_pc_hi20(sym)
+ // addi.d $rj, $r0, %got_pc_lo12(sym)
+ // lu32i.d $rj, %got64_pc_lo20(sym)
+ // lu52i.d $rj, $rj, %got64_pc_hi12(sym)
+ // add.d $rd, $rd, $rj
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ MCRegister TmpReg = Inst.getOperand(1).getReg();
+ const MCExpr *Symbol = Inst.getOperand(2).getExpr();
+ InstSeq Insts;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_LD_PC_HI20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12));
+ Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D));
+
+ emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
+}
+
+void LoongArchAsmParser::emitLoadAddressTLSGD(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.tls.gd $rd, sym
+ // expands to:
+ // pcalau12i $rd, %gd_pc_hi20(sym)
+ // addi.w/d $rd, $rd, %got_pc_lo12(sym)
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ const MCExpr *Symbol = Inst.getOperand(1).getExpr();
+ InstSeq Insts;
+ unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ ADDI, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
+
+ emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
+}
+
+void LoongArchAsmParser::emitLoadAddressTLSGDLarge(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // la.tls.gd $rd, $rj, sym
+ // expands to:
+ // pcalau12i $rd, %gd_pc_hi20(sym)
+ // addi.d $rj, $r0, %got_pc_lo12(sym)
+ // lu32i.d $rj, %got64_pc_lo20(sym)
+ // lu52i.d $rj, $rj, %got64_pc_hi12(sym)
+ // add.d $rd, $rd, $rj
+ MCRegister DestReg = Inst.getOperand(0).getReg();
+ MCRegister TmpReg = Inst.getOperand(1).getReg();
+ const MCExpr *Symbol = Inst.getOperand(2).getExpr();
+ InstSeq Insts;
+
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::PCALAU12I, LoongArchMCExpr::VK_LoongArch_TLS_GD_PC_HI20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::ADDI_D, LoongArchMCExpr::VK_LoongArch_GOT_PC_LO12));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU32I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_LO20));
+ Insts.push_back(LoongArchAsmParser::Inst(
+ LoongArch::LU52I_D, LoongArchMCExpr::VK_LoongArch_GOT64_PC_HI12));
+ Insts.push_back(LoongArchAsmParser::Inst(LoongArch::ADD_D));
+
+ emitLAInstSeq(DestReg, TmpReg, Symbol, Insts, IDLoc, Out);
+}
+
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;
+ }
Out.emitInstruction(Inst, getSTI());
return false;
}
@@ -679,6 +1120,17 @@ unsigned LoongArchAsmParser::checkTargetMatchPredicate(MCInst &Inst) {
return Match_RequiresAMORdDifferRkRj;
}
break;
+ case LoongArch::PseudoLA_PCREL_LARGE:
+ case LoongArch::PseudoLA_GOT_LARGE:
+ case LoongArch::PseudoLA_TLS_IE_LARGE:
+ case LoongArch::PseudoLA_TLS_LD_LARGE:
+ case LoongArch::PseudoLA_TLS_GD_LARGE: {
+ unsigned Rd = Inst.getOperand(0).getReg();
+ unsigned Rj = Inst.getOperand(1).getReg();
+ if (Rd == Rj)
+ return Match_RequiresLAORdDifferRj;
+ break;
+ }
case LoongArch::CSRXCHG: {
unsigned Rj = Inst.getOperand(2).getReg();
if (Rj == LoongArch::R0 || Rj == LoongArch::R1)
@@ -803,6 +1255,8 @@ bool LoongArchAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
case Match_RequiresAMORdDifferRkRj:
return Error(Operands[1]->getStartLoc(),
"$rd must be
diff erent from both $rk and $rj");
+ case Match_RequiresLAORdDifferRj:
+ return Error(Operands[1]->getStartLoc(), "$rd must be
diff erent from $rj");
case Match_InvalidUImm2:
return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
/*Upper=*/(1 << 2) - 1);
@@ -888,6 +1342,10 @@ bool LoongArchAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
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_InvalidBareSymbol: {
+ SMLoc ErrorLoc = ((LoongArchOperand &)*Operands[ErrorInfo]).getStartLoc();
+ return Error(ErrorLoc, "operand must be a bare symbol name");
+ }
}
llvm_unreachable("Unknown match type detected!");
}
diff --git a/llvm/lib/Target/LoongArch/LoongArch.td b/llvm/lib/Target/LoongArch/LoongArch.td
index 07b347b056361..dd85b11ac9ec2 100644
--- a/llvm/lib/Target/LoongArch/LoongArch.td
+++ b/llvm/lib/Target/LoongArch/LoongArch.td
@@ -85,6 +85,33 @@ def HasExtLBT
AssemblerPredicate<(all_of FeatureExtLBT),
"'LBT' (Loongson Binary Translation Extension)">;
+// Expand la.global as la.pcrel
+def LaGlobalWithPcrel
+ : SubtargetFeature<"la-global-with-pcrel", "HasLaGlobalWithPcrel", "true",
+ "Expand la.global as la.pcrel">;
+def HasLaGlobalWithPcrel
+ : Predicate<"Subtarget->hasLaGlobalWithPcrel()">,
+ AssemblerPredicate<(all_of LaGlobalWithPcrel),
+ "Expand la.global as la.pcrel">;
+
+// Expand la.global as la.abs
+def LaGlobalWithAbs
+ : SubtargetFeature<"la-global-with-abs", "HasLaGlobalWithAbs", "true",
+ "Expand la.global as la.abs">;
+def HasLaGlobalWithAbs
+ : Predicate<"Subtarget->hasLaGlobalWithAbs()">,
+ AssemblerPredicate<(all_of LaGlobalWithAbs),
+ "Expand la.global as la.abs">;
+
+// Expand la.local as la.abs
+def LaLocalWithAbs
+ : SubtargetFeature<"la-local-with-abs", "HasLaLocalWithAbs", "true",
+ "Expand la.local as la.abs">;
+def HasLaLocalWithAbs
+ : Predicate<"Subtarget->hasLaLocalWithAbs()">,
+ AssemblerPredicate<(all_of LaLocalWithAbs),
+ "Expand la.local as la.abs">;
+
//===----------------------------------------------------------------------===//
// Registers, instruction descriptions ...
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
index 79b2b482824ca..71bf6b53e42e8 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
@@ -249,6 +249,18 @@ def simm26_symbol : Operand<GRLenVT> {
let DecoderMethod = "decodeSImmOperand<26, 2>";
}
+def BareSymbol : AsmOperandClass {
+ let Name = "BareSymbol";
+ let RenderMethod = "addImmOperands";
+ let DiagnosticType = "InvalidBareSymbol";
+ let ParserMethod = "parseImmediate";
+}
+
+// A bare symbol used in "PseudoLA_*" instructions.
+def bare_symbol : Operand<GRLenVT> {
+ let ParserMatchClass = BareSymbol;
+}
+
// Standalone (codegen-only) immleaf patterns.
// A 12-bit signed immediate plus one where the imm range will be [-2047, 2048].
@@ -985,23 +997,102 @@ def PseudoJIRL_TAIL : Pseudo<(outs), (ins GPR:$rj, simm16_lsl2:$imm16)>,
PseudoInstExpansion<(JIRL R0, GPR:$rj,
simm16_lsl2:$imm16)>;
-let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
-def PseudoLA_PCREL : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>;
-
-let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in
-def PseudoLA_GOT : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>;
-
-let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in
-def PseudoLA_TLS_LE : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>;
-
-let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in
-def PseudoLA_TLS_IE : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>;
-
-let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in
-def PseudoLA_TLS_LD : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>;
+/// Load address (la*) macro instructions.
+
+// Define isCodeGenOnly = 0 to expose them to tablegened assembly parser.
+let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0,
+ isAsmParserOnly = 1 in {
+def PseudoLA_ABS : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
+ "la.abs", "$dst, $src">;
+def PseudoLA_ABS_LARGE : Pseudo<(outs GPR:$dst),
+ (ins GPR:$tmp, bare_symbol:$src), [],
+ "la.abs", "$dst, $src">;
+def PseudoLA_PCREL : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
+ "la.pcrel", "$dst, $src">;
+def PseudoLA_PCREL_LARGE : Pseudo<(outs GPR:$dst),
+ (ins GPR:$tmp, bare_symbol:$src), [],
+ "la.pcrel", "$dst, $tmp, $src">,
+ Requires<[IsLA64]>;
+def PseudoLA_TLS_LE : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
+ "la.tls.le", "$dst, $src">;
+}
+let hasSideEffects = 0, mayLoad = 1, mayStore = 0, isCodeGenOnly = 0,
+ isAsmParserOnly = 1 in {
+def PseudoLA_GOT : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
+ "la.got", "$dst, $src">;
+def PseudoLA_GOT_LARGE : Pseudo<(outs GPR:$dst),
+ (ins GPR:$tmp, bare_symbol:$src), [],
+ "la.got", "$dst, $tmp, $src">,
+ Requires<[IsLA64]>;
+def PseudoLA_TLS_IE : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
+ "la.tls.ie", "$dst, $src">;
+def PseudoLA_TLS_IE_LARGE : Pseudo<(outs GPR:$dst),
+ (ins GPR:$tmp, bare_symbol:$src), [],
+ "la.tls.ie", "$dst, $tmp, $src">,
+ Requires<[IsLA64]>;
+def PseudoLA_TLS_LD : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
+ "la.tls.ld", "$dst, $src">;
+def PseudoLA_TLS_LD_LARGE : Pseudo<(outs GPR:$dst),
+ (ins GPR:$tmp, bare_symbol:$src), [],
+ "la.tls.ld", "$dst, $tmp, $src">,
+ Requires<[IsLA64]>;
+def PseudoLA_TLS_GD : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
+ "la.tls.gd", "$dst, $src">;
+def PseudoLA_TLS_GD_LARGE : Pseudo<(outs GPR:$dst),
+ (ins GPR:$tmp, bare_symbol:$src), [],
+ "la.tls.gd", "$dst, $tmp, $src">,
+ Requires<[IsLA64]>;
+}
-let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in
-def PseudoLA_TLS_GD : Pseudo<(outs GPR:$dst), (ins grlenimm:$src)>;
+// Load address inst alias: "la", "la.global" and "la.local".
+// Default:
+// la = la.global = la.got
+// la.local = la.pcrel
+// With feature "+la-global-with-pcrel":
+// la = la.global = la.pcrel
+// With feature "+la-global-with-abs":
+// la = la.global = la.abs
+// With feature "+la-local-with-abs":
+// la.local = la.abs
+// With features "+la-global-with-pcrel,+la-global-with-abs"(disorder):
+// la = la.global = la.pcrel
+// Note: To keep consistent with gnu-as behavior, the "la" can only have one
+// register operand.
+def : InstAlias<"la $dst, $src", (PseudoLA_GOT GPR:$dst, bare_symbol:$src)>;
+def : InstAlias<"la.global $dst, $src",
+ (PseudoLA_GOT GPR:$dst, bare_symbol:$src)>;
+def : InstAlias<"la.global $dst, $tmp, $src",
+ (PseudoLA_GOT_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>;
+def : InstAlias<"la.local $dst, $src",
+ (PseudoLA_PCREL GPR:$dst, bare_symbol:$src)>;
+def : InstAlias<"la.local $dst, $tmp, $src",
+ (PseudoLA_PCREL_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>;
+
+// Note: Keep HasLaGlobalWithPcrel before HasLaGlobalWithAbs to ensure
+// "la-global-with-pcrel" takes effect when bose "la-global-with-pcrel" and
+// "la-global-with-abs" are enabled.
+let Predicates = [HasLaGlobalWithPcrel] in {
+def : InstAlias<"la $dst, $src", (PseudoLA_PCREL GPR:$dst, bare_symbol:$src)>;
+def : InstAlias<"la.global $dst, $src",
+ (PseudoLA_PCREL GPR:$dst, bare_symbol:$src)>;
+def : InstAlias<"la.global $dst, $tmp, $src",
+ (PseudoLA_PCREL_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>;
+} // Predicates = [HasLaGlobalWithPcrel]
+
+let Predicates = [HasLaGlobalWithAbs] in {
+def : InstAlias<"la $dst, $src", (PseudoLA_ABS GPR:$dst, bare_symbol:$src)>;
+def : InstAlias<"la.global $dst, $src",
+ (PseudoLA_ABS GPR:$dst, bare_symbol:$src)>;
+def : InstAlias<"la.global $dst, $tmp, $src",
+ (PseudoLA_ABS_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>;
+} // Predicates = [HasLaGlobalWithAbs]
+
+let Predicates = [HasLaLocalWithAbs] in {
+def : InstAlias<"la.local $dst, $src",
+ (PseudoLA_ABS GPR:$dst, bare_symbol:$src)>;
+def : InstAlias<"la.local $dst, $tmp, $src",
+ (PseudoLA_ABS_LARGE GPR:$dst, GPR:$tmp, bare_symbol:$src)>;
+} // Predicates = [HasLaLocalWithAbs]
/// BSTRINS and BSTRPICK
diff --git a/llvm/lib/Target/LoongArch/LoongArchSubtarget.h b/llvm/lib/Target/LoongArch/LoongArchSubtarget.h
index b7776d3c8f411..4b049bbf59ead 100644
--- a/llvm/lib/Target/LoongArch/LoongArchSubtarget.h
+++ b/llvm/lib/Target/LoongArch/LoongArchSubtarget.h
@@ -38,6 +38,9 @@ class LoongArchSubtarget : public LoongArchGenSubtargetInfo {
bool HasExtLASX = false;
bool HasExtLVZ = false;
bool HasExtLBT = false;
+ bool HasLaGlobalWithPcrel = false;
+ bool HasLaGlobalWithAbs = false;
+ bool HasLaLocalWithAbs = false;
unsigned GRLen = 32;
MVT GRLenVT = MVT::i32;
LoongArchABI::ABI TargetABI = LoongArchABI::ABI_Unknown;
@@ -84,6 +87,9 @@ class LoongArchSubtarget : public LoongArchGenSubtargetInfo {
bool hasExtLASX() const { return HasExtLASX; }
bool hasExtLVZ() const { return HasExtLVZ; }
bool hasExtLBT() const { return HasExtLBT; }
+ bool hasLaGlobalWithPcrel() const { return HasLaGlobalWithPcrel; }
+ bool hasLaGlobalWithAbs() const { return HasLaGlobalWithAbs; }
+ bool hasLaLocalWithAbs() const { return HasLaLocalWithAbs; }
MVT getGRLenVT() const { return GRLenVT; }
unsigned getGRLen() const { return GRLen; }
LoongArchABI::ABI getTargetABI() const { return TargetABI; }
diff --git a/llvm/test/MC/LoongArch/Macros/aliases-la-bad.s b/llvm/test/MC/LoongArch/Macros/aliases-la-bad.s
new file mode 100644
index 0000000000000..d371ec23935f4
--- /dev/null
+++ b/llvm/test/MC/LoongArch/Macros/aliases-la-bad.s
@@ -0,0 +1,10 @@
+# RUN: not llvm-mc --triple=loongarch64 %s 2>&1 | FileCheck %s
+
+la $a0, $a1, sym
+# CHECK: :[[#@LINE-1]]:10: error: operand must be a bare symbol name
+
+la $a0, 1
+# CHECK: :[[#@LINE-1]]:9: error: operand must be a bare symbol name
+
+la.global $a0, $a1, 1
+# CHECK: :[[#@LINE-1]]:21: error: operand must be a bare symbol name
diff --git a/llvm/test/MC/LoongArch/Macros/aliases-la.s b/llvm/test/MC/LoongArch/Macros/aliases-la.s
new file mode 100644
index 0000000000000..dd5a4d474e001
--- /dev/null
+++ b/llvm/test/MC/LoongArch/Macros/aliases-la.s
@@ -0,0 +1,74 @@
+## Test la/la.global/la.local expand to
diff erent instructions sequence under
+##
diff erent features.
+
+# RUN: llvm-mc --triple=loongarch64 %s \
+# RUN: | FileCheck %s --check-prefix=NORMAL
+# RUN: llvm-mc --triple=loongarch64 --mattr=+la-global-with-pcrel < %s \
+# RUN: | FileCheck %s --check-prefix=GTOPCR
+# RUN: llvm-mc --triple=loongarch64 --mattr=+la-global-with-abs < %s \
+# RUN: | FileCheck %s --check-prefix=GTOABS
+# RUN: llvm-mc --triple=loongarch64 --mattr=+la-local-with-abs < %s \
+# RUN: | FileCheck %s --check-prefix=LTOABS
+
+la $a0, sym
+# NORMAL: pcalau12i $a0, %got_pc_hi20(sym)
+# NORMAL-NEXT: ld.d $a0, $a0, %got_pc_lo12(sym)
+
+# GTOPCR: pcalau12i $a0, %pc_hi20(sym)
+# GTOPCR-NEXT: addi.d $a0, $a0, %pc_lo12(sym)
+
+# GTOABS: lu12i.w $a0, %abs_hi20(sym)
+# GTOABS-NEXT: ori $a0, $a0, %abs_lo12(sym)
+# GTOABS-NEXT: lu32i.d $a0, %abs64_lo20(sym)
+# GTOABS-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym)
+
+la.global $a0, sym_global
+# NORMAL: pcalau12i $a0, %got_pc_hi20(sym_global)
+# NORMAL-NEXT: ld.d $a0, $a0, %got_pc_lo12(sym_global)
+
+# GTOPCR: pcalau12i $a0, %pc_hi20(sym_global)
+# GTOPCR-NEXT: addi.d $a0, $a0, %pc_lo12(sym_global)
+
+# GTOABS: lu12i.w $a0, %abs_hi20(sym_global)
+# GTOABS-NEXT: ori $a0, $a0, %abs_lo12(sym_global)
+# GTOABS-NEXT: lu32i.d $a0, %abs64_lo20(sym_global)
+# GTOABS-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym_global)
+
+la.global $a0, $a1, sym_global_large
+# NORMAL: pcalau12i $a0, %got_pc_hi20(sym_global_large)
+# NORMAL-NEXT: addi.d $a1, $zero, %got_pc_lo12(sym_global_large)
+# NORMAL-NEXT: lu32i.d $a1, %got64_pc_lo20(sym_global_large)
+# NORMAL-NEXT: lu52i.d $a1, $a1, %got64_pc_hi12(sym_global_large)
+# NORMAL-NEXT: ldx.d $a0, $a0, $a1
+
+# GTOPCR: pcalau12i $a0, %pc_hi20(sym_global_large)
+# GTOPCR-NEXT: addi.d $a1, $zero, %pc_lo12(sym_global_large)
+# GTOPCR-NEXT: lu32i.d $a1, %pc64_lo20(sym_global_large)
+# GTOPCR-NEXT: lu52i.d $a1, $a1, %pc64_hi12(sym_global_large)
+# GTOPCR-NEXT: add.d $a0, $a0, $a1
+
+# GTOABS: lu12i.w $a0, %abs_hi20(sym_global_large)
+# GTOABS-NEXT: ori $a0, $a0, %abs_lo12(sym_global_large)
+# GTOABS-NEXT: lu32i.d $a0, %abs64_lo20(sym_global_large)
+# GTOABS-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym_global_large)
+
+la.local $a0, sym_local
+# NORMAL: pcalau12i $a0, %pc_hi20(sym_local)
+# NORMAL-NEXT: addi.d $a0, $a0, %pc_lo12(sym_local)
+
+# LTOABS: lu12i.w $a0, %abs_hi20(sym_local)
+# LTOABS-NEXT: ori $a0, $a0, %abs_lo12(sym_local)
+# LTOABS-NEXT: lu32i.d $a0, %abs64_lo20(sym_local)
+# LTOABS-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym_local)
+
+la.local $a0, $a1, sym_local_large
+# NORMAL: pcalau12i $a0, %pc_hi20(sym_local_large)
+# NORMAL-NEXT: addi.d $a1, $zero, %pc_lo12(sym_local_large)
+# NORMAL-NEXT: lu32i.d $a1, %pc64_lo20(sym_local_large)
+# NORMAL-NEXT: lu52i.d $a1, $a1, %pc64_hi12(sym_local_large)
+# NORMAL-NEXT: add.d $a0, $a0, $a1
+
+# LTOABS: lu12i.w $a0, %abs_hi20(sym_local_large)
+# LTOABS-NEXT: ori $a0, $a0, %abs_lo12(sym_local_large)
+# LTOABS-NEXT: lu32i.d $a0, %abs64_lo20(sym_local_large)
+# LTOABS-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym_local_large)
diff --git a/llvm/test/MC/LoongArch/Macros/macros-la-bad.s b/llvm/test/MC/LoongArch/Macros/macros-la-bad.s
new file mode 100644
index 0000000000000..03c6355e40b09
--- /dev/null
+++ b/llvm/test/MC/LoongArch/Macros/macros-la-bad.s
@@ -0,0 +1,13 @@
+# RUN: not llvm-mc --triple=loongarch64 %s 2>&1 | FileCheck %s
+
+la.got $a0, 1
+# CHECK: :[[#@LINE-1]]:13: error: operand must be a bare symbol name
+
+la.pcrel $a0, $a1, 1
+# CHECK: :[[#@LINE-1]]:20: error: operand must be a bare symbol name
+
+la.abs $a0, $a1, sym
+# CHECK: :[[#@LINE-1]]:14: error: operand must be a bare symbol name
+
+la.pcrel $a0, $a0, sym
+# CHECK: :[[#@LINE-1]]:11: error: $rd must be
diff erent from $rj
diff --git a/llvm/test/MC/LoongArch/Macros/macros-la.s b/llvm/test/MC/LoongArch/Macros/macros-la.s
new file mode 100644
index 0000000000000..924e4326b8e5d
--- /dev/null
+++ b/llvm/test/MC/LoongArch/Macros/macros-la.s
@@ -0,0 +1,66 @@
+# RUN: llvm-mc --triple=loongarch64 %s | FileCheck %s
+
+la.abs $a0, sym_abs
+# CHECK: lu12i.w $a0, %abs_hi20(sym_abs)
+# CHECK-NEXT: ori $a0, $a0, %abs_lo12(sym_abs)
+# CHECK-NEXT: lu32i.d $a0, %abs64_lo20(sym_abs)
+# CHECK-NEXT: lu52i.d $a0, $a0, %abs64_hi12(sym_abs)
+
+la.pcrel $a0, sym_pcrel
+# CHECK: pcalau12i $a0, %pc_hi20(sym_pcrel)
+# CHECK-NEXT: addi.d $a0, $a0, %pc_lo12(sym_pcrel)
+
+la.pcrel $a0, $a1, sym_pcrel_large
+# CHECK: pcalau12i $a0, %pc_hi20(sym_pcrel_large)
+# CHECK-NEXT: addi.d $a1, $zero, %pc_lo12(sym_pcrel_large)
+# CHECK-NEXT: lu32i.d $a1, %pc64_lo20(sym_pcrel_large)
+# CHECK-NEXT: lu52i.d $a1, $a1, %pc64_hi12(sym_pcrel_large)
+# CHECK-NEXT: add.d $a0, $a0, $a1
+
+la.got $a0, sym_got
+# CHECK: pcalau12i $a0, %got_pc_hi20(sym_got)
+# CHECK-NEXT: ld.d $a0, $a0, %got_pc_lo12(sym_got)
+
+la.got $a0, $a1, sym_got_large
+# CHECK: pcalau12i $a0, %got_pc_hi20(sym_got_large)
+# CHECK-NEXT: addi.d $a1, $zero, %got_pc_lo12(sym_got_large)
+# CHECK-NEXT: lu32i.d $a1, %got64_pc_lo20(sym_got_large)
+# CHECK-NEXT: lu52i.d $a1, $a1, %got64_pc_hi12(sym_got_large)
+# CHECK-NEXT: ldx.d $a0, $a0, $a1
+
+la.tls.le $a0, sym_le
+# CHECK: lu12i.w $a0, %le_hi20(sym_le)
+# CHECK-NEXT: ori $a0, $a0, %le_lo12(sym_le)
+
+la.tls.ie $a0, sym_ie
+# CHECK: pcalau12i $a0, %ie_pc_hi20(sym_ie)
+# CHECK-NEXT: ld.d $a0, $a0, %ie_pc_lo12(sym_ie)
+
+la.tls.ie $a0, $a1, sym_ie_large
+# CHECK: pcalau12i $a0, %ie_pc_hi20(sym_ie_large)
+# CHECK-NEXT: addi.d $a1, $zero, %ie_pc_lo12(sym_ie_large)
+# CHECK-NEXT: lu32i.d $a1, %ie64_pc_lo20(sym_ie_large)
+# CHECK-NEXT: lu52i.d $a1, $a1, %ie64_pc_hi12(sym_ie_large)
+# CHECK-NEXT: ldx.d $a0, $a0, $a1
+
+la.tls.ld $a0, sym_ld
+# CHECK: pcalau12i $a0, %ld_pc_hi20(sym_ld)
+# CHECK-NEXT: addi.d $a0, $a0, %got_pc_lo12(sym_ld)
+
+la.tls.ld $a0, $a1, sym_ld_large
+# CHECK: pcalau12i $a0, %ld_pc_hi20(sym_ld_large)
+# CHECK-NEXT: addi.d $a1, $zero, %got_pc_lo12(sym_ld_large)
+# CHECK-NEXT: lu32i.d $a1, %got64_pc_lo20(sym_ld_large)
+# CHECK-NEXT: lu52i.d $a1, $a1, %got64_pc_hi12(sym_ld_large)
+# CHECK-NEXT: add.d $a0, $a0, $a1
+
+la.tls.gd $a0, sym_gd
+# CHECK: pcalau12i $a0, %gd_pc_hi20(sym_gd)
+# CHECK-NEXT: addi.d $a0, $a0, %got_pc_lo12(sym_gd)
+
+la.tls.gd $a0, $a1, sym_gd_large
+# CHECK: pcalau12i $a0, %gd_pc_hi20(sym_gd_large)
+# CHECK-NEXT: addi.d $a1, $zero, %got_pc_lo12(sym_gd_large)
+# CHECK-NEXT: lu32i.d $a1, %got64_pc_lo20(sym_gd_large)
+# CHECK-NEXT: lu52i.d $a1, $a1, %got64_pc_hi12(sym_gd_large)
+# CHECK-NEXT: add.d $a0, $a0, $a1
More information about the llvm-commits
mailing list