[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