[llvm-branch-commits] [clang] [lld] [llvm] release/22.x: [LoongArch] Add support for LA32R/LA32S (PR #176700)

Cullen Rhodes via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Jan 22 00:27:53 PST 2026


https://github.com/c-rhodes updated https://github.com/llvm/llvm-project/pull/176700

>From cb16312a22177cd05809843b3c8d43875c75750a Mon Sep 17 00:00:00 2001
From: hev <wangrui at loongson.cn>
Date: Tue, 13 Jan 2026 23:24:34 +0800
Subject: [PATCH 01/10] [llvm][LoongArch] Add call30 and tail30 macro
 instruction support (#175356)

Link:
https://sourceware.org/pipermail/binutils/2025-December/146091.html
(cherry picked from commit c88cbaf2b204795c04596ee3071ce3484f0e610a)
---
 .../AsmParser/LoongArchAsmParser.cpp          | 64 +++++++++++++++----
 .../Target/LoongArch/LoongArchInstrInfo.cpp   |  1 +
 .../Target/LoongArch/LoongArchInstrInfo.td    | 16 ++++-
 .../Target/LoongArch/LoongArchMCInstLower.cpp |  3 +
 .../MCTargetDesc/LoongArchBaseInfo.h          |  1 +
 .../MCTargetDesc/LoongArchMCAsmInfo.cpp       |  3 +
 .../MCTargetDesc/LoongArchMCCodeEmitter.cpp   |  1 +
 .../test/MC/LoongArch/Basic/Integer/invalid.s |  4 +-
 llvm/test/MC/LoongArch/Macros/macros-call.s   | 15 ++++-
 9 files changed, 92 insertions(+), 16 deletions(-)

diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
index 9b11201d0312d..483be6b9a1a57 100644
--- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
+++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
@@ -171,9 +171,9 @@ class LoongArchAsmParser : public MCTargetAsmParser {
   // 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);
+  // Helper to emit pseudo instruction "call{3x} sym" or "tail{3x} $rj, sym".
+  void emitFuncCall(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, bool IsTailCall,
+                    bool IsCall36);
 
 public:
   enum LoongArchMatchResultTy {
@@ -463,6 +463,22 @@ class LoongArchOperand : public MCParsedAsmOperand {
                      IsValidKind;
   }
 
+  bool isSImm20pcaddu12i() 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 == ELF::R_LARCH_CALL30;
+
+    return IsConstantImm
+               ? isInt<20>(Imm) && IsValidKind
+               : LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
+                     IsValidKind;
+  }
+
   bool isSImm20pcaddu18i() const {
     if (!isImm())
       return false;
@@ -1465,8 +1481,19 @@ void LoongArchAsmParser::emitLoadImm(MCInst &Inst, SMLoc IDLoc,
   }
 }
 
-void LoongArchAsmParser::emitFuncCall36(MCInst &Inst, SMLoc IDLoc,
-                                        MCStreamer &Out, bool IsTailCall) {
+void LoongArchAsmParser::emitFuncCall(MCInst &Inst, SMLoc IDLoc,
+                                      MCStreamer &Out, bool IsTailCall,
+                                      bool IsCall36) {
+  // call30 sym
+  // expands to:
+  //   pcaddu12i $ra, %call30(sym)
+  //   jirl      $ra, $ra, 0
+  //
+  // tail30 $rj, sym
+  // expands to:
+  //   pcaddu12i $rj, %call30(sym)
+  //   jirl      $r0, $rj, 0
+  //
   // call36 sym
   // expands to:
   //   pcaddu18i $ra, %call36(sym)
@@ -1478,14 +1505,15 @@ void LoongArchAsmParser::emitFuncCall36(MCInst &Inst, SMLoc IDLoc,
   //   jirl      $r0, $rj, 0
   MCRegister ScratchReg =
       IsTailCall ? Inst.getOperand(0).getReg() : MCRegister(LoongArch::R1);
+  unsigned PCAI = IsCall36 ? LoongArch::PCADDU18I : LoongArch::PCADDU12I;
+  unsigned Rel = IsCall36 ? ELF::R_LARCH_CALL36 : ELF::R_LARCH_CALL30;
   const MCExpr *Sym =
       IsTailCall ? Inst.getOperand(1).getExpr() : Inst.getOperand(0).getExpr();
-  const LoongArchMCExpr *LE = LoongArchMCExpr::create(
-      Sym, ELF::R_LARCH_CALL36, getContext(), /*RelaxHint=*/true);
+  const LoongArchMCExpr *LE =
+      LoongArchMCExpr::create(Sym, Rel, getContext(), /*RelaxHint=*/true);
 
-  Out.emitInstruction(
-      MCInstBuilder(LoongArch::PCADDU18I).addReg(ScratchReg).addExpr(LE),
-      getSTI());
+  Out.emitInstruction(MCInstBuilder(PCAI).addReg(ScratchReg).addExpr(LE),
+                      getSTI());
   Out.emitInstruction(
       MCInstBuilder(LoongArch::JIRL)
           .addReg(IsTailCall ? MCRegister(LoongArch::R0) : ScratchReg)
@@ -1548,11 +1576,17 @@ bool LoongArchAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
   case LoongArch::PseudoLI_D:
     emitLoadImm(Inst, IDLoc, Out);
     return false;
+  case LoongArch::PseudoCALL30:
+    emitFuncCall(Inst, IDLoc, Out, /*IsTailCall=*/false, /*IsCall36=*/false);
+    return false;
   case LoongArch::PseudoCALL36:
-    emitFuncCall36(Inst, IDLoc, Out, /*IsTailCall=*/false);
+    emitFuncCall(Inst, IDLoc, Out, /*IsTailCall=*/false, /*IsCall36=*/true);
+    return false;
+  case LoongArch::PseudoTAIL30:
+    emitFuncCall(Inst, IDLoc, Out, /*IsTailCall=*/true, /*IsCall36=*/false);
     return false;
   case LoongArch::PseudoTAIL36:
-    emitFuncCall36(Inst, IDLoc, Out, /*IsTailCall=*/true);
+    emitFuncCall(Inst, IDLoc, Out, /*IsTailCall=*/true, /*IsCall36=*/true);
     return false;
   }
   Out.emitInstruction(Inst, getSTI());
@@ -1849,6 +1883,12 @@ bool LoongArchAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
         /*Upper=*/(1 << 19) - 1,
         "operand must be a symbol with modifier (e.g. %pc_hi20) or an integer "
         "in the range");
+  case Match_InvalidSImm20pcaddu12i:
+    return generateImmOutOfRangeError(
+        Operands, ErrorInfo, /*Lower=*/-(1 << 19),
+        /*Upper=*/(1 << 19) - 1,
+        "operand must be a symbol with modifier (e.g. %call30) or an integer "
+        "in the range");
   case Match_InvalidSImm20pcaddu18i:
     return generateImmOutOfRangeError(
         Operands, ErrorInfo, /*Lower=*/-(1 << 19),
diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp
index 69ef349042d8a..44bb1f011dc6d 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp
@@ -744,6 +744,7 @@ LoongArchInstrInfo::getSerializableDirectMachineOperandTargetFlags() const {
       {MO_IE_PC64_HI, "loongarch-ie-pc64-hi"},
       {MO_LD_PC_HI, "loongarch-ld-pc-hi"},
       {MO_GD_PC_HI, "loongarch-gd-pc-hi"},
+      {MO_CALL30, "loongarch-call30"},
       {MO_CALL36, "loongarch-call36"},
       {MO_DESC_PC_HI, "loongarch-desc-pc-hi"},
       {MO_DESC_PC_LO, "loongarch-desc-pc-lo"},
diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
index d971f8bc1986b..6f840dae3f0ae 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
@@ -474,6 +474,10 @@ def simm20_lu32id : SImm20Operand {
   let ParserMatchClass = SImmAsmOperand<20, "lu32id">;
 }
 
+def simm20_pcaddu12i : SImm20Operand {
+  let ParserMatchClass = SImmAsmOperand<20, "pcaddu12i">;
+}
+
 def simm20_pcaddu18i : SImm20Operand {
   let ParserMatchClass = SImmAsmOperand<20, "pcaddu18i">;
 }
@@ -855,7 +859,7 @@ def SLT  : ALU_3R<0x00120000>;
 def SLTU : ALU_3R<0x00128000>;
 def SLTI  : ALU_2RI12<0x02000000, simm12>;
 def SLTUI : ALU_2RI12<0x02400000, simm12>;
-def PCADDU12I : ALU_1RI20<0x1c000000, simm20>;
+def PCADDU12I : ALU_1RI20<0x1c000000, simm20_pcaddu12i>;
 def AND  : ALU_3R<0x00148000>;
 def OR   : ALU_3R<0x00150000>;
 def NOR  : ALU_3R<0x00140000>;
@@ -1718,6 +1722,16 @@ def PseudoJIRL_TAIL : Pseudo<(outs), (ins GPR:$rj, simm16_lsl2:$imm16)>,
                       PseudoInstExpansion<(JIRL R0, GPR:$rj,
                                            simm16_lsl2:$imm16)>;
 
+/// call30/tail30 macro instructions
+let isCall = 1, isBarrier = 1, isCodeGenOnly = 0, isAsmParserOnly = 1,
+    Defs = [R1], hasSideEffects = 0, mayStore = 0, mayLoad = 0 in
+def PseudoCALL30 : Pseudo<(outs), (ins bare_symbol:$dst), [], "call30", "$dst">;
+let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [R3],
+    isCodeGenOnly = 0, isAsmParserOnly = 1, hasSideEffects = 0,
+    mayStore = 0, mayLoad = 0 in
+def PseudoTAIL30 : Pseudo<(outs), (ins GPR:$tmp, bare_symbol:$dst), [],
+                          "tail30", "$tmp, $dst">;
+
 /// call36/taill36 macro instructions
 let isCall = 1, isBarrier = 1, isCodeGenOnly = 0, isAsmParserOnly = 1,
     Defs = [R1], hasSideEffects = 0, mayStore = 0, mayLoad = 0 in
diff --git a/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp b/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp
index 455e6ad7c3ac5..ae4090784a9f2 100644
--- a/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp
@@ -92,6 +92,9 @@ static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym,
   case LoongArchII::MO_GD_PC_HI:
     Kind = ELF::R_LARCH_TLS_GD_PC_HI20;
     break;
+  case LoongArchII::MO_CALL30:
+    Kind = ELF::R_LARCH_CALL30;
+    break;
   case LoongArchII::MO_CALL36:
     Kind = ELF::R_LARCH_CALL36;
     break;
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h
index 833cd06261624..99d7df63fce04 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h
@@ -48,6 +48,7 @@ enum {
   MO_IE_PC64_HI,
   MO_LD_PC_HI,
   MO_GD_PC_HI,
+  MO_CALL30,
   MO_CALL36,
   MO_DESC_PC_HI,
   MO_DESC_PC_LO,
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCAsmInfo.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCAsmInfo.cpp
index 8ecb62d0ea7bb..3f9bea5d69bf9 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCAsmInfo.cpp
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCAsmInfo.cpp
@@ -97,6 +97,8 @@ static StringRef getLoongArchSpecifierName(uint16_t S) {
     return "gd_pc_hi20";
   case ELF::R_LARCH_TLS_GD_HI20:
     return "gd_hi20";
+  case ELF::R_LARCH_CALL30:
+    return "call30";
   case ELF::R_LARCH_CALL36:
     return "call36";
   case ELF::R_LARCH_TLS_DESC_PC_HI20:
@@ -174,6 +176,7 @@ LoongArchMCExpr::Specifier LoongArch::parseSpecifier(StringRef name) {
       .Case("ld_hi20", ELF::R_LARCH_TLS_LD_HI20)
       .Case("gd_pc_hi20", ELF::R_LARCH_TLS_GD_PC_HI20)
       .Case("gd_hi20", ELF::R_LARCH_TLS_GD_HI20)
+      .Case("call30", ELF::R_LARCH_CALL30)
       .Case("call36", ELF::R_LARCH_CALL36)
       .Case("desc_pc_hi20", ELF::R_LARCH_TLS_DESC_PC_HI20)
       .Case("desc_pc_lo12", ELF::R_LARCH_TLS_DESC_PC_LO12)
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp
index 08fa51d333346..661cf8aae99e4 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp
@@ -180,6 +180,7 @@ LoongArchMCCodeEmitter::getExprOpValue(const MCInst &MI, const MCOperand &MO,
     case ELF::R_LARCH_ABS64_HI12:
       FixupKind = LoongArch::fixup_loongarch_abs64_hi12;
       break;
+    case ELF::R_LARCH_CALL30:
     case ELF::R_LARCH_CALL36:
     case ELF::R_LARCH_TLS_LE_HI20_R:
     case ELF::R_LARCH_TLS_LE_LO12_R:
diff --git a/llvm/test/MC/LoongArch/Basic/Integer/invalid.s b/llvm/test/MC/LoongArch/Basic/Integer/invalid.s
index 08a131d4d43f9..8488129bc253a 100644
--- a/llvm/test/MC/LoongArch/Basic/Integer/invalid.s
+++ b/llvm/test/MC/LoongArch/Basic/Integer/invalid.s
@@ -98,9 +98,9 @@ bgeu $a0, $a0, 0x1FFFF
 jirl $a0, $a0, 0x20000
 # CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %b16) or an integer in the range [-131072, 131068]
 
-## simm20
+## simm20_pcaddu12i
 pcaddu12i $a0, 0x80000
-# CHECK: :[[#@LINE-1]]:16: error: immediate must be an integer in the range [-524288, 524287]
+# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %call30) or an integer in the range [-524288, 524287]
 
 ## simm20_pcaddi
 pcaddi $a0, -0x80001
diff --git a/llvm/test/MC/LoongArch/Macros/macros-call.s b/llvm/test/MC/LoongArch/Macros/macros-call.s
index df7715050a0f9..0fbc2160c14f5 100644
--- a/llvm/test/MC/LoongArch/Macros/macros-call.s
+++ b/llvm/test/MC/LoongArch/Macros/macros-call.s
@@ -7,6 +7,20 @@
 # RELOC:      Relocations [
 # RELOC-NEXT:   Section ({{.*}}) .rela.text {
 
+call30 sym_call
+# CHECK:      pcaddu12i $ra, %call30(sym_call)
+# CHECK-NEXT: jirl $ra, $ra, 0
+
+# RELOC-NEXT: R_LARCH_CALL30 sym_call 0x0
+# RELAX-NEXT: R_LARCH_RELAX - 0x0
+
+tail30 $t0, sym_tail
+# CHECK:      pcaddu12i $t0, %call30(sym_tail)
+# CHECK-NEXT: jr $t0
+
+# RELOC-NEXT: R_LARCH_CALL30 sym_tail 0x0
+# RELAX-NEXT: R_LARCH_RELAX - 0x0
+
 call36 sym_call
 # CHECK:      pcaddu18i $ra, %call36(sym_call)
 # CHECK-NEXT: jirl $ra, $ra, 0
@@ -21,6 +35,5 @@ tail36 $t0, sym_tail
 # RELOC-NEXT: R_LARCH_CALL36 sym_tail 0x0
 # RELAX-NEXT: R_LARCH_RELAX - 0x0
 
-
 # RELOC-NEXT:   }
 # RELOC-NEXT: ]

>From e8c6fcc3f7147b4143ecec38cae22dda059b4eba Mon Sep 17 00:00:00 2001
From: hev <wangrui at loongson.cn>
Date: Wed, 14 Jan 2026 07:49:16 +0800
Subject: [PATCH 02/10] [llvm][LoongArch] Add call and tail macro instruction
 support (#175357)

Link:
https://sourceware.org/pipermail/binutils/2025-December/146091.html
(cherry picked from commit 9f7af289728002487b032a8278bc2c540a02ff59)
---
 .../AsmParser/LoongArchAsmParser.cpp          |  7 +++
 .../LoongArch/LoongArchExpandPseudoInsts.cpp  | 31 +++++++----
 .../LoongArch/LoongArchISelLowering.cpp       |  3 +-
 .../Target/LoongArch/LoongArchInstrInfo.td    | 36 +++++++-----
 .../Target/LoongArch/LoongArchOptWInstrs.cpp  |  2 +-
 .../LoongArch/LoongArchTargetMachine.cpp      |  4 +-
 llvm/test/CodeGen/LoongArch/expand-call.ll    |  2 +-
 llvm/test/MC/LoongArch/Macros/macros-call.s   | 55 ++++++++++++++-----
 8 files changed, 96 insertions(+), 44 deletions(-)

diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
index 483be6b9a1a57..5a6a0927a8974 100644
--- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
+++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
@@ -1576,12 +1576,19 @@ bool LoongArchAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
   case LoongArch::PseudoLI_D:
     emitLoadImm(Inst, IDLoc, Out);
     return false;
+  case LoongArch::PseudoCALL:
+    emitFuncCall(Inst, IDLoc, Out, /*IsTailCall=*/false,
+                 /*IsCall36=*/is64Bit());
+    return false;
   case LoongArch::PseudoCALL30:
     emitFuncCall(Inst, IDLoc, Out, /*IsTailCall=*/false, /*IsCall36=*/false);
     return false;
   case LoongArch::PseudoCALL36:
     emitFuncCall(Inst, IDLoc, Out, /*IsTailCall=*/false, /*IsCall36=*/true);
     return false;
+  case LoongArch::PseudoTAIL:
+    emitFuncCall(Inst, IDLoc, Out, /*IsTailCall=*/true, /*IsCall36=*/is64Bit());
+    return false;
   case LoongArch::PseudoTAIL30:
     emitFuncCall(Inst, IDLoc, Out, /*IsTailCall=*/true, /*IsCall36=*/false);
     return false;
diff --git a/llvm/lib/Target/LoongArch/LoongArchExpandPseudoInsts.cpp b/llvm/lib/Target/LoongArch/LoongArchExpandPseudoInsts.cpp
index 7aef4ab53e4ea..6cef279c6131c 100644
--- a/llvm/lib/Target/LoongArch/LoongArchExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchExpandPseudoInsts.cpp
@@ -161,10 +161,10 @@ bool LoongArchPreRAExpandPseudo::expandMI(
     return expandLoadAddressTLSDesc(MBB, MBBI, NextMBBI);
   case LoongArch::PseudoLA_TLS_DESC_LARGE:
     return expandLoadAddressTLSDesc(MBB, MBBI, NextMBBI, /*Large=*/true);
-  case LoongArch::PseudoCALL:
+  case LoongArch::PseudoCALL_SMALL:
   case LoongArch::PseudoCALL_LARGE:
     return expandFunctionCALL(MBB, MBBI, NextMBBI, /*IsTailCall=*/false);
-  case LoongArch::PseudoTAIL:
+  case LoongArch::PseudoTAIL_SMALL:
   case LoongArch::PseudoTAIL_LARGE:
     return expandFunctionCALL(MBB, MBBI, NextMBBI, /*IsTailCall=*/true);
   case LoongArch::PseudoBRIND:
@@ -784,25 +784,36 @@ bool LoongArchExpandPseudo::expandFunctionCALL(
     report_fatal_error("Unexpected code model");
     break;
   case CodeModel::Medium: {
+    // for la32 expands to:
     // CALL:
-    // pcaddu18i $ra, %call36(func)
-    // jirl      $ra, $ra, 0
+    //   pcaddu12i $ra, %call30(func)
+    //   jirl      $ra, $ra, 0
     // TAIL:
-    // pcaddu18i $t8, %call36(func)
-    // jirl      $r0, $t8, 0
+    //   pcaddu12i $t8, %call30(func)
+    //   jirl      $r0, $t8, 0
+    //
+    // for la64 expands to:
+    // CALL:
+    //   pcaddu18i $ra, %call36(func)
+    //   jirl      $ra, $ra, 0
+    // TAIL:
+    //   pcaddu18i $t8, %call36(func)
+    //   jirl      $r0, $t8, 0
     Opcode =
         IsTailCall ? LoongArch::PseudoJIRL_TAIL : LoongArch::PseudoJIRL_CALL;
     Register ScratchReg = IsTailCall ? LoongArch::R20 : LoongArch::R1;
-    MachineInstrBuilder MIB =
-        BuildMI(MBB, MBBI, DL, TII->get(LoongArch::PCADDU18I), ScratchReg);
+    bool Is64Bit = MF->getSubtarget<LoongArchSubtarget>().is64Bit();
+    unsigned PC = Is64Bit ? LoongArch::PCADDU18I : LoongArch::PCADDU12I;
+    unsigned MO = Is64Bit ? LoongArchII::MO_CALL36 : LoongArchII::MO_CALL30;
+    MachineInstrBuilder MIB = BuildMI(MBB, MBBI, DL, TII->get(PC), ScratchReg);
 
     CALL =
         BuildMI(MBB, MBBI, DL, TII->get(Opcode)).addReg(ScratchReg).addImm(0);
 
     if (Func.isSymbol())
-      MIB.addExternalSymbol(Func.getSymbolName(), LoongArchII::MO_CALL36);
+      MIB.addExternalSymbol(Func.getSymbolName(), MO);
     else
-      MIB.addDisp(Func, 0, LoongArchII::MO_CALL36);
+      MIB.addDisp(Func, 0, MO);
     break;
   }
   }
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index bc9ae3eafb3b2..8451adcfa5faa 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -8628,7 +8628,7 @@ LoongArchTargetLowering::LowerCall(CallLoweringInfo &CLI,
 
   // If the callee is a GlobalAddress/ExternalSymbol node, turn it into a
   // TargetGlobalAddress/TargetExternalSymbol node so that legalize won't
-  // split it and then direct call can be matched by PseudoCALL.
+  // split it and then direct call can be matched by PseudoCALL_SMALL.
   if (GlobalAddressSDNode *S = dyn_cast<GlobalAddressSDNode>(Callee)) {
     const GlobalValue *GV = S->getGlobal();
     unsigned OpFlags = getTargetMachine().shouldAssumeDSOLocal(GV)
@@ -8674,7 +8674,6 @@ LoongArchTargetLowering::LowerCall(CallLoweringInfo &CLI,
     Op = IsTailCall ? LoongArchISD::TAIL : LoongArchISD::CALL;
     break;
   case CodeModel::Medium:
-    assert(Subtarget.is64Bit() && "Medium code model requires LA64");
     Op = IsTailCall ? LoongArchISD::TAIL_MEDIUM : LoongArchISD::CALL_MEDIUM;
     break;
   case CodeModel::Large:
diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
index 6f840dae3f0ae..555dc31e77755 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
@@ -515,7 +515,7 @@ def SImm26OperandBL: AsmOperandClass {
   let ParserMethod = "parseSImm26Operand";
 }
 
-// A symbol or an imm used in BL/PseudoCALL/PseudoTAIL.
+// A symbol or an imm used in BL/PseudoCALL_SMALL/PseudoTAIL_SMALL.
 def simm26_symbol : Operand<GRLenVT> {
   let ParserMatchClass = SImm26OperandBL;
   let EncoderMethod = "getImmOpValueAsr<2>";
@@ -1625,21 +1625,21 @@ def : Pat<(brind (add GPRJR:$rj, simm16_lsl2:$imm16)),
 
 // Function call with 'Small' code model.
 let isCall = 1, Defs = [R1] in
-def PseudoCALL : Pseudo<(outs), (ins bare_symbol:$func)>;
+def PseudoCALL_SMALL : Pseudo<(outs), (ins bare_symbol:$func)>;
 
-def : Pat<(loongarch_call tglobaladdr:$func), (PseudoCALL tglobaladdr:$func)>;
-def : Pat<(loongarch_call texternalsym:$func), (PseudoCALL texternalsym:$func)>;
+def : Pat<(loongarch_call tglobaladdr:$func),
+          (PseudoCALL_SMALL tglobaladdr:$func)>;
+def : Pat<(loongarch_call texternalsym:$func),
+          (PseudoCALL_SMALL texternalsym:$func)>;
 
 // Function call with 'Medium' code model.
 let isCall = 1, Defs = [R1, R20], Size = 8 in
 def PseudoCALL_MEDIUM : Pseudo<(outs), (ins bare_symbol:$func)>;
 
-let Predicates = [IsLA64] in {
 def : Pat<(loongarch_call_medium tglobaladdr:$func),
           (PseudoCALL_MEDIUM tglobaladdr:$func)>;
 def : Pat<(loongarch_call_medium texternalsym:$func),
           (PseudoCALL_MEDIUM texternalsym:$func)>;
-} // Predicates = [IsLA64]
 
 // Function call with 'Large' code model.
 let isCall = 1, Defs = [R1] in
@@ -1656,10 +1656,9 @@ let isCall = 1, Defs = [R1] in
 def PseudoCALLIndirect : Pseudo<(outs), (ins GPR:$rj),
                                 [(loongarch_call GPR:$rj)]>,
                          PseudoInstExpansion<(JIRL R1, GPR:$rj, 0)>;
-let Predicates = [IsLA64] in {
 def : Pat<(loongarch_call_medium GPR:$rj), (PseudoCALLIndirect GPR:$rj)>;
+let Predicates = [IsLA64] in
 def : Pat<(loongarch_call_large GPR:$rj), (PseudoCALLIndirect GPR:$rj)>;
-}
 
 let isCall = 1, hasSideEffects = 0, mayStore = 0, mayLoad = 0, Defs = [R1] in
 def PseudoJIRL_CALL : Pseudo<(outs), (ins GPR:$rj, simm16_lsl2:$imm16)>,
@@ -1672,24 +1671,22 @@ def PseudoRET : Pseudo<(outs), (ins), [(loongarch_ret)]>,
 
 // Tail call with 'Small' code model.
 let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [R3] in
-def PseudoTAIL : Pseudo<(outs), (ins bare_symbol:$dst)>;
+def PseudoTAIL_SMALL : Pseudo<(outs), (ins bare_symbol:$dst)>;
 
 def : Pat<(loongarch_tail (iPTR tglobaladdr:$dst)),
-          (PseudoTAIL tglobaladdr:$dst)>;
+          (PseudoTAIL_SMALL tglobaladdr:$dst)>;
 def : Pat<(loongarch_tail (iPTR texternalsym:$dst)),
-          (PseudoTAIL texternalsym:$dst)>;
+          (PseudoTAIL_SMALL texternalsym:$dst)>;
 
 // Tail call with 'Medium' code model.
 let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1,
     Uses = [R3], Defs = [R20], Size = 8 in
 def PseudoTAIL_MEDIUM : Pseudo<(outs), (ins bare_symbol:$dst)>;
 
-let Predicates = [IsLA64] in {
 def : Pat<(loongarch_tail_medium (iPTR tglobaladdr:$dst)),
           (PseudoTAIL_MEDIUM tglobaladdr:$dst)>;
 def : Pat<(loongarch_tail_medium (iPTR texternalsym:$dst)),
           (PseudoTAIL_MEDIUM texternalsym:$dst)>;
-} // Predicates = [IsLA64]
 
 // Tail call with 'Large' code model.
 let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [R3] in
@@ -1706,10 +1703,9 @@ let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [R3] in
 def PseudoTAILIndirect : Pseudo<(outs), (ins GPRT:$rj),
                                 [(loongarch_tail GPRT:$rj)]>,
                          PseudoInstExpansion<(JIRL R0, GPR:$rj, 0)>;
-let Predicates = [IsLA64] in {
 def : Pat<(loongarch_tail_medium GPR:$rj), (PseudoTAILIndirect GPR:$rj)>;
+let Predicates = [IsLA64] in
 def : Pat<(loongarch_tail_large GPR:$rj), (PseudoTAILIndirect GPR:$rj)>;
-}
 
 let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1,
     hasSideEffects = 0, mayStore = 0, mayLoad = 0, Uses = [R3] in
@@ -1722,6 +1718,16 @@ def PseudoJIRL_TAIL : Pseudo<(outs), (ins GPR:$rj, simm16_lsl2:$imm16)>,
                       PseudoInstExpansion<(JIRL R0, GPR:$rj,
                                            simm16_lsl2:$imm16)>;
 
+/// call/tail macro instructions
+let isCall = 1, isBarrier = 1, isCodeGenOnly = 0, isAsmParserOnly = 1,
+    Defs = [R1], hasSideEffects = 0, mayStore = 0, mayLoad = 0 in
+def PseudoCALL : Pseudo<(outs), (ins bare_symbol:$dst), [], "call", "$dst">;
+let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, Uses = [R3],
+    isCodeGenOnly = 0, isAsmParserOnly = 1, hasSideEffects = 0,
+    mayStore = 0, mayLoad = 0 in
+def PseudoTAIL : Pseudo<(outs), (ins GPR:$tmp, bare_symbol:$dst), [],
+                        "tail", "$tmp, $dst">;
+
 /// call30/tail30 macro instructions
 let isCall = 1, isBarrier = 1, isCodeGenOnly = 0, isAsmParserOnly = 1,
     Defs = [R1], hasSideEffects = 0, mayStore = 0, mayLoad = 0 in
diff --git a/llvm/lib/Target/LoongArch/LoongArchOptWInstrs.cpp b/llvm/lib/Target/LoongArch/LoongArchOptWInstrs.cpp
index 0e5acff5d1cbc..b91724e0112a9 100644
--- a/llvm/lib/Target/LoongArch/LoongArchOptWInstrs.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchOptWInstrs.cpp
@@ -561,7 +561,7 @@ static bool isSignExtendedW(Register SrcReg, const LoongArchSubtarget &ST,
       if (CopySrcReg == LoongArch::R4) {
         // For a method return value, we check the ZExt/SExt flags in attribute.
         // We assume the following code sequence for method call.
-        // PseudoCALL @bar, ...
+        // PseudoCALL_SMALL @bar, ...
         // ADJCALLSTACKUP 0, 0, implicit-def dead $r3, implicit $r3
         // %0:gpr = COPY $r4
         //
diff --git a/llvm/lib/Target/LoongArch/LoongArchTargetMachine.cpp b/llvm/lib/Target/LoongArch/LoongArchTargetMachine.cpp
index 92a9388e5cb7b..8054cb2d5e896 100644
--- a/llvm/lib/Target/LoongArch/LoongArchTargetMachine.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchTargetMachine.cpp
@@ -79,11 +79,11 @@ getEffectiveLoongArchCodeModel(const Triple &TT,
 
   switch (*CM) {
   case CodeModel::Small:
-    return *CM;
   case CodeModel::Medium:
+    return *CM;
   case CodeModel::Large:
     if (!TT.isArch64Bit())
-      report_fatal_error("Medium/Large code model requires LA64");
+      report_fatal_error("Large code model requires LA64");
     return *CM;
   default:
     report_fatal_error(
diff --git a/llvm/test/CodeGen/LoongArch/expand-call.ll b/llvm/test/CodeGen/LoongArch/expand-call.ll
index 495cf04c95b32..7a9a5067c54ce 100644
--- a/llvm/test/CodeGen/LoongArch/expand-call.ll
+++ b/llvm/test/CodeGen/LoongArch/expand-call.ll
@@ -7,7 +7,7 @@ declare void @callee()
 
 define void @caller() nounwind {
 ; NOEXPAND-LABEL: name: caller
-; NOEXPAND: PseudoCALL target-flags{{.*}}callee
+; NOEXPAND: PseudoCALL_SMALL target-flags{{.*}}callee
 ;
 ; EXPAND-LABEL: name: caller
 ; EXPAND: BL target-flags{{.*}}callee
diff --git a/llvm/test/MC/LoongArch/Macros/macros-call.s b/llvm/test/MC/LoongArch/Macros/macros-call.s
index 0fbc2160c14f5..49b2fa5af848f 100644
--- a/llvm/test/MC/LoongArch/Macros/macros-call.s
+++ b/llvm/test/MC/LoongArch/Macros/macros-call.s
@@ -1,12 +1,37 @@
-# RUN: llvm-mc --triple=loongarch64 %s | FileCheck %s
-# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s -o %t
-# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELOC
-# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.relax
-# RUN: llvm-readobj -r %t.relax | FileCheck %s --check-prefixes=RELOC,RELAX
+# RUN: llvm-mc --triple=loongarch32 %s | FileCheck %s --check-prefixes=CHECK,LA32
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=-relax %s -o %t
+# RUN: llvm-readobj -r %t | FileCheck %s --check-prefixes=RELOC,LA32-RELOC
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+relax %s -o %t.relax
+# RUN: llvm-readobj -r %t.relax | FileCheck %s --check-prefixes=RELOC,RELAX,LA32-RELOC
+# RUN: llvm-mc --triple=loongarch64 %s --defsym=LA64=1 | FileCheck %s --check-prefixes=CHECK,LA64
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=-relax %s -o %t --defsym=LA64=1
+# RUN: llvm-readobj -r %t | FileCheck %s --check-prefixes=RELOC,LA64-RELOC
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax %s -o %t.relax --defsym=LA64=1
+# RUN: llvm-readobj -r %t.relax | FileCheck %s --check-prefixes=RELOC,RELAX,LA64-RELOC,LA64-RELAX
 
 # RELOC:      Relocations [
 # RELOC-NEXT:   Section ({{.*}}) .rela.text {
 
+call sym_call
+# LA32:      pcaddu12i $ra, %call30(sym_call)
+# LA32-NEXT: jirl $ra, $ra, 0
+# LA64:      pcaddu18i $ra, %call36(sym_call)
+# LA64-NEXT: jirl $ra, $ra, 0
+
+# LA32-RELOC-NEXT: R_LARCH_CALL30 sym_call 0x0
+# LA64-RELOC-NEXT: R_LARCH_CALL36 sym_call 0x0
+# RELAX-NEXT: R_LARCH_RELAX - 0x0
+
+tail $t0, sym_tail
+# LA32:      pcaddu12i $t0, %call30(sym_tail)
+# LA32-NEXT: jr $t0
+# LA64:      pcaddu18i $t0, %call36(sym_tail)
+# LA64-NEXT: jr $t0
+
+# LA32-RELOC-NEXT: R_LARCH_CALL30 sym_tail 0x0
+# LA64-RELOC-NEXT: R_LARCH_CALL36 sym_tail 0x0
+# RELAX-NEXT: R_LARCH_RELAX - 0x0
+
 call30 sym_call
 # CHECK:      pcaddu12i $ra, %call30(sym_call)
 # CHECK-NEXT: jirl $ra, $ra, 0
@@ -21,19 +46,23 @@ tail30 $t0, sym_tail
 # RELOC-NEXT: R_LARCH_CALL30 sym_tail 0x0
 # RELAX-NEXT: R_LARCH_RELAX - 0x0
 
+.ifdef LA64
+
 call36 sym_call
-# CHECK:      pcaddu18i $ra, %call36(sym_call)
-# CHECK-NEXT: jirl $ra, $ra, 0
+# LA64:      pcaddu18i $ra, %call36(sym_call)
+# LA64-NEXT: jirl $ra, $ra, 0
 
-# RELOC-NEXT: R_LARCH_CALL36 sym_call 0x0
-# RELAX-NEXT: R_LARCH_RELAX - 0x0
+# LA64-RELOC-NEXT: R_LARCH_CALL36 sym_call 0x0
+# LA64-RELAX-NEXT: R_LARCH_RELAX - 0x0
 
 tail36 $t0, sym_tail
-# CHECK:      pcaddu18i $t0, %call36(sym_tail)
-# CHECK-NEXT: jr $t0
+# LA64:      pcaddu18i $t0, %call36(sym_tail)
+# LA64-NEXT: jr $t0
 
-# RELOC-NEXT: R_LARCH_CALL36 sym_tail 0x0
-# RELAX-NEXT: R_LARCH_RELAX - 0x0
+# LA64-RELOC-NEXT: R_LARCH_CALL36 sym_tail 0x0
+# LA64-RELAX-NEXT: R_LARCH_RELAX - 0x0
+
+.endif
 
 # RELOC-NEXT:   }
 # RELOC-NEXT: ]

>From 33c94305615cf0df4d83e6c6befe4cb4d0cda9dc Mon Sep 17 00:00:00 2001
From: hev <wangrui at loongson.cn>
Date: Wed, 14 Jan 2026 16:12:18 +0800
Subject: [PATCH 03/10] [llvm][LoongArch] Add PC-relative address
 materialization using pcadd instructions (#175358)

This patch adds support for PC-relative address materialization using
pcadd-class relocations, covering the HI20/LO12 pair and their GOT and
TLS variants (IE, LD, GD, and DESC).

Link:
https://gcc.gnu.org/pipermail/gcc-patches/2025-December/703312.html
(cherry picked from commit c9cc782e0b5fddd22114cdf3c510ddea4959447f)
---
 .../AsmParser/LoongArchAsmParser.cpp          | 144 ++++++++---
 .../LoongArch/LoongArchExpandPseudoInsts.cpp  | 207 +++++++++++----
 .../LoongArch/LoongArchISelLowering.cpp       |  18 +-
 .../Target/LoongArch/LoongArchInstrInfo.cpp   |  56 ++--
 .../Target/LoongArch/LoongArchMCInstLower.cpp |  39 +++
 .../LoongArch/LoongArchMergeBaseOffset.cpp    |  55 ++--
 .../MCTargetDesc/LoongArchBaseInfo.h          |  12 +
 .../MCTargetDesc/LoongArchMCAsmInfo.cpp       |  36 +++
 llvm/test/CodeGen/LoongArch/block-address.ll  |  12 +-
 .../LoongArch/branch-relaxation-spill-32.ll   |   9 +-
 .../CodeGen/LoongArch/branch-relaxation.ll    |   5 +-
 .../CodeGen/LoongArch/calling-conv-ilp32d.ll  |  40 +--
 llvm/test/CodeGen/LoongArch/code-models.ll    | 239 +++++++++++-------
 .../test/CodeGen/LoongArch/ctlz-cttz-ctpop.ll |  20 +-
 llvm/test/CodeGen/LoongArch/double-imm.ll     |  70 +++--
 llvm/test/CodeGen/LoongArch/float-imm.ll      |  15 +-
 llvm/test/CodeGen/LoongArch/global-address.ll |  20 +-
 .../LoongArch/inline-asm-constraint-f.ll      |   5 +-
 .../LoongArch/inline-asm-constraint-m.ll      |  18 +-
 .../LoongArch/ir-instruction/load-store.ll    |  24 +-
 .../LoongArch/machinelicm-address-pseudos.ll  |  25 +-
 .../CodeGen/LoongArch/merge-base-offset.ll    | 221 +++++++++-------
 llvm/test/CodeGen/LoongArch/musttail.ll       |   5 +-
 .../CodeGen/LoongArch/numeric-reg-names.ll    |   5 +-
 .../LoongArch/stack-protector-target.ll       |  19 +-
 llvm/test/CodeGen/LoongArch/tls-models.ll     |  45 ++--
 .../LoongArch/unaligned-memcpy-inline.ll      |   5 +-
 llvm/test/CodeGen/LoongArch/vector-fp-imm.ll  |  10 +-
 .../JITLink/LoongArch/ELF_reloc_addsub.s      |   7 +-
 .../test/MC/LoongArch/Basic/Integer/invalid.s |   2 +-
 ...arch_generated_funcs.ll.generated.expected |   5 +-
 ...ch_generated_funcs.ll.nogenerated.expected |   5 +-
 32 files changed, 950 insertions(+), 448 deletions(-)

diff --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
index 5a6a0927a8974..c8c3a2bc9ce6d 100644
--- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
+++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
@@ -38,6 +38,7 @@ class LoongArchAsmParser : public MCTargetAsmParser {
 
   SMLoc getLoc() const { return getParser().getTok().getLoc(); }
   bool is64Bit() const { return getSTI().hasFeature(LoongArch::Feature64Bit); }
+  bool has32S() const { return getSTI().hasFeature(LoongArch::Feature32S); }
   LoongArchTargetStreamer &getTargetStreamer() {
     assert(getParser().getStreamer().getTargetStreamer() &&
            "do not have a target streamer");
@@ -328,8 +329,13 @@ class LoongArchOperand : public MCParsedAsmOperand {
     bool IsValidKind =
         VK == LoongArchMCExpr::VK_None || VK == ELF::R_LARCH_PCALA_LO12 ||
         VK == ELF::R_LARCH_GOT_PC_LO12 || VK == ELF::R_LARCH_TLS_IE_PC_LO12 ||
-        VK == ELF::R_LARCH_TLS_LE_LO12_R ||
-        VK == ELF::R_LARCH_TLS_DESC_PC_LO12 || VK == ELF::R_LARCH_TLS_DESC_LD;
+        VK == ELF::R_LARCH_TLS_LE_LO12_R || VK == ELF::R_LARCH_TLS_DESC_LD ||
+        VK == ELF::R_LARCH_TLS_DESC_PC_LO12 || VK == ELF::R_LARCH_PCADD_LO12 ||
+        VK == ELF::R_LARCH_GOT_PCADD_LO12 ||
+        VK == ELF::R_LARCH_TLS_IE_PCADD_LO12 ||
+        VK == ELF::R_LARCH_TLS_LD_PCADD_LO12 ||
+        VK == ELF::R_LARCH_TLS_GD_PCADD_LO12 ||
+        VK == ELF::R_LARCH_TLS_DESC_PCADD_LO12;
     return IsConstantImm
                ? isInt<12>(Imm) && IsValidKind
                : LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
@@ -471,7 +477,12 @@ class LoongArchOperand : public MCParsedAsmOperand {
     LoongArchMCExpr::Specifier VK = LoongArchMCExpr::VK_None;
     bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
     bool IsValidKind =
-        VK == LoongArchMCExpr::VK_None || VK == ELF::R_LARCH_CALL30;
+        VK == LoongArchMCExpr::VK_None || VK == ELF::R_LARCH_CALL30 ||
+        VK == ELF::R_LARCH_PCADD_HI20 || VK == ELF::R_LARCH_GOT_PCADD_HI20 ||
+        VK == ELF::R_LARCH_TLS_IE_PCADD_HI20 ||
+        VK == ELF::R_LARCH_TLS_LD_PCADD_HI20 ||
+        VK == ELF::R_LARCH_TLS_GD_PCADD_HI20 ||
+        VK == ELF::R_LARCH_TLS_DESC_PCADD_HI20;
 
     return IsConstantImm
                ? isInt<20>(Imm) && IsValidKind
@@ -885,6 +896,7 @@ void LoongArchAsmParser::emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg,
                                        SMLoc IDLoc, MCStreamer &Out,
                                        bool RelaxHint) {
   MCContext &Ctx = getContext();
+  MCSymbol *PCALabel = nullptr;
   for (LoongArchAsmParser::Inst &Inst : Insts) {
     unsigned Opc = Inst.Opc;
     auto VK = LoongArchMCExpr::Specifier(Inst.Specifier);
@@ -893,6 +905,10 @@ void LoongArchAsmParser::emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg,
     switch (Opc) {
     default:
       llvm_unreachable("unexpected opcode");
+    case LoongArch::PCADDU12I:
+      PCALabel = Ctx.createNamedTempSymbol("pcadd_hi");
+      Out.emitLabel(PCALabel);
+      LLVM_FALLTHROUGH;
     case LoongArch::PCALAU12I:
     case LoongArch::LU12I_W:
       Out.emitInstruction(MCInstBuilder(Opc).addReg(DestReg).addExpr(LE),
@@ -914,6 +930,18 @@ void LoongArchAsmParser::emitLAInstSeq(MCRegister DestReg, MCRegister TmpReg,
                                 .addExpr(LE),
                             getSTI());
         continue;
+      } else if (VK == ELF::R_LARCH_PCADD_LO12 ||
+                 VK == ELF::R_LARCH_GOT_PCADD_LO12 ||
+                 VK == ELF::R_LARCH_TLS_IE_PCADD_LO12 ||
+                 VK == ELF::R_LARCH_TLS_LD_PCADD_LO12 ||
+                 VK == ELF::R_LARCH_TLS_GD_PCADD_LO12 ||
+                 VK == ELF::R_LARCH_TLS_DESC_PCADD_LO12) {
+        Out.emitInstruction(
+            MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addExpr(
+                LoongArchMCExpr::create(MCSymbolRefExpr::create(PCALabel, Ctx),
+                                        VK, Ctx, RelaxHint)),
+            getSTI());
+        continue;
       }
       Out.emitInstruction(
           MCInstBuilder(Opc).addReg(DestReg).addReg(DestReg).addExpr(LE),
@@ -993,17 +1021,27 @@ void LoongArchAsmParser::emitLoadAddressAbs(MCInst &Inst, SMLoc IDLoc,
 void LoongArchAsmParser::emitLoadAddressPcrel(MCInst &Inst, SMLoc IDLoc,
                                               MCStreamer &Out) {
   // la.pcrel $rd, sym
-  // expands to:
+  //
+  // for la32r expands to:
+  // .Lpcadd_hi:
+  //   pcaddu12i $rd, %pcadd_hi20(sym)
+  //   addi.w    $rd, rd, %pcadd_lo12(.Lpcadd_hi)
+  //
+  // for la32s and la64 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 PCAI = has32S() ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
   unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
+  unsigned PCAIRel =
+      has32S() ? ELF::R_LARCH_PCALA_HI20 : ELF::R_LARCH_PCADD_HI20;
+  unsigned ADDIRel =
+      has32S() ? ELF::R_LARCH_PCALA_LO12 : ELF::R_LARCH_PCADD_LO12;
 
-  Insts.push_back(
-      LoongArchAsmParser::Inst(LoongArch::PCALAU12I, ELF::R_LARCH_PCALA_HI20));
-  Insts.push_back(LoongArchAsmParser::Inst(ADDI, ELF::R_LARCH_PCALA_LO12));
+  Insts.push_back(LoongArchAsmParser::Inst(PCAI, PCAIRel));
+  Insts.push_back(LoongArchAsmParser::Inst(ADDI, ADDIRel));
 
   emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out,
                 /*RelaxHint=*/true);
@@ -1042,7 +1080,12 @@ void LoongArchAsmParser::emitLoadAddressGot(MCInst &Inst, SMLoc IDLoc,
   MCRegister DestReg = Inst.getOperand(0).getReg();
   const MCExpr *Symbol = Inst.getOperand(1).getExpr();
   InstSeq Insts;
+  unsigned PCAI = has32S() ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
   unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
+  unsigned PCAIRel =
+      has32S() ? ELF::R_LARCH_GOT_PC_HI20 : ELF::R_LARCH_GOT_PCADD_HI20;
+  unsigned LDRel =
+      has32S() ? ELF::R_LARCH_GOT_PC_LO12 : ELF::R_LARCH_GOT_PCADD_LO12;
 
   if (getSTI().hasFeature(LoongArch::LaGlobalWithAbs)) {
     // with feature: +la-glabal-with-abs
@@ -1072,12 +1115,16 @@ void LoongArchAsmParser::emitLoadAddressGot(MCInst &Inst, SMLoc IDLoc,
     emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out);
     return;
   }
-  // expands to:
+  // for la32r expands to:
+  // .Lpcadd_hi:
+  //   pcaddu12i $rd, %got_pcadd_hi20(sym)
+  //   ld.w      $rd, $rd, %got_pcadd_lo12(.Lpcadd_hi)
+  //
+  // for la32s and la64 expands to:
   //   pcalau12i $rd, %got_pc_hi20(sym)
   //   ld.w/d    $rd, $rd, %got_pc_lo12(sym)
-  Insts.push_back(
-      LoongArchAsmParser::Inst(LoongArch::PCALAU12I, ELF::R_LARCH_GOT_PC_HI20));
-  Insts.push_back(LoongArchAsmParser::Inst(LD, ELF::R_LARCH_GOT_PC_LO12));
+  Insts.push_back(LoongArchAsmParser::Inst(PCAI, PCAIRel));
+  Insts.push_back(LoongArchAsmParser::Inst(LD, LDRel));
 
   emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out,
                 /*RelaxHint=*/true);
@@ -1134,7 +1181,12 @@ void LoongArchAsmParser::emitLoadAddressTLSIE(MCInst &Inst, SMLoc IDLoc,
   MCRegister DestReg = Inst.getOperand(0).getReg();
   const MCExpr *Symbol = Inst.getOperand(1).getExpr();
   InstSeq Insts;
+  unsigned PCAI = has32S() ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
   unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
+  unsigned PCAIRel =
+      has32S() ? ELF::R_LARCH_TLS_IE_PC_HI20 : ELF::R_LARCH_TLS_IE_PCADD_HI20;
+  unsigned LDRel =
+      has32S() ? ELF::R_LARCH_TLS_IE_PC_LO12 : ELF::R_LARCH_TLS_IE_PCADD_LO12;
 
   if (getSTI().hasFeature(LoongArch::LaGlobalWithAbs)) {
     // with feature: +la-glabal-with-abs
@@ -1165,12 +1217,16 @@ void LoongArchAsmParser::emitLoadAddressTLSIE(MCInst &Inst, SMLoc IDLoc,
     return;
   }
 
-  // expands to:
+  // for la32r expands to:
+  // .Lpcadd_hi:
+  //   pcaddu12i $rd, %ie_pcadd_hi20(sym)
+  //   ld.w      $rd, $rd, %ie_pcadd_lo12(.Lpcadd_hi)
+  //
+  // for la32s and la64 expands to:
   //   pcalau12i $rd, %ie_pc_hi20(sym)
   //   ld.w/d    $rd, $rd, %ie_pc_lo12(sym)
-  Insts.push_back(LoongArchAsmParser::Inst(LoongArch::PCALAU12I,
-                                           ELF::R_LARCH_TLS_IE_PC_HI20));
-  Insts.push_back(LoongArchAsmParser::Inst(LD, ELF::R_LARCH_TLS_IE_PC_LO12));
+  Insts.push_back(LoongArchAsmParser::Inst(PCAI, PCAIRel));
+  Insts.push_back(LoongArchAsmParser::Inst(LD, LDRel));
 
   emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out,
                 /*RelaxHint=*/true);
@@ -1209,7 +1265,12 @@ void LoongArchAsmParser::emitLoadAddressTLSLD(MCInst &Inst, SMLoc IDLoc,
   MCRegister DestReg = Inst.getOperand(0).getReg();
   const MCExpr *Symbol = Inst.getOperand(1).getExpr();
   InstSeq Insts;
+  unsigned PCAI = has32S() ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
   unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
+  unsigned PCAIRel =
+      has32S() ? ELF::R_LARCH_TLS_LD_PC_HI20 : ELF::R_LARCH_TLS_LD_PCADD_HI20;
+  unsigned ADDIRel =
+      has32S() ? ELF::R_LARCH_GOT_PC_LO12 : ELF::R_LARCH_TLS_LD_PCADD_LO12;
 
   if (getSTI().hasFeature(LoongArch::LaGlobalWithAbs)) {
     // with feature: +la-glabal-with-abs
@@ -1237,12 +1298,16 @@ void LoongArchAsmParser::emitLoadAddressTLSLD(MCInst &Inst, SMLoc IDLoc,
     return;
   }
 
-  // expands to:
+  // for la32r expands to:
+  // .Lpcadd_hi:
+  //   pcaddu12i $rd, %ld_pcadd_hi20(sym)
+  //   addi.w    $rd, $rd, %ld_pcadd_lo12(.Lpcadd_hi)
+  //
+  // for la32s and la64 expands to:
   //   pcalau12i $rd, %ld_pc_hi20(sym)
   //   addi.w/d  $rd, $rd, %got_pc_lo12(sym)
-  Insts.push_back(LoongArchAsmParser::Inst(LoongArch::PCALAU12I,
-                                           ELF::R_LARCH_TLS_LD_PC_HI20));
-  Insts.push_back(LoongArchAsmParser::Inst(ADDI, ELF::R_LARCH_GOT_PC_LO12));
+  Insts.push_back(LoongArchAsmParser::Inst(PCAI, PCAIRel));
+  Insts.push_back(LoongArchAsmParser::Inst(ADDI, ADDIRel));
 
   emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out,
                 /*RelaxHint=*/true);
@@ -1281,7 +1346,12 @@ void LoongArchAsmParser::emitLoadAddressTLSGD(MCInst &Inst, SMLoc IDLoc,
   MCRegister DestReg = Inst.getOperand(0).getReg();
   const MCExpr *Symbol = Inst.getOperand(1).getExpr();
   InstSeq Insts;
+  unsigned PCAI = has32S() ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
   unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
+  unsigned PCAIRel =
+      has32S() ? ELF::R_LARCH_TLS_GD_PC_HI20 : ELF::R_LARCH_TLS_GD_PCADD_HI20;
+  unsigned ADDIRel =
+      has32S() ? ELF::R_LARCH_GOT_PC_LO12 : ELF::R_LARCH_TLS_GD_PCADD_LO12;
 
   if (getSTI().hasFeature(LoongArch::LaGlobalWithAbs)) {
     // with feature: +la-glabal-with-abs
@@ -1309,12 +1379,16 @@ void LoongArchAsmParser::emitLoadAddressTLSGD(MCInst &Inst, SMLoc IDLoc,
     return;
   }
 
-  // expands to:
+  // for la32r expands to:
+  // .Lpcadd_hi:
+  //   pcaddu12i $rd, %gd_pcadd_hi20(sym)
+  //   addi.w    $rd, $rd, %gd_pcadd_lo12(.Lpcadd_hi)
+  //
+  // for la32s and la64 expands to:
   //   pcalau12i $rd, %gd_pc_hi20(sym)
   //   addi.w/d  $rd, $rd, %got_pc_lo12(sym)
-  Insts.push_back(LoongArchAsmParser::Inst(LoongArch::PCALAU12I,
-                                           ELF::R_LARCH_TLS_GD_PC_HI20));
-  Insts.push_back(LoongArchAsmParser::Inst(ADDI, ELF::R_LARCH_GOT_PC_LO12));
+  Insts.push_back(LoongArchAsmParser::Inst(PCAI, PCAIRel));
+  Insts.push_back(LoongArchAsmParser::Inst(ADDI, ADDIRel));
 
   emitLAInstSeq(DestReg, DestReg, Symbol, Insts, IDLoc, Out,
                 /*RelaxHint=*/true);
@@ -1352,8 +1426,13 @@ void LoongArchAsmParser::emitLoadAddressTLSDesc(MCInst &Inst, SMLoc IDLoc,
   // la.tls.desc $rd, sym
   MCRegister DestReg = Inst.getOperand(0).getReg();
   const MCExpr *Symbol = Inst.getOperand(1).getExpr();
+  unsigned PCAI = has32S() ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
   unsigned ADDI = is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
   unsigned LD = is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
+  unsigned PCAIRel = has32S() ? ELF::R_LARCH_TLS_DESC_PC_HI20
+                              : ELF::R_LARCH_TLS_DESC_PCADD_HI20;
+  unsigned ADDIRel = has32S() ? ELF::R_LARCH_TLS_DESC_PC_LO12
+                              : ELF::R_LARCH_TLS_DESC_PCADD_LO12;
   InstSeq Insts;
 
   if (getSTI().hasFeature(LoongArch::LaGlobalWithAbs)) {
@@ -1391,15 +1470,20 @@ void LoongArchAsmParser::emitLoadAddressTLSDesc(MCInst &Inst, SMLoc IDLoc,
     return;
   }
 
-  // expands to:
+  // for la32r expands to:
+  // .Lpcadd_hi:
+  //   pcaddu12i $rd, %desc_pcadd_hi20(sym)
+  //   addi.w    $rd, $rd, %desc_pcadd_lo12(.Lpcadd_hi)
+  //   ld.w      $ra, $rd, %desc_ld(sym)
+  //   jirl      $ra, $ra, %desc_call(sym)
+  //
+  // for la32s and la64 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,
-                                           ELF::R_LARCH_TLS_DESC_PC_HI20));
-  Insts.push_back(
-      LoongArchAsmParser::Inst(ADDI, ELF::R_LARCH_TLS_DESC_PC_LO12));
+  Insts.push_back(LoongArchAsmParser::Inst(PCAI, PCAIRel));
+  Insts.push_back(LoongArchAsmParser::Inst(ADDI, ADDIRel));
   Insts.push_back(LoongArchAsmParser::Inst(LD, ELF::R_LARCH_TLS_DESC_LD));
   Insts.push_back(
       LoongArchAsmParser::Inst(LoongArch::JIRL, ELF::R_LARCH_TLS_DESC_CALL));
@@ -1894,8 +1978,8 @@ bool LoongArchAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
     return generateImmOutOfRangeError(
         Operands, ErrorInfo, /*Lower=*/-(1 << 19),
         /*Upper=*/(1 << 19) - 1,
-        "operand must be a symbol with modifier (e.g. %call30) or an integer "
-        "in the range");
+        "operand must be a symbol with modifier (e.g. %call30 or %pcadd_hi20) "
+        "or an integer in the range");
   case Match_InvalidSImm20pcaddu18i:
     return generateImmOutOfRangeError(
         Operands, ErrorInfo, /*Lower=*/-(1 << 19),
diff --git a/llvm/lib/Target/LoongArch/LoongArchExpandPseudoInsts.cpp b/llvm/lib/Target/LoongArch/LoongArchExpandPseudoInsts.cpp
index 6cef279c6131c..b3167174454a3 100644
--- a/llvm/lib/Target/LoongArch/LoongArchExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchExpandPseudoInsts.cpp
@@ -57,11 +57,11 @@ class LoongArchPreRAExpandPseudo : public MachineFunctionPass {
   bool expandMBB(MachineBasicBlock &MBB);
   bool expandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
                 MachineBasicBlock::iterator &NextMBBI);
-  bool expandPcalau12iInstPair(MachineBasicBlock &MBB,
+  bool expandPcaxxu12iInstPair(MachineBasicBlock &MBB,
                                MachineBasicBlock::iterator MBBI,
                                MachineBasicBlock::iterator &NextMBBI,
-                               unsigned FlagsHi, unsigned SecondOpcode,
-                               unsigned FlagsLo);
+                               unsigned OpcodeHi, unsigned OpcodeLo,
+                               unsigned FlagsHi, unsigned FlagsLo);
   bool expandLargeAddressLoad(MachineBasicBlock &MBB,
                               MachineBasicBlock::iterator MBBI,
                               MachineBasicBlock::iterator &NextMBBI,
@@ -177,10 +177,10 @@ bool LoongArchPreRAExpandPseudo::expandMI(
   return false;
 }
 
-bool LoongArchPreRAExpandPseudo::expandPcalau12iInstPair(
+bool LoongArchPreRAExpandPseudo::expandPcaxxu12iInstPair(
     MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI,
-    MachineBasicBlock::iterator &NextMBBI, unsigned FlagsHi,
-    unsigned SecondOpcode, unsigned FlagsLo) {
+    MachineBasicBlock::iterator &NextMBBI, unsigned OpcodeHi, unsigned OpcodeLo,
+    unsigned FlagsHi, unsigned FlagsLo) {
   MachineFunction *MF = MBB.getParent();
   MachineInstr &MI = *MBBI;
   DebugLoc DL = MI.getDebugLoc();
@@ -193,13 +193,24 @@ bool LoongArchPreRAExpandPseudo::expandPcalau12iInstPair(
       MF->getRegInfo().createVirtualRegister(&LoongArch::GPRRegClass);
   MachineOperand &Symbol = MI.getOperand(1);
 
-  BuildMI(MBB, MBBI, DL, TII->get(LoongArch::PCALAU12I), ScratchReg)
-      .addDisp(Symbol, 0, LoongArchII::encodeFlags(FlagsHi, EnableRelax));
+  MachineInstr *FirstMI =
+      BuildMI(MBB, MBBI, DL, TII->get(OpcodeHi), ScratchReg)
+          .addDisp(Symbol, 0, LoongArchII::encodeFlags(FlagsHi, EnableRelax));
 
-  MachineInstr *SecondMI =
-      BuildMI(MBB, MBBI, DL, TII->get(SecondOpcode), DestReg)
-          .addReg(ScratchReg)
-          .addDisp(Symbol, 0, LoongArchII::encodeFlags(FlagsLo, EnableRelax));
+  MachineInstr *SecondMI = nullptr;
+  FlagsLo = LoongArchII::encodeFlags(FlagsLo, EnableRelax);
+
+  if (OpcodeHi == LoongArch::PCALAU12I) {
+    SecondMI = BuildMI(MBB, MBBI, DL, TII->get(OpcodeLo), DestReg)
+                   .addReg(ScratchReg)
+                   .addDisp(Symbol, 0, FlagsLo);
+  } else {
+    MCSymbol *PCAddSymbol = MF->getContext().createNamedTempSymbol("pcadd_hi");
+    FirstMI->setPreInstrSymbol(*MF, PCAddSymbol);
+    SecondMI = BuildMI(MBB, MBBI, DL, TII->get(OpcodeLo), DestReg)
+                   .addReg(ScratchReg)
+                   .addSym(PCAddSymbol, FlagsLo);
+  }
 
   if (MI.hasOneMemOperand())
     SecondMI->addMemOperand(*MF, *MI.memoperands_begin());
@@ -321,13 +332,26 @@ bool LoongArchPreRAExpandPseudo::expandLoadAddressPcrel(
                                   LoongArchII::MO_PCREL_LO);
 
   // Code Sequence:
-  // pcalau12i $rd, %pc_hi20(sym)
-  // addi.w/d $rd, $rd, %pc_lo12(sym)
+  //
+  // for la32r expands to:
+  // .Lpcadd_hi:
+  //   pcaddu12i $rd, %pcadd_hi20(sym)
+  //   addi.w    $rd, $rd, %pcadd_lo12(.Lpcadd_hi)
+  //
+  // for la32s and la64 expands to:
+  //   pcalau12i $rd, %pc_hi20(sym)
+  //   addi.w/d  $rd, $rd, %pc_lo12(sym)
   MachineFunction *MF = MBB.getParent();
   const auto &STI = MF->getSubtarget<LoongArchSubtarget>();
-  unsigned SecondOpcode = STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
-  return expandPcalau12iInstPair(MBB, MBBI, NextMBBI, LoongArchII::MO_PCREL_HI,
-                                 SecondOpcode, LoongArchII::MO_PCREL_LO);
+  bool Has32S = STI.hasFeature(LoongArch::Feature32S);
+  unsigned OpcodeHi = Has32S ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
+  unsigned OpcodeLo = STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
+  unsigned FlagsHi =
+      Has32S ? LoongArchII::MO_PCREL_HI : LoongArchII::MO_PCADD_HI;
+  unsigned FlagsLo =
+      Has32S ? LoongArchII::MO_PCREL_LO : LoongArchII::MO_PCADD_LO;
+  return expandPcaxxu12iInstPair(MBB, MBBI, NextMBBI, OpcodeHi, OpcodeLo,
+                                 FlagsHi, FlagsLo);
 }
 
 bool LoongArchPreRAExpandPseudo::expandLoadAddressGot(
@@ -340,13 +364,26 @@ bool LoongArchPreRAExpandPseudo::expandLoadAddressGot(
                                   LoongArchII::MO_GOT_PC_HI);
 
   // Code Sequence:
-  // pcalau12i $rd, %got_pc_hi20(sym)
-  // ld.w/d $rd, $rd, %got_pc_lo12(sym)
+  //
+  // for la32r expands to:
+  // .Lpcadd_hi:
+  //   pcaddu12i $rd, %got_pcadd_hi20(sym)
+  //   ld.w      $rd, $rd, %got_pcadd_lo12(.Lpcadd_hi)
+  //
+  // for la32s and la64 expands to:
+  //   pcalau12i $rd, %got_pc_hi20(sym)
+  //   ld.w/d    $rd, $rd, %got_pc_lo12(sym)
   MachineFunction *MF = MBB.getParent();
   const auto &STI = MF->getSubtarget<LoongArchSubtarget>();
-  unsigned SecondOpcode = STI.is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
-  return expandPcalau12iInstPair(MBB, MBBI, NextMBBI, LoongArchII::MO_GOT_PC_HI,
-                                 SecondOpcode, LoongArchII::MO_GOT_PC_LO);
+  bool Has32S = STI.hasFeature(LoongArch::Feature32S);
+  unsigned OpcodeHi = Has32S ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
+  unsigned OpcodeLo = STI.is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
+  unsigned FlagsHi =
+      Has32S ? LoongArchII::MO_GOT_PC_HI : LoongArchII::MO_GOT_PCADD_HI;
+  unsigned FlagsLo =
+      Has32S ? LoongArchII::MO_GOT_PC_LO : LoongArchII::MO_GOT_PCADD_LO;
+  return expandPcaxxu12iInstPair(MBB, MBBI, NextMBBI, OpcodeHi, OpcodeLo,
+                                 FlagsHi, FlagsLo);
 }
 
 bool LoongArchPreRAExpandPseudo::expandLoadAddressTLSLE(
@@ -424,13 +461,26 @@ bool LoongArchPreRAExpandPseudo::expandLoadAddressTLSIE(
                                   LoongArchII::MO_IE_PC_LO);
 
   // Code Sequence:
-  // pcalau12i $rd, %ie_pc_hi20(sym)
-  // ld.w/d $rd, $rd, %ie_pc_lo12(sym)
+  //
+  // for la32r expands to:
+  // .Lpcadd_hi:
+  //   pcaddu12i $rd, %ie_pcadd_hi20(sym)
+  //   ld.w      $rd, $rd, %ie_pcadd_lo12(.Lpcadd_hi)
+  //
+  // for la32s and la64 expands to:
+  //   pcalau12i $rd, %ie_pc_hi20(sym)
+  //   ld.w/d    $rd, $rd, %ie_pc_lo12(sym)
   MachineFunction *MF = MBB.getParent();
   const auto &STI = MF->getSubtarget<LoongArchSubtarget>();
-  unsigned SecondOpcode = STI.is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
-  return expandPcalau12iInstPair(MBB, MBBI, NextMBBI, LoongArchII::MO_IE_PC_HI,
-                                 SecondOpcode, LoongArchII::MO_IE_PC_LO);
+  bool Has32S = STI.hasFeature(LoongArch::Feature32S);
+  unsigned OpcodeHi = Has32S ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
+  unsigned OpcodeLo = STI.is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
+  unsigned FlagsHi =
+      Has32S ? LoongArchII::MO_IE_PC_HI : LoongArchII::MO_IE_PCADD_HI;
+  unsigned FlagsLo =
+      Has32S ? LoongArchII::MO_IE_PC_LO : LoongArchII::MO_IE_PCADD_LO;
+  return expandPcaxxu12iInstPair(MBB, MBBI, NextMBBI, OpcodeHi, OpcodeLo,
+                                 FlagsHi, FlagsLo);
 }
 
 bool LoongArchPreRAExpandPseudo::expandLoadAddressTLSLD(
@@ -443,13 +493,26 @@ bool LoongArchPreRAExpandPseudo::expandLoadAddressTLSLD(
                                   LoongArchII::MO_LD_PC_HI);
 
   // Code Sequence:
-  // pcalau12i $rd, %ld_pc_hi20(sym)
-  // addi.w/d $rd, $rd, %got_pc_lo12(sym)
+  //
+  // for la32r expands to:
+  // .Lpcadd_hi:
+  //   pcaddu12i $rd, %ld_pcadd_hi20(sym)
+  //   addi.w    $rd, $rd, %ld_pcadd_lo12(.Lpcadd_hi)
+  //
+  // for la32s and la64 expands to:
+  //   pcalau12i $rd, %ld_pc_hi20(sym)
+  //   addi.w/d  $rd, $rd, %got_pc_lo12(sym)
   MachineFunction *MF = MBB.getParent();
   const auto &STI = MF->getSubtarget<LoongArchSubtarget>();
-  unsigned SecondOpcode = STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
-  return expandPcalau12iInstPair(MBB, MBBI, NextMBBI, LoongArchII::MO_LD_PC_HI,
-                                 SecondOpcode, LoongArchII::MO_GOT_PC_LO);
+  bool Has32S = STI.hasFeature(LoongArch::Feature32S);
+  unsigned OpcodeHi = Has32S ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
+  unsigned OpcodeLo = STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
+  unsigned FlagsHi =
+      Has32S ? LoongArchII::MO_LD_PC_HI : LoongArchII::MO_LD_PCADD_HI;
+  unsigned FlagsLo =
+      Has32S ? LoongArchII::MO_GOT_PC_LO : LoongArchII::MO_LD_PCADD_LO;
+  return expandPcaxxu12iInstPair(MBB, MBBI, NextMBBI, OpcodeHi, OpcodeLo,
+                                 FlagsHi, FlagsLo);
 }
 
 bool LoongArchPreRAExpandPseudo::expandLoadAddressTLSGD(
@@ -462,13 +525,26 @@ bool LoongArchPreRAExpandPseudo::expandLoadAddressTLSGD(
                                   LoongArchII::MO_GD_PC_HI);
 
   // Code Sequence:
-  // pcalau12i $rd, %gd_pc_hi20(sym)
-  // addi.w/d $rd, $rd, %got_pc_lo12(sym)
+  //
+  // for la32r expands to:
+  // .Lpcadd_hi:
+  //   pcaddu12i $rd, %gd_pcadd_hi20(sym)
+  //   addi.w    $rd, $rd, %gd_pcadd_lo12(.Lpcadd_hi)
+  //
+  // for la32s and la64 expands to:
+  //   pcalau12i $rd, %gd_pc_hi20(sym)
+  //   addi.w/d  $rd, $rd, %got_pc_lo12(sym)
   MachineFunction *MF = MBB.getParent();
   const auto &STI = MF->getSubtarget<LoongArchSubtarget>();
-  unsigned SecondOpcode = STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
-  return expandPcalau12iInstPair(MBB, MBBI, NextMBBI, LoongArchII::MO_GD_PC_HI,
-                                 SecondOpcode, LoongArchII::MO_GOT_PC_LO);
+  bool Has32S = STI.hasFeature(LoongArch::Feature32S);
+  unsigned OpcodeHi = Has32S ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
+  unsigned OpcodeLo = STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
+  unsigned FlagsHi =
+      Has32S ? LoongArchII::MO_GD_PC_HI : LoongArchII::MO_GD_PCADD_HI;
+  unsigned FlagsLo =
+      Has32S ? LoongArchII::MO_GOT_PC_LO : LoongArchII::MO_GD_PCADD_LO;
+  return expandPcaxxu12iInstPair(MBB, MBBI, NextMBBI, OpcodeHi, OpcodeLo,
+                                 FlagsHi, FlagsLo);
 }
 
 bool LoongArchPreRAExpandPseudo::expandLoadAddressTLSDesc(
@@ -479,20 +555,24 @@ bool LoongArchPreRAExpandPseudo::expandLoadAddressTLSDesc(
   DebugLoc DL = MI.getDebugLoc();
 
   const auto &STI = MF->getSubtarget<LoongArchSubtarget>();
+  bool Has32S = STI.hasFeature(LoongArch::Feature32S);
+  bool EnableRelax = STI.hasFeature(LoongArch::FeatureRelax);
+  unsigned PCA = Has32S ? LoongArch::PCALAU12I : LoongArch::PCADDU12I;
   unsigned ADD = STI.is64Bit() ? LoongArch::ADD_D : LoongArch::ADD_W;
   unsigned ADDI = STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
   unsigned LD = STI.is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
-  bool EnableRelax = STI.hasFeature(LoongArch::FeatureRelax);
+  unsigned MO =
+      Has32S ? LoongArchII::MO_DESC_PC_HI : LoongArchII::MO_DESC_PCADD_HI;
 
   Register DestReg = MI.getOperand(0).getReg();
   Register Tmp1Reg =
       MF->getRegInfo().createVirtualRegister(&LoongArch::GPRRegClass);
   MachineOperand &Symbol = MI.getOperand(Large ? 2 : 1);
 
-  BuildMI(MBB, MBBI, DL, TII->get(LoongArch::PCALAU12I), Tmp1Reg)
-      .addDisp(Symbol, 0,
-               LoongArchII::encodeFlags(LoongArchII::MO_DESC_PC_HI,
-                                        EnableRelax && !Large));
+  MachineInstr *PCAMI =
+      BuildMI(MBB, MBBI, DL, TII->get(PCA), Tmp1Reg)
+          .addDisp(Symbol, 0,
+                   LoongArchII::encodeFlags(MO, EnableRelax && !Large));
 
   if (Large) {
     // Code Sequence:
@@ -527,16 +607,35 @@ bool LoongArchPreRAExpandPseudo::expandLoadAddressTLSDesc(
         .addReg(Tmp4Reg);
   } else {
     // Code Sequence:
-    // pcalau12i $a0, %desc_pc_hi20(sym)
-    // addi.w/d  $a0, $a0, %desc_pc_lo12(sym)
-    // ld.w/d    $ra, $a0, %desc_ld(sym)
-    // jirl      $ra, $ra, %desc_call(sym)
-    // add.w/d   $dst, $a0, $tp
-    BuildMI(MBB, MBBI, DL, TII->get(ADDI), LoongArch::R4)
-        .addReg(Tmp1Reg)
-        .addDisp(
-            Symbol, 0,
-            LoongArchII::encodeFlags(LoongArchII::MO_DESC_PC_LO, EnableRelax));
+    //
+    // for la32r expands to:
+    // .Lpcadd_hi:
+    //   pcaddu12i $a0, %desc_pcadd_hi20(sym)
+    //   addi.w    $a0, $a0, %desc_pcadd_lo12(.Lpcadd_hi)
+    //   ld.w      $ra, $a0, %desc_ld(sym)
+    //   jirl      $ra, $ra, %desc_call(sym)
+    //   add.w     $dst, $a0, $tp
+    //
+    // for la32s and la64 expands to:
+    //   pcalau12i $a0, %desc_pc_hi20(sym)
+    //   addi.w/d  $a0, $a0, %desc_pc_lo12(sym)
+    //   ld.w/d    $ra, $a0, %desc_ld(sym)
+    //   jirl      $ra, $ra, %desc_call(sym)
+    //   add.w/d   $dst, $a0, $tp
+    if (Has32S) {
+      BuildMI(MBB, MBBI, DL, TII->get(ADDI), LoongArch::R4)
+          .addReg(Tmp1Reg)
+          .addDisp(Symbol, 0,
+                   LoongArchII::encodeFlags(LoongArchII::MO_DESC_PC_LO,
+                                            EnableRelax));
+    } else {
+      MCSymbol *PCASymbol = MF->getContext().createNamedTempSymbol("pcadd_hi");
+      PCAMI->setPreInstrSymbol(*MF, PCASymbol);
+      BuildMI(MBB, MBBI, DL, TII->get(ADDI), LoongArch::R4)
+          .addReg(Tmp1Reg)
+          .addSym(PCASymbol, LoongArchII::encodeFlags(
+                                 LoongArchII::MO_DESC_PCADD_LO, EnableRelax));
+    }
   }
 
   BuildMI(MBB, MBBI, DL, TII->get(LD), LoongArch::R1)
@@ -646,8 +745,8 @@ void LoongArchPreRAExpandPseudo::annotateTableJump(
     }
   };
 
-  // FindDepth = 3, probably sufficient.
-  FindJTIMI(&*MBBI, /*FindDepth=*/3);
+  // FindDepth = 4, probably sufficient.
+  FindJTIMI(&*MBBI, /*FindDepth=*/4);
 }
 
 class LoongArchExpandPseudo : public MachineFunctionPass {
diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index 8451adcfa5faa..0ad103fe7a2a2 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -3678,13 +3678,23 @@ SDValue LoongArchTargetLowering::getAddr(NodeTy *N, SelectionDAG &DAG,
   case CodeModel::Small:
   case CodeModel::Medium:
     if (IsLocal) {
-      // This generates the pattern (PseudoLA_PCREL sym), which expands to
-      // (addi.w/d (pcalau12i %pc_hi20(sym)) %pc_lo12(sym)).
+      // This generates the pattern (PseudoLA_PCREL sym), which
+      //
+      // for la32r expands to:
+      //   (addi.w (pcaddu12i %pcadd_hi20(sym)) %pcadd_lo12(.Lpcadd_hi)).
+      //
+      // for la32s and la64 expands to:
+      //   (addi.w/d (pcalau12i %pc_hi20(sym)) %pc_lo12(sym)).
       Load = SDValue(
           DAG.getMachineNode(LoongArch::PseudoLA_PCREL, DL, Ty, Addr), 0);
     } else {
-      // This generates the pattern (PseudoLA_GOT sym), which expands to (ld.w/d
-      // (pcalau12i %got_pc_hi20(sym)) %got_pc_lo12(sym)).
+      // This generates the pattern (PseudoLA_GOT sym), which
+      //
+      // for la32r expands to:
+      //   (ld.w (pcaddu12i %got_pcadd_hi20(sym)) %pcadd_lo12(.Lpcadd_hi)).
+      //
+      // for la32s and la64 expands to:
+      //   (ld.w/d (pcalau12i %got_pc_hi20(sym)) %got_pc_lo12(sym)).
       Load =
           SDValue(DAG.getMachineNode(LoongArch::PseudoLA_GOT, DL, Ty, Addr), 0);
     }
diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp
index 44bb1f011dc6d..8de6eea10e562 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.cpp
@@ -18,6 +18,7 @@
 #include "MCTargetDesc/LoongArchMatInt.h"
 #include "llvm/CodeGen/RegisterScavenging.h"
 #include "llvm/CodeGen/StackMaps.h"
+#include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCInstBuilder.h"
 
 using namespace llvm;
@@ -630,30 +631,40 @@ void LoongArchInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB,
   const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo();
   LoongArchMachineFunctionInfo *LAFI =
       MF->getInfo<LoongArchMachineFunctionInfo>();
+  bool Has32S = STI.hasFeature(LoongArch::Feature32S);
 
   if (!isInt<32>(BrOffset))
     report_fatal_error(
         "Branch offsets outside of the signed 32-bit range not supported");
 
   Register ScratchReg = MRI.createVirtualRegister(&LoongArch::GPRRegClass);
+  MachineInstr *PCAI = nullptr;
+  MachineInstr *ADDI = nullptr;
   auto II = MBB.end();
-
-  MachineInstr &PCALAU12I =
-      *BuildMI(MBB, II, DL, get(LoongArch::PCALAU12I), ScratchReg)
-           .addMBB(&DestBB, LoongArchII::MO_PCREL_HI);
-  MachineInstr &ADDI =
-      *BuildMI(MBB, II, DL,
-               get(STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W),
-               ScratchReg)
-           .addReg(ScratchReg)
-           .addMBB(&DestBB, LoongArchII::MO_PCREL_LO);
+  unsigned ADDIOp = STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
+
+  if (Has32S) {
+    PCAI = BuildMI(MBB, II, DL, get(LoongArch::PCALAU12I), ScratchReg)
+               .addMBB(&DestBB, LoongArchII::MO_PCREL_HI);
+    ADDI = BuildMI(MBB, II, DL, get(ADDIOp), ScratchReg)
+               .addReg(ScratchReg)
+               .addMBB(&DestBB, LoongArchII::MO_PCREL_LO);
+  } else {
+    MCSymbol *PCAddSymbol = MF->getContext().createNamedTempSymbol("pcadd_hi");
+    PCAI = BuildMI(MBB, II, DL, get(LoongArch::PCADDU12I), ScratchReg)
+               .addMBB(&DestBB, LoongArchII::MO_PCADD_HI);
+    PCAI->setPreInstrSymbol(*MF, PCAddSymbol);
+    ADDI = BuildMI(MBB, II, DL, get(ADDIOp), ScratchReg)
+               .addReg(ScratchReg)
+               .addSym(PCAddSymbol, LoongArchII::MO_PCADD_LO);
+  }
   BuildMI(MBB, II, DL, get(LoongArch::PseudoBRIND))
       .addReg(ScratchReg, RegState::Kill)
       .addImm(0);
 
   RS->enterBasicBlockEnd(MBB);
   Register Scav = RS->scavengeRegisterBackwards(
-      LoongArch::GPRRegClass, PCALAU12I.getIterator(), /*RestoreAfter=*/false,
+      LoongArch::GPRRegClass, PCAI->getIterator(), /*RestoreAfter=*/false,
       /*SPAdj=*/0, /*AllowSpill=*/false);
   if (Scav != LoongArch::NoRegister)
     RS->setRegUsed(Scav);
@@ -664,12 +675,13 @@ void LoongArchInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB,
     int FrameIndex = LAFI->getBranchRelaxationSpillFrameIndex();
     if (FrameIndex == -1)
       report_fatal_error("The function size is incorrectly estimated.");
-    storeRegToStackSlot(MBB, PCALAU12I, Scav, /*IsKill=*/true, FrameIndex,
+    storeRegToStackSlot(MBB, PCAI, Scav, /*IsKill=*/true, FrameIndex,
                         &LoongArch::GPRRegClass, Register());
-    TRI->eliminateFrameIndex(std::prev(PCALAU12I.getIterator()),
+    TRI->eliminateFrameIndex(std::prev(PCAI->getIterator()),
                              /*SpAdj=*/0, /*FIOperandNum=*/1);
-    PCALAU12I.getOperand(1).setMBB(&RestoreBB);
-    ADDI.getOperand(2).setMBB(&RestoreBB);
+    PCAI->getOperand(1).setMBB(&RestoreBB);
+    if (Has32S)
+      ADDI->getOperand(2).setMBB(&RestoreBB);
     loadRegFromStackSlot(RestoreBB, RestoreBB.end(), Scav, FrameIndex,
                          &LoongArch::GPRRegClass, Register());
     TRI->eliminateFrameIndex(RestoreBB.back(),
@@ -754,7 +766,19 @@ LoongArchInstrInfo::getSerializableDirectMachineOperandTargetFlags() const {
       {MO_DESC_CALL, "loongarch-desc-call"},
       {MO_LE_HI_R, "loongarch-le-hi-r"},
       {MO_LE_ADD_R, "loongarch-le-add-r"},
-      {MO_LE_LO_R, "loongarch-le-lo-r"}};
+      {MO_LE_LO_R, "loongarch-le-lo-r"},
+      {MO_PCADD_HI, "loongarch-pcadd-hi"},
+      {MO_PCADD_LO, "loongarch-pcadd-lo"},
+      {MO_GOT_PCADD_HI, "loongarch-got-pcadd-hi"},
+      {MO_GOT_PCADD_LO, "loongarch-got-pcadd-lo"},
+      {MO_IE_PCADD_HI, "loongarch-ie-pcadd-hi"},
+      {MO_IE_PCADD_LO, "loongarch-ie-pcadd-lo"},
+      {MO_LD_PCADD_HI, "loongarch-ld-pcadd-hi"},
+      {MO_LD_PCADD_LO, "loongarch-ld-pcadd-lo"},
+      {MO_GD_PCADD_HI, "loongarch-gd-pcadd-hi"},
+      {MO_GD_PCADD_LO, "loongarch-gd-pcadd-lo"},
+      {MO_DESC_PCADD_HI, "loongarch-pcadd-desc-hi"},
+      {MO_DESC_PCADD_LO, "loongarch-pcadd-desc-lo"}};
   return ArrayRef(TargetFlags);
 }
 
diff --git a/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp b/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp
index ae4090784a9f2..6f9c5070f1768 100644
--- a/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp
@@ -125,6 +125,42 @@ static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym,
   case LoongArchII::MO_LE_LO_R:
     Kind = ELF::R_LARCH_TLS_LE_LO12_R;
     break;
+  case LoongArchII::MO_PCADD_HI:
+    Kind = ELF::R_LARCH_PCADD_HI20;
+    break;
+  case LoongArchII::MO_PCADD_LO:
+    Kind = ELF::R_LARCH_PCADD_LO12;
+    break;
+  case LoongArchII::MO_GOT_PCADD_HI:
+    Kind = ELF::R_LARCH_GOT_PCADD_HI20;
+    break;
+  case LoongArchII::MO_GOT_PCADD_LO:
+    Kind = ELF::R_LARCH_GOT_PCADD_LO12;
+    break;
+  case LoongArchII::MO_IE_PCADD_HI:
+    Kind = ELF::R_LARCH_TLS_IE_PCADD_HI20;
+    break;
+  case LoongArchII::MO_IE_PCADD_LO:
+    Kind = ELF::R_LARCH_TLS_IE_PCADD_LO12;
+    break;
+  case LoongArchII::MO_LD_PCADD_HI:
+    Kind = ELF::R_LARCH_TLS_LD_PCADD_HI20;
+    break;
+  case LoongArchII::MO_LD_PCADD_LO:
+    Kind = ELF::R_LARCH_TLS_LD_PCADD_LO12;
+    break;
+  case LoongArchII::MO_GD_PCADD_HI:
+    Kind = ELF::R_LARCH_TLS_GD_PCADD_HI20;
+    break;
+  case LoongArchII::MO_GD_PCADD_LO:
+    Kind = ELF::R_LARCH_TLS_GD_PCADD_LO12;
+    break;
+  case LoongArchII::MO_DESC_PCADD_HI:
+    Kind = ELF::R_LARCH_TLS_DESC_PCADD_HI20;
+    break;
+  case LoongArchII::MO_DESC_PCADD_LO:
+    Kind = ELF::R_LARCH_TLS_DESC_PCADD_LO12;
+    break;
     // TODO: Handle more target-flags.
   }
 
@@ -178,6 +214,9 @@ bool llvm::lowerLoongArchMachineOperandToMCOperand(const MachineOperand &MO,
   case MachineOperand::MO_JumpTableIndex:
     MCOp = lowerSymbolOperand(MO, AP.GetJTISymbol(MO.getIndex()), AP);
     break;
+  case MachineOperand::MO_MCSymbol:
+    MCOp = lowerSymbolOperand(MO, MO.getMCSymbol(), AP);
+    break;
   }
   return true;
 }
diff --git a/llvm/lib/Target/LoongArch/LoongArchMergeBaseOffset.cpp b/llvm/lib/Target/LoongArch/LoongArchMergeBaseOffset.cpp
index a2292d016efa1..3b111d29316d6 100644
--- a/llvm/lib/Target/LoongArch/LoongArchMergeBaseOffset.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchMergeBaseOffset.cpp
@@ -76,10 +76,23 @@ char LoongArchMergeBaseOffsetOpt::ID = 0;
 INITIALIZE_PASS(LoongArchMergeBaseOffsetOpt, DEBUG_TYPE,
                 LoongArch_MERGE_BASE_OFFSET_NAME, false, false)
 
+static inline bool isPCAddLo(unsigned Flags) {
+  switch (Flags) {
+  case LoongArchII::MO_PCADD_LO:
+  case LoongArchII::MO_GOT_PCADD_LO:
+  case LoongArchII::MO_IE_PCADD_LO:
+  case LoongArchII::MO_LD_PCADD_LO:
+  case LoongArchII::MO_GD_PCADD_LO:
+  case LoongArchII::MO_DESC_PCADD_LO:
+    return true;
+  }
+  return false;
+}
+
 // Detect either of the patterns:
 //
 // 1. (small/medium):
-//   pcalau12i vreg1, %pc_hi20(s)
+//   pcaxxu12i vreg1, %pc_hi20(s)
 //   addi.d    vreg2, vreg1, %pc_lo12(s)
 //
 // 2. (large):
@@ -102,11 +115,13 @@ bool LoongArchMergeBaseOffsetOpt::detectFoldable(MachineInstr &Hi20,
                                                  MachineInstr *&Lo20,
                                                  MachineInstr *&Hi12,
                                                  MachineInstr *&Last) {
-  if (Hi20.getOpcode() != LoongArch::PCALAU12I)
+  if (Hi20.getOpcode() != LoongArch::PCALAU12I &&
+      Hi20.getOpcode() != LoongArch::PCADDU12I)
     return false;
 
   const MachineOperand &Hi20Op1 = Hi20.getOperand(1);
-  if (LoongArchII::getDirectFlags(Hi20Op1) != LoongArchII::MO_PCREL_HI)
+  if (LoongArchII::getDirectFlags(Hi20Op1) != LoongArchII::MO_PCREL_HI &&
+      LoongArchII::getDirectFlags(Hi20Op1) != LoongArchII::MO_PCADD_HI)
     return false;
 
   auto isGlobalOrCPIOrBlockAddress = [](const MachineOperand &Op) {
@@ -157,8 +172,10 @@ bool LoongArchMergeBaseOffsetOpt::detectFoldable(MachineInstr &Hi20,
   }
 
   const MachineOperand &Lo12Op2 = Lo12->getOperand(2);
-  assert(Hi20.getOpcode() == LoongArch::PCALAU12I);
-  if (LoongArchII::getDirectFlags(Lo12Op2) != LoongArchII::MO_PCREL_LO ||
+  assert(Hi20.getOpcode() == LoongArch::PCALAU12I ||
+         Hi20.getOpcode() == LoongArch::PCADDU12I);
+  if ((LoongArchII::getDirectFlags(Lo12Op2) != LoongArchII::MO_PCREL_LO &&
+       !isPCAddLo(LoongArchII::getDirectFlags(Lo12Op2))) ||
       !(isGlobalOrCPIOrBlockAddress(Lo12Op2) || Lo12Op2.isMCSymbol()) ||
       Lo12Op2.getOffset() != 0)
     return false;
@@ -258,8 +275,10 @@ void LoongArchMergeBaseOffsetOpt::foldOffset(
     MachineInstr *&Hi12, MachineInstr *&Last, MachineInstr &Tail,
     int64_t Offset) {
   // Put the offset back in Hi and the Lo
+  MachineOperand &Lo12Op2 = Lo12.getOperand(2);
   Hi20.getOperand(1).setOffset(Offset);
-  Lo12.getOperand(2).setOffset(Offset);
+  if (!isPCAddLo(LoongArchII::getDirectFlags(Lo12Op2)))
+    Lo12Op2.setOffset(Offset);
   if (Lo20 && Hi12) {
     Lo20->getOperand(2).setOffset(Offset);
     Hi12->getOperand(2).setOffset(Offset);
@@ -298,7 +317,7 @@ void LoongArchMergeBaseOffsetOpt::foldOffset(
 //
 //        Base address lowering is of the form:
 //           1) pcala:
-//             Hi20:  pcalau12i vreg1, %pc_hi20(s)
+//             Hi20:  pcaxxu12i vreg1, %pc_hi20(s)
 //        +--- Lo12:  addi.d vreg2, vreg1, %pc_lo12(s)
 //        |    Lo20:  lu32i.d vreg2, %pc64_lo20(s) !
 //        +--- Hi12:  lu52i.d vreg2, vreg2, %pc64_hi12(s) !
@@ -426,7 +445,7 @@ bool LoongArchMergeBaseOffsetOpt::detectAndFoldOffset(MachineInstr &Hi20,
 
   // Look for arithmetic instructions we can get an offset from.
   // We might be able to remove the arithmetic instructions by folding the
-  // offset into the PCALAU12I+(ADDI/ADDI+LU32I+LU52I) or
+  // offset into the PCAXXU12I+(ADDI/ADDI+LU32I+LU52I) or
   // LU12I_W+PseudoAddTPRel+ADDI.
   if (!MRI->hasOneUse(DestReg))
     return false;
@@ -548,13 +567,13 @@ bool LoongArchMergeBaseOffsetOpt::foldIntoMemoryOps(MachineInstr &Hi20,
   //
   // 1. (small/medium):
   //  1.1. pcala
-  //   pcalau12i vreg1, %pc_hi20(s)
+  //   pcaxxu12i vreg1, %pc_hi20(s)
   //   addi.d    vreg2, vreg1, %pc_lo12(s)
   //   ld.w      vreg3, 8(vreg2)
   //
   //   =>
   //
-  //   pcalau12i vreg1, %pc_hi20(s+8)
+  //   pcalxx12i vreg1, %pc_hi20(s+8)
   //   ld.w      vreg3, vreg1, %pc_lo12(s+8)(vreg1)
   //
   //  1.2. tls-le
@@ -708,13 +727,13 @@ bool LoongArchMergeBaseOffsetOpt::foldIntoMemoryOps(MachineInstr &Hi20,
   // be relaxed after being optimized.
   //
   // For example:
-  //   pcalau12i $a0, %pc_hi20(symbol)
+  //   pcaxxu12i $a0, %pc_hi20(symbol)
   //   addi.d $a0, $a0, %pc_lo12(symbol)
   //   ld.w $a0, $a0, 0
   //
   //   =>
   //
-  //   pcalau12i $a0, %pc_hi20(symbol)
+  //   pcaxxu12i $a0, %pc_hi20(symbol)
   //   ld.w $a0, $a0, %pc_lo12(symbol)
   //
   // Code sequence optimized before can be relax by linker. But after being
@@ -722,12 +741,14 @@ bool LoongArchMergeBaseOffsetOpt::foldIntoMemoryOps(MachineInstr &Hi20,
   // carried by them.
   Hi20.getOperand(1).setOffset(NewOffset);
   MachineOperand &ImmOp = Lo12.getOperand(2);
-  ImmOp.setOffset(NewOffset);
+  if (!isPCAddLo(LoongArchII::getDirectFlags(ImmOp)))
+    ImmOp.setOffset(NewOffset);
   if (Lo20 && Hi12) {
     Lo20->getOperand(2).setOffset(NewOffset);
     Hi12->getOperand(2).setOffset(NewOffset);
   }
-  if (Hi20.getOpcode() == LoongArch::PCALAU12I) {
+  if (Hi20.getOpcode() == LoongArch::PCADDU12I ||
+      Hi20.getOpcode() == LoongArch::PCALAU12I) {
     Hi20.getOperand(1).setTargetFlags(
         LoongArchII::getDirectFlags(Hi20.getOperand(1)));
     ImmOp.setTargetFlags(LoongArchII::getDirectFlags(ImmOp));
@@ -789,7 +810,8 @@ bool LoongArchMergeBaseOffsetOpt::foldIntoMemoryOps(MachineInstr &Hi20,
     return true;
   }
 
-  if (Hi20.getOpcode() == LoongArch::PCALAU12I) {
+  if (Hi20.getOpcode() == LoongArch::PCADDU12I ||
+      Hi20.getOpcode() == LoongArch::PCALAU12I) {
     MRI->replaceRegWith(Lo12.getOperand(0).getReg(),
                         Hi20.getOperand(0).getReg());
   } else if (Hi20.getOpcode() == LoongArch::LU12I_W) {
@@ -816,7 +838,8 @@ bool LoongArchMergeBaseOffsetOpt::runOnMachineFunction(MachineFunction &Fn) {
       MachineInstr *Lo20 = nullptr;
       MachineInstr *Hi12 = nullptr;
       MachineInstr *Last = nullptr;
-      if (Hi20.getOpcode() == LoongArch::PCALAU12I) {
+      if (Hi20.getOpcode() == LoongArch::PCADDU12I ||
+          Hi20.getOpcode() == LoongArch::PCALAU12I) {
         // Detect foldable pcala code sequence in small/medium/large code model.
         if (!detectFoldable(Hi20, Lo12, Lo20, Hi12, Last))
           continue;
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h
index 99d7df63fce04..e73b49efc5895 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h
@@ -59,6 +59,18 @@ enum {
   MO_LE_HI_R,
   MO_LE_ADD_R,
   MO_LE_LO_R,
+  MO_PCADD_HI,
+  MO_PCADD_LO,
+  MO_GOT_PCADD_HI,
+  MO_GOT_PCADD_LO,
+  MO_IE_PCADD_HI,
+  MO_IE_PCADD_LO,
+  MO_LD_PCADD_HI,
+  MO_LD_PCADD_LO,
+  MO_GD_PCADD_HI,
+  MO_GD_PCADD_LO,
+  MO_DESC_PCADD_HI,
+  MO_DESC_PCADD_LO,
   // TODO: Add more flags.
 
   // Used to differentiate between target-specific "direct" flags and "bitmask"
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCAsmInfo.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCAsmInfo.cpp
index 3f9bea5d69bf9..25e91ac2a2dad 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCAsmInfo.cpp
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCAsmInfo.cpp
@@ -135,6 +135,30 @@ static StringRef getLoongArchSpecifierName(uint16_t S) {
     return "gd_pcrel_20";
   case ELF::R_LARCH_TLS_DESC_PCREL20_S2:
     return "desc_pcrel_20";
+  case ELF::R_LARCH_PCADD_HI20:
+    return "pcadd_hi20";
+  case ELF::R_LARCH_PCADD_LO12:
+    return "pcadd_lo12";
+  case ELF::R_LARCH_GOT_PCADD_HI20:
+    return "got_pcadd_hi20";
+  case ELF::R_LARCH_GOT_PCADD_LO12:
+    return "got_pcadd_lo12";
+  case ELF::R_LARCH_TLS_IE_PCADD_HI20:
+    return "ie_pcadd_hi20";
+  case ELF::R_LARCH_TLS_IE_PCADD_LO12:
+    return "ie_pcadd_lo12";
+  case ELF::R_LARCH_TLS_LD_PCADD_HI20:
+    return "ld_pcadd_hi20";
+  case ELF::R_LARCH_TLS_LD_PCADD_LO12:
+    return "ld_pcadd_lo12";
+  case ELF::R_LARCH_TLS_GD_PCADD_HI20:
+    return "gd_pcadd_hi20";
+  case ELF::R_LARCH_TLS_GD_PCADD_LO12:
+    return "gd_pcadd_lo12";
+  case ELF::R_LARCH_TLS_DESC_PCADD_HI20:
+    return "desc_pcadd_hi20";
+  case ELF::R_LARCH_TLS_DESC_PCADD_LO12:
+    return "desc_pcadd_lo12";
   }
 }
 
@@ -195,6 +219,18 @@ LoongArchMCExpr::Specifier LoongArch::parseSpecifier(StringRef name) {
       .Case("ld_pcrel_20", ELF::R_LARCH_TLS_LD_PCREL20_S2)
       .Case("gd_pcrel_20", ELF::R_LARCH_TLS_GD_PCREL20_S2)
       .Case("desc_pcrel_20", ELF::R_LARCH_TLS_DESC_PCREL20_S2)
+      .Case("pcadd_hi20", ELF::R_LARCH_PCADD_HI20)
+      .Case("pcadd_lo12", ELF::R_LARCH_PCADD_LO12)
+      .Case("got_pcadd_hi20", ELF::R_LARCH_GOT_PCADD_HI20)
+      .Case("got_pcadd_lo12", ELF::R_LARCH_GOT_PCADD_LO12)
+      .Case("ie_pcadd_hi20", ELF::R_LARCH_TLS_IE_PCADD_HI20)
+      .Case("ie_pcadd_lo12", ELF::R_LARCH_TLS_IE_PCADD_LO12)
+      .Case("ld_pcadd_hi20", ELF::R_LARCH_TLS_LD_PCADD_HI20)
+      .Case("ld_pcadd_lo12", ELF::R_LARCH_TLS_LD_PCADD_LO12)
+      .Case("gd_pcadd_hi20", ELF::R_LARCH_TLS_GD_PCADD_HI20)
+      .Case("gd_pcadd_lo12", ELF::R_LARCH_TLS_GD_PCADD_LO12)
+      .Case("desc_pcadd_hi20", ELF::R_LARCH_TLS_DESC_PCADD_HI20)
+      .Case("desc_pcadd_lo12", ELF::R_LARCH_TLS_DESC_PCADD_LO12)
       .Default(0);
 }
 
diff --git a/llvm/test/CodeGen/LoongArch/block-address.ll b/llvm/test/CodeGen/LoongArch/block-address.ll
index 114cbb73a5125..6b8c8ce93f944 100644
--- a/llvm/test/CodeGen/LoongArch/block-address.ll
+++ b/llvm/test/CodeGen/LoongArch/block-address.ll
@@ -7,11 +7,13 @@
 define void @test_blockaddress() nounwind {
 ; LA32-LABEL: test_blockaddress:
 ; LA32:       # %bb.0:
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(addr)
-; LA32-NEXT:    pcalau12i $a1, %pc_hi20(.Ltmp0)
-; LA32-NEXT:    addi.w $a1, $a1, %pc_lo12(.Ltmp0)
-; LA32-NEXT:    st.w $a1, $a0, %pc_lo12(addr)
-; LA32-NEXT:    ld.w $a0, $a0, %pc_lo12(addr)
+; LA32-NEXT:  .Lpcadd_hi0:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(addr)
+; LA32-NEXT:  .Lpcadd_hi1:
+; LA32-NEXT:    pcaddu12i $a1, %pcadd_hi20(.Ltmp0)
+; LA32-NEXT:    addi.w $a1, $a1, %pcadd_lo12(.Lpcadd_hi1)
+; LA32-NEXT:    st.w $a1, $a0, %pcadd_lo12(.Lpcadd_hi0)
+; LA32-NEXT:    ld.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-NEXT:    jr $a0
 ; LA32-NEXT:  .Ltmp0: # Block address taken
 ; LA32-NEXT:  .LBB0_1: # %block
diff --git a/llvm/test/CodeGen/LoongArch/branch-relaxation-spill-32.ll b/llvm/test/CodeGen/LoongArch/branch-relaxation-spill-32.ll
index 5c96d5c416b9c..e37863efdf5f3 100644
--- a/llvm/test/CodeGen/LoongArch/branch-relaxation-spill-32.ll
+++ b/llvm/test/CodeGen/LoongArch/branch-relaxation-spill-32.ll
@@ -122,9 +122,10 @@ define void @relax_b28_spill() {
 ; CHECK-NEXT:    #NO_APP
 ; CHECK-NEXT:    beq $s7, $s8, .LBB0_1
 ; CHECK-NEXT:  # %bb.4:
-; CHECK-NEXT:    st.w $t8, $sp, 0
-; CHECK-NEXT:    pcalau12i $t8, %pc_hi20(.LBB0_5)
-; CHECK-NEXT:    addi.w $t8, $t8, %pc_lo12(.LBB0_5)
+; CHECK-NEXT:    st.w $t8, $sp, 0 # 4-byte Folded Spill
+; CHECK-NEXT:  .Lpcadd_hi0:
+; CHECK-NEXT:    pcaddu12i $t8, %pcadd_hi20(.LBB0_5)
+; CHECK-NEXT:    addi.w $t8, $t8, %pcadd_lo12(.Lpcadd_hi0)
 ; CHECK-NEXT:    jr $t8
 ; CHECK-NEXT:  .LBB0_1: # %iftrue
 ; CHECK-NEXT:    #APP
@@ -132,7 +133,7 @@ define void @relax_b28_spill() {
 ; CHECK-NEXT:    #NO_APP
 ; CHECK-NEXT:    b .LBB0_3
 ; CHECK-NEXT:  .LBB0_5: # %iffalse
-; CHECK-NEXT:    ld.w $t8, $sp, 0
+; CHECK-NEXT:    ld.w $t8, $sp, 0 # 4-byte Folded Reload
 ; CHECK-NEXT:  # %bb.2: # %iffalse
 ; CHECK-NEXT:    #APP
 ; CHECK-NEXT:    # reg use $zero
diff --git a/llvm/test/CodeGen/LoongArch/branch-relaxation.ll b/llvm/test/CodeGen/LoongArch/branch-relaxation.ll
index f88603b6c79db..ffeaa5283ada8 100644
--- a/llvm/test/CodeGen/LoongArch/branch-relaxation.ll
+++ b/llvm/test/CodeGen/LoongArch/branch-relaxation.ll
@@ -119,8 +119,9 @@ define i32 @relax_b28(i1 %a) {
 ; LA32R-NEXT:    andi $a0, $a0, 1
 ; LA32R-NEXT:    bne $a0, $zero, .LBB2_1
 ; LA32R-NEXT:  # %bb.3:
-; LA32R-NEXT:    pcalau12i $a0, %pc_hi20(.LBB2_2)
-; LA32R-NEXT:    addi.w $a0, $a0, %pc_lo12(.LBB2_2)
+; LA32R-NEXT:  .Lpcadd_hi0:
+; LA32R-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LBB2_2)
+; LA32R-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32R-NEXT:    jr $a0
 ; LA32R-NEXT:  .LBB2_1: # %iftrue
 ; LA32R-NEXT:    ori $a0, $zero, 1
diff --git a/llvm/test/CodeGen/LoongArch/calling-conv-ilp32d.ll b/llvm/test/CodeGen/LoongArch/calling-conv-ilp32d.ll
index 95f9aa514b340..f8b6dd3fd038f 100644
--- a/llvm/test/CodeGen/LoongArch/calling-conv-ilp32d.ll
+++ b/llvm/test/CodeGen/LoongArch/calling-conv-ilp32d.ll
@@ -75,14 +75,18 @@ define i32 @caller_double_in_gpr_exhausted_fprs() nounwind {
 ; CHECK-NEXT:    lu12i.w $a0, 262400
 ; CHECK-NEXT:    fmov.d $fa3, $fa7
 ; CHECK-NEXT:    movgr2frh.w $fa3, $a0
-; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI3_0)
-; CHECK-NEXT:    fld.d $fa2, $a0, %pc_lo12(.LCPI3_0)
-; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI3_1)
-; CHECK-NEXT:    fld.d $fa4, $a0, %pc_lo12(.LCPI3_1)
-; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI3_2)
-; CHECK-NEXT:    fld.d $fa5, $a0, %pc_lo12(.LCPI3_2)
-; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI3_3)
-; CHECK-NEXT:    fld.d $fa6, $a0, %pc_lo12(.LCPI3_3)
+; CHECK-NEXT:  .Lpcadd_hi0:
+; CHECK-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI3_0)
+; CHECK-NEXT:    fld.d $fa2, $a0, %pcadd_lo12(.Lpcadd_hi0)
+; CHECK-NEXT:  .Lpcadd_hi1:
+; CHECK-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI3_1)
+; CHECK-NEXT:    fld.d $fa4, $a0, %pcadd_lo12(.Lpcadd_hi1)
+; CHECK-NEXT:  .Lpcadd_hi2:
+; CHECK-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI3_2)
+; CHECK-NEXT:    fld.d $fa5, $a0, %pcadd_lo12(.Lpcadd_hi2)
+; CHECK-NEXT:  .Lpcadd_hi3:
+; CHECK-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI3_3)
+; CHECK-NEXT:    fld.d $fa6, $a0, %pcadd_lo12(.Lpcadd_hi3)
 ; CHECK-NEXT:    lu12i.w $a0, 262656
 ; CHECK-NEXT:    movgr2frh.w $fa7, $a0
 ; CHECK-NEXT:    lu12i.w $a1, 262688
@@ -139,14 +143,18 @@ define i32 @caller_double_on_stack_exhausted_fprs_gprs() nounwind {
 ; CHECK-NEXT:    movgr2frh.w $fa3, $a0
 ; CHECK-NEXT:    lu12i.w $a0, 262656
 ; CHECK-NEXT:    movgr2frh.w $fa7, $a0
-; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI5_0)
-; CHECK-NEXT:    fld.d $fa2, $a0, %pc_lo12(.LCPI5_0)
-; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI5_1)
-; CHECK-NEXT:    fld.d $fa4, $a0, %pc_lo12(.LCPI5_1)
-; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI5_2)
-; CHECK-NEXT:    fld.d $fa5, $a0, %pc_lo12(.LCPI5_2)
-; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI5_3)
-; CHECK-NEXT:    fld.d $fa6, $a0, %pc_lo12(.LCPI5_3)
+; CHECK-NEXT:  .Lpcadd_hi4:
+; CHECK-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI5_0)
+; CHECK-NEXT:    fld.d $fa2, $a0, %pcadd_lo12(.Lpcadd_hi4)
+; CHECK-NEXT:  .Lpcadd_hi5:
+; CHECK-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI5_1)
+; CHECK-NEXT:    fld.d $fa4, $a0, %pcadd_lo12(.Lpcadd_hi5)
+; CHECK-NEXT:  .Lpcadd_hi6:
+; CHECK-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI5_2)
+; CHECK-NEXT:    fld.d $fa5, $a0, %pcadd_lo12(.Lpcadd_hi6)
+; CHECK-NEXT:  .Lpcadd_hi7:
+; CHECK-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI5_3)
+; CHECK-NEXT:    fld.d $fa6, $a0, %pcadd_lo12(.Lpcadd_hi7)
 ; CHECK-NEXT:    lu12i.w $a1, 262688
 ; CHECK-NEXT:    lu12i.w $a3, 262720
 ; CHECK-NEXT:    lu12i.w $a5, 262752
diff --git a/llvm/test/CodeGen/LoongArch/code-models.ll b/llvm/test/CodeGen/LoongArch/code-models.ll
index f4459655e6138..907961d449f38 100644
--- a/llvm/test/CodeGen/LoongArch/code-models.ll
+++ b/llvm/test/CodeGen/LoongArch/code-models.ll
@@ -1,96 +1,146 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc --mtriple=loongarch32 -mattr=+d --code-model=small < %s | \
+; RUN:    FileCheck --check-prefix=LA32-SMALL %s
+; RUN: llc --mtriple=loongarch32 -mattr=+d --code-model=medium < %s | \
+; RUN:    FileCheck --check-prefix=LA32-MEDIUM %s
 ; RUN: llc --mtriple=loongarch64 -mattr=+d --code-model=small < %s | \
-; RUN:    FileCheck --check-prefix=SMALL %s
+; RUN:    FileCheck --check-prefix=LA64-SMALL %s
 ; RUN: llc --mtriple=loongarch64 -mattr=+d --code-model=medium < %s | \
-; RUN:    FileCheck --check-prefix=MEDIUM %s
+; RUN:    FileCheck --check-prefix=LA64-MEDIUM %s
 ; RUN: llc --mtriple=loongarch64 -mattr=+d --code-model=large < %s | \
-; RUN:    FileCheck --check-prefix=LARGE %s
+; RUN:    FileCheck --check-prefix=LA64-LARGE %s
 
 declare void @llvm.memset.p0.i64(ptr, i8, i64, i1)
 declare i32 @callee(i32)
 
 define i32 @call_globaladdress(i32 %a) nounwind {
-; SMALL-LABEL: call_globaladdress:
-; SMALL:       # %bb.0:
-; SMALL-NEXT:    addi.d $sp, $sp, -16
-; SMALL-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; SMALL-NEXT:    bl callee
-; SMALL-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
-; SMALL-NEXT:    addi.d $sp, $sp, 16
-; SMALL-NEXT:    ret
+; LA32-SMALL-LABEL: call_globaladdress:
+; LA32-SMALL:       # %bb.0:
+; LA32-SMALL-NEXT:    addi.w $sp, $sp, -16
+; LA32-SMALL-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
+; LA32-SMALL-NEXT:    bl callee
+; LA32-SMALL-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
+; LA32-SMALL-NEXT:    addi.w $sp, $sp, 16
+; LA32-SMALL-NEXT:    ret
 ;
-; MEDIUM-LABEL: call_globaladdress:
-; MEDIUM:       # %bb.0:
-; MEDIUM-NEXT:    addi.d $sp, $sp, -16
-; MEDIUM-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; MEDIUM-NEXT:    pcaddu18i $ra, %call36(callee)
-; MEDIUM-NEXT:    jirl $ra, $ra, 0
-; MEDIUM-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
-; MEDIUM-NEXT:    addi.d $sp, $sp, 16
-; MEDIUM-NEXT:    ret
+; LA32-MEDIUM-LABEL: call_globaladdress:
+; LA32-MEDIUM:       # %bb.0:
+; LA32-MEDIUM-NEXT:    addi.w $sp, $sp, -16
+; LA32-MEDIUM-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
+; LA32-MEDIUM-NEXT:    pcaddu12i $ra, %call30(callee)
+; LA32-MEDIUM-NEXT:    jirl $ra, $ra, 0
+; LA32-MEDIUM-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
+; LA32-MEDIUM-NEXT:    addi.w $sp, $sp, 16
+; LA32-MEDIUM-NEXT:    ret
 ;
-; LARGE-LABEL: call_globaladdress:
-; LARGE:       # %bb.0:
-; LARGE-NEXT:    addi.d $sp, $sp, -16
-; LARGE-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; LARGE-NEXT:    pcalau12i $a1, %got_pc_hi20(callee)
-; LARGE-NEXT:    addi.d $ra, $zero, %got_pc_lo12(callee)
-; LARGE-NEXT:    lu32i.d $ra, %got64_pc_lo20(callee)
-; LARGE-NEXT:    lu52i.d $ra, $ra, %got64_pc_hi12(callee)
-; LARGE-NEXT:    ldx.d $ra, $ra, $a1
-; LARGE-NEXT:    jirl $ra, $ra, 0
-; LARGE-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
-; LARGE-NEXT:    addi.d $sp, $sp, 16
-; LARGE-NEXT:    ret
+; LA64-SMALL-LABEL: call_globaladdress:
+; LA64-SMALL:       # %bb.0:
+; LA64-SMALL-NEXT:    addi.d $sp, $sp, -16
+; LA64-SMALL-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
+; LA64-SMALL-NEXT:    bl callee
+; LA64-SMALL-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
+; LA64-SMALL-NEXT:    addi.d $sp, $sp, 16
+; LA64-SMALL-NEXT:    ret
+;
+; LA64-MEDIUM-LABEL: call_globaladdress:
+; LA64-MEDIUM:       # %bb.0:
+; LA64-MEDIUM-NEXT:    addi.d $sp, $sp, -16
+; LA64-MEDIUM-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
+; LA64-MEDIUM-NEXT:    pcaddu18i $ra, %call36(callee)
+; LA64-MEDIUM-NEXT:    jirl $ra, $ra, 0
+; LA64-MEDIUM-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
+; LA64-MEDIUM-NEXT:    addi.d $sp, $sp, 16
+; LA64-MEDIUM-NEXT:    ret
+;
+; LA64-LARGE-LABEL: call_globaladdress:
+; LA64-LARGE:       # %bb.0:
+; LA64-LARGE-NEXT:    addi.d $sp, $sp, -16
+; LA64-LARGE-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
+; LA64-LARGE-NEXT:    pcalau12i $a1, %got_pc_hi20(callee)
+; LA64-LARGE-NEXT:    addi.d $ra, $zero, %got_pc_lo12(callee)
+; LA64-LARGE-NEXT:    lu32i.d $ra, %got64_pc_lo20(callee)
+; LA64-LARGE-NEXT:    lu52i.d $ra, $ra, %got64_pc_hi12(callee)
+; LA64-LARGE-NEXT:    ldx.d $ra, $ra, $a1
+; LA64-LARGE-NEXT:    jirl $ra, $ra, 0
+; LA64-LARGE-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
+; LA64-LARGE-NEXT:    addi.d $sp, $sp, 16
+; LA64-LARGE-NEXT:    ret
   %1 = call i32 @callee(i32 %a)
   ret i32 %1
 }
 
 define void @call_external_sym(ptr %dst) {
-; SMALL-LABEL: call_external_sym:
-; SMALL:       # %bb.0: # %entry
-; SMALL-NEXT:    addi.d $sp, $sp, -16
-; SMALL-NEXT:    .cfi_def_cfa_offset 16
-; SMALL-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; SMALL-NEXT:    .cfi_offset 1, -8
-; SMALL-NEXT:    ori $a2, $zero, 1000
-; SMALL-NEXT:    move $a1, $zero
-; SMALL-NEXT:    bl memset
-; SMALL-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
-; SMALL-NEXT:    addi.d $sp, $sp, 16
-; SMALL-NEXT:    ret
+; LA32-SMALL-LABEL: call_external_sym:
+; LA32-SMALL:       # %bb.0: # %entry
+; LA32-SMALL-NEXT:    addi.w $sp, $sp, -16
+; LA32-SMALL-NEXT:    .cfi_def_cfa_offset 16
+; LA32-SMALL-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
+; LA32-SMALL-NEXT:    .cfi_offset 1, -4
+; LA32-SMALL-NEXT:    ori $a2, $zero, 1000
+; LA32-SMALL-NEXT:    move $a1, $zero
+; LA32-SMALL-NEXT:    bl memset
+; LA32-SMALL-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
+; LA32-SMALL-NEXT:    addi.w $sp, $sp, 16
+; LA32-SMALL-NEXT:    ret
+;
+; LA32-MEDIUM-LABEL: call_external_sym:
+; LA32-MEDIUM:       # %bb.0: # %entry
+; LA32-MEDIUM-NEXT:    addi.w $sp, $sp, -16
+; LA32-MEDIUM-NEXT:    .cfi_def_cfa_offset 16
+; LA32-MEDIUM-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
+; LA32-MEDIUM-NEXT:    .cfi_offset 1, -4
+; LA32-MEDIUM-NEXT:    ori $a2, $zero, 1000
+; LA32-MEDIUM-NEXT:    move $a1, $zero
+; LA32-MEDIUM-NEXT:    pcaddu12i $ra, %call30(memset)
+; LA32-MEDIUM-NEXT:    jirl $ra, $ra, 0
+; LA32-MEDIUM-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
+; LA32-MEDIUM-NEXT:    addi.w $sp, $sp, 16
+; LA32-MEDIUM-NEXT:    ret
 ;
-; MEDIUM-LABEL: call_external_sym:
-; MEDIUM:       # %bb.0: # %entry
-; MEDIUM-NEXT:    addi.d $sp, $sp, -16
-; MEDIUM-NEXT:    .cfi_def_cfa_offset 16
-; MEDIUM-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; MEDIUM-NEXT:    .cfi_offset 1, -8
-; MEDIUM-NEXT:    ori $a2, $zero, 1000
-; MEDIUM-NEXT:    move $a1, $zero
-; MEDIUM-NEXT:    pcaddu18i $ra, %call36(memset)
-; MEDIUM-NEXT:    jirl $ra, $ra, 0
-; MEDIUM-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
-; MEDIUM-NEXT:    addi.d $sp, $sp, 16
-; MEDIUM-NEXT:    ret
+; LA64-SMALL-LABEL: call_external_sym:
+; LA64-SMALL:       # %bb.0: # %entry
+; LA64-SMALL-NEXT:    addi.d $sp, $sp, -16
+; LA64-SMALL-NEXT:    .cfi_def_cfa_offset 16
+; LA64-SMALL-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
+; LA64-SMALL-NEXT:    .cfi_offset 1, -8
+; LA64-SMALL-NEXT:    ori $a2, $zero, 1000
+; LA64-SMALL-NEXT:    move $a1, $zero
+; LA64-SMALL-NEXT:    bl memset
+; LA64-SMALL-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
+; LA64-SMALL-NEXT:    addi.d $sp, $sp, 16
+; LA64-SMALL-NEXT:    ret
 ;
-; LARGE-LABEL: call_external_sym:
-; LARGE:       # %bb.0: # %entry
-; LARGE-NEXT:    addi.d $sp, $sp, -16
-; LARGE-NEXT:    .cfi_def_cfa_offset 16
-; LARGE-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; LARGE-NEXT:    .cfi_offset 1, -8
-; LARGE-NEXT:    ori $a2, $zero, 1000
-; LARGE-NEXT:    move $a1, $zero
-; LARGE-NEXT:    pcalau12i $a3, %got_pc_hi20(memset)
-; LARGE-NEXT:    addi.d $ra, $zero, %got_pc_lo12(memset)
-; LARGE-NEXT:    lu32i.d $ra, %got64_pc_lo20(memset)
-; LARGE-NEXT:    lu52i.d $ra, $ra, %got64_pc_hi12(memset)
-; LARGE-NEXT:    ldx.d $ra, $ra, $a3
-; LARGE-NEXT:    jirl $ra, $ra, 0
-; LARGE-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
-; LARGE-NEXT:    addi.d $sp, $sp, 16
-; LARGE-NEXT:    ret
+; LA64-MEDIUM-LABEL: call_external_sym:
+; LA64-MEDIUM:       # %bb.0: # %entry
+; LA64-MEDIUM-NEXT:    addi.d $sp, $sp, -16
+; LA64-MEDIUM-NEXT:    .cfi_def_cfa_offset 16
+; LA64-MEDIUM-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
+; LA64-MEDIUM-NEXT:    .cfi_offset 1, -8
+; LA64-MEDIUM-NEXT:    ori $a2, $zero, 1000
+; LA64-MEDIUM-NEXT:    move $a1, $zero
+; LA64-MEDIUM-NEXT:    pcaddu18i $ra, %call36(memset)
+; LA64-MEDIUM-NEXT:    jirl $ra, $ra, 0
+; LA64-MEDIUM-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
+; LA64-MEDIUM-NEXT:    addi.d $sp, $sp, 16
+; LA64-MEDIUM-NEXT:    ret
+;
+; LA64-LARGE-LABEL: call_external_sym:
+; LA64-LARGE:       # %bb.0: # %entry
+; LA64-LARGE-NEXT:    addi.d $sp, $sp, -16
+; LA64-LARGE-NEXT:    .cfi_def_cfa_offset 16
+; LA64-LARGE-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
+; LA64-LARGE-NEXT:    .cfi_offset 1, -8
+; LA64-LARGE-NEXT:    ori $a2, $zero, 1000
+; LA64-LARGE-NEXT:    move $a1, $zero
+; LA64-LARGE-NEXT:    pcalau12i $a3, %got_pc_hi20(memset)
+; LA64-LARGE-NEXT:    addi.d $ra, $zero, %got_pc_lo12(memset)
+; LA64-LARGE-NEXT:    lu32i.d $ra, %got64_pc_lo20(memset)
+; LA64-LARGE-NEXT:    lu52i.d $ra, $ra, %got64_pc_hi12(memset)
+; LA64-LARGE-NEXT:    ldx.d $ra, $ra, $a3
+; LA64-LARGE-NEXT:    jirl $ra, $ra, 0
+; LA64-LARGE-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
+; LA64-LARGE-NEXT:    addi.d $sp, $sp, 16
+; LA64-LARGE-NEXT:    ret
 entry:
   call void @llvm.memset.p0.i64(ptr %dst, i8 0, i64 1000, i1 false)
   ret void
@@ -99,23 +149,32 @@ entry:
 ;; Tail call with different codemodel.
 declare i32 @callee_tail(i32 %i)
 define i32 @caller_tail(i32 %i) nounwind {
-; SMALL-LABEL: caller_tail:
-; SMALL:       # %bb.0: # %entry
-; SMALL-NEXT:    b callee_tail
+; LA32-SMALL-LABEL: caller_tail:
+; LA32-SMALL:       # %bb.0: # %entry
+; LA32-SMALL-NEXT:    b callee_tail
+;
+; LA32-MEDIUM-LABEL: caller_tail:
+; LA32-MEDIUM:       # %bb.0: # %entry
+; LA32-MEDIUM-NEXT:    pcaddu12i $t8, %call30(callee_tail)
+; LA32-MEDIUM-NEXT:    jr $t8
+;
+; LA64-SMALL-LABEL: caller_tail:
+; LA64-SMALL:       # %bb.0: # %entry
+; LA64-SMALL-NEXT:    b callee_tail
 ;
-; MEDIUM-LABEL: caller_tail:
-; MEDIUM:       # %bb.0: # %entry
-; MEDIUM-NEXT:    pcaddu18i $t8, %call36(callee_tail)
-; MEDIUM-NEXT:    jr $t8
+; LA64-MEDIUM-LABEL: caller_tail:
+; LA64-MEDIUM:       # %bb.0: # %entry
+; LA64-MEDIUM-NEXT:    pcaddu18i $t8, %call36(callee_tail)
+; LA64-MEDIUM-NEXT:    jr $t8
 ;
-; LARGE-LABEL: caller_tail:
-; LARGE:       # %bb.0: # %entry
-; LARGE-NEXT:    pcalau12i $a1, %got_pc_hi20(callee_tail)
-; LARGE-NEXT:    addi.d $a2, $zero, %got_pc_lo12(callee_tail)
-; LARGE-NEXT:    lu32i.d $a2, %got64_pc_lo20(callee_tail)
-; LARGE-NEXT:    lu52i.d $a2, $a2, %got64_pc_hi12(callee_tail)
-; LARGE-NEXT:    ldx.d $a1, $a2, $a1
-; LARGE-NEXT:    jr $a1
+; LA64-LARGE-LABEL: caller_tail:
+; LA64-LARGE:       # %bb.0: # %entry
+; LA64-LARGE-NEXT:    pcalau12i $a1, %got_pc_hi20(callee_tail)
+; LA64-LARGE-NEXT:    addi.d $a2, $zero, %got_pc_lo12(callee_tail)
+; LA64-LARGE-NEXT:    lu32i.d $a2, %got64_pc_lo20(callee_tail)
+; LA64-LARGE-NEXT:    lu52i.d $a2, $a2, %got64_pc_hi12(callee_tail)
+; LA64-LARGE-NEXT:    ldx.d $a1, $a2, $a1
+; LA64-LARGE-NEXT:    jr $a1
 entry:
   %r = tail call i32 @callee_tail(i32 %i)
   ret i32 %r
diff --git a/llvm/test/CodeGen/LoongArch/ctlz-cttz-ctpop.ll b/llvm/test/CodeGen/LoongArch/ctlz-cttz-ctpop.ll
index 27be02c50f1c7..384c2d25135ea 100644
--- a/llvm/test/CodeGen/LoongArch/ctlz-cttz-ctpop.ll
+++ b/llvm/test/CodeGen/LoongArch/ctlz-cttz-ctpop.ll
@@ -810,8 +810,9 @@ define i32 @test_cttz_i32(i32 %a) nounwind {
 ; LA32R-NEXT:    ori $a1, $a1, 1329
 ; LA32R-NEXT:    mul.w $a0, $a0, $a1
 ; LA32R-NEXT:    srli.w $a0, $a0, 27
-; LA32R-NEXT:    pcalau12i $a1, %pc_hi20(.LCPI14_0)
-; LA32R-NEXT:    addi.w $a1, $a1, %pc_lo12(.LCPI14_0)
+; LA32R-NEXT:  .Lpcadd_hi0:
+; LA32R-NEXT:    pcaddu12i $a1, %pcadd_hi20(.LCPI14_0)
+; LA32R-NEXT:    addi.w $a1, $a1, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32R-NEXT:    add.w $a0, $a1, $a0
 ; LA32R-NEXT:    ld.bu $a0, $a0, 0
 ; LA32R-NEXT:    ret
@@ -834,8 +835,9 @@ define i64 @test_cttz_i64(i64 %a) nounwind {
 ; LA32R:       # %bb.0:
 ; LA32R-NEXT:    lu12i.w $a2, 30667
 ; LA32R-NEXT:    ori $a2, $a2, 1329
-; LA32R-NEXT:    pcalau12i $a3, %pc_hi20(.LCPI15_0)
-; LA32R-NEXT:    addi.w $a3, $a3, %pc_lo12(.LCPI15_0)
+; LA32R-NEXT:  .Lpcadd_hi1:
+; LA32R-NEXT:    pcaddu12i $a3, %pcadd_hi20(.LCPI15_0)
+; LA32R-NEXT:    addi.w $a3, $a3, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32R-NEXT:    bne $a1, $zero, .LBB15_3
 ; LA32R-NEXT:  # %bb.1:
 ; LA32R-NEXT:    ori $a1, $zero, 32
@@ -974,8 +976,9 @@ define i32 @test_not_cttz_i32(i32 %a) nounwind {
 ; LA32R-NEXT:    ori $a1, $a1, 1329
 ; LA32R-NEXT:    mul.w $a0, $a0, $a1
 ; LA32R-NEXT:    srli.w $a0, $a0, 27
-; LA32R-NEXT:    pcalau12i $a1, %pc_hi20(.LCPI18_0)
-; LA32R-NEXT:    addi.w $a1, $a1, %pc_lo12(.LCPI18_0)
+; LA32R-NEXT:  .Lpcadd_hi2:
+; LA32R-NEXT:    pcaddu12i $a1, %pcadd_hi20(.LCPI18_0)
+; LA32R-NEXT:    addi.w $a1, $a1, %pcadd_lo12(.Lpcadd_hi2)
 ; LA32R-NEXT:    add.w $a0, $a1, $a0
 ; LA32R-NEXT:    ld.bu $a0, $a0, 0
 ; LA32R-NEXT:    ret
@@ -1001,8 +1004,9 @@ define i64 @test_not_cttz_i64(i64 %a) nounwind {
 ; LA32R-NEXT:    nor $a2, $a0, $zero
 ; LA32R-NEXT:    lu12i.w $a3, 30667
 ; LA32R-NEXT:    ori $a3, $a3, 1329
-; LA32R-NEXT:    pcalau12i $a4, %pc_hi20(.LCPI19_0)
-; LA32R-NEXT:    addi.w $a4, $a4, %pc_lo12(.LCPI19_0)
+; LA32R-NEXT:  .Lpcadd_hi3:
+; LA32R-NEXT:    pcaddu12i $a4, %pcadd_hi20(.LCPI19_0)
+; LA32R-NEXT:    addi.w $a4, $a4, %pcadd_lo12(.Lpcadd_hi3)
 ; LA32R-NEXT:    bne $a5, $zero, .LBB19_3
 ; LA32R-NEXT:  # %bb.1:
 ; LA32R-NEXT:    ori $a1, $zero, 32
diff --git a/llvm/test/CodeGen/LoongArch/double-imm.ll b/llvm/test/CodeGen/LoongArch/double-imm.ll
index 0b715cb18f8ad..577d7bd5be241 100644
--- a/llvm/test/CodeGen/LoongArch/double-imm.ll
+++ b/llvm/test/CodeGen/LoongArch/double-imm.ll
@@ -45,14 +45,16 @@ define double @f64_negative_zero() nounwind {
 define double @f64_constant_ins1() nounwind {
 ; LA32-0-LABEL: f64_constant_ins1:
 ; LA32-0:       # %bb.0:
-; LA32-0-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI2_0)
-; LA32-0-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI2_0)
+; LA32-0-NEXT:  .Lpcadd_hi0:
+; LA32-0-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI2_0)
+; LA32-0-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-0-NEXT:    ret
 ;
 ; LA32-2-LABEL: f64_constant_ins1:
 ; LA32-2:       # %bb.0:
-; LA32-2-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI2_0)
-; LA32-2-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI2_0)
+; LA32-2-NEXT:  .Lpcadd_hi0:
+; LA32-2-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI2_0)
+; LA32-2-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-2-NEXT:    ret
 ;
 ; LA32-3-LABEL: f64_constant_ins1:
@@ -118,20 +120,23 @@ define double @f64_constant_ins1() nounwind {
 define double @f64_constant_ins2() nounwind {
 ; LA32-0-LABEL: f64_constant_ins2:
 ; LA32-0:       # %bb.0:
-; LA32-0-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI3_0)
-; LA32-0-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI3_0)
+; LA32-0-NEXT:  .Lpcadd_hi1:
+; LA32-0-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI3_0)
+; LA32-0-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32-0-NEXT:    ret
 ;
 ; LA32-2-LABEL: f64_constant_ins2:
 ; LA32-2:       # %bb.0:
-; LA32-2-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI3_0)
-; LA32-2-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI3_0)
+; LA32-2-NEXT:  .Lpcadd_hi1:
+; LA32-2-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI3_0)
+; LA32-2-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32-2-NEXT:    ret
 ;
 ; LA32-3-LABEL: f64_constant_ins2:
 ; LA32-3:       # %bb.0:
-; LA32-3-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI3_0)
-; LA32-3-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI3_0)
+; LA32-3-NEXT:  .Lpcadd_hi0:
+; LA32-3-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI3_0)
+; LA32-3-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-3-NEXT:    ret
 ;
 ; LA32-4-LABEL: f64_constant_ins2:
@@ -196,26 +201,30 @@ define double @f64_constant_ins2() nounwind {
 define double @f64_constant_ins3() nounwind {
 ; LA32-0-LABEL: f64_constant_ins3:
 ; LA32-0:       # %bb.0:
-; LA32-0-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI4_0)
-; LA32-0-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI4_0)
+; LA32-0-NEXT:  .Lpcadd_hi2:
+; LA32-0-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI4_0)
+; LA32-0-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi2)
 ; LA32-0-NEXT:    ret
 ;
 ; LA32-2-LABEL: f64_constant_ins3:
 ; LA32-2:       # %bb.0:
-; LA32-2-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI4_0)
-; LA32-2-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI4_0)
+; LA32-2-NEXT:  .Lpcadd_hi2:
+; LA32-2-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI4_0)
+; LA32-2-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi2)
 ; LA32-2-NEXT:    ret
 ;
 ; LA32-3-LABEL: f64_constant_ins3:
 ; LA32-3:       # %bb.0:
-; LA32-3-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI4_0)
-; LA32-3-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI4_0)
+; LA32-3-NEXT:  .Lpcadd_hi1:
+; LA32-3-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI4_0)
+; LA32-3-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32-3-NEXT:    ret
 ;
 ; LA32-4-LABEL: f64_constant_ins3:
 ; LA32-4:       # %bb.0:
-; LA32-4-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI4_0)
-; LA32-4-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI4_0)
+; LA32-4-NEXT:  .Lpcadd_hi0:
+; LA32-4-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI4_0)
+; LA32-4-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-4-NEXT:    ret
 ;
 ; LA32-5-LABEL: f64_constant_ins3:
@@ -275,32 +284,37 @@ define double @f64_constant_ins3() nounwind {
 define double @f64_constant_pi() nounwind {
 ; LA32-0-LABEL: f64_constant_pi:
 ; LA32-0:       # %bb.0:
-; LA32-0-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI5_0)
-; LA32-0-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI5_0)
+; LA32-0-NEXT:  .Lpcadd_hi3:
+; LA32-0-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI5_0)
+; LA32-0-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi3)
 ; LA32-0-NEXT:    ret
 ;
 ; LA32-2-LABEL: f64_constant_pi:
 ; LA32-2:       # %bb.0:
-; LA32-2-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI5_0)
-; LA32-2-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI5_0)
+; LA32-2-NEXT:  .Lpcadd_hi3:
+; LA32-2-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI5_0)
+; LA32-2-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi3)
 ; LA32-2-NEXT:    ret
 ;
 ; LA32-3-LABEL: f64_constant_pi:
 ; LA32-3:       # %bb.0:
-; LA32-3-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI5_0)
-; LA32-3-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI5_0)
+; LA32-3-NEXT:  .Lpcadd_hi2:
+; LA32-3-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI5_0)
+; LA32-3-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi2)
 ; LA32-3-NEXT:    ret
 ;
 ; LA32-4-LABEL: f64_constant_pi:
 ; LA32-4:       # %bb.0:
-; LA32-4-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI5_0)
-; LA32-4-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI5_0)
+; LA32-4-NEXT:  .Lpcadd_hi1:
+; LA32-4-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI5_0)
+; LA32-4-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32-4-NEXT:    ret
 ;
 ; LA32-5-LABEL: f64_constant_pi:
 ; LA32-5:       # %bb.0:
-; LA32-5-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI5_0)
-; LA32-5-NEXT:    fld.d $fa0, $a0, %pc_lo12(.LCPI5_0)
+; LA32-5-NEXT:  .Lpcadd_hi0:
+; LA32-5-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI5_0)
+; LA32-5-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-5-NEXT:    ret
 ;
 ; LA32-6-LABEL: f64_constant_pi:
diff --git a/llvm/test/CodeGen/LoongArch/float-imm.ll b/llvm/test/CodeGen/LoongArch/float-imm.ll
index 4611d8801cf2f..b6352b66fafb8 100644
--- a/llvm/test/CodeGen/LoongArch/float-imm.ll
+++ b/llvm/test/CodeGen/LoongArch/float-imm.ll
@@ -43,8 +43,9 @@ define float @f32_negative_zero() nounwind {
 define float @f32_constant_ins1() nounwind {
 ; LA32-0-LABEL: f32_constant_ins1:
 ; LA32-0:       # %bb.0:
-; LA32-0-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI2_0)
-; LA32-0-NEXT:    fld.s $fa0, $a0, %pc_lo12(.LCPI2_0)
+; LA32-0-NEXT:  .Lpcadd_hi0:
+; LA32-0-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI2_0)
+; LA32-0-NEXT:    fld.s $fa0, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-0-NEXT:    ret
 ;
 ; LA32-2-LABEL: f32_constant_ins1:
@@ -82,14 +83,16 @@ define float @f32_constant_ins1() nounwind {
 define float @f32_constant_pi() nounwind {
 ; LA32-0-LABEL: f32_constant_pi:
 ; LA32-0:       # %bb.0:
-; LA32-0-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI3_0)
-; LA32-0-NEXT:    fld.s $fa0, $a0, %pc_lo12(.LCPI3_0)
+; LA32-0-NEXT:  .Lpcadd_hi1:
+; LA32-0-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI3_0)
+; LA32-0-NEXT:    fld.s $fa0, $a0, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32-0-NEXT:    ret
 ;
 ; LA32-2-LABEL: f32_constant_pi:
 ; LA32-2:       # %bb.0:
-; LA32-2-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI3_0)
-; LA32-2-NEXT:    fld.s $fa0, $a0, %pc_lo12(.LCPI3_0)
+; LA32-2-NEXT:  .Lpcadd_hi0:
+; LA32-2-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI3_0)
+; LA32-2-NEXT:    fld.s $fa0, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-2-NEXT:    ret
 ;
 ; LA32-3-LABEL: f32_constant_pi:
diff --git a/llvm/test/CodeGen/LoongArch/global-address.ll b/llvm/test/CodeGen/LoongArch/global-address.ll
index 89ea48c3b1cbf..750246104b4fa 100644
--- a/llvm/test/CodeGen/LoongArch/global-address.ll
+++ b/llvm/test/CodeGen/LoongArch/global-address.ll
@@ -12,20 +12,24 @@
 define void @foo() nounwind {
 ; LA32NOPIC-LABEL: foo:
 ; LA32NOPIC:       # %bb.0:
-; LA32NOPIC-NEXT:    pcalau12i $a0, %got_pc_hi20(G)
-; LA32NOPIC-NEXT:    ld.w $a0, $a0, %got_pc_lo12(G)
+; LA32NOPIC-NEXT:  .Lpcadd_hi0:
+; LA32NOPIC-NEXT:    pcaddu12i $a0, %got_pcadd_hi20(G)
+; LA32NOPIC-NEXT:    ld.w $a0, $a0, %got_pcadd_lo12(.Lpcadd_hi0)
 ; LA32NOPIC-NEXT:    ld.w $zero, $a0, 0
-; LA32NOPIC-NEXT:    pcalau12i $a0, %pc_hi20(g)
-; LA32NOPIC-NEXT:    ld.w $zero, $a0, %pc_lo12(g)
+; LA32NOPIC-NEXT:  .Lpcadd_hi1:
+; LA32NOPIC-NEXT:    pcaddu12i $a0, %pcadd_hi20(g)
+; LA32NOPIC-NEXT:    ld.w $zero, $a0, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32NOPIC-NEXT:    ret
 ;
 ; LA32PIC-LABEL: foo:
 ; LA32PIC:       # %bb.0:
-; LA32PIC-NEXT:    pcalau12i $a0, %got_pc_hi20(G)
-; LA32PIC-NEXT:    ld.w $a0, $a0, %got_pc_lo12(G)
+; LA32PIC-NEXT:  .Lpcadd_hi0:
+; LA32PIC-NEXT:    pcaddu12i $a0, %got_pcadd_hi20(G)
+; LA32PIC-NEXT:    ld.w $a0, $a0, %got_pcadd_lo12(.Lpcadd_hi0)
 ; LA32PIC-NEXT:    ld.w $zero, $a0, 0
-; LA32PIC-NEXT:    pcalau12i $a0, %pc_hi20(.Lg$local)
-; LA32PIC-NEXT:    ld.w $zero, $a0, %pc_lo12(.Lg$local)
+; LA32PIC-NEXT:  .Lpcadd_hi1:
+; LA32PIC-NEXT:    pcaddu12i $a0, %pcadd_hi20(.Lg$local)
+; LA32PIC-NEXT:    ld.w $zero, $a0, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32PIC-NEXT:    ret
 ;
 ; LA64NOPIC-LABEL: foo:
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-f.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-f.ll
index c1d75ddd32803..b5f1c23a95207 100644
--- a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-f.ll
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-f.ll
@@ -9,8 +9,9 @@
 define double @constraint_f_double(double %a) nounwind {
 ; LA32-LABEL: constraint_f_double:
 ; LA32:       # %bb.0:
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(gd)
-; LA32-NEXT:    fld.d $fa1, $a0, %pc_lo12(gd)
+; LA32-NEXT:  .Lpcadd_hi0:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(gd)
+; LA32-NEXT:    fld.d $fa1, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-NEXT:    #APP
 ; LA32-NEXT:    fadd.d $fa0, $fa0, $fa1
 ; LA32-NEXT:    #NO_APP
diff --git a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-m.ll b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-m.ll
index 38e06d15670a8..9255ad3010cff 100644
--- a/llvm/test/CodeGen/LoongArch/inline-asm-constraint-m.ll
+++ b/llvm/test/CodeGen/LoongArch/inline-asm-constraint-m.ll
@@ -145,7 +145,8 @@ define i32 @m_offset_2048(ptr %p) nounwind {
 define i32 @m_constant_0() nounwind {
 ; LA32-LABEL: m_constant_0:
 ; LA32:       # %bb.0:
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI7_0)
+; LA32-NEXT:  .Lpcadd_hi0:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI7_0)
 ; LA32-NEXT:    #APP
 ; LA32-NEXT:    #NO_APP
 ; LA32-NEXT:    ret
@@ -163,9 +164,10 @@ define i32 @m_constant_0() nounwind {
 define i32 @m_constant_1() nounwind {
 ; LA32-LABEL: m_constant_1:
 ; LA32:       # %bb.0:
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI8_0)
+; LA32-NEXT:  .Lpcadd_hi1:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI8_0)
 ; LA32-NEXT:    #APP
-; LA32-NEXT:    ld.w $a0, $a0, %pc_lo12(.LCPI8_0)
+; LA32-NEXT:    ld.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi10)
 ; LA32-NEXT:    #NO_APP
 ; LA32-NEXT:    ret
 ;
@@ -185,9 +187,10 @@ define i32 @m_constant_1() nounwind {
 define i32 @m_addr_pcrel() nounwind {
 ; LA32-LABEL: m_addr_pcrel:
 ; LA32:       # %bb.0:
-; LA32-NEXT:    pcalau12i $a1, %pc_hi20(g_i32)
+; LA32-NEXT:  .Lpcadd_hi2:
+; LA32-NEXT:    pcaddu12i $a1, %pcadd_hi20(g_i32)
 ; LA32-NEXT:    #APP
-; LA32-NEXT:    ld.w $a0, $a1, %pc_lo12(g_i32)
+; LA32-NEXT:    ld.w $a0, $a1, %pcadd_lo12(.Lpcadd_hi20)
 ; LA32-NEXT:    #NO_APP
 ; LA32-NEXT:    ret
 ;
@@ -205,8 +208,9 @@ define i32 @m_addr_pcrel() nounwind {
 define i32 @m_addr_should_not_fold() nounwind {
 ; LA32-LABEL: m_addr_should_not_fold:
 ; LA32:       # %bb.0:
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i32)
-; LA32-NEXT:    addi.w $a1, $a0, %pc_lo12(g_i32)
+; LA32-NEXT:  .Lpcadd_hi3:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i32)
+; LA32-NEXT:    addi.w $a1, $a0, %pcadd_lo12(.Lpcadd_hi3)
 ; LA32-NEXT:    #APP
 ; LA32-NEXT:    ld.w $a0, $a1, 0
 ; LA32-NEXT:    #NO_APP
diff --git a/llvm/test/CodeGen/LoongArch/ir-instruction/load-store.ll b/llvm/test/CodeGen/LoongArch/ir-instruction/load-store.ll
index 18ba4810cfb72..1679659a8823b 100644
--- a/llvm/test/CodeGen/LoongArch/ir-instruction/load-store.ll
+++ b/llvm/test/CodeGen/LoongArch/ir-instruction/load-store.ll
@@ -13,10 +13,11 @@
 define i32 @load_store_global() nounwind {
 ; LA32RNOPIC-LABEL: load_store_global:
 ; LA32RNOPIC:       # %bb.0:
-; LA32RNOPIC-NEXT:    pcalau12i $a1, %pc_hi20(G)
-; LA32RNOPIC-NEXT:    ld.w $a0, $a1, %pc_lo12(G)
+; LA32RNOPIC-NEXT:  .Lpcadd_hi0:
+; LA32RNOPIC-NEXT:    pcaddu12i $a1, %pcadd_hi20(G)
+; LA32RNOPIC-NEXT:    ld.w $a0, $a1, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32RNOPIC-NEXT:    addi.w $a0, $a0, 1
-; LA32RNOPIC-NEXT:    st.w $a0, $a1, %pc_lo12(G)
+; LA32RNOPIC-NEXT:    st.w $a0, $a1, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32RNOPIC-NEXT:    ret
 ;
 ; LA32SNOPIC-LABEL: load_store_global:
@@ -29,10 +30,11 @@ define i32 @load_store_global() nounwind {
 ;
 ; LA32RPIC-LABEL: load_store_global:
 ; LA32RPIC:       # %bb.0:
-; LA32RPIC-NEXT:    pcalau12i $a1, %pc_hi20(.LG$local)
-; LA32RPIC-NEXT:    ld.w $a0, $a1, %pc_lo12(.LG$local)
+; LA32RPIC-NEXT:  .Lpcadd_hi0:
+; LA32RPIC-NEXT:    pcaddu12i $a1, %pcadd_hi20(.LG$local)
+; LA32RPIC-NEXT:    ld.w $a0, $a1, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32RPIC-NEXT:    addi.w $a0, $a0, 1
-; LA32RPIC-NEXT:    st.w $a0, $a1, %pc_lo12(.LG$local)
+; LA32RPIC-NEXT:    st.w $a0, $a1, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32RPIC-NEXT:    ret
 ;
 ; LA32SPIC-LABEL: load_store_global:
@@ -67,8 +69,9 @@ define i32 @load_store_global() nounwind {
 define i32 @load_store_global_array(i32 %a) nounwind {
 ; LA32RNOPIC-LABEL: load_store_global_array:
 ; LA32RNOPIC:       # %bb.0:
-; LA32RNOPIC-NEXT:    pcalau12i $a1, %pc_hi20(arr)
-; LA32RNOPIC-NEXT:    addi.w $a2, $a1, %pc_lo12(arr)
+; LA32RNOPIC-NEXT:  .Lpcadd_hi1:
+; LA32RNOPIC-NEXT:    pcaddu12i $a1, %pcadd_hi20(arr)
+; LA32RNOPIC-NEXT:    addi.w $a2, $a1, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32RNOPIC-NEXT:    ld.w $a1, $a2, 0
 ; LA32RNOPIC-NEXT:    st.w $a0, $a2, 0
 ; LA32RNOPIC-NEXT:    ld.w $zero, $a2, 36
@@ -89,8 +92,9 @@ define i32 @load_store_global_array(i32 %a) nounwind {
 ;
 ; LA32RPIC-LABEL: load_store_global_array:
 ; LA32RPIC:       # %bb.0:
-; LA32RPIC-NEXT:    pcalau12i $a1, %pc_hi20(.Larr$local)
-; LA32RPIC-NEXT:    addi.w $a2, $a1, %pc_lo12(.Larr$local)
+; LA32RPIC-NEXT:  .Lpcadd_hi1:
+; LA32RPIC-NEXT:    pcaddu12i $a1, %pcadd_hi20(.Larr$local)
+; LA32RPIC-NEXT:    addi.w $a2, $a1, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32RPIC-NEXT:    ld.w $a1, $a2, 0
 ; LA32RPIC-NEXT:    st.w $a0, $a2, 0
 ; LA32RPIC-NEXT:    ld.w $zero, $a2, 36
diff --git a/llvm/test/CodeGen/LoongArch/machinelicm-address-pseudos.ll b/llvm/test/CodeGen/LoongArch/machinelicm-address-pseudos.ll
index 9142e718e8adc..e76bf417ec8f6 100644
--- a/llvm/test/CodeGen/LoongArch/machinelicm-address-pseudos.ll
+++ b/llvm/test/CodeGen/LoongArch/machinelicm-address-pseudos.ll
@@ -14,11 +14,12 @@ define void @test_la_pcrel(i32 signext %n) {
 ; LA32-LABEL: test_la_pcrel:
 ; LA32:       # %bb.0: # %entry
 ; LA32-NEXT:    move $a1, $zero
-; LA32-NEXT:    pcalau12i $a2, %pc_hi20(l)
+; LA32-NEXT:  .Lpcadd_hi0:
+; LA32-NEXT:    pcaddu12i $a2, %pcadd_hi20(l)
 ; LA32-NEXT:    .p2align 4, , 16
 ; LA32-NEXT:  .LBB0_1: # %loop
 ; LA32-NEXT:    # =>This Inner Loop Header: Depth=1
-; LA32-NEXT:    ld.w $zero, $a2, %pc_lo12(l)
+; LA32-NEXT:    ld.w $zero, $a2, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-NEXT:    addi.w $a1, $a1, 1
 ; LA32-NEXT:    blt $a1, $a0, .LBB0_1
 ; LA32-NEXT:  # %bb.2: # %ret
@@ -71,8 +72,9 @@ ret:
 define void @test_la_got(i32 signext %n) {
 ; LA32-LABEL: test_la_got:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a1, %got_pc_hi20(g)
-; LA32-NEXT:    ld.w $a1, $a1, %got_pc_lo12(g)
+; LA32-NEXT:  .Lpcadd_hi1:
+; LA32-NEXT:    pcaddu12i $a1, %got_pcadd_hi20(g)
+; LA32-NEXT:    ld.w $a1, $a1, %got_pcadd_lo12(.Lpcadd_hi1)
 ; LA32-NEXT:    move $a2, $zero
 ; LA32-NEXT:    .p2align 4, , 16
 ; LA32-NEXT:  .LBB1_1: # %loop
@@ -132,8 +134,9 @@ ret:
 define void @test_la_tls_ie(i32 signext %n) {
 ; LA32-LABEL: test_la_tls_ie:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a1, %ie_pc_hi20(ie)
-; LA32-NEXT:    ld.w $a2, $a1, %ie_pc_lo12(ie)
+; LA32-NEXT:  .Lpcadd_hi2:
+; LA32-NEXT:    pcaddu12i $a1, %ie_pcadd_hi20(ie)
+; LA32-NEXT:    ld.w $a2, $a1, %ie_pcadd_lo12(.Lpcadd_hi2)
 ; LA32-NEXT:    move $a1, $zero
 ; LA32-NEXT:    add.w $a2, $a2, $tp
 ; LA32-NEXT:    .p2align 4, , 16
@@ -206,8 +209,9 @@ define void @test_la_tls_ld(i32 signext %n) {
 ; LA32-NEXT:    .cfi_offset 24, -16
 ; LA32-NEXT:    move $fp, $a0
 ; LA32-NEXT:    move $s1, $zero
-; LA32-NEXT:    pcalau12i $a0, %ld_pc_hi20(ld)
-; LA32-NEXT:    addi.w $s0, $a0, %got_pc_lo12(ld)
+; LA32-NEXT:  .Lpcadd_hi3:
+; LA32-NEXT:    pcaddu12i $a0, %ld_pcadd_hi20(ld)
+; LA32-NEXT:    addi.w $s0, $a0, %ld_pcadd_lo12(.Lpcadd_hi3)
 ; LA32-NEXT:    .p2align 4, , 16
 ; LA32-NEXT:  .LBB3_1: # %loop
 ; LA32-NEXT:    # =>This Inner Loop Header: Depth=1
@@ -382,8 +386,9 @@ define void @test_la_tls_gd(i32 signext %n) nounwind {
 ; LA32-NEXT:    st.w $s1, $sp, 0 # 4-byte Folded Spill
 ; LA32-NEXT:    move $fp, $a0
 ; LA32-NEXT:    move $s1, $zero
-; LA32-NEXT:    pcalau12i $a0, %gd_pc_hi20(gd)
-; LA32-NEXT:    addi.w $s0, $a0, %got_pc_lo12(gd)
+; LA32-NEXT:  .Lpcadd_hi4:
+; LA32-NEXT:    pcaddu12i $a0, %gd_pcadd_hi20(gd)
+; LA32-NEXT:    addi.w $s0, $a0, %gd_pcadd_lo12(.Lpcadd_hi4)
 ; LA32-NEXT:    .p2align 4, , 16
 ; LA32-NEXT:  .LBB5_1: # %loop
 ; LA32-NEXT:    # =>This Inner Loop Header: Depth=1
diff --git a/llvm/test/CodeGen/LoongArch/merge-base-offset.ll b/llvm/test/CodeGen/LoongArch/merge-base-offset.ll
index 1151c77c9af76..ea0fe3d0b0178 100644
--- a/llvm/test/CodeGen/LoongArch/merge-base-offset.ll
+++ b/llvm/test/CodeGen/LoongArch/merge-base-offset.ll
@@ -11,8 +11,9 @@
 define dso_local signext i8 @load_s8() nounwind {
 ; LA32-LABEL: load_s8:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i8)
-; LA32-NEXT:    ld.b $a0, $a0, %pc_lo12(g_i8)
+; LA32-NEXT:  .Lpcadd_hi0:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i8)
+; LA32-NEXT:    ld.b $a0, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_s8:
@@ -37,8 +38,9 @@ entry:
 define dso_local zeroext i8 @load_u8() nounwind {
 ; LA32-LABEL: load_u8:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i8)
-; LA32-NEXT:    ld.bu $a0, $a0, %pc_lo12(g_i8)
+; LA32-NEXT:  .Lpcadd_hi1:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i8)
+; LA32-NEXT:    ld.bu $a0, $a0, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_u8:
@@ -63,9 +65,10 @@ entry:
 define dso_local void @store_i8() nounwind {
 ; LA32-LABEL: store_i8:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i8)
+; LA32-NEXT:  .Lpcadd_hi2:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i8)
 ; LA32-NEXT:    ori $a1, $zero, 1
-; LA32-NEXT:    st.b $a1, $a0, %pc_lo12(g_i8)
+; LA32-NEXT:    st.b $a1, $a0, %pcadd_lo12(.Lpcadd_hi2)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: store_i8:
@@ -94,8 +97,9 @@ entry:
 define dso_local signext i16 @load_s16() nounwind {
 ; LA32-LABEL: load_s16:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i16)
-; LA32-NEXT:    ld.h $a0, $a0, %pc_lo12(g_i16)
+; LA32-NEXT:  .Lpcadd_hi3:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i16)
+; LA32-NEXT:    ld.h $a0, $a0, %pcadd_lo12(.Lpcadd_hi3)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_s16:
@@ -120,8 +124,9 @@ entry:
 define dso_local zeroext i16 @load_u16() nounwind {
 ; LA32-LABEL: load_u16:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i16)
-; LA32-NEXT:    ld.hu $a0, $a0, %pc_lo12(g_i16)
+; LA32-NEXT:  .Lpcadd_hi4:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i16)
+; LA32-NEXT:    ld.hu $a0, $a0, %pcadd_lo12(.Lpcadd_hi4)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_u16:
@@ -146,9 +151,10 @@ entry:
 define dso_local void @store_i16() nounwind {
 ; LA32-LABEL: store_i16:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i16)
+; LA32-NEXT:  .Lpcadd_hi5:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i16)
 ; LA32-NEXT:    ori $a1, $zero, 1
-; LA32-NEXT:    st.h $a1, $a0, %pc_lo12(g_i16)
+; LA32-NEXT:    st.h $a1, $a0, %pcadd_lo12(.Lpcadd_hi5)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: store_i16:
@@ -177,8 +183,9 @@ entry:
 define dso_local signext i32 @load_s32() nounwind {
 ; LA32-LABEL: load_s32:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i32)
-; LA32-NEXT:    ld.w $a0, $a0, %pc_lo12(g_i32)
+; LA32-NEXT:  .Lpcadd_hi6:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i32)
+; LA32-NEXT:    ld.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi6)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_s32:
@@ -203,8 +210,9 @@ entry:
 define dso_local zeroext i32 @load_u32() nounwind {
 ; LA32-LABEL: load_u32:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i32)
-; LA32-NEXT:    ld.w $a0, $a0, %pc_lo12(g_i32)
+; LA32-NEXT:  .Lpcadd_hi7:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i32)
+; LA32-NEXT:    ld.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi7)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_u32:
@@ -229,9 +237,10 @@ entry:
 define dso_local void @store_i32() nounwind {
 ; LA32-LABEL: store_i32:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i32)
+; LA32-NEXT:  .Lpcadd_hi8:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i32)
 ; LA32-NEXT:    ori $a1, $zero, 1
-; LA32-NEXT:    st.w $a1, $a0, %pc_lo12(g_i32)
+; LA32-NEXT:    st.w $a1, $a0, %pcadd_lo12(.Lpcadd_hi8)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: store_i32:
@@ -260,8 +269,9 @@ entry:
 define dso_local i64 @load_i64() nounwind {
 ; LA32-LABEL: load_i64:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i64)
-; LA32-NEXT:    addi.w $a1, $a0, %pc_lo12(g_i64)
+; LA32-NEXT:  .Lpcadd_hi9:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i64)
+; LA32-NEXT:    addi.w $a1, $a0, %pcadd_lo12(.Lpcadd_hi9)
 ; LA32-NEXT:    ld.w $a0, $a1, 0
 ; LA32-NEXT:    ld.w $a1, $a1, 4
 ; LA32-NEXT:    ret
@@ -288,8 +298,9 @@ entry:
 define dso_local void @store_i64() nounwind {
 ; LA32-LABEL: store_i64:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i64)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_i64)
+; LA32-NEXT:  .Lpcadd_hi10:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i64)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi10)
 ; LA32-NEXT:    st.w $zero, $a0, 4
 ; LA32-NEXT:    ori $a1, $zero, 1
 ; LA32-NEXT:    st.w $a1, $a0, 0
@@ -321,8 +332,9 @@ entry:
 define dso_local float @load_f32() nounwind {
 ; LA32-LABEL: load_f32:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_f32)
-; LA32-NEXT:    fld.s $fa0, $a0, %pc_lo12(g_f32)
+; LA32-NEXT:  .Lpcadd_hi11:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_f32)
+; LA32-NEXT:    fld.s $fa0, $a0, %pcadd_lo12(.Lpcadd_hi11)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_f32:
@@ -347,9 +359,10 @@ entry:
 define dso_local void @store_f32() nounwind {
 ; LA32-LABEL: store_f32:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_f32)
+; LA32-NEXT:  .Lpcadd_hi12:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_f32)
 ; LA32-NEXT:    lu12i.w $a1, 260096
-; LA32-NEXT:    st.w $a1, $a0, %pc_lo12(g_f32)
+; LA32-NEXT:    st.w $a1, $a0, %pcadd_lo12(.Lpcadd_hi12)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: store_f32:
@@ -378,8 +391,9 @@ entry:
 define dso_local double @load_f64() nounwind {
 ; LA32-LABEL: load_f64:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_f64)
-; LA32-NEXT:    fld.d $fa0, $a0, %pc_lo12(g_f64)
+; LA32-NEXT:  .Lpcadd_hi13:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_f64)
+; LA32-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi13)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_f64:
@@ -404,9 +418,10 @@ entry:
 define dso_local void @store_f64() nounwind {
 ; LA32-LABEL: store_f64:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_f64)
+; LA32-NEXT:  .Lpcadd_hi14:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_f64)
 ; LA32-NEXT:    vldi $vr0, -912
-; LA32-NEXT:    fst.d $fa0, $a0, %pc_lo12(g_f64)
+; LA32-NEXT:    fst.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi14)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: store_f64:
@@ -435,8 +450,9 @@ entry:
 define dso_local void @store_multi() nounwind {
 ; LA32-LABEL: store_multi:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_m64)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_m64)
+; LA32-NEXT:  .Lpcadd_hi15:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_m64)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi15)
 ; LA32-NEXT:    st.w $zero, $a0, 4
 ; LA32-NEXT:    ori $a1, $zero, 1
 ; LA32-NEXT:    st.w $a1, $a0, 0
@@ -476,9 +492,10 @@ entry:
 define dso_local void @store_sf32() nounwind {
 ; LA32-LABEL: store_sf32:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_sf32)
-; LA32-NEXT:    fld.s $fa0, $a0, %pc_lo12(g_sf32)
-; LA32-NEXT:    fst.s $fa0, $a0, %pc_lo12(g_sf32)
+; LA32-NEXT:  .Lpcadd_hi16:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_sf32)
+; LA32-NEXT:    fld.s $fa0, $a0, %pcadd_lo12(.Lpcadd_hi16)
+; LA32-NEXT:    fst.s $fa0, $a0, %pcadd_lo12(.Lpcadd_hi16)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: store_sf32:
@@ -508,9 +525,10 @@ entry:
 define dso_local void @store_sf64() nounwind {
 ; LA32-LABEL: store_sf64:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_sf64)
-; LA32-NEXT:    fld.d $fa0, $a0, %pc_lo12(g_sf64)
-; LA32-NEXT:    fst.d $fa0, $a0, %pc_lo12(g_sf64)
+; LA32-NEXT:  .Lpcadd_hi17:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_sf64)
+; LA32-NEXT:    fld.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi17)
+; LA32-NEXT:    fst.d $fa0, $a0, %pcadd_lo12(.Lpcadd_hi17)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: store_sf64:
@@ -541,10 +559,12 @@ entry:
 define dso_local void @copy_i32x4() nounwind {
 ; LA32-LABEL: copy_i32x4:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i32x4_src)
-; LA32-NEXT:    vld $vr0, $a0, %pc_lo12(g_i32x4_src)
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i32x4_dst)
-; LA32-NEXT:    vst $vr0, $a0, %pc_lo12(g_i32x4_dst)
+; LA32-NEXT:  .Lpcadd_hi18:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i32x4_src)
+; LA32-NEXT:    vld $vr0, $a0, %pcadd_lo12(.Lpcadd_hi18)
+; LA32-NEXT:  .Lpcadd_hi19:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i32x4_dst)
+; LA32-NEXT:    vst $vr0, $a0, %pcadd_lo12(.Lpcadd_hi19)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: copy_i32x4:
@@ -580,10 +600,12 @@ entry:
 define dso_local void @copy_i32x8() nounwind {
 ; LA32-LABEL: copy_i32x8:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i32x8_src)
-; LA32-NEXT:    xvld $xr0, $a0, %pc_lo12(g_i32x8_src)
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i32x8_dst)
-; LA32-NEXT:    xvst $xr0, $a0, %pc_lo12(g_i32x8_dst)
+; LA32-NEXT:  .Lpcadd_hi20:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i32x8_src)
+; LA32-NEXT:    xvld $xr0, $a0, %pcadd_lo12(.Lpcadd_hi20)
+; LA32-NEXT:  .Lpcadd_hi21:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i32x8_dst)
+; LA32-NEXT:    xvst $xr0, $a0, %pcadd_lo12(.Lpcadd_hi21)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: copy_i32x8:
@@ -618,10 +640,12 @@ entry:
 define void @copy_i8_to_i8x16() {
 ; LA32-LABEL: copy_i8_to_i8x16:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i8)
-; LA32-NEXT:    vldrepl.b $vr0, $a0, %pc_lo12(g_i8)
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i8x16)
-; LA32-NEXT:    vst $vr0, $a0, %pc_lo12(g_i8x16)
+; LA32-NEXT:  .Lpcadd_hi22:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i8)
+; LA32-NEXT:    vldrepl.b $vr0, $a0, %pcadd_lo12(.Lpcadd_hi22)
+; LA32-NEXT:  .Lpcadd_hi23:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i8x16)
+; LA32-NEXT:    vst $vr0, $a0, %pcadd_lo12(.Lpcadd_hi23)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: copy_i8_to_i8x16:
@@ -657,10 +681,12 @@ entry:
 define void @copy_i8_to_i8x32() {
 ; LA32-LABEL: copy_i8_to_i8x32:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i8)
-; LA32-NEXT:    xvldrepl.b $xr0, $a0, %pc_lo12(g_i8)
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_i8x32)
-; LA32-NEXT:    xvst $xr0, $a0, %pc_lo12(g_i8x32)
+; LA32-NEXT:  .Lpcadd_hi24:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i8)
+; LA32-NEXT:    xvldrepl.b $xr0, $a0, %pcadd_lo12(.Lpcadd_hi24)
+; LA32-NEXT:  .Lpcadd_hi25:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_i8x32)
+; LA32-NEXT:    xvst $xr0, $a0, %pcadd_lo12(.Lpcadd_hi25)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: copy_i8_to_i8x32:
@@ -696,8 +722,9 @@ entry:
 define dso_local void @rmw() nounwind {
 ; LA32-LABEL: rmw:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_rmw)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_rmw)
+; LA32-NEXT:  .Lpcadd_hi26:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_rmw)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi26)
 ; LA32-NEXT:    ld.w $a1, $a0, 0
 ; LA32-NEXT:    ld.w $a2, $a0, 4
 ; LA32-NEXT:    addi.w $a1, $a1, 1
@@ -737,9 +764,10 @@ entry:
 define dso_local void @store_a32() nounwind {
 ; LA32-LABEL: store_a32:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a32+4096)
+; LA32-NEXT:  .Lpcadd_hi27:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a32+4096)
 ; LA32-NEXT:    ori $a1, $zero, 1
-; LA32-NEXT:    st.w $a1, $a0, %pc_lo12(g_a32+4096)
+; LA32-NEXT:    st.w $a1, $a0, %pcadd_lo12(.Lpcadd_hi27)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: store_a32:
@@ -766,8 +794,9 @@ entry:
 define dso_local void @store_a32_2() nounwind {
 ; LA32-LABEL: store_a32_2:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a32)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a32)
+; LA32-NEXT:  .Lpcadd_hi28:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a32)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi28)
 ; LA32-NEXT:    lu12i.w $a1, 1
 ; LA32-NEXT:    add.w $a2, $a0, $a1
 ; LA32-NEXT:    ori $a3, $zero, 1
@@ -809,12 +838,13 @@ entry:
 define dso_local void @control_flow_with_mem_access() nounwind {
 ; LA32-LABEL: control_flow_with_mem_access:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a32+4)
-; LA32-NEXT:    ld.w $a1, $a0, %pc_lo12(g_a32+4)
+; LA32-NEXT:  .Lpcadd_hi29:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a32+4)
+; LA32-NEXT:    ld.w $a1, $a0, %pcadd_lo12(.Lpcadd_hi29)
 ; LA32-NEXT:    blez $a1, .LBB25_2
 ; LA32-NEXT:  # %bb.1: # %if.then
 ; LA32-NEXT:    ori $a1, $zero, 10
-; LA32-NEXT:    st.w $a1, $a0, %pc_lo12(g_a32+4)
+; LA32-NEXT:    st.w $a1, $a0, %pcadd_lo12(.Lpcadd_hi29)
 ; LA32-NEXT:  .LBB25_2: # %if.end
 ; LA32-NEXT:    ret
 ;
@@ -864,8 +894,9 @@ define dso_local ptr @load_ba_1() nounwind {
 ; LA32:       # %bb.0: # %entry
 ; LA32-NEXT:  .Ltmp0: # Block address taken
 ; LA32-NEXT:  # %bb.1: # %label
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(.Ltmp0)
-; LA32-NEXT:    ld.w $a0, $a0, %pc_lo12(.Ltmp0)
+; LA32-NEXT:  .Lpcadd_hi30:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(.Ltmp0)
+; LA32-NEXT:    ld.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi30)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_ba_1:
@@ -898,8 +929,9 @@ define dso_local ptr @load_ba_2() nounwind {
 ; LA32:       # %bb.0: # %entry
 ; LA32-NEXT:  .Ltmp1: # Block address taken
 ; LA32-NEXT:  # %bb.1: # %label
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(.Ltmp1+8)
-; LA32-NEXT:    ld.w $a0, $a0, %pc_lo12(.Ltmp1+8)
+; LA32-NEXT:  .Lpcadd_hi31:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(.Ltmp1+8)
+; LA32-NEXT:    ld.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi31)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_ba_2:
@@ -932,8 +964,9 @@ label:
 define dso_local ptr @load_addr_offset_1() nounwind {
 ; LA32-LABEL: load_addr_offset_1:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a64+8)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a64+8)
+; LA32-NEXT:  .Lpcadd_hi32:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a64+8)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi32)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_addr_offset_1:
@@ -957,8 +990,9 @@ entry:
 define dso_local ptr @load_addr_offset_257() nounwind {
 ; LA32-LABEL: load_addr_offset_257:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a64+2056)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a64+2056)
+; LA32-NEXT:  .Lpcadd_hi33:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a64+2056)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi33)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_addr_offset_257:
@@ -982,8 +1016,9 @@ entry:
 define dso_local ptr @load_addr_offset_1048576() nounwind {
 ; LA32-LABEL: load_addr_offset_1048576:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a64+8388608)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a64+8388608)
+; LA32-NEXT:  .Lpcadd_hi34:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a64+8388608)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi34)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_addr_offset_1048576:
@@ -1007,8 +1042,9 @@ entry:
 define dso_local ptr @load_addr_offset_1048577() nounwind {
 ; LA32-LABEL: load_addr_offset_1048577:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a64+8388616)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a64+8388616)
+; LA32-NEXT:  .Lpcadd_hi35:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a64+8388616)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi35)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_addr_offset_1048577:
@@ -1032,8 +1068,9 @@ entry:
 define dso_local ptr @load_addr_offset_268432896() nounwind {
 ; LA32-LABEL: load_addr_offset_268432896:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a64+2147463168)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a64+2147463168)
+; LA32-NEXT:  .Lpcadd_hi36:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a64+2147463168)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi36)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_addr_offset_268432896:
@@ -1057,8 +1094,9 @@ entry:
 define dso_local ptr @load_addr_offset_268432897() nounwind {
 ; LA32-LABEL: load_addr_offset_268432897:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a64+2147463176)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a64+2147463176)
+; LA32-NEXT:  .Lpcadd_hi37:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a64+2147463176)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi37)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_addr_offset_268432897:
@@ -1082,8 +1120,9 @@ entry:
 define dso_local ptr @load_addr_offset_281474439839744() nounwind {
 ; LA32-LABEL: load_addr_offset_281474439839744:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a64)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a64)
+; LA32-NEXT:  .Lpcadd_hi38:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a64)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi38)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_addr_offset_281474439839744:
@@ -1110,8 +1149,9 @@ entry:
 define dso_local ptr @load_addr_offset_248792680471040() nounwind {
 ; LA32-LABEL: load_addr_offset_248792680471040:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a64+2059194368)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a64+2059194368)
+; LA32-NEXT:  .Lpcadd_hi39:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a64+2059194368)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi39)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_addr_offset_248792680471040:
@@ -1138,8 +1178,9 @@ entry:
 define dso_local ptr @load_addr_offset_9380351707272() nounwind {
 ; LA32-LABEL: load_addr_offset_9380351707272:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a64+1145062464)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a64+1145062464)
+; LA32-NEXT:  .Lpcadd_hi40:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a64+1145062464)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi40)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_addr_offset_9380351707272:
@@ -1167,8 +1208,9 @@ entry:
 define dso_local ptr @load_addr_offset_562949953421312() nounwind {
 ; LA32-LABEL: load_addr_offset_562949953421312:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a64)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a64)
+; LA32-NEXT:  .Lpcadd_hi41:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a64)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi41)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_addr_offset_562949953421312:
@@ -1194,8 +1236,9 @@ entry:
 define dso_local ptr @load_addr_offset_614749556925924693() nounwind {
 ; LA32-LABEL: load_addr_offset_614749556925924693:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(g_a64+858794664)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(g_a64+858794664)
+; LA32-NEXT:  .Lpcadd_hi42:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(g_a64+858794664)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi42)
 ; LA32-NEXT:    ret
 ;
 ; LA64-LABEL: load_addr_offset_614749556925924693:
diff --git a/llvm/test/CodeGen/LoongArch/musttail.ll b/llvm/test/CodeGen/LoongArch/musttail.ll
index 4d9be2869fd9f..23369ddd81fca 100644
--- a/llvm/test/CodeGen/LoongArch/musttail.ll
+++ b/llvm/test/CodeGen/LoongArch/musttail.ll
@@ -536,8 +536,9 @@ entry:
 define void @large_caller_from_global(%twenty_bytes* byval(%twenty_bytes) align 4 %a) {
 ; LA32-LABEL: large_caller_from_global:
 ; LA32:       # %bb.0: # %entry
-; LA32-NEXT:    pcalau12i $a1, %got_pc_hi20(large_global)
-; LA32-NEXT:    ld.w $a1, $a1, %got_pc_lo12(large_global)
+; LA32-NEXT:  .Lpcadd_hi0:
+; LA32-NEXT:    pcaddu12i $a1, %got_pcadd_hi20(large_global)
+; LA32-NEXT:    ld.w $a1, $a1, %got_pcadd_lo12(.Lpcadd_hi0)
 ; LA32-NEXT:    ld.w $a2, $a1, 16
 ; LA32-NEXT:    st.w $a2, $a0, 16
 ; LA32-NEXT:    ld.w $a2, $a1, 12
diff --git a/llvm/test/CodeGen/LoongArch/numeric-reg-names.ll b/llvm/test/CodeGen/LoongArch/numeric-reg-names.ll
index 73f4dbbbdd026..38de4564c3d3f 100644
--- a/llvm/test/CodeGen/LoongArch/numeric-reg-names.ll
+++ b/llvm/test/CodeGen/LoongArch/numeric-reg-names.ll
@@ -15,8 +15,9 @@ define i32 @main() {
 ; LA32-NEXT:    .cfi_def_cfa_offset 16
 ; LA32-NEXT:    st.w $r1, $r3, 12 # 4-byte Folded Spill
 ; LA32-NEXT:    .cfi_offset 1, -4
-; LA32-NEXT:    pcalau12i $r4, %pc_hi20(.str_1)
-; LA32-NEXT:    addi.w $r4, $r4, %pc_lo12(.str_1)
+; LA32-NEXT:  .Lpcadd_hi0:
+; LA32-NEXT:    pcaddu12i $r4, %pcadd_hi20(.str_1)
+; LA32-NEXT:    addi.w $r4, $r4, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-NEXT:    bl printf
 ; LA32-NEXT:    move $r4, $r0
 ; LA32-NEXT:    ld.w $r1, $r3, 12 # 4-byte Folded Reload
diff --git a/llvm/test/CodeGen/LoongArch/stack-protector-target.ll b/llvm/test/CodeGen/LoongArch/stack-protector-target.ll
index ea4569e198d02..d24d59be32ba7 100644
--- a/llvm/test/CodeGen/LoongArch/stack-protector-target.ll
+++ b/llvm/test/CodeGen/LoongArch/stack-protector-target.ll
@@ -10,12 +10,13 @@ define void @func() sspreq nounwind {
 ; LINUX32-NEXT:    addi.w $sp, $sp, -16
 ; LINUX32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LINUX32-NEXT:    st.w $fp, $sp, 8 # 4-byte Folded Spill
-; LINUX32-NEXT:    pcalau12i $fp, %pc_hi20(__stack_chk_guard)
-; LINUX32-NEXT:    ld.w $a0, $fp, %pc_lo12(__stack_chk_guard)
+; LINUX32-NEXT:  .Lpcadd_hi0:
+; LINUX32-NEXT:    pcaddu12i $fp, %pcadd_hi20(__stack_chk_guard)
+; LINUX32-NEXT:    ld.w $a0, $fp, %pcadd_lo12(.Lpcadd_hi0)
 ; LINUX32-NEXT:    st.w $a0, $sp, 4
 ; LINUX32-NEXT:    addi.w $a0, $sp, 0
 ; LINUX32-NEXT:    bl capture
-; LINUX32-NEXT:    ld.w $a0, $fp, %pc_lo12(__stack_chk_guard)
+; LINUX32-NEXT:    ld.w $a0, $fp, %pcadd_lo12(.Lpcadd_hi0)
 ; LINUX32-NEXT:    ld.w $a1, $sp, 4
 ; LINUX32-NEXT:    bne $a0, $a1, .LBB0_2
 ; LINUX32-NEXT:  # %bb.1:
@@ -54,12 +55,13 @@ define void @func() sspreq nounwind {
 ; OPENBSD32-NEXT:    addi.w $sp, $sp, -16
 ; OPENBSD32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; OPENBSD32-NEXT:    st.w $fp, $sp, 8 # 4-byte Folded Spill
-; OPENBSD32-NEXT:    pcalau12i $fp, %pc_hi20(__guard_local)
-; OPENBSD32-NEXT:    ld.w $a0, $fp, %pc_lo12(__guard_local)
+; OPENBSD32-NEXT:  .Lpcadd_hi0:
+; OPENBSD32-NEXT:    pcaddu12i $fp, %pcadd_hi20(__guard_local)
+; OPENBSD32-NEXT:    ld.w $a0, $fp, %pcadd_lo12(.Lpcadd_hi0)
 ; OPENBSD32-NEXT:    st.w $a0, $sp, 4
 ; OPENBSD32-NEXT:    addi.w $a0, $sp, 0
 ; OPENBSD32-NEXT:    bl capture
-; OPENBSD32-NEXT:    ld.w $a0, $fp, %pc_lo12(__guard_local)
+; OPENBSD32-NEXT:    ld.w $a0, $fp, %pcadd_lo12(.Lpcadd_hi0)
 ; OPENBSD32-NEXT:    ld.w $a1, $sp, 4
 ; OPENBSD32-NEXT:    bne $a0, $a1, .LBB0_2
 ; OPENBSD32-NEXT:  # %bb.1: # %SP_return
@@ -68,8 +70,9 @@ define void @func() sspreq nounwind {
 ; OPENBSD32-NEXT:    addi.w $sp, $sp, 16
 ; OPENBSD32-NEXT:    ret
 ; OPENBSD32-NEXT:  .LBB0_2: # %CallStackCheckFailBlk
-; OPENBSD32-NEXT:    pcalau12i $a0, %pc_hi20(.LSSH)
-; OPENBSD32-NEXT:    addi.w $a0, $a0, %pc_lo12(.LSSH)
+; OPENBSD32-NEXT:  .Lpcadd_hi1:
+; OPENBSD32-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LSSH)
+; OPENBSD32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi1)
 ; OPENBSD32-NEXT:    bl __stack_smash_handler
 ;
 ; OPENBSD64-LABEL: func:
diff --git a/llvm/test/CodeGen/LoongArch/tls-models.ll b/llvm/test/CodeGen/LoongArch/tls-models.ll
index 50d994fb85327..b4e4c4ac420d8 100644
--- a/llvm/test/CodeGen/LoongArch/tls-models.ll
+++ b/llvm/test/CodeGen/LoongArch/tls-models.ll
@@ -28,8 +28,9 @@ define ptr @f1() nounwind {
 ; LA32PIC:       # %bb.0: # %entry
 ; LA32PIC-NEXT:    addi.w $sp, $sp, -16
 ; LA32PIC-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32PIC-NEXT:    pcalau12i $a0, %gd_pc_hi20(unspecified)
-; LA32PIC-NEXT:    addi.w $a0, $a0, %got_pc_lo12(unspecified)
+; LA32PIC-NEXT:  .Lpcadd_hi0:
+; LA32PIC-NEXT:    pcaddu12i $a0, %gd_pcadd_hi20(unspecified)
+; LA32PIC-NEXT:    addi.w $a0, $a0, %gd_pcadd_lo12(.Lpcadd_hi0)
 ; LA32PIC-NEXT:    bl __tls_get_addr
 ; LA32PIC-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32PIC-NEXT:    addi.w $sp, $sp, 16
@@ -68,8 +69,9 @@ define ptr @f1() nounwind {
 ;
 ; LA32NOPIC-LABEL: f1:
 ; LA32NOPIC:       # %bb.0: # %entry
-; LA32NOPIC-NEXT:    pcalau12i $a0, %ie_pc_hi20(unspecified)
-; LA32NOPIC-NEXT:    ld.w $a0, $a0, %ie_pc_lo12(unspecified)
+; LA32NOPIC-NEXT:  .Lpcadd_hi0:
+; LA32NOPIC-NEXT:    pcaddu12i $a0, %ie_pcadd_hi20(unspecified)
+; LA32NOPIC-NEXT:    ld.w $a0, $a0, %ie_pcadd_lo12(.Lpcadd_hi0)
 ; LA32NOPIC-NEXT:    add.w $a0, $a0, $tp
 ; LA32NOPIC-NEXT:    ret
 ;
@@ -94,8 +96,9 @@ define ptr @f1() nounwind {
 ; LA32DESC:       # %bb.0: # %entry
 ; LA32DESC-NEXT:    addi.w $sp, $sp, -16
 ; LA32DESC-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32DESC-NEXT:    pcalau12i $a0, %desc_pc_hi20(unspecified)
-; LA32DESC-NEXT:    addi.w $a0, $a0, %desc_pc_lo12(unspecified)
+; LA32DESC-NEXT:  .Lpcadd_hi0:
+; LA32DESC-NEXT:    pcaddu12i $a0, %desc_pcadd_hi20(unspecified)
+; LA32DESC-NEXT:    addi.w $a0, $a0, %desc_pcadd_lo12(.Lpcadd_hi0)
 ; LA32DESC-NEXT:    ld.w $ra, $a0, %desc_ld(unspecified)
 ; LA32DESC-NEXT:    jirl $ra, $ra, %desc_call(unspecified)
 ; LA32DESC-NEXT:    add.w $a0, $a0, $tp
@@ -142,8 +145,9 @@ define ptr @f2() nounwind {
 ; LA32PIC:       # %bb.0: # %entry
 ; LA32PIC-NEXT:    addi.w $sp, $sp, -16
 ; LA32PIC-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32PIC-NEXT:    pcalau12i $a0, %ld_pc_hi20(ld)
-; LA32PIC-NEXT:    addi.w $a0, $a0, %got_pc_lo12(ld)
+; LA32PIC-NEXT:  .Lpcadd_hi1:
+; LA32PIC-NEXT:    pcaddu12i $a0, %ld_pcadd_hi20(ld)
+; LA32PIC-NEXT:    addi.w $a0, $a0, %ld_pcadd_lo12(.Lpcadd_hi1)
 ; LA32PIC-NEXT:    bl __tls_get_addr
 ; LA32PIC-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32PIC-NEXT:    addi.w $sp, $sp, 16
@@ -182,8 +186,9 @@ define ptr @f2() nounwind {
 ;
 ; LA32NOPIC-LABEL: f2:
 ; LA32NOPIC:       # %bb.0: # %entry
-; LA32NOPIC-NEXT:    pcalau12i $a0, %ie_pc_hi20(ld)
-; LA32NOPIC-NEXT:    ld.w $a0, $a0, %ie_pc_lo12(ld)
+; LA32NOPIC-NEXT:  .Lpcadd_hi1:
+; LA32NOPIC-NEXT:    pcaddu12i $a0, %ie_pcadd_hi20(ld)
+; LA32NOPIC-NEXT:    ld.w $a0, $a0, %ie_pcadd_lo12(.Lpcadd_hi1)
 ; LA32NOPIC-NEXT:    add.w $a0, $a0, $tp
 ; LA32NOPIC-NEXT:    ret
 ;
@@ -208,8 +213,9 @@ define ptr @f2() nounwind {
 ; LA32DESC:       # %bb.0: # %entry
 ; LA32DESC-NEXT:    addi.w $sp, $sp, -16
 ; LA32DESC-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32DESC-NEXT:    pcalau12i $a0, %desc_pc_hi20(ld)
-; LA32DESC-NEXT:    addi.w $a0, $a0, %desc_pc_lo12(ld)
+; LA32DESC-NEXT:  .Lpcadd_hi1:
+; LA32DESC-NEXT:    pcaddu12i $a0, %desc_pcadd_hi20(ld)
+; LA32DESC-NEXT:    addi.w $a0, $a0, %desc_pcadd_lo12(.Lpcadd_hi1)
 ; LA32DESC-NEXT:    ld.w $ra, $a0, %desc_ld(ld)
 ; LA32DESC-NEXT:    jirl $ra, $ra, %desc_call(ld)
 ; LA32DESC-NEXT:    add.w $a0, $a0, $tp
@@ -254,8 +260,9 @@ entry:
 define ptr @f3() nounwind {
 ; LA32PIC-LABEL: f3:
 ; LA32PIC:       # %bb.0: # %entry
-; LA32PIC-NEXT:    pcalau12i $a0, %ie_pc_hi20(ie)
-; LA32PIC-NEXT:    ld.w $a0, $a0, %ie_pc_lo12(ie)
+; LA32PIC-NEXT:  .Lpcadd_hi2:
+; LA32PIC-NEXT:    pcaddu12i $a0, %ie_pcadd_hi20(ie)
+; LA32PIC-NEXT:    ld.w $a0, $a0, %ie_pcadd_lo12(.Lpcadd_hi2)
 ; LA32PIC-NEXT:    add.w $a0, $a0, $tp
 ; LA32PIC-NEXT:    ret
 ;
@@ -278,8 +285,9 @@ define ptr @f3() nounwind {
 ;
 ; LA32NOPIC-LABEL: f3:
 ; LA32NOPIC:       # %bb.0: # %entry
-; LA32NOPIC-NEXT:    pcalau12i $a0, %ie_pc_hi20(ie)
-; LA32NOPIC-NEXT:    ld.w $a0, $a0, %ie_pc_lo12(ie)
+; LA32NOPIC-NEXT:  .Lpcadd_hi2:
+; LA32NOPIC-NEXT:    pcaddu12i $a0, %ie_pcadd_hi20(ie)
+; LA32NOPIC-NEXT:    ld.w $a0, $a0, %ie_pcadd_lo12(.Lpcadd_hi2)
 ; LA32NOPIC-NEXT:    add.w $a0, $a0, $tp
 ; LA32NOPIC-NEXT:    ret
 ;
@@ -302,8 +310,9 @@ define ptr @f3() nounwind {
 ;
 ; LA32DESC-LABEL: f3:
 ; LA32DESC:       # %bb.0: # %entry
-; LA32DESC-NEXT:    pcalau12i $a0, %ie_pc_hi20(ie)
-; LA32DESC-NEXT:    ld.w $a0, $a0, %ie_pc_lo12(ie)
+; LA32DESC-NEXT:  .Lpcadd_hi2:
+; LA32DESC-NEXT:    pcaddu12i $a0, %ie_pcadd_hi20(ie)
+; LA32DESC-NEXT:    ld.w $a0, $a0, %ie_pcadd_lo12(.Lpcadd_hi2)
 ; LA32DESC-NEXT:    add.w $a0, $a0, $tp
 ; LA32DESC-NEXT:    ret
 ;
diff --git a/llvm/test/CodeGen/LoongArch/unaligned-memcpy-inline.ll b/llvm/test/CodeGen/LoongArch/unaligned-memcpy-inline.ll
index 0d441e66a0c84..7a5b04b387def 100644
--- a/llvm/test/CodeGen/LoongArch/unaligned-memcpy-inline.ll
+++ b/llvm/test/CodeGen/LoongArch/unaligned-memcpy-inline.ll
@@ -102,8 +102,9 @@ define void @t3() {
 ; LA32:       # %bb.0: # %entry
 ; LA32-NEXT:    addi.w $sp, $sp, -64
 ; LA32-NEXT:    .cfi_def_cfa_offset 64
-; LA32-NEXT:    pcalau12i $a0, %pc_hi20(.L.str)
-; LA32-NEXT:    addi.w $a0, $a0, %pc_lo12(.L.str)
+; LA32-NEXT:  .Lpcadd_hi0:
+; LA32-NEXT:    pcaddu12i $a0, %pcadd_hi20(.L.str)
+; LA32-NEXT:    addi.w $a0, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32-NEXT:    ld.h $a1, $a0, 20
 ; LA32-NEXT:    ld.w $a2, $a0, 16
 ; LA32-NEXT:    st.h $a1, $sp, 20
diff --git a/llvm/test/CodeGen/LoongArch/vector-fp-imm.ll b/llvm/test/CodeGen/LoongArch/vector-fp-imm.ll
index 16c9e754fb94d..de6c3abf4ab45 100644
--- a/llvm/test/CodeGen/LoongArch/vector-fp-imm.ll
+++ b/llvm/test/CodeGen/LoongArch/vector-fp-imm.ll
@@ -532,8 +532,9 @@ define void @test_d4(ptr %P, ptr %S) nounwind {
 ; LA32D-NEXT:    fmov.d $fa5, $fa4
 ; LA32D-NEXT:    movgr2frh.w $fa5, $a0
 ; LA32D-NEXT:    fadd.d $fa2, $fa2, $fa5
-; LA32D-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI5_0)
-; LA32D-NEXT:    fld.d $fa5, $a0, %pc_lo12(.LCPI5_0)
+; LA32D-NEXT:  .Lpcadd_hi0:
+; LA32D-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI5_0)
+; LA32D-NEXT:    fld.d $fa5, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; LA32D-NEXT:    lu12i.w $a0, 262400
 ; LA32D-NEXT:    movgr2frh.w $fa4, $a0
 ; LA32D-NEXT:    fadd.d $fa1, $fa1, $fa4
@@ -760,8 +761,9 @@ define void @test_d8(ptr %P, ptr %S) nounwind {
 ; LA32D-NEXT:    lu12i.w $a0, 262400
 ; LA32D-NEXT:    movgr2frh.w $ft0, $a0
 ; LA32D-NEXT:    fadd.d $fa4, $fa4, $ft1
-; LA32D-NEXT:    pcalau12i $a0, %pc_hi20(.LCPI6_0)
-; LA32D-NEXT:    fld.d $ft1, $a0, %pc_lo12(.LCPI6_0)
+; LA32D-NEXT:  .Lpcadd_hi1:
+; LA32D-NEXT:    pcaddu12i $a0, %pcadd_hi20(.LCPI6_0)
+; LA32D-NEXT:    fld.d $ft1, $a0, %pcadd_lo12(.Lpcadd_hi1)
 ; LA32D-NEXT:    fadd.d $fa5, $fa5, $ft0
 ; LA32D-NEXT:    fadd.d $fa3, $fa3, $ft2
 ; LA32D-NEXT:    fadd.d $fa2, $fa2, $ft0
diff --git a/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_reloc_addsub.s b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_reloc_addsub.s
index 86e3008ef4094..d9d03efab30d3 100644
--- a/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_reloc_addsub.s
+++ b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_reloc_addsub.s
@@ -1,5 +1,5 @@
 # RUN: rm -rf %t && mkdir -p %t
-# RUN: llvm-mc --triple=loongarch32 -mattr=+relax --filetype=obj \
+# RUN: llvm-mc --triple=loongarch32 -mattr=+32s,+relax --filetype=obj \
 # RUN:     -o %t/la32_reloc_addsub.o %s
 # RUN: llvm-jitlink --noexec --check %s %t/la32_reloc_addsub.o \
 # RUN:     --slab-allocate=1Mb --slab-address=0x1000 --slab-page-size=0x4000
@@ -27,7 +27,10 @@ main:
 .L0:
 # Referencing named_data symbol to avoid the following relocations be
 # skipped. This macro instruction will be expand to two instructions
-# (pcalau12i + ld.w/d).
+# 32bit:
+#   (pcaddu12i + ld.w)
+# 64bit:
+#   (pcalau12i + ld.d)
   la.global $t0, named_data
 .L1:
 
diff --git a/llvm/test/MC/LoongArch/Basic/Integer/invalid.s b/llvm/test/MC/LoongArch/Basic/Integer/invalid.s
index 8488129bc253a..4811702c093ba 100644
--- a/llvm/test/MC/LoongArch/Basic/Integer/invalid.s
+++ b/llvm/test/MC/LoongArch/Basic/Integer/invalid.s
@@ -100,7 +100,7 @@ jirl $a0, $a0, 0x20000
 
 ## simm20_pcaddu12i
 pcaddu12i $a0, 0x80000
-# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %call30) or an integer in the range [-524288, 524287]
+# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %call30 or %pcadd_hi20) or an integer in the range [-524288, 524287]
 
 ## simm20_pcaddi
 pcaddi $a0, -0x80001
diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/loongarch_generated_funcs.ll.generated.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/loongarch_generated_funcs.ll.generated.expected
index eda7e771c128b..c2f5f345624bc 100644
--- a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/loongarch_generated_funcs.ll.generated.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/loongarch_generated_funcs.ll.generated.expected
@@ -121,7 +121,8 @@ attributes #0 = { noredzone nounwind ssp uwtable "frame-pointer"="all" }
 ; CHECK-NEXT:    addi.w $fp, $sp, 32
 ; CHECK-NEXT:    .cfi_def_cfa 22, 0
 ; CHECK-NEXT:    st.w $zero, $fp, -12
-; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(x)
+; CHECK-NEXT:  .Lpcadd_hi0:
+; CHECK-NEXT:    pcaddu12i $a0, %pcadd_hi20(x)
 ; CHECK-NEXT:    ori $a1, $zero, 1
 ; CHECK-NEXT:    st.w $a1, $fp, -16
 ; CHECK-NEXT:    ori $a2, $zero, 2
@@ -130,7 +131,7 @@ attributes #0 = { noredzone nounwind ssp uwtable "frame-pointer"="all" }
 ; CHECK-NEXT:    st.w $a3, $fp, -24
 ; CHECK-NEXT:    ori $a4, $zero, 4
 ; CHECK-NEXT:    st.w $a4, $fp, -28
-; CHECK-NEXT:    st.w $a1, $a0, %pc_lo12(x)
+; CHECK-NEXT:    st.w $a1, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; CHECK-NEXT:    #APP
 ; CHECK-NEXT:    #NO_APP
 ; CHECK-NEXT:    st.w $a1, $fp, -16
diff --git a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/loongarch_generated_funcs.ll.nogenerated.expected b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/loongarch_generated_funcs.ll.nogenerated.expected
index aab63fa7176c1..1ab256f6fa268 100644
--- a/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/loongarch_generated_funcs.ll.nogenerated.expected
+++ b/llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/loongarch_generated_funcs.ll.nogenerated.expected
@@ -98,7 +98,8 @@ define dso_local i32 @main() #0 {
 ; CHECK-NEXT:    addi.w $fp, $sp, 32
 ; CHECK-NEXT:    .cfi_def_cfa 22, 0
 ; CHECK-NEXT:    st.w $zero, $fp, -12
-; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(x)
+; CHECK-NEXT:  .Lpcadd_hi0:
+; CHECK-NEXT:    pcaddu12i $a0, %pcadd_hi20(x)
 ; CHECK-NEXT:    ori $a1, $zero, 1
 ; CHECK-NEXT:    st.w $a1, $fp, -16
 ; CHECK-NEXT:    ori $a2, $zero, 2
@@ -107,7 +108,7 @@ define dso_local i32 @main() #0 {
 ; CHECK-NEXT:    st.w $a3, $fp, -24
 ; CHECK-NEXT:    ori $a4, $zero, 4
 ; CHECK-NEXT:    st.w $a4, $fp, -28
-; CHECK-NEXT:    st.w $a1, $a0, %pc_lo12(x)
+; CHECK-NEXT:    st.w $a1, $a0, %pcadd_lo12(.Lpcadd_hi0)
 ; CHECK-NEXT:    #APP
 ; CHECK-NEXT:    #NO_APP
 ; CHECK-NEXT:    st.w $a1, $fp, -16

>From ea58f6b4c0274c3c2a6b5132712c65e5cd8c80c1 Mon Sep 17 00:00:00 2001
From: hev <wangrui at loongson.cn>
Date: Wed, 14 Jan 2026 18:03:56 +0800
Subject: [PATCH 04/10] [JITLink][LoongArch] Add reloc types for LA32R/LA32S
 (#175353)

(cherry picked from commit 9c7904bac281caf68be377daa4366c1f166c39f2)
---
 .../llvm/ExecutionEngine/JITLink/loongarch.h  |  73 +++++++++++-
 .../ExecutionEngine/JITLink/ELF_loongarch.cpp |  85 +++++++++++++-
 .../lib/ExecutionEngine/JITLink/loongarch.cpp |   4 +
 .../LoongArch/ELF_loongarch32_relocations.s   | 106 +++++++++++++++---
 4 files changed, 248 insertions(+), 20 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
index 1bb18e38bab33..2da2a4201ae16 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
@@ -170,6 +170,30 @@ enum EdgeKind_loongarch : Edge::Kind {
   ///
   PageOffset12,
 
+  /// The upper 20 bits of the offset from the fixup to the target.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- (Target + Addend - Fixup + 0x800) >> 12 : int20
+  ///
+  /// Notes:
+  ///   For PCADDU12I fixups.
+  ///
+  /// Errors:
+  ///   - The result of the fixup expression must fit into an int20 otherwise an
+  ///     out-of-range error will be returned.
+  ///
+  PCAddHi20,
+
+  /// The lower 12 bits of the offset from the paired PCADDU12I (the initial
+  /// target) to the final target it points to.
+  ///
+  /// Typically used to fix up ADDI/LD_W/LD_D immediates.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- (FinalTarget - InitialTarget) & 0xfff : int12
+  ///
+  PCAddLo12,
+
   /// A GOT entry getter/constructor, transformed to Page20 pointing at the GOT
   /// entry for the original target.
   ///
@@ -206,6 +230,49 @@ enum EdgeKind_loongarch : Edge::Kind {
   ///
   RequestGOTAndTransformToPageOffset12,
 
+  /// A GOT entry getter/constructor, transformed to PCAddHi20 pointing at the
+  /// GOT entry for the original target.
+  ///
+  /// Indicates that this edge should be transformed into a PCAddHi20 targeting
+  /// the GOT entry for the edge's current target, maintaining the same addend.
+  /// A GOT entry for the target should be created if one does not already
+  /// exist.
+  ///
+  /// Edges of this kind are usually handled by a GOT/PLT builder pass inserted
+  /// by default.
+  ///
+  /// Fixup expression:
+  ///   NONE
+  ///
+  /// Errors:
+  ///   - *ASSERTION* Failure to handle edges of this kind prior to the fixup
+  ///     phase will result in an assert/unreachable during the fixup phase.
+  ///
+  RequestGOTAndTransformToPCAddHi20,
+
+  /// A 30-bit PC-relative call.
+  ///
+  /// Represents a PC-relative call to a target within [-4G, +4G)
+  /// The target must be 4-byte aligned. For adjacent pcaddu12i+jirl
+  /// instruction pairs.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- (Target - Fixup + Addend) >> 2 : int30
+  ///
+  /// Notes:
+  ///   The '30' in the name refers to the number operand bits and follows the
+  /// naming convention used by the corresponding ELF relocations. Since the low
+  /// two bits must be zero (because of the 4-byte alignment of the target) the
+  /// operand is effectively a signed 32-bit number.
+  ///
+  /// Errors:
+  ///   - The result of the unshifted part of the fixup expression must be
+  ///     4-byte aligned otherwise an alignment error will be returned.
+  ///   - The result of the fixup expression must fit into an int30 otherwise an
+  ///     out-of-range error will be returned.
+  ///
+  Call30PCRel,
+
   /// A 36-bit PC-relative call.
   ///
   /// Represents a PC-relative call to a target within [-128G - 0x20000, +128G
@@ -399,6 +466,9 @@ class GOTTableManager : public TableManager<GOTTableManager> {
     case RequestGOTAndTransformToPageOffset12:
       KindToSet = PageOffset12;
       break;
+    case RequestGOTAndTransformToPCAddHi20:
+      KindToSet = PCAddHi20;
+      break;
     default:
       return false;
     }
@@ -437,7 +507,8 @@ class PLTTableManager : public TableManager<PLTTableManager> {
   static StringRef getSectionName() { return "$__STUBS"; }
 
   bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
-    if ((E.getKind() == Branch26PCRel || E.getKind() == Call36PCRel) &&
+    if ((E.getKind() == Branch26PCRel || E.getKind() == Call36PCRel ||
+         E.getKind() == Call30PCRel) &&
         !E.getTarget().isDefined()) {
       DEBUG_WITH_TYPE("jitlink", {
         dbgs() << "  Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp
index ee4a3280f18c4..167c0fa72830a 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp
@@ -37,9 +37,41 @@ class ELFJITLinker_loongarch : public JITLinker<ELFJITLinker_loongarch> {
   ELFJITLinker_loongarch(std::unique_ptr<JITLinkContext> Ctx,
                          std::unique_ptr<LinkGraph> G,
                          PassConfiguration PassConfig)
-      : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
+      : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {
+    JITLinkerBase::getPassConfig().PostAllocationPasses.push_back(
+        [this](LinkGraph &G) { return gatherLoongArchPCAddHi20(G); });
+  }
 
 private:
+  DenseMap<std::pair<const Block *, orc::ExecutorAddrDiff>, const Edge *>
+      RelPCAddHi20Map;
+
+  Error gatherLoongArchPCAddHi20(LinkGraph &G) {
+    for (Block *B : G.blocks())
+      for (Edge &E : B->edges())
+        if (E.getKind() == PCAddHi20)
+          RelPCAddHi20Map[{B, E.getOffset()}] = &E;
+
+    return Error::success();
+  }
+
+  Expected<const Edge &> getLoongArchPCAddHi20(const Edge &E) const {
+    using namespace loongarch;
+    assert((E.getKind() == PCAddLo12) &&
+           "Can only have high relocation for PCAddLo12");
+
+    const Symbol &Sym = E.getTarget();
+    const Block &B = Sym.getBlock();
+    orc::ExecutorAddrDiff Offset = Sym.getOffset() + E.getAddend();
+
+    auto It = RelPCAddHi20Map.find({&B, Offset});
+    if (It != RelPCAddHi20Map.end())
+      return *It->second;
+
+    return make_error<JITLinkError>("No PCAddHi20 relocation type be found "
+                                    "for PCAddLo12 relocation type");
+  }
+
   /// Apply fixup expression for edge to block content.
   Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
     using namespace support;
@@ -149,6 +181,48 @@ class ELFJITLinker_loongarch : public JITLinker<ELFJITLinker_loongarch> {
       *(ulittle32_t *)FixupPtr = RawInstr | Imm11_0;
       break;
     }
+    case PCAddHi20: {
+      uint64_t Target = TargetAddress + Addend;
+      int64_t Delta = Target - FixupAddress + 0x800;
+
+      if (!isInt<32>(Delta))
+        return makeTargetOutOfRangeError(G, B, E);
+
+      uint32_t RawInstr = *(little32_t *)FixupPtr;
+      uint32_t Imm31_12 = extractBits(Delta, /*Hi=*/31, /*Lo=*/12) << 5;
+      *(little32_t *)FixupPtr = RawInstr | Imm31_12;
+      break;
+    }
+    case PCAddLo12: {
+      auto RelPCAddHi20 = getLoongArchPCAddHi20(E);
+      if (!RelPCAddHi20)
+        return RelPCAddHi20.takeError();
+      int64_t Delta =
+          (RelPCAddHi20->getTarget().getAddress() + RelPCAddHi20->getAddend()) -
+          (E.getTarget().getAddress() + E.getAddend());
+
+      uint32_t RawInstr = *(ulittle32_t *)FixupPtr;
+      uint32_t Imm11_0 = extractBits(Delta, /*Hi=*/11, /*Lo=*/0) << 10;
+      *(ulittle32_t *)FixupPtr = RawInstr | Imm11_0;
+      break;
+    }
+    case Call30PCRel: {
+      int64_t Value = TargetAddress - FixupAddress + Addend;
+
+      if (Value != llvm::SignExtend64(Value, 32))
+        return makeTargetOutOfRangeError(G, B, E);
+
+      if (!isShiftedInt<30, 2>(Value))
+        return makeAlignmentError(orc::ExecutorAddr(FixupAddress), Value, 4, E);
+
+      uint32_t Pcaddu12i = *(little32_t *)FixupPtr;
+      uint32_t Hi20 = extractBits(Value, /*Hi=*/31, /*Lo=*/12) << 5;
+      *(little32_t *)FixupPtr = Pcaddu12i | Hi20;
+      uint32_t Jirl = *(little32_t *)(FixupPtr + 4);
+      uint32_t Lo10 = extractBits(Value, /*Hi=*/11, /*Lo=*/2) << 10;
+      *(little32_t *)(FixupPtr + 4) = Jirl | Lo10;
+      break;
+    }
     case Call36PCRel: {
       int64_t Value = TargetAddress - FixupAddress + Addend;
 
@@ -534,6 +608,8 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
       return RequestGOTAndTransformToPage20;
     case ELF::R_LARCH_GOT_PC_LO12:
       return RequestGOTAndTransformToPageOffset12;
+    case ELF::R_LARCH_CALL30:
+      return Call30PCRel;
     case ELF::R_LARCH_CALL36:
       return Call36PCRel;
     case ELF::R_LARCH_ADD6:
@@ -562,6 +638,13 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
       return SubUleb128;
     case ELF::R_LARCH_ALIGN:
       return AlignRelaxable;
+    case ELF::R_LARCH_PCADD_HI20:
+      return PCAddHi20;
+    case ELF::R_LARCH_PCADD_LO12:
+    case ELF::R_LARCH_GOT_PCADD_LO12:
+      return PCAddLo12;
+    case ELF::R_LARCH_GOT_PCADD_HI20:
+      return RequestGOTAndTransformToPCAddHi20;
     }
 
     return make_error<JITLinkError>(
diff --git a/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp b/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp
index 55389adb31b60..69609c1b9c982 100644
--- a/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp
@@ -49,8 +49,12 @@ const char *getEdgeKindName(Edge::Kind K) {
     KIND_NAME_CASE(Branch26PCRel)
     KIND_NAME_CASE(Page20)
     KIND_NAME_CASE(PageOffset12)
+    KIND_NAME_CASE(PCAddHi20)
+    KIND_NAME_CASE(PCAddLo12)
     KIND_NAME_CASE(RequestGOTAndTransformToPage20)
     KIND_NAME_CASE(RequestGOTAndTransformToPageOffset12)
+    KIND_NAME_CASE(RequestGOTAndTransformToPCAddHi20)
+    KIND_NAME_CASE(Call30PCRel)
     KIND_NAME_CASE(Call36PCRel)
     KIND_NAME_CASE(Add6)
     KIND_NAME_CASE(Add8)
diff --git a/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch32_relocations.s b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch32_relocations.s
index da9f9982aade7..50de1c237dafe 100644
--- a/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch32_relocations.s
+++ b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch32_relocations.s
@@ -39,24 +39,43 @@ local_func_jump26:
     b local_func
     .size local_func_jump26, .-local_func_jump26
 
+## Check R_LARCH_PCADD_HI20 / R_LARCH_PCADD_LO12 relocation of a local symbol.
+
+# jitlink-check: decode_operand(test_pcadd_hi20, 1)[19:0] = \
+# jitlink-check:   (named_data - test_pcadd_hi20)[31:12] + \
+# jitlink-check:      (named_data - test_pcadd_hi20)[11:11]
+# jitlink-check: decode_operand(test_pcadd_lo12, 2)[11:0] = \
+# jitlink-check:   (named_data - test_pcadd_hi20)[11:0]
+    .globl test_pcadd_hi20
+    .p2align 2
+test_pcadd_hi20:
+    pcaddu12i $a0, %pcadd_hi20(named_data)
+    .size test_pcadd_hi20, .-test_pcadd_hi20
+
+    .globl test_pcadd_lo12
+    .p2align 2
+test_pcadd_lo12:
+    addi.w $a0, $a0, %pcadd_lo12(test_pcadd_hi20)
+    .size test_pcadd_lo12, .-test_pcadd_lo12
+
 ## Check R_LARCH_PCALA_HI20 / R_LARCH_PCALA_LO12 relocation of a local symbol.
 
-# jitlink-check: decode_operand(test_pcalau12i_pcrel, 1)[19:0] = \
-# jitlink-check:   (named_data - test_pcalau12i_pcrel)[31:12] + \
+# jitlink-check: decode_operand(test_pc_hi20, 1)[19:0] = \
+# jitlink-check:   (named_data - test_pc_hi20)[31:12] + \
 # jitlink-check:      named_data[11:11]
-# jitlink-check: decode_operand(test_addi_pcrel_lo12, 2)[11:0] = \
+# jitlink-check: decode_operand(test_pc_lo12, 2)[11:0] = \
 # jitlink-check:   (named_data)[11:0]
-    .globl test_pcalau12i_pcrel
+    .globl test_pc_hi20
     .p2align 2
-test_pcalau12i_pcrel:
+test_pc_hi20:
     pcalau12i $a0, %pc_hi20(named_data)
-    .size test_pcalau12i_pcrel, .-test_pcalau12i_pcrel
+    .size test_pc_hi20, .-test_pc_hi20
 
-    .globl test_addi_pcrel_lo12
+    .globl test_pc_lo12
     .p2align 2
-test_addi_pcrel_lo12:
+test_pc_lo12:
     addi.w $a0, $a0, %pc_lo12(named_data)
-    .size test_addi_pcrel_lo12, .-test_addi_pcrel_lo12
+    .size test_pc_lo12, .-test_pc_lo12
 
 ## Check that calls/jumps to external functions trigger the generation of stubs
 ## and GOT entries.
@@ -68,6 +87,12 @@ test_addi_pcrel_lo12:
 # jitlink-check: decode_operand(test_external_jump, 0) = \
 # jitlink-check:   (stub_addr(elf_reloc.o, external_func) - \
 # jitlink-check:      test_external_jump)[27:0]
+# jitlink-check: decode_operand(test_external_call30, 1)[19:0] = \
+# jitlink-check:   (stub_addr(elf_reloc.o, external_func) - \
+# jitlink-check:      test_external_call30)[31:12]
+# jitlink-check: decode_operand(test_external_call30 + 4, 2)[17:0] = \
+# jitlink-check:   (stub_addr(elf_reloc.o, external_func) - \
+# jitlink-check:      test_external_call30)[11:0]
     .globl test_external_call
     .p2align  2
 test_external_call:
@@ -80,28 +105,73 @@ test_external_jump:
     b external_func
     .size test_external_jump, .-test_external_jump
 
+    .globl test_external_call30
+    .p2align  2
+test_external_call30:
+    pcaddu12i $ra, %call30(external_func)
+    jirl $ra, $ra, 0
+    .size test_external_call30, .-test_external_call30
+
+## Check R_LARCH_GOT_PCADD_HI20 / R_LARCH_GOT_PCADD_LO12 handling with a
+## reference to an external symbol. Validate both the reference to the GOT
+## entry, and also the content of the GOT entry.
+
+# jitlink-check: *{4}(got_addr(elf_reloc.o, external_data)) = external_data
+# jitlink-check: decode_operand(test_got_pcadd_hi20_external, 1)[19:0] = \
+# jitlink-check:   (got_addr(elf_reloc.o, external_data) - \
+# jitlink-check:      test_got_pcadd_hi20_external)[31:12] + \
+# jitlink-check:      (got_addr(elf_reloc.o, external_data) - \
+# jitlink-check:         test_got_pcadd_hi20_external)[11:11]
+# jitlink-check: decode_operand(test_got_pcadd_lo12_external, 2)[11:0] = \
+# jitlink-check:   (got_addr(elf_reloc.o, external_data) - \
+# jitlink-check:      test_got_pcadd_hi20_external)[11:0]
+    .globl test_got_pcadd_hi20_external
+    .p2align 2
+test_got_pcadd_hi20_external:
+    pcaddu12i $a0, %got_pcadd_hi20(external_data)
+    .size test_got_pcadd_hi20_external, .-test_got_pcadd_hi20_external
+
+    .globl test_got_pcadd_lo12_external
+    .p2align 2
+test_got_pcadd_lo12_external:
+    ld.w $a0, $a0, %got_pcadd_lo12(test_got_pcadd_hi20_external)
+    .size test_got_pcadd_lo12_external, .-test_got_pcadd_lo12_external
+
 ## Check R_LARCH_GOT_PC_HI20 / R_LARCH_GOT_PC_LO12 handling with a reference to
 ## an external symbol. Validate both the reference to the GOT entry, and also
 ## the content of the GOT entry.
 
 # jitlink-check: *{4}(got_addr(elf_reloc.o, external_data)) = external_data
-# jitlink-check: decode_operand(test_gotpage_external, 1)[19:0] = \
+# jitlink-check: decode_operand(test_got_pc_hi20_external, 1)[19:0] = \
 # jitlink-check:   (got_addr(elf_reloc.o, external_data)[31:12] - \
-# jitlink-check:      test_gotpage_external[31:12] + \
+# jitlink-check:      test_got_pc_hi20_external[31:12] + \
 # jitlink-check:      got_addr(elf_reloc.o, external_data)[11:11])[19:0]
-# jitlink-check: decode_operand(test_gotoffset12_external, 2)[11:0] = \
+# jitlink-check: decode_operand(test_got_pc_lo12_external, 2)[11:0] = \
 # jitlink-check:   got_addr(elf_reloc.o, external_data)[11:0]
-    .globl test_gotpage_external
+    .globl test_got_pc_hi20_external
     .p2align 2
-test_gotpage_external:
+test_got_pc_hi20_external:
     pcalau12i $a0, %got_pc_hi20(external_data)
-    .size test_gotpage_external, .-test_gotpage_external
+    .size test_got_pc_hi20_external, .-test_got_pc_hi20_external
 
-    .globl test_gotoffset12_external
+    .globl test_got_pc_lo12_external
     .p2align 2
-test_gotoffset12_external:
+test_got_pc_lo12_external:
     ld.w $a0, $a0, %got_pc_lo12(external_data)
-    .size test_gotoffset12_external, .-test_gotoffset12_external
+    .size test_got_pc_lo12_external, .-test_got_pc_lo12_external
+
+## Check R_LARCH_CALL30 relocation of a local function call.
+
+# jitlink-check: decode_operand(local_func_call30, 1)[19:0] = \
+# jitlink-check:   (local_func - local_func_call30)[31:12]
+# jitlink-check: decode_operand(local_func_call30 + 4, 2)[17:0] = \
+# jitlink-check:   (local_func - local_func_call30)[11:0]
+    .globl local_func_call30
+    .p2align 2
+local_func_call30:
+    pcaddu12i $ra, %call30(local_func)
+    jirl $ra, $ra, 0
+    .size local_func_call30, .-local_func_call30
 
 ## Check R_LARCH_B16 relocation for compare and branch instructions.
 

>From 3dafa81323f98ca427e32943a1f665a90a4b72fa Mon Sep 17 00:00:00 2001
From: hev <wangrui at loongson.cn>
Date: Thu, 15 Jan 2026 10:54:22 +0800
Subject: [PATCH 05/10] [JITLink][LoongArch] Refactor jump stub to support
 LA32R (#175355)

(cherry picked from commit 29419c48d518e6e9082ec9a3c5dbba309ec25ebe)
---
 .../llvm/ExecutionEngine/JITLink/loongarch.h     | 11 ++++++++---
 llvm/lib/ExecutionEngine/JITLink/loongarch.cpp   |  4 ++--
 .../ExecutionEngine/JITLink/StubsTests.cpp       | 16 ++++++++--------
 3 files changed, 18 insertions(+), 13 deletions(-)

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
index 2da2a4201ae16..50a0ded54c3e3 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
@@ -447,9 +447,14 @@ inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
                                               Symbol &PointerSymbol) {
   Block &StubContentBlock = G.createContentBlock(
       StubSection, getStubBlockContent(G), orc::ExecutorAddr(), 4, 0);
-  StubContentBlock.addEdge(Page20, 0, PointerSymbol, 0);
-  StubContentBlock.addEdge(PageOffset12, 4, PointerSymbol, 0);
-  return G.addAnonymousSymbol(StubContentBlock, 0, StubEntrySize, true, false);
+  Symbol &StubSymbol =
+      G.addAnonymousSymbol(StubContentBlock, 0, StubEntrySize, true, false);
+  StubContentBlock.addEdge(G.getPointerSize() == 8 ? Page20 : PCAddHi20, 0,
+                           PointerSymbol, 0);
+  StubContentBlock.addEdge(
+      G.getPointerSize() == 8 ? PageOffset12 : PCAddLo12, 4,
+      G.getPointerSize() == 8 ? PointerSymbol : StubSymbol, 0);
+  return StubSymbol;
 }
 
 /// Global Offset Table Builder.
diff --git a/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp b/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp
index 69609c1b9c982..221dd45a7eff0 100644
--- a/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp
@@ -28,8 +28,8 @@ const uint8_t LA64StubContent[StubEntrySize] = {
 };
 
 const uint8_t LA32StubContent[StubEntrySize] = {
-    0x14, 0x00, 0x00, 0x1a, // pcalau12i $t8, %page20(imm)
-    0x94, 0x02, 0x80, 0x28, // ld.w $t8, $t8, %pageoff12(imm)
+    0x14, 0x00, 0x00, 0x1c, // pcaddu12i $t8, %pcadd20(imm)
+    0x94, 0x02, 0x80, 0x28, // ld.w $t8, $t8, %pcadd12(.Lpcadd_hi)
     0x80, 0x02, 0x00, 0x4c  // jr $t8
 };
 
diff --git a/llvm/unittests/ExecutionEngine/JITLink/StubsTests.cpp b/llvm/unittests/ExecutionEngine/JITLink/StubsTests.cpp
index 643ea6754f2d1..8473c71ef0ce3 100644
--- a/llvm/unittests/ExecutionEngine/JITLink/StubsTests.cpp
+++ b/llvm/unittests/ExecutionEngine/JITLink/StubsTests.cpp
@@ -119,11 +119,11 @@ TEST(StubsTest, StubsGeneration_loongarch32) {
       0x14,
       0x00,
       0x00,
-      0x1a, // pcalau12i $t8, %page20(imm)
+      0x1c, // pcaddu12i $t8, %pcadd20(imm)
       static_cast<char>(0x94),
       0x02,
       static_cast<char>(0x80),
-      0x28, // ld.d $t8, $t8, %pageoff12(imm)
+      0x28, // ld.w $t8, $t8, %pcadd12(.Lpcadd_hi)
       static_cast<char>(0x80),
       0x02,
       0x00,
@@ -137,12 +137,12 @@ TEST(StubsTest, StubsGeneration_loongarch32) {
   EXPECT_EQ(std::distance(StubSym.getBlock().edges().begin(),
                           StubSym.getBlock().edges().end()),
             2U);
-  auto &PageHighEdge = *StubSym.getBlock().edges().begin();
-  auto &PageLowEdge = *++StubSym.getBlock().edges().begin();
-  EXPECT_EQ(PageHighEdge.getKind(), loongarch::Page20);
-  EXPECT_EQ(&PageHighEdge.getTarget(), &PointerSym);
-  EXPECT_EQ(PageLowEdge.getKind(), loongarch::PageOffset12);
-  EXPECT_EQ(&PageLowEdge.getTarget(), &PointerSym);
+  auto &HighEdge = *StubSym.getBlock().edges().begin();
+  auto &LowEdge = *++StubSym.getBlock().edges().begin();
+  EXPECT_EQ(HighEdge.getKind(), loongarch::PCAddHi20);
+  EXPECT_EQ(&HighEdge.getTarget(), &PointerSym);
+  EXPECT_EQ(LowEdge.getKind(), loongarch::PCAddLo12);
+  EXPECT_EQ(&LowEdge.getTarget(), &StubSym);
   EXPECT_EQ(StubSym.getBlock().getContent(),
             ArrayRef<char>(PointerJumpStubContent));
 }

>From 5a73eb40013c3969607ae6eac736698fe8c30c98 Mon Sep 17 00:00:00 2001
From: hev <wangrui at loongson.cn>
Date: Thu, 15 Jan 2026 19:54:52 +0800
Subject: [PATCH 06/10] [lld][LoongArch] Support reloc types for LA32R/LA32S
 (#172618)

This patch adds support for processing the relocation types introduced
in la-abi-specs v2.50.

Link: https://github.com/loongson/la-abi-specs/pull/16
Link:
https://sourceware.org/pipermail/binutils/2025-December/146091.html
(cherry picked from commit 019bf036972f061ee42b1fa7d64bc3456689a664)
---
 lld/ELF/Arch/LoongArch.cpp                 |  69 ++++++++++-
 lld/ELF/InputSection.cpp                   |  70 +++++++++++
 lld/ELF/Relocations.cpp                    |   2 +-
 lld/ELF/Relocations.h                      |   1 +
 lld/test/ELF/loongarch-call30.s            |  64 ++++++++++
 lld/test/ELF/loongarch-relax-call30-2.s    |  65 ++++++++++
 lld/test/ELF/loongarch-relax-call30.s      | 136 +++++++++++++++++++++
 lld/test/ELF/loongarch-relax-emit-relocs.s | 101 ++++++++++-----
 lld/test/ELF/loongarch-tls-gd-edge-case.s  |  10 +-
 lld/test/ELF/loongarch-tls-gd.s            |  10 +-
 lld/test/ELF/loongarch-tls-ie.s            |  21 ++--
 lld/test/ELF/loongarch-tls-ld.s            |  10 +-
 lld/test/ELF/loongarch-tlsdesc.s           |  30 ++++-
 13 files changed, 524 insertions(+), 65 deletions(-)
 create mode 100644 lld/test/ELF/loongarch-call30.s
 create mode 100644 lld/test/ELF/loongarch-relax-call30-2.s
 create mode 100644 lld/test/ELF/loongarch-relax-call30.s

diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index c6cdf05547d3f..264990be48ec3 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -184,6 +184,10 @@ static uint32_t setJ5(uint32_t insn, uint32_t imm) {
   return (insn & 0xfffffc1f) | (extractBits(imm, 4, 0) << 5);
 }
 
+static uint32_t setK10(uint32_t insn, uint32_t imm) {
+  return (insn & 0xffc003ff) | (extractBits(imm, 9, 0) << 10);
+}
+
 static uint32_t setK12(uint32_t insn, uint32_t imm) {
   return (insn & 0xffc003ff) | (extractBits(imm, 11, 0) << 10);
 }
@@ -439,6 +443,13 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
     // [1]: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=9f482b73f41a9a1bbfb173aad0733d1c824c788a
     // [2]: https://github.com/loongson/la-abi-specs/pull/3
     return isJirl(read32le(loc)) ? R_PLT : R_ABS;
+  case R_LARCH_PCADD_LO12:
+  case R_LARCH_GOT_PCADD_LO12:
+  case R_LARCH_TLS_IE_PCADD_LO12:
+  case R_LARCH_TLS_LD_PCADD_LO12:
+  case R_LARCH_TLS_GD_PCADD_LO12:
+  case R_LARCH_TLS_DESC_PCADD_LO12:
+    return RE_LOONGARCH_PC_INDIRECT;
   case R_LARCH_TLS_DTPREL32:
   case R_LARCH_TLS_DTPREL64:
     return R_DTPREL;
@@ -469,10 +480,12 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
   case R_LARCH_32_PCREL:
   case R_LARCH_64_PCREL:
   case R_LARCH_PCREL20_S2:
+  case R_LARCH_PCADD_HI20:
     return R_PC;
   case R_LARCH_B16:
   case R_LARCH_B21:
   case R_LARCH_B26:
+  case R_LARCH_CALL30:
   case R_LARCH_CALL36:
     return R_PLT_PC;
   case R_LARCH_GOT_PC_HI20:
@@ -482,6 +495,9 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
   case R_LARCH_TLS_IE64_PC_LO20:
   case R_LARCH_TLS_IE64_PC_HI12:
     return RE_LOONGARCH_GOT_PAGE_PC;
+  case R_LARCH_GOT_PCADD_HI20:
+  case R_LARCH_TLS_IE_PCADD_HI20:
+    return R_GOT_PC;
   case R_LARCH_GOT_PC_LO12:
   case R_LARCH_TLS_IE_PC_LO12:
     return RE_LOONGARCH_GOT;
@@ -545,12 +561,15 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
   case R_LARCH_TLS_DESC_LO12:
   case R_LARCH_TLS_DESC64_LO20:
   case R_LARCH_TLS_DESC64_HI12:
+  case R_LARCH_TLS_DESC_PCADD_HI20:
     return R_TLSDESC;
   case R_LARCH_TLS_DESC_CALL:
     return R_TLSDESC_CALL;
   case R_LARCH_TLS_LD_PCREL20_S2:
+  case R_LARCH_TLS_LD_PCADD_HI20:
     return R_TLSLD_PC;
   case R_LARCH_TLS_GD_PCREL20_S2:
+  case R_LARCH_TLS_GD_PCADD_HI20:
     return R_TLSGD_PC;
   case R_LARCH_TLS_DESC_PCREL20_S2:
     return R_TLSDESC_PC;
@@ -628,6 +647,24 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
     write32le(loc, setD10k16(read32le(loc), val >> 2));
     return;
 
+  case R_LARCH_CALL30: {
+    // This relocation is designed for adjacent pcaddu12i+jirl pairs that
+    // are patched in one time.
+    // The relocation range is [-2G, +2G) (of course must be 4-byte aligned).
+    if ((int64_t)val != llvm::SignExtend64(val, 32))
+      reportRangeError(ctx, loc, rel, Twine(val), llvm::minIntN(32),
+                       llvm::maxIntN(32));
+    checkAlignment(ctx, loc, val, 4, rel);
+    // Although jirl adds the immediate as a signed value, it is always positive
+    // in this case, so no adjustment is needed, unlink CALL36.
+    uint32_t hi20 = extractBits(val, 31, 12);
+    // Despite the name, the lower part is actually 12 bits with 4-byte aligned.
+    uint32_t lo10 = extractBits(val, 11, 2);
+    write32le(loc, setJ20(read32le(loc), hi20));
+    write32le(loc + 4, setK10(read32le(loc + 4), lo10));
+    return;
+  }
+
   case R_LARCH_CALL36: {
     // This relocation is designed for adjacent pcaddu18i+jirl pairs that
     // are patched in one time. Because of sign extension of these insns'
@@ -671,6 +708,12 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
   case R_LARCH_TLS_LE_LO12_R:
   case R_LARCH_TLS_DESC_PC_LO12:
   case R_LARCH_TLS_DESC_LO12:
+  case R_LARCH_PCADD_LO12:
+  case R_LARCH_GOT_PCADD_LO12:
+  case R_LARCH_TLS_IE_PCADD_LO12:
+  case R_LARCH_TLS_LD_PCADD_LO12:
+  case R_LARCH_TLS_GD_PCADD_LO12:
+  case R_LARCH_TLS_DESC_PCADD_LO12:
     write32le(loc, setK12(read32le(loc), extractBits(val, 11, 0)));
     return;
 
@@ -690,6 +733,17 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
   case R_LARCH_TLS_DESC_HI20:
     write32le(loc, setJ20(read32le(loc), extractBits(val, 31, 12)));
     return;
+  case R_LARCH_PCADD_HI20:
+  case R_LARCH_GOT_PCADD_HI20:
+  case R_LARCH_TLS_IE_PCADD_HI20:
+  case R_LARCH_TLS_LD_PCADD_HI20:
+  case R_LARCH_TLS_GD_PCADD_HI20:
+  case R_LARCH_TLS_DESC_PCADD_HI20: {
+    uint64_t hi = val + 0x800;
+    checkInt(ctx, loc, val, 32, rel);
+    write32le(loc, setJ20(read32le(loc), extractBits(hi, 31, 12)));
+    return;
+  }
   case R_LARCH_TLS_LE_HI20_R:
     write32le(loc, setJ20(read32le(loc), extractBits(val + 0x800, 31, 12)));
     return;
@@ -998,12 +1052,16 @@ static void relaxPCHi20Lo12(Ctx &ctx, const InputSection &sec, size_t i,
 
 // Relax code sequence.
 // From:
-//   pcaddu18i $ra, %call36(foo)
-//   jirl $ra, $ra, 0
+//   la32r:
+//     pcaddu12i $ra, %call30(foo)
+//     jirl $ra, $ra, 0
+//   la32s/la64:
+//     pcaddu18i $ra, %call36(foo)
+//     jirl $ra, $ra, 0
 // To:
 //   b/bl foo
-static void relaxCall36(Ctx &ctx, const InputSection &sec, size_t i,
-                        uint64_t loc, Relocation &r, uint32_t &remove) {
+static void relaxMediumCall(Ctx &ctx, const InputSection &sec, size_t i,
+                            uint64_t loc, Relocation &r, uint32_t &remove) {
   const uint64_t dest =
       (r.expr == R_PLT_PC ? r.sym->getPltVA(ctx) : r.sym->getVA(ctx)) +
       r.addend;
@@ -1108,9 +1166,10 @@ static bool relax(Ctx &ctx, InputSection &sec) {
       } else if (isPairRelaxable(relocs, i))
         relaxPCHi20Lo12(ctx, sec, i, loc, r, relocs[i + 2], remove);
       break;
+    case R_LARCH_CALL30:
     case R_LARCH_CALL36:
       if (relaxable(relocs, i))
-        relaxCall36(ctx, sec, i, loc, r, remove);
+        relaxMediumCall(ctx, sec, i, loc, r, remove);
       break;
     case R_LARCH_TLS_LE_HI20_R:
     case R_LARCH_TLS_LE_ADD_R:
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index ca8a9c0d27f29..a9afd3449ebb2 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -714,6 +714,71 @@ static Relocation *getRISCVPCRelHi20(Ctx &ctx, const InputSectionBase *loSec,
   return nullptr;
 }
 
+// For RE_LARCH_PC_INDIRECT (R_LARCH_*PCADD_LO12), the symbol actually points
+// the corresponding R_LARCH_*PCADD_HI20 relocation, and the target VA is
+// calculated using PCADD_HI20's symbol.
+//
+// This function returns the R_LARCH_*PCADD_HI20 relocation from the
+// R_LARCH_*PCADD_LO12 relocation.
+static Relocation *getLoongArchPCAddHi20(Ctx &ctx,
+                                         const InputSectionBase *loSec,
+                                         const Relocation &loReloc) {
+  int64_t addend = loReloc.addend;
+  Symbol *sym = loReloc.sym;
+
+  const Defined *d = dyn_cast<Defined>(sym);
+  if (!d) {
+    Err(ctx) << loSec->getLocation(loReloc.offset)
+             << " points to undefined symbol";
+    return nullptr;
+  }
+  if (!d->section) {
+    Err(ctx)
+        << loSec->getLocation(loReloc.offset)
+        << ": R_LARCH_*PCADD_LO12 relocation points to an absolute symbol: "
+        << sym->getName();
+    return nullptr;
+  }
+  InputSection *hiSec = cast<InputSection>(d->section);
+
+  if (hiSec != loSec) {
+    Err(ctx) << loSec->getLocation(loReloc.offset)
+             << ": R_LARCH_*PCADD_LO12 relocation points to a symbol '"
+             << sym->getName() << "' in a different section '" << hiSec->name
+             << "'";
+    return nullptr;
+  }
+
+  if (addend != 0)
+    Warn(ctx) << loSec->getLocation(loReloc.offset)
+              << ": non-zero addend in R_LARCH_*PCADD_LO12 relocation to "
+              << hiSec->getObjMsg(d->value) << " is ignored";
+
+  // Relocations are sorted by offset, so we can use std::equal_range to do
+  // binary search.
+  Relocation hiReloc;
+  hiReloc.offset = d->value + addend;
+  auto range =
+      std::equal_range(hiSec->relocs().begin(), hiSec->relocs().end(), hiReloc,
+                       [](const Relocation &lhs, const Relocation &rhs) {
+                         return lhs.offset < rhs.offset;
+                       });
+
+  for (auto it = range.first; it != range.second; ++it)
+    if (it->type == R_LARCH_PCADD_HI20 || it->type == R_LARCH_GOT_PCADD_HI20 ||
+        it->type == R_LARCH_TLS_IE_PCADD_HI20 ||
+        it->type == R_LARCH_TLS_LD_PCADD_HI20 ||
+        it->type == R_LARCH_TLS_GD_PCADD_HI20 ||
+        it->type == R_LARCH_TLS_DESC_PCADD_HI20)
+      return &*it;
+
+  Err(ctx) << loSec->getLocation(loReloc.offset)
+           << ": R_LARCH_*PCADD_LO12 relocation points to "
+           << hiSec->getObjMsg(d->value)
+           << " without an associated R_LARCH_*PCADD_HI20 relocation";
+  return nullptr;
+}
+
 // A TLS symbol's virtual address is relative to the TLS segment. Add a
 // target-specific adjustment to produce a thread-pointer-relative offset.
 static int64_t getTlsTpOffset(Ctx &ctx, const Symbol &s) {
@@ -893,6 +958,11 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
       return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx));
     return 0;
   }
+  case RE_LOONGARCH_PC_INDIRECT: {
+    if (const Relocation *hiRel = getLoongArchPCAddHi20(ctx, this, r))
+      return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx, a));
+    return 0;
+  }
   case RE_LOONGARCH_PAGE_PC:
     return getLoongArchPageDelta(r.sym->getVA(ctx, a), p, r.type);
   case R_PC:
diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 560fd8214720d..226c4e7907a5e 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -148,7 +148,7 @@ static bool isRelExpr(RelExpr expr) {
   return oneof<R_PC, R_GOTREL, R_GOTPLTREL, RE_ARM_PCA, RE_MIPS_GOTREL,
                RE_PPC64_CALL, RE_PPC64_RELAX_TOC, RE_AARCH64_PAGE_PC,
                R_RELAX_GOT_PC, RE_RISCV_PC_INDIRECT, RE_PPC64_RELAX_GOT_PC,
-               RE_LOONGARCH_PAGE_PC>(expr);
+               RE_LOONGARCH_PAGE_PC, RE_LOONGARCH_PC_INDIRECT>(expr);
 }
 
 static RelExpr toPlt(RelExpr expr) {
diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index 0cba1fa11d234..11d7aef8c635f 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -133,6 +133,7 @@ enum RelExpr {
   // also reused for TLS, making the semantics differ from other architectures.
   RE_LOONGARCH_GOT,
   RE_LOONGARCH_GOT_PAGE_PC,
+  RE_LOONGARCH_PC_INDIRECT,
   RE_LOONGARCH_TLSGD_PAGE_PC,
   RE_LOONGARCH_TLSDESC_PAGE_PC,
   RE_LOONGARCH_RELAX_TLS_GD_TO_IE_PAGE_PC,
diff --git a/lld/test/ELF/loongarch-call30.s b/lld/test/ELF/loongarch-call30.s
new file mode 100644
index 0000000000000..907e8704e908b
--- /dev/null
+++ b/lld/test/ELF/loongarch-call30.s
@@ -0,0 +1,64 @@
+# REQUIRES: loongarch
+
+# RUN: rm -rf %t && split-file %s %t
+# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf %t/a.s -o %t/a.o
+
+# RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x21020 -o %t/exe1
+# RUN: llvm-objdump --no-show-raw-insn -d %t/exe1 | FileCheck --match-full-lines %s --check-prefix=EXE1
+## hi20 = target - pc >> 12 = 0x21020 - 0x20010 >> 12 = 1
+## lo12 = target - pc & (1 << 12) - 1 = 0x21020 - 0x20010 & 0xfff = 16
+# EXE1:      20010: pcaddu12i $t0, 1
+# EXE1-NEXT: 20014: jirl $zero, $t0, 16
+
+# RUN: ld.lld %t/a.o --section-start=.text=0x20010 --section-start=.sec.foo=0x21820 -o %t/exe2
+# RUN: llvm-objdump --no-show-raw-insn -d %t/exe2 | FileCheck --match-full-lines %s --check-prefix=EXE2
+## hi20 = target - pc >> 12 = 0x21820 - 0x20010 >> 12 = 1
+## lo12 = target - pc & (1 << 12) - 1 = 0x21820 - 0x20010 & 0xfff = 2064
+# EXE2:      20010: pcaddu12i $t0, 1
+# EXE2-NEXT: 20014: jirl $zero, $t0, 2064
+
+# RUN: ld.lld %t/a.o -shared -T %t/a.t -o %t/a.so
+# RUN: llvm-readelf -x .got.plt %t/a.so | FileCheck --check-prefix=GOTPLT %s
+# RUN: llvm-objdump -d --no-show-raw-insn %t/a.so | FileCheck --check-prefix=SO %s
+## PLT should be present in this case.
+# SO:    Disassembly of section .plt:
+# SO:    <.plt>:
+##       foo at plt:
+# SO:    1234520:  pcaddu12i $t3, 64{{$}}
+# SO-NEXT:         ld.w $t3, $t3, 444{{$}}
+# SO-NEXT:         jirl $t1, $t3, 0
+# SO-NEXT:         nop
+
+# SO:   Disassembly of section .text:
+# SO:   <_start>:
+## hi20 = foo at plt - pc >> 12 = 0x1234520 - 0x1274670 >> 12 = -65
+## lo18 = foo at plt - pc & (1 << 12) - 1 = 0x1234520 - 0x1274670 & 0xfff = 3760
+# SO-NEXT: pcaddu12i $t0, -65{{$}}
+# SO-NEXT: jirl $zero, $t0, 3760{{$}}
+
+# GOTPLT:      section '.got.plt':
+# GOTPLT-NEXT: 0x012746d4 00000000 00000000 00452301
+
+## Impossible case in reality becasue all LoongArch instructions are fixed 4-bytes long.
+# RUN: not ld.lld %t/a.o --section-start=.text=0x20000 --section-start=.sec.foo=0x40001 -o /dev/null 2>&1 | \
+# RUN:   FileCheck -DFILE=%t/a.o --check-prefix=ERROR-ALIGN %s
+# ERROR-ALIGN: error: [[FILE]]:(.text+0x0): improper alignment for relocation R_LARCH_CALL30: 0x20001 is not aligned to 4 bytes
+
+#--- a.t
+SECTIONS {
+ .plt   0x1234500: { *(.plt) }
+ .text  0x1274670: { *(.text) }
+}
+
+#--- a.s
+.text
+.global _start
+_start:
+  .reloc ., R_LARCH_CALL30, foo
+  pcaddu12i $t0, 0
+  jirl      $zero, $t0, 0
+
+.section .sec.foo,"awx"
+.global foo
+foo:
+  ret
diff --git a/lld/test/ELF/loongarch-relax-call30-2.s b/lld/test/ELF/loongarch-relax-call30-2.s
new file mode 100644
index 0000000000000..45584f4e14dc8
--- /dev/null
+++ b/lld/test/ELF/loongarch-relax-call30-2.s
@@ -0,0 +1,65 @@
+# REQUIRES: loongarch
+## Relax R_LARCH_CALL30. This test tests boundary cases and some special symbols.
+
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=loongarch32 -mattr=+relax a.s -o a.o
+
+# RUN: ld.lld -T lds a.o -o a
+# RUN: llvm-objdump -d --no-show-raw-insn a | FileCheck %s --check-prefixes=RELAX,RELAX-MID
+
+## Unsure whether this needs a diagnostic. GNU ld allows this.
+# RUN: ld.lld -T lds -pie a.o -o a.pie
+# RUN: llvm-objdump -d --no-show-raw-insn a.pie | FileCheck %s --check-prefixes=RELAX,RELAX-MID
+
+# RUN: ld.lld -T lds -pie -z notext -z ifunc-noplt a.o -o a.ifunc-noplt
+# RUN: llvm-objdump -d --no-show-raw-insn a.ifunc-noplt | FileCheck %s --check-prefixes=RELAX,NORELAX-MID
+
+# RELAX-LABEL:  <_start>:
+## offset = 0x10000000 - 0x8000000 = 0x8000000(134217728), hi=512, lo18=0
+# RELAX-NEXT:    8000000:  pcaddu12i $ra, 32768
+# RELAX-NEXT:              jirl   $ra, $ra, 0
+# RELAX-NEXT:              bl     134217720
+# RELAX-NEXT:              bl     -134217728
+## offset = 12 - 0x8000010 = -0x8000004(-134217732), hi=512, lo18=-4
+# RELAX-NEXT:    8000010:  pcaddu12i $ra, -32769
+# RELAX-NEXT:              jirl   $ra, $ra, 4092
+# RELAX-EMPTY:
+
+# RELAX-MID-LABEL:  <.mid>:
+## offset = 0x8010000 - 0x8008000 = 32768
+# RELAX-MID-NEXT:    8008000:  bl     32768
+# RELAX-MID-NEXT:              b      32764
+# RELAX-MID-EMPTY:
+
+# NORELAX-MID-LABEL: <.mid>:
+# NORELAX-MID-NEXT:  8008000:  pcaddu12i $ra, 0
+# NORELAX-MID-NEXT:            jirl   $ra, $ra, 0
+# NORELAX-MID-NEXT:            pcaddu12i $t0, 0
+# NORELAX-MID-NEXT:            jr     $t0
+# NORELAX-MID-EMPTY:
+
+#--- a.s
+.global _start, ifunc
+_start:
+  call30 pos       # exceed positive range (.text+0x7fffffc), not relaxed
+  call30 pos       # relaxed
+  call30 neg       # relaxed
+  call30 neg       # exceed negative range (.text+16-0x8000000), not relaxed
+
+.section .mid,"ax", at progbits
+.balign 16
+  call30 ifunc      # enable ifunc, not relaxed
+  tail30 $t0, ifunc # enable ifunc, not relaxed
+
+.type ifunc, @gnu_indirect_function
+ifunc:
+  ret
+
+#--- lds
+SECTIONS {
+  .text 0x8000000 : { *(.text) }
+  .mid  0x8008000 : { *(.mid) }
+  .iplt 0x8010000 : { *(.iplt) }
+}
+neg = 12;
+pos = 0x10000000;
diff --git a/lld/test/ELF/loongarch-relax-call30.s b/lld/test/ELF/loongarch-relax-call30.s
new file mode 100644
index 0000000000000..f3172897f47fb
--- /dev/null
+++ b/lld/test/ELF/loongarch-relax-call30.s
@@ -0,0 +1,136 @@
+# REQUIRES: loongarch
+## Relax R_LARCH_CALL30, which involves the macro instructions call30/tail30.
+
+# RUN: rm -rf %t && split-file %s %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=loongarch32 -mattr=+relax a.s -o a.32.o
+# RUN: llvm-mc -filetype=obj -triple=loongarch32 -mattr=+relax b.s -o b.32.o
+# RUN: ld.lld -shared -soname=b.so b.32.o -o b.32.so
+# RUN: ld.lld -T lds a.32.o b.32.so -o 32
+# RUN: llvm-objdump -td --no-show-raw-insn 32 | FileCheck %s --check-prefix=RELAX
+
+## --no-relax disables relaxation.
+# RUN: ld.lld -T lds a.32.o b.32.so --no-relax -o 32.norelax
+# RUN: llvm-objdump -td --no-show-raw-insn 32.norelax | FileCheck %s --check-prefix=NORELAX
+
+# RELAX:       {{0*}}00010000 g       .text  {{0*}}0000001c _start
+# RELAX:       {{0*}}0001001c g       .text  {{0*}}00000000 _start_end
+# RELAX:       {{0*}}00010808 g       .mid   {{0*}}00000000 mid_end
+# RELAX:       {{0*}}10010010 g       .high  {{0*}}00000000 high_end
+
+# RELAX-LABEL: <_start>:
+## offset = 0x10018 - 0x10000 = 24
+# RELAX-NEXT:      10000:  bl     24 <a>
+# RELAX-NEXT:              b      20 <a>
+# RELAX-NEXT:              nop
+# RELAX-NEXT:              nop
+## offset = .plt(0x10400)+32 - 0x10010 = 1040
+# RELAX-NEXT:      10010:  bl     1040 <bar+0x10420>
+# RELAX-NEXT:              b      1036 <bar+0x10420>
+# RELAX-EMPTY:
+# RELAX-NEXT: <a>:
+# RELAX-NEXT:      10018:  ret
+# RELAX-EMPTY:
+
+# RELAX-LABEL: <.mid>:
+## offset = 0x10000 - 0x10800 = -2048
+# RELAX-NEXT:      10800:  bl     -2048 <_start>
+# RELAX-NEXT:              b      -2052 <_start>
+# RELAX-EMPTY:
+
+# RELAX-LABEL: <.mid2>:
+## offset = 0x10000 - 0x1010000 = -16777216
+# RELAX-NEXT:    1010000:  bl     -16777216 <_start>
+# RELAX-NEXT:              b      -16777220 <_start>
+# RELAX-EMPTY:
+
+# RELAX-LABEL: <.high>:
+## offset = 0x10000 - 0x10010000 = -0x10000000, hi=-1024, lo18=0
+# RELAX-NEXT:   10010000:  pcaddu12i $ra, -65536
+# RELAX-NEXT:              jirl   $ra, $ra, 0
+# RELAX-NEXT:              pcaddu12i $t0, -65537
+# RELAX-NEXT:              jirl   $zero, $t0, 4088
+# RELAX-EMPTY:
+
+
+# NORELAX-LABEL: <_start>:
+## offset = 0x10020 - 0x10000 = 0x20, hi=0, lo18=32
+# NORELAX-NEXT:    10000:  pcaddu12i $ra, 0
+# NORELAX-NEXT:            jirl   $ra, $ra, 32
+## offset = 0x10020 - 0x10008 = 0x18, hi=0, lo18=24
+# NORELAX-NEXT:    10008:  pcaddu12i $t0, 0
+# NORELAX-NEXT:            jirl   $zero, $t0, 24
+## offset = .plt(0x10400)+32 - 0x10010 = 0x410, hi=0, lo18=1040
+# NORELAX-NEXT:    10010:  pcaddu12i $ra, 0
+# NORELAX-NEXT:            jirl   $ra, $ra, 1040
+## offset = .plt(0x10400)+32 - 0x10018 = 0x408, hi=0, lo18=1032
+# NORELAX-NEXT:    10018:  pcaddu12i $t0, 0
+# NORELAX-NEXT:            jirl   $zero, $t0, 1032
+# NORELAX-EMPTY:
+# NORELAX-NEXT: <a>:
+# NORELAX-NEXT:      10020:  ret
+# NORELAX-EMPTY:
+
+# NORELAX-LABEL: <.mid>:
+## offset = 0x10000 - 0x10800 = -0x800, hi=0, lo18=-2048
+# NORELAX-NEXT:    10800:  pcaddu12i $ra, -1
+# NORELAX-NEXT:            jirl   $ra, $ra, 2048
+# NORELAX-NEXT:            pcaddu12i $t0, -1
+# NORELAX-NEXT:            jirl   $zero, $t0, 2040
+# NORELAX-EMPTY:
+
+# NORELAX-LABEL: <.mid2>:
+## offset = 0x10000 - 0x1010000 = -0x1000000, hi=-64, lo18=0
+# NORELAX-NEXT:  1010000:  pcaddu12i $ra, -4096
+# NORELAX-NEXT:            jirl   $ra, $ra, 0
+# NORELAX-NEXT:            pcaddu12i $t0, -4097
+# NORELAX-NEXT:            jirl   $zero, $t0, 4088
+# NORELAX-EMPTY:
+
+# NORELAX-LABEL: <.high>:
+## offset = 0x10000 - 0x10010000 = -0x10000000, hi=-1024, lo18=0
+# NORELAX-NEXT: 10010000:  pcaddu12i $ra, -65536
+# NORELAX-NEXT:            jirl   $ra, $ra, 0
+# NORELAX-NEXT:            pcaddu12i $t0, -65537
+# NORELAX-NEXT:            jirl   $zero, $t0, 4088
+# NORELAX-EMPTY:
+
+#--- a.s
+.global _start, _start_end
+_start:
+  call30 a          # relaxed. la32: bl
+  tail30 $t0, a     # relaxed. la32: b
+.balign 16
+  call30 bar        # PLT call30 can be relaxed. la32: bl
+  tail30 $t0, bar   # PLT tail30 can be relaxed. la32: bl
+
+a:
+  ret
+.size _start, . - _start
+_start_end:
+
+.section .mid,"ax", at progbits
+  call30 _start         # relaxed. la32: bl
+  tail30 $t0, _start    # relaxed. la32: b
+
+.section .mid2,"ax", at progbits
+  call30 _start         # relaxed. la32: bl
+  tail30 $t0, _start    # relaxed. la32: b
+
+.section .high,"ax", at progbits
+  call30 _start         # exceed range, not relaxed
+  tail30 $t0, _start    # exceed range, not relaxed
+
+#--- b.s
+.globl bar
+bar:
+  ret
+
+#--- lds
+SECTIONS {
+  .text 0x10000 : { *(.text) }
+  .plt 0x10400 : { *(.plt) }
+  .mid 0x10800 : { *(.mid); mid_end = .; }
+  .mid2 0x1010000 : { *(.mid2) }
+  .high 0x10010000 : { *(.high); high_end = .; }
+}
diff --git a/lld/test/ELF/loongarch-relax-emit-relocs.s b/lld/test/ELF/loongarch-relax-emit-relocs.s
index 6e1e85c004439..f2d6edc97d27a 100644
--- a/lld/test/ELF/loongarch-relax-emit-relocs.s
+++ b/lld/test/ELF/loongarch-relax-emit-relocs.s
@@ -1,7 +1,7 @@
 # REQUIRES: loongarch
 ## Test that we can handle --emit-relocs while relaxing.
 
-# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+32s,+relax %s -o %t.32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+relax %s -o %t.32.o
 # RUN: llvm-mc --filetype=obj --triple=loongarch64 --mattr=+relax --defsym ELF64=1 %s -o %t.64.o
 # RUN: ld.lld -Ttext=0x10000 -section-start=.got=0x20000 --emit-relocs %t.32.o -o %t.32
 # RUN: ld.lld -Ttext=0x10000 -section-start=.got=0x20000 --emit-relocs %t.64.o -o %t.64
@@ -16,17 +16,39 @@
 # RUN: ld.lld -Ttext=0x10000 -section-start=.got=0x20000 --emit-relocs --no-relax %t.64.o -o %t.64.norelax
 # RUN: llvm-objdump -dr %t.64.norelax | FileCheck %s --check-prefix=NORELAX
 
-# RELAX:      00010000 <_start>:
-# RELAX-NEXT:   pcaddi $a0, 0
-# RELAX-NEXT:     R_LARCH_RELAX _start
-# RELAX-NEXT:     R_LARCH_RELAX *ABS*
-# RELAX-NEXT:     R_LARCH_PCREL20_S2 _start
-# RELAX-NEXT:     R_LARCH_RELAX *ABS*
-# RELAX-NEXT:   pcaddi $a0, -1
-# RELAX-NEXT:     R_LARCH_RELAX _start
-# RELAX-NEXT:     R_LARCH_RELAX *ABS*
-# RELAX-NEXT:     R_LARCH_PCREL20_S2 _start
-# RELAX-NEXT:     R_LARCH_RELAX *ABS*
+# RELAX32:     00010000 <_start>:
+# RELAX32-NEXT:  pcaddu12i $a0, 0
+# RELAX32-NEXT:    R_LARCH_PCADD_HI20 _start
+# RELAX32-NEXT:    R_LARCH_RELAX *ABS*
+# RELAX32-NEXT:  addi.w $a0, $a0, 0
+# RELAX32-NEXT:    R_LARCH_PCADD_LO12 .Lpcadd_hi0
+# RELAX32-NEXT:    R_LARCH_RELAX *ABS*
+# RELAX32:     00010008 <.Lpcadd_hi1>:
+# RELAX32-NEXT:  pcaddu12i $a0, 16
+# RELAX32-NEXT:    R_LARCH_GOT_PCADD_HI20 _start
+# RELAX32-NEXT:    R_LARCH_RELAX *ABS*
+# RELAX32-NEXT:  ld.w $a0, $a0, 0
+# RELAX32-NEXT:    R_LARCH_GOT_PCADD_LO12 .Lpcadd_hi1
+# RELAX32-NEXT:    R_LARCH_RELAX *ABS*
+
+# RELAX64:     00010000 <_start>:
+# RELAX64-NEXT:  pcaddi $a0, 0
+# RELAX64-NEXT:    R_LARCH_RELAX _start
+# RELAX64-NEXT:    R_LARCH_RELAX *ABS*
+# RELAX64-NEXT:    R_LARCH_PCREL20_S2 _start
+# RELAX64-NEXT:    R_LARCH_RELAX *ABS*
+# RELAX64-NEXT:  pcaddi $a0, -1
+# RELAX64-NEXT:    R_LARCH_RELAX _start
+# RELAX64-NEXT:    R_LARCH_RELAX *ABS*
+# RELAX64-NEXT:    R_LARCH_PCREL20_S2 _start
+# RELAX64-NEXT:    R_LARCH_RELAX *ABS*
+
+# RELAX32-NEXT:  bl  -16
+# RELAX32-NEXT:    R_LARCH_B26 _start
+# RELAX32-NEXT:    R_LARCH_RELAX *ABS*
+# RELAX32-NEXT:  b   -20
+# RELAX32-NEXT:    R_LARCH_B26 _start
+# RELAX32-NEXT:    R_LARCH_RELAX *ABS*
 
 # RELAX64-NEXT:  bl  -8
 # RELAX64-NEXT:    R_LARCH_B26 _start
@@ -39,16 +61,34 @@
 # RELAX-NEXT:     R_LARCH_TLS_LE_HI20 a
 # RELAX-NEXT:   ori       $a0, $a0, 0
 # RELAX-NEXT:     R_LARCH_TLS_LE_LO12 a
-# RELAX-NEXT:   pcaddi    $a0, [[#]]
-# RELAX-NEXT:     R_LARCH_RELAX a
-# RELAX-NEXT:     R_LARCH_RELAX *ABS*
-# RELAX-NEXT:     R_LARCH_TLS_GD_PCREL20_S2 a
-# RELAX-NEXT:     R_LARCH_RELAX *ABS*
-# RELAX-NEXT:   pcaddi    $a0, [[#]]
-# RELAX-NEXT:     R_LARCH_RELAX a
-# RELAX-NEXT:     R_LARCH_RELAX *ABS*
-# RELAX-NEXT:     R_LARCH_TLS_LD_PCREL20_S2 a
-# RELAX-NEXT:     R_LARCH_RELAX *ABS*
+
+# RELAX32:     00010020 <.Lpcadd_hi2>:
+# RELAX32-NEXT:   pcaddu12i    $a0, 16
+# RELAX32-NEXT:     R_LARCH_TLS_GD_PCADD_HI20 a
+# RELAX32-NEXT:     R_LARCH_RELAX *ABS*
+# RELAX32-NEXT:   addi.w $a0, $a0, -20
+# RELAX32-NEXT:     R_LARCH_TLS_GD_PCADD_LO12 .Lpcadd_hi2
+# RELAX32-NEXT:     R_LARCH_RELAX *ABS*
+
+# RELAX32:     00010028 <.Lpcadd_hi3>:
+# RELAX32-NEXT:   pcaddu12i    $a0, 16
+# RELAX32-NEXT:     R_LARCH_TLS_LD_PCADD_HI20 a
+# RELAX32-NEXT:     R_LARCH_RELAX *ABS*
+# RELAX32-NEXT:   addi.w $a0, $a0, -40
+# RELAX32-NEXT:     R_LARCH_TLS_LD_PCADD_LO12 .Lpcadd_hi3
+# RELAX32-NEXT:     R_LARCH_RELAX *ABS*
+
+# RELAX64-NEXT:   pcaddi    $a0, [[#]]
+# RELAX64-NEXT:     R_LARCH_RELAX a
+# RELAX64-NEXT:     R_LARCH_RELAX *ABS*
+# RELAX64-NEXT:     R_LARCH_TLS_GD_PCREL20_S2 a
+# RELAX64-NEXT:     R_LARCH_RELAX *ABS*
+# RELAX64-NEXT:   pcaddi    $a0, [[#]]
+# RELAX64-NEXT:     R_LARCH_RELAX a
+# RELAX64-NEXT:     R_LARCH_RELAX *ABS*
+# RELAX64-NEXT:     R_LARCH_TLS_LD_PCREL20_S2 a
+# RELAX64-NEXT:     R_LARCH_RELAX *ABS*
+
 # RELAX-NEXT:   addi.{{[dw]}} $a0, $tp, 0
 # RELAX-NEXT:     R_LARCH_RELAX a
 # RELAX-NEXT:     R_LARCH_RELAX *ABS*
@@ -57,15 +97,11 @@
 # RELAX-NEXT:     R_LARCH_TLS_LE_LO12_R a
 # RELAX-NEXT:     R_LARCH_RELAX *ABS*
 
-# RELAX32-NEXT:  nop
-# RELAX32-NEXT:    R_LARCH_ALIGN *ABS*+0xc
-# RELAX32-NEXT:  ret
-
-# RELAX64-NEXT:  nop
-# RELAX64-NEXT:    R_LARCH_ALIGN *ABS*+0xc
-# RELAX64-NEXT:  nop
-# RELAX64-NEXT:  nop
-# RELAX64-NEXT:  ret
+# RELAX-NEXT:  nop
+# RELAX-NEXT:    R_LARCH_ALIGN *ABS*+0xc
+# RELAX-NEXT:  nop
+# RELAX-NEXT:  nop
+# RELAX-NEXT:  ret
 
 # NORELAX:      <_start>:
 # NORELAX-NEXT:   pcalau12i $a0, 0
@@ -195,6 +231,9 @@ _start:
 .ifdef ELF64
   call36 _start
   tail36 $a0, _start
+.else
+  call30 _start
+  tail30 $a0, _start
 .endif
 
   la.tls.le $a0, a  # without R_LARCH_RELAX reloaction
diff --git a/lld/test/ELF/loongarch-tls-gd-edge-case.s b/lld/test/ELF/loongarch-tls-gd-edge-case.s
index dd87d3002b180..1ebc69990b403 100644
--- a/lld/test/ELF/loongarch-tls-gd-edge-case.s
+++ b/lld/test/ELF/loongarch-tls-gd-edge-case.s
@@ -3,7 +3,7 @@
 ## Edge case: when a TLS symbol is being accessed in both GD and IE manners,
 ## correct reloc behavior should be preserved for both kinds of accesses.
 
-# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+32s %s -o %t.la32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=-32s %s -o %t.la32.o
 # RUN: ld.lld %t.la32.o -shared -o %t.la32
 # RUN: llvm-mc --filetype=obj --triple=loongarch64 %s -o %t.la64.o
 # RUN: ld.lld %t.la64.o -shared -o %t.la64
@@ -24,10 +24,10 @@
 # LA64-REL-NEXT: 00000000000203a8  0000000200000009 R_LARCH_TLS_DTPREL64   0000000000000000 y + 0
 # LA64-REL-NEXT: 00000000000203b0  000000020000000b R_LARCH_TLS_TPREL64    0000000000000000 y + 0
 
-# LA32:      101d4: pcalau12i $a0, 16
-# LA32-NEXT:        ld.w $a0, $a0, 580
-# LA32-NEXT:        pcalau12i $a1, 16
-# LA32-NEXT:        addi.w $a1, $a1, 572
+# LA32:      101d4: pcaddu12i $a0, 16
+# LA32-NEXT:        ld.w $a0, $a0, 112
+# LA32:      101dc: pcaddu12i $a1, 16
+# LA32-NEXT:        addi.w $a1, $a1, 96
 
 # LA64:      102e0: pcalau12i $a0, 16
 # LA64-NEXT:        ld.d $a0, $a0, 944
diff --git a/lld/test/ELF/loongarch-tls-gd.s b/lld/test/ELF/loongarch-tls-gd.s
index 9a4ccfa2201a4..c52690e9a1297 100644
--- a/lld/test/ELF/loongarch-tls-gd.s
+++ b/lld/test/ELF/loongarch-tls-gd.s
@@ -5,7 +5,7 @@
 ## (a) code sequence can be converted from `pcalau12i+addi.[wd]` to `pcaddi`.
 ## (b) dynamic relocations can be omitted for GD->LE relaxation.
 
-# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+32s %t/a.s -o %t/a.32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=-32s %t/a.s -o %t/a.32.o
 # RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+32s,+relax %t/a.s -o %t/a.32.relax.o
 # RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+32s %t/bc.s -o %t/bc.32.o
 # RUN: ld.lld -shared -soname=bc.so %t/bc.32.o -o %t/bc.32.so
@@ -66,13 +66,13 @@
 # GD32-REL-NEXT: }
 
 ## &DTPMOD(a) - . = 0x20310 - 0x10250: 0x10 pages, page offset 0x310
-# GD32:      10250: pcalau12i $a0, 16
-# GD32-NEXT:        addi.w $a0, $a0, 784
+# GD32:      10250: pcaddu12i $a0, 16
+# GD32-NEXT:        addi.w $a0, $a0, 192
 # GD32-NEXT:        bl 56
 
 ## &DTPMOD(b) - . = 0x20318 - 0x1025c: 0x10 pages, page offset 0x318
-# GD32:      1025c: pcalau12i $a0, 16
-# GD32-NEXT:        addi.w $a0, $a0, 792
+# GD32:      1025c: pcaddu12i $a0, 16
+# GD32-NEXT:        addi.w $a0, $a0, 188
 # GD32-NEXT:        bl 44
 
 # GD32-REL-RELAX:      .rela.dyn {
diff --git a/lld/test/ELF/loongarch-tls-ie.s b/lld/test/ELF/loongarch-tls-ie.s
index e7e9617dd3efc..3b3fd944712fe 100644
--- a/lld/test/ELF/loongarch-tls-ie.s
+++ b/lld/test/ELF/loongarch-tls-ie.s
@@ -1,7 +1,7 @@
 # REQUIRES: loongarch
 # RUN: rm -rf %t && split-file %s %t
 
-# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=+32s %t/32.s -o %t/32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 --mattr=-32s %t/32.s -o %t/32.o
 # RUN: llvm-mc --filetype=obj --triple=loongarch64 %t/64.s -o %t/64.o
 
 ## LA32 IE
@@ -41,11 +41,11 @@
 ## LA32:
 ## &.got[0] - . = 0x20214 - 0x101a4: 0x10 pages, page offset 0x214
 ## &.got[1] - . = 0x20218 - 0x101b0: 0x10 pages, page offset 0x218
-# IE32:      101a4: pcalau12i $a4, 16
-# IE32-NEXT:        ld.w $a4, $a4, 532
+# IE32:      101a4: pcaddu12i $a4, 16
+# IE32-NEXT:        ld.w $a4, $a4, 112
 # IE32-NEXT:        add.w $a4, $a4, $tp
-# IE32-NEXT: 101b0: pcalau12i $a5, 16
-# IE32-NEXT:        ld.w $a5, $a5, 536
+# IE32:      101b0: pcaddu12i $a5, 16
+# IE32-NEXT:        ld.w $a5, $a5, 104
 # IE32-NEXT:        add.w $a5, $a5, $tp
 
 ## LA64:
@@ -62,15 +62,16 @@
 
 # a at tprel = st_value(a) = 0x8
 # b at tprel = st_value(a) = 0xc
-# LE32-GOT: could not find section '.got'
+# LE32-GOT: section '.got':
+# LE32-GOT-NEXT: 0x0003012c 08000000 0c000000
 # LE64-GOT: could not find section '.got'
 
 ## LA32:
-# LE32:      200d4: nop
-# LE32-NEXT:        ori $a4, $zero, 8
+# LE32:      20114: pcaddu12i $a4, 16
+# LE32-NEXT:        ld.w $a4, $a4, 24
 # LE32-NEXT:        add.w $a4, $a4, $tp
-# LE32-NEXT: 200e0: nop
-# LE32-NEXT:        ori $a5, $zero, 12
+# LE32:      20120: pcaddu12i $a5, 16
+# LE32-NEXT:        ld.w $a5, $a5, 16
 # LE32-NEXT:        add.w $a5, $a5, $tp
 
 ## LA64:
diff --git a/lld/test/ELF/loongarch-tls-ld.s b/lld/test/ELF/loongarch-tls-ld.s
index 65be2f8fd36e4..179bd50aa47be 100644
--- a/lld/test/ELF/loongarch-tls-ld.s
+++ b/lld/test/ELF/loongarch-tls-ld.s
@@ -5,7 +5,7 @@
 ## (a) code sequence can be converted from `pcalau12i+addi.[wd]` to `pcaddi`.
 ## (b) dynamic relocations can be omitted for LD->LE relaxation.
 
-# RUN: llvm-mc --filetype=obj --triple=loongarch32 --position-independent -mattr=+32s %t/a.s -o %t/a.32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch32 --position-independent -mattr=-32s %t/a.s -o %t/a.32.o
 # RUN: llvm-mc --filetype=obj --triple=loongarch32 --position-independent -mattr=+32s,+relax %t/a.s -o %t/a.32.relax.o
 # RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+32s %t/tga.s -o %t/tga.32.o
 # RUN: llvm-mc --filetype=obj --triple=loongarch64 --position-independent %t/a.s -o %t/a.64.o
@@ -60,8 +60,8 @@
 # LD64-GOT-NEXT: 0x00020400 00000000 00000000 00000000 00000000
 
 ## LA32: &DTPMOD(a) - . = 0x20280 - 0x101cc: 0x10 pages, page offset 0x280
-# LD32:      101cc: pcalau12i $a0, 16
-# LD32-NEXT:        addi.w $a0, $a0, 640
+# LD32:      101cc: pcaddu12i $a0, 16
+# LD32-NEXT:        addi.w $a0, $a0, 180
 # LD32-NEXT:        bl 44
 
 ## LA64: &DTPMOD(a) - . = 0x20400 - 0x102e0: 0x10 pages, page offset 0x400
@@ -94,8 +94,8 @@
 # LE64-GOT-RELAX-NEXT: 0x000301d0 01000000 00000000 00000000 00000000
 
 ## LA32: DTPMOD(.LANCHOR0) - . = 0x30120 - 0x20114: 0x10 pages, page offset 0x120
-# LE32:      20114: pcalau12i $a0, 16
-# LE32-NEXT:        addi.w $a0, $a0, 288
+# LE32:      20114: pcaddu12i $a0, 16
+# LE32-NEXT:        addi.w $a0, $a0, 12
 # LE32-NEXT:        bl 4
 
 ## LA64: DTPMOD(.LANCHOR0) - . = 0x301d8 - 0x201c8: 0x10 pages, page offset 0x1d8
diff --git a/lld/test/ELF/loongarch-tlsdesc.s b/lld/test/ELF/loongarch-tlsdesc.s
index 7d07c66606a87..835e98907516c 100644
--- a/lld/test/ELF/loongarch-tlsdesc.s
+++ b/lld/test/ELF/loongarch-tlsdesc.s
@@ -3,8 +3,8 @@
 # RUN: llvm-mc -filetype=obj -triple=loongarch64 a.s -o a.64.o
 # RUN: llvm-mc -filetype=obj -triple=loongarch64 c.s -o c.64.o
 # RUN: ld.lld -shared -soname=c.64.so c.64.o -o c.64.so
-# RUN: llvm-mc -filetype=obj -triple=loongarch32 --mattr=+32s --defsym ELF32=1 a.s -o a.32.o
-# RUN: llvm-mc -filetype=obj -triple=loongarch32 --mattr=+32s --defsym ELF32=1 c.s -o c.32.o
+# RUN: llvm-mc -filetype=obj -triple=loongarch32 --defsym ELF32=1 a.s -o a.32.o
+# RUN: llvm-mc -filetype=obj -triple=loongarch32 --defsym ELF32=1 c.s -o c.32.o
 # RUN: ld.lld -shared -soname=c.32.so c.32.o -o c.32.so
 
 # RUN: ld.lld -shared -z now a.64.o c.64.o -o a.64.so
@@ -27,6 +27,7 @@
 
 # RUN: ld.lld -shared -z now a.32.o c.32.o -o rel.32.so -z rel
 # RUN: llvm-readobj -r -x .got rel.32.so | FileCheck --check-prefix=GD32-REL %s
+# RUN: llvm-objdump --no-show-raw-insn -h -d rel.32.so | FileCheck %s --check-prefix=GD32
 
 # GD64-RELA:      .rela.dyn {
 # GD64-RELA-NEXT:   0x20400 R_LARCH_TLS_DESC64 - 0x7FF
@@ -64,7 +65,7 @@
 # GD64-NEXT:          jirl $ra, $ra, 0
 # GD64-NEXT:          add.d $a2, $a0, $tp
 
-## &.got[c]-. = 0x23e0+16 - 0x10308: 0x10 pages, page offset 0x3f0
+## &.got[c]-. = 0x203e0+16 - 0x10308: 0x10 pages, page offset 0x3f0
 # GD64:        10308: pcalau12i $a0, 16
 # GD64-NEXT:          addi.d $a0, $a0, 1008
 # GD64-NEXT:          ld.d $ra, $a0, 0
@@ -127,6 +128,29 @@
 # GD32-REL-NEXT: 0x00020260 00000000 00000000 00000000 00000000 .
 # GD32-REL-NEXT: 0x00020270 00000000 ff070000                   .
 
+# GD32:      .got     00000018 00020260
+
+## &.got[a]-. = 0x20260 - 0x101c4
+# GD32:        101c4: pcaddu12i $a0, 32
+# GD32-NEXT:          addi.w $a0, $a0, 608
+# GD32-NEXT:          ld.w $ra, $a0, 0
+# GD32-NEXT:          jirl $ra, $ra, 0
+# GD32-NEXT:          add.w $a1, $a0, $tp
+
+## &.got[b]-. = 0x20260+16 - 0x101d8
+# GD32:        101d8: pcaddu12i $a0, 32
+# GD32-NEXT:          addi.w $a0, $a0, 624
+# GD32-NEXT:          ld.w $ra, $a0, 0
+# GD32-NEXT:          jirl $ra, $ra, 0
+# GD32-NEXT:          add.w $a2, $a0, $tp
+
+## &.got[c]-. = 0x20260+8 - 0x101ec
+# GD32:        101ec: pcaddu12i $a0, 32
+# GD32-NEXT:          addi.w $a0, $a0, 616
+# GD32-NEXT:          ld.w $ra, $a0, 0
+# GD32-NEXT:          jirl $ra, $ra, 0
+# GD32-NEXT:          add.w $a3, $a0, $tp
+
 #--- a.s
 .macro add dst, src1, src2
 .ifdef ELF32

>From 51e92b2e03d57a254c82cce4ad97f471f7ebacbf Mon Sep 17 00:00:00 2001
From: WANG Rui <wangrui at loongson.cn>
Date: Thu, 15 Jan 2026 17:17:05 +0800
Subject: [PATCH 07/10] [JITLink][LoongArch][NFC] Fix Call30PCRel range docs

(cherry picked from commit 16f690e0ff97b8e0dece0e0237552208cafe1c7f)
---
 llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
index 50a0ded54c3e3..bafd83b8b21dc 100644
--- a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
@@ -252,7 +252,7 @@ enum EdgeKind_loongarch : Edge::Kind {
 
   /// A 30-bit PC-relative call.
   ///
-  /// Represents a PC-relative call to a target within [-4G, +4G)
+  /// Represents a PC-relative call to a target within [-2G, +2G)
   /// The target must be 4-byte aligned. For adjacent pcaddu12i+jirl
   /// instruction pairs.
   ///

>From 82a29d757450a8d3c7a06f79a92a334694dd6202 Mon Sep 17 00:00:00 2001
From: hev <wangrui at loongson.cn>
Date: Fri, 16 Jan 2026 17:35:05 +0800
Subject: [PATCH 08/10] [lld][LoongArch] Clean up CALL30 relocation with setK16
 and checkInt (#176311)

(cherry picked from commit fe8447d00796bc4795b28b8036b0f49e5f118f1d)
---
 lld/ELF/Arch/LoongArch.cpp | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/lld/ELF/Arch/LoongArch.cpp b/lld/ELF/Arch/LoongArch.cpp
index 264990be48ec3..9682b4722ac1e 100644
--- a/lld/ELF/Arch/LoongArch.cpp
+++ b/lld/ELF/Arch/LoongArch.cpp
@@ -184,10 +184,6 @@ static uint32_t setJ5(uint32_t insn, uint32_t imm) {
   return (insn & 0xfffffc1f) | (extractBits(imm, 4, 0) << 5);
 }
 
-static uint32_t setK10(uint32_t insn, uint32_t imm) {
-  return (insn & 0xffc003ff) | (extractBits(imm, 9, 0) << 10);
-}
-
 static uint32_t setK12(uint32_t insn, uint32_t imm) {
   return (insn & 0xffc003ff) | (extractBits(imm, 11, 0) << 10);
 }
@@ -651,17 +647,15 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
     // This relocation is designed for adjacent pcaddu12i+jirl pairs that
     // are patched in one time.
     // The relocation range is [-2G, +2G) (of course must be 4-byte aligned).
-    if ((int64_t)val != llvm::SignExtend64(val, 32))
-      reportRangeError(ctx, loc, rel, Twine(val), llvm::minIntN(32),
-                       llvm::maxIntN(32));
+    checkInt(ctx, loc, val, 32, rel);
     checkAlignment(ctx, loc, val, 4, rel);
     // Although jirl adds the immediate as a signed value, it is always positive
-    // in this case, so no adjustment is needed, unlink CALL36.
+    // in this case, so no adjustment is needed, unlike CALL36.
     uint32_t hi20 = extractBits(val, 31, 12);
     // Despite the name, the lower part is actually 12 bits with 4-byte aligned.
     uint32_t lo10 = extractBits(val, 11, 2);
     write32le(loc, setJ20(read32le(loc), hi20));
-    write32le(loc + 4, setK10(read32le(loc + 4), lo10));
+    write32le(loc + 4, setK16(read32le(loc + 4), lo10));
     return;
   }
 

>From 43299d52234bd98ca649329c49d513351c586b44 Mon Sep 17 00:00:00 2001
From: hev <wangrui at loongson.cn>
Date: Fri, 16 Jan 2026 17:35:27 +0800
Subject: [PATCH 09/10] [lld][ELF] Deduplicate PC-relative indirect relocation
 logic for RISC-V and LoongArch (#176312)

(cherry picked from commit 3af2e51eb91a96232efc4497059d5c6360cc094b)
---
 lld/ELF/InputSection.cpp | 125 ++++++++++++++-------------------------
 1 file changed, 45 insertions(+), 80 deletions(-)

diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index a9afd3449ebb2..7402c8454c023 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -661,68 +661,41 @@ static uint64_t getARMStaticBase(const Symbol &sym) {
   return os->ptLoad->firstSec->addr;
 }
 
-// For RE_RISCV_PC_INDIRECT (R_RISCV_PCREL_LO12_{I,S}), the symbol actually
-// points the corresponding R_RISCV_PCREL_HI20 relocation, and the target VA
-// is calculated using PCREL_HI20's symbol.
-//
-// This function returns the R_RISCV_PCREL_HI20 relocation from the
-// R_RISCV_PCREL_LO12 relocation.
-static Relocation *getRISCVPCRelHi20(Ctx &ctx, const InputSectionBase *loSec,
-                                     const Relocation &loReloc) {
-  uint64_t addend = loReloc.addend;
-  Symbol *sym = loReloc.sym;
-
-  const Defined *d = cast<Defined>(sym);
-  if (!d->section) {
-    Err(ctx) << loSec->getLocation(loReloc.offset)
-             << ": R_RISCV_PCREL_LO12 relocation points to an absolute symbol: "
-             << sym->getName();
-    return nullptr;
+struct RISCVPCRel {
+  static constexpr const char *loReloc = "R_RISCV_PCREL_LO12";
+  static constexpr const char *hiReloc = "R_RISCV_PCREL_HI20";
+
+  static bool isHiReloc(uint32_t type) {
+    return is_contained({R_RISCV_PCREL_HI20, R_RISCV_GOT_HI20,
+                         R_RISCV_TLS_GD_HI20, R_RISCV_TLS_GOT_HI20},
+                        type);
   }
-  InputSection *hiSec = cast<InputSection>(d->section);
-
-  if (hiSec != loSec)
-    Err(ctx) << loSec->getLocation(loReloc.offset)
-             << ": R_RISCV_PCREL_LO12 relocation points to a symbol '"
-             << sym->getName() << "' in a different section '" << hiSec->name
-             << "'";
-
-  if (addend != 0)
-    Warn(ctx) << loSec->getLocation(loReloc.offset)
-              << ": non-zero addend in R_RISCV_PCREL_LO12 relocation to "
-              << hiSec->getObjMsg(d->value) << " is ignored";
-
-  // Relocations are sorted by offset, so we can use std::equal_range to do
-  // binary search.
-  Relocation hiReloc;
-  hiReloc.offset = d->value;
-  auto range =
-      std::equal_range(hiSec->relocs().begin(), hiSec->relocs().end(), hiReloc,
-                       [](const Relocation &lhs, const Relocation &rhs) {
-                         return lhs.offset < rhs.offset;
-                       });
-
-  for (auto it = range.first; it != range.second; ++it)
-    if (it->type == R_RISCV_PCREL_HI20 || it->type == R_RISCV_GOT_HI20 ||
-        it->type == R_RISCV_TLS_GD_HI20 || it->type == R_RISCV_TLS_GOT_HI20)
-      return &*it;
-
-  Err(ctx) << loSec->getLocation(loReloc.offset)
-           << ": R_RISCV_PCREL_LO12 relocation points to "
-           << hiSec->getObjMsg(d->value)
-           << " without an associated R_RISCV_PCREL_HI20 relocation";
-  return nullptr;
-}
+};
+
+struct LoongArchPCAdd {
+  static constexpr const char *loReloc = "R_LARCH_*PCADD_LO12";
+  static constexpr const char *hiReloc = "R_LARCH_*PCADD_HI20";
+
+  static bool isHiReloc(uint32_t type) {
+    return is_contained({R_LARCH_PCADD_HI20, R_LARCH_GOT_PCADD_HI20,
+                         R_LARCH_TLS_IE_PCADD_HI20, R_LARCH_TLS_LD_PCADD_HI20,
+                         R_LARCH_TLS_GD_PCADD_HI20,
+                         R_LARCH_TLS_DESC_PCADD_HI20},
+                        type);
+  }
+};
 
-// For RE_LARCH_PC_INDIRECT (R_LARCH_*PCADD_LO12), the symbol actually points
-// the corresponding R_LARCH_*PCADD_HI20 relocation, and the target VA is
-// calculated using PCADD_HI20's symbol.
+// For PC-relative indirect relocations (e.g. R_RISCV_PCREL_LO12_* and
+// R_LARCH_*PCADD_LO12), the symbol referenced by the LO12 relocation does not
+// directly represent the final target address. Instead, it points to the
+// corresponding HI20 relocation, and the target VA is computed using the
+// symbol associated with that HI20 relocation.
 //
-// This function returns the R_LARCH_*PCADD_HI20 relocation from the
-// R_LARCH_*PCADD_LO12 relocation.
-static Relocation *getLoongArchPCAddHi20(Ctx &ctx,
-                                         const InputSectionBase *loSec,
-                                         const Relocation &loReloc) {
+// This helper locates and returns the matching HI20 relocation corresponding
+// to a given LO12 relocation.
+template <typename PCRel>
+static Relocation *getPCRelHi20(Ctx &ctx, const InputSectionBase *loSec,
+                                const Relocation &loReloc) {
   int64_t addend = loReloc.addend;
   Symbol *sym = loReloc.sym;
 
@@ -733,25 +706,22 @@ static Relocation *getLoongArchPCAddHi20(Ctx &ctx,
     return nullptr;
   }
   if (!d->section) {
-    Err(ctx)
-        << loSec->getLocation(loReloc.offset)
-        << ": R_LARCH_*PCADD_LO12 relocation points to an absolute symbol: "
-        << sym->getName();
+    Err(ctx) << loSec->getLocation(loReloc.offset) << ": " << PCRel::loReloc
+             << " relocation points to an absolute symbol: " << sym->getName();
     return nullptr;
   }
   InputSection *hiSec = cast<InputSection>(d->section);
 
   if (hiSec != loSec) {
-    Err(ctx) << loSec->getLocation(loReloc.offset)
-             << ": R_LARCH_*PCADD_LO12 relocation points to a symbol '"
-             << sym->getName() << "' in a different section '" << hiSec->name
-             << "'";
+    Err(ctx) << loSec->getLocation(loReloc.offset) << ": " << PCRel::loReloc
+             << " relocation points to a symbol '" << sym->getName()
+             << "' in a different section '" << hiSec->name << "'";
     return nullptr;
   }
 
   if (addend != 0)
-    Warn(ctx) << loSec->getLocation(loReloc.offset)
-              << ": non-zero addend in R_LARCH_*PCADD_LO12 relocation to "
+    Warn(ctx) << loSec->getLocation(loReloc.offset) << ": non-zero addend in "
+              << PCRel::loReloc << " relocation to "
               << hiSec->getObjMsg(d->value) << " is ignored";
 
   // Relocations are sorted by offset, so we can use std::equal_range to do
@@ -765,17 +735,12 @@ static Relocation *getLoongArchPCAddHi20(Ctx &ctx,
                        });
 
   for (auto it = range.first; it != range.second; ++it)
-    if (it->type == R_LARCH_PCADD_HI20 || it->type == R_LARCH_GOT_PCADD_HI20 ||
-        it->type == R_LARCH_TLS_IE_PCADD_HI20 ||
-        it->type == R_LARCH_TLS_LD_PCADD_HI20 ||
-        it->type == R_LARCH_TLS_GD_PCADD_HI20 ||
-        it->type == R_LARCH_TLS_DESC_PCADD_HI20)
+    if (PCRel::isHiReloc(it->type))
       return &*it;
 
-  Err(ctx) << loSec->getLocation(loReloc.offset)
-           << ": R_LARCH_*PCADD_LO12 relocation points to "
-           << hiSec->getObjMsg(d->value)
-           << " without an associated R_LARCH_*PCADD_HI20 relocation";
+  Err(ctx) << loSec->getLocation(loReloc.offset) << ": " << PCRel::loReloc
+           << " relocation points to " << hiSec->getObjMsg(d->value)
+           << " without an associated " << PCRel::hiReloc << " relocation";
   return nullptr;
 }
 
@@ -954,12 +919,12 @@ uint64_t InputSectionBase::getRelocTargetVA(Ctx &ctx, const Relocation &r,
     return getAArch64Page(val) - getAArch64Page(p);
   }
   case RE_RISCV_PC_INDIRECT: {
-    if (const Relocation *hiRel = getRISCVPCRelHi20(ctx, this, r))
+    if (const Relocation *hiRel = getPCRelHi20<RISCVPCRel>(ctx, this, r))
       return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx));
     return 0;
   }
   case RE_LOONGARCH_PC_INDIRECT: {
-    if (const Relocation *hiRel = getLoongArchPCAddHi20(ctx, this, r))
+    if (const Relocation *hiRel = getPCRelHi20<LoongArchPCAdd>(ctx, this, r))
       return getRelocTargetVA(ctx, *hiRel, r.sym->getVA(ctx, a));
     return 0;
   }

>From 21d0215fd8b13911ce0daaedc160a54b3f096698 Mon Sep 17 00:00:00 2001
From: hev <wangrui at loongson.cn>
Date: Sat, 17 Jan 2026 16:27:54 +0800
Subject: [PATCH 10/10] [clang][LoongArch] Add support for LoongArch32
 (#172619)

This patch adds support for LoongArch32, as introduced in
la-toolchain-conventions v1.2.

Co-authored-by: Sun Haiyong <sunhaiyong at zdbr.net>
Link:
https://github.com/loongson/la-toolchain-conventions/releases/tag/releases%2Fv1.2
Link:
https://gcc.gnu.org/pipermail/gcc-patches/2025-December/703312.html
(cherry picked from commit 0a9d480fadf07aaaee8ccfa26a2a2afa3f621499)
---
 clang/lib/Basic/Targets/LoongArch.cpp         | 18 +++++++++++--
 clang/lib/Basic/Targets/LoongArch.h           |  2 ++
 .../lib/Driver/ToolChains/Arch/LoongArch.cpp  |  3 ++-
 clang/lib/Driver/ToolChains/Gnu.cpp           | 18 +++++++++++--
 clang/lib/Driver/ToolChains/Linux.cpp         | 13 ++++++++++
 .../test/CodeGen/LoongArch/targetattr-la32.c  | 26 +++++++++++++++++++
 .../{targetattr.c => targetattr-la64.c}       |  0
 clang/test/Driver/loongarch-march.c           | 23 ++++++++++++++++
 clang/test/Driver/loongarch-mtune.c           |  5 ++++
 clang/test/Preprocessor/init-loongarch.c      |  6 +++++
 .../TargetParser/LoongArchTargetParser.def    |  3 +++
 .../llvm/TargetParser/LoongArchTargetParser.h |  6 +++++
 llvm/lib/Target/LoongArch/LoongArch.td        |  3 +++
 .../MCTargetDesc/LoongArchBaseInfo.cpp        |  8 ++----
 .../TargetParser/LoongArchTargetParser.cpp    | 10 +++++--
 llvm/test/CodeGen/LoongArch/target-abi.ll     |  8 +++---
 16 files changed, 135 insertions(+), 17 deletions(-)
 create mode 100644 clang/test/CodeGen/LoongArch/targetattr-la32.c
 rename clang/test/CodeGen/LoongArch/{targetattr.c => targetattr-la64.c} (100%)

diff --git a/clang/lib/Basic/Targets/LoongArch.cpp b/clang/lib/Basic/Targets/LoongArch.cpp
index 5863af3f3b920..eabb1498a4935 100644
--- a/clang/lib/Basic/Targets/LoongArch.cpp
+++ b/clang/lib/Basic/Targets/LoongArch.cpp
@@ -228,6 +228,13 @@ void LoongArchTargetInfo::getTargetDefines(const LangOptions &Opts,
       Builder.defineMacro("__loongarch_arch",
                           Twine('"') + ArchName + Twine('"'));
     }
+  } else if (ArchName == "loongarch32") {
+    if (HasFeature32S)
+      Builder.defineMacro("__loongarch_arch",
+                          Twine('"') + "la32v1.0" + Twine('"'));
+    else
+      Builder.defineMacro("__loongarch_arch",
+                          Twine('"') + "la32rv1.0" + Twine('"'));
   } else {
     Builder.defineMacro("__loongarch_arch", Twine('"') + ArchName + Twine('"'));
   }
@@ -268,6 +275,8 @@ void LoongArchTargetInfo::getTargetDefines(const LangOptions &Opts,
   StringRef ABI = getABI();
   if (ABI == "lp64d" || ABI == "lp64f" || ABI == "lp64s")
     Builder.defineMacro("__loongarch_lp64");
+  else if (ABI == "ilp32d" || ABI == "ilp32f" || ABI == "ilp32s")
+    Builder.defineMacro("__loongarch_ilp32");
 
   if (ABI == "lp64d" || ABI == "ilp32d") {
     Builder.defineMacro("__loongarch_hard_float");
@@ -356,6 +365,7 @@ bool LoongArchTargetInfo::hasFeature(StringRef Feature) const {
       .Case("loongarch64", Is64Bit)
       .Case("32bit", !Is64Bit)
       .Case("64bit", Is64Bit)
+      .Case("32s", HasFeature32S)
       .Case("lsx", HasFeatureLSX)
       .Case("lasx", HasFeatureLASX)
       .Default(false);
@@ -373,7 +383,9 @@ LoongArchTargetInfo::getTargetBuiltins() const {
 bool LoongArchTargetInfo::handleTargetFeatures(
     std::vector<std::string> &Features, DiagnosticsEngine &Diags) {
   for (const auto &Feature : Features) {
-    if (Feature == "+d" || Feature == "+f") {
+    if (Feature == "+32s") {
+      HasFeature32S = true;
+    } else if (Feature == "+d" || Feature == "+f") {
       // "d" implies "f".
       HasFeatureF = true;
       if (Feature == "+d") {
@@ -430,7 +442,7 @@ LoongArchTargetInfo::parseTargetAttr(StringRef Features) const {
     switch (Kind) {
     case AttrFeatureKind::Arch: {
       if (llvm::LoongArch::isValidArchName(Value) || Value == "la64v1.0" ||
-          Value == "la64v1.1") {
+          Value == "la64v1.1" || Value == "la32v1.0" || Value == "la32rv1.0") {
         std::vector<llvm::StringRef> ArchFeatures;
         if (llvm::LoongArch::getArchFeatures(Value, ArchFeatures)) {
           Ret.Features.insert(Ret.Features.end(), ArchFeatures.begin(),
@@ -441,6 +453,8 @@ LoongArchTargetInfo::parseTargetAttr(StringRef Features) const {
           Ret.Duplicate = "arch=";
         else if (Value == "la64v1.0" || Value == "la64v1.1")
           Ret.CPU = "loongarch64";
+        else if (Value == "la32v1.0" || Value == "la32rv1.0")
+          Ret.CPU = "loongarch32";
         else
           Ret.CPU = Value;
       } else {
diff --git a/clang/lib/Basic/Targets/LoongArch.h b/clang/lib/Basic/Targets/LoongArch.h
index 31afd3eed96f9..bb1c2edacf103 100644
--- a/clang/lib/Basic/Targets/LoongArch.h
+++ b/clang/lib/Basic/Targets/LoongArch.h
@@ -25,6 +25,7 @@ class LLVM_LIBRARY_VISIBILITY LoongArchTargetInfo : public TargetInfo {
 protected:
   std::string ABI;
   std::string CPU;
+  bool HasFeature32S;
   bool HasFeatureD;
   bool HasFeatureF;
   bool HasFeatureLSX;
@@ -39,6 +40,7 @@ class LLVM_LIBRARY_VISIBILITY LoongArchTargetInfo : public TargetInfo {
 public:
   LoongArchTargetInfo(const llvm::Triple &Triple, const TargetOptions &)
       : TargetInfo(Triple) {
+    HasFeature32S = false;
     HasFeatureD = false;
     HasFeatureF = false;
     HasFeatureLSX = false;
diff --git a/clang/lib/Driver/ToolChains/Arch/LoongArch.cpp b/clang/lib/Driver/ToolChains/Arch/LoongArch.cpp
index 33c61c1e4962e..3c129c84f6929 100644
--- a/clang/lib/Driver/ToolChains/Arch/LoongArch.cpp
+++ b/clang/lib/Driver/ToolChains/Arch/LoongArch.cpp
@@ -299,7 +299,8 @@ std::string loongarch::getLoongArchTargetCPU(const llvm::opt::ArgList &Args,
   // If we have -march, use that.
   if (const Arg *A = Args.getLastArg(options::OPT_march_EQ)) {
     Arch = A->getValue();
-    if (Arch == "la64v1.0" || Arch == "la64v1.1")
+    if (Arch == "la64v1.0" || Arch == "la64v1.1" || Arch == "la32v1.0" ||
+        Arch == "la32rv1.0")
       CPU = llvm::LoongArch::getDefaultArch(Triple.isLoongArch64());
     else
       CPU = Arch;
diff --git a/clang/lib/Driver/ToolChains/Gnu.cpp b/clang/lib/Driver/ToolChains/Gnu.cpp
index 7ecdbe7c57650..cb6a9b2424212 100644
--- a/clang/lib/Driver/ToolChains/Gnu.cpp
+++ b/clang/lib/Driver/ToolChains/Gnu.cpp
@@ -751,7 +751,12 @@ void tools::gnutools::Assembler::ConstructJob(Compilation &C,
 
     break;
   }
-  // TODO: handle loongarch32.
+  case llvm::Triple::loongarch32: {
+    StringRef ABIName =
+        loongarch::getLoongArchABI(D, Args, getToolChain().getTriple());
+    CmdArgs.push_back(Args.MakeArgString("-mabi=" + ABIName));
+    break;
+  }
   case llvm::Triple::loongarch64: {
     StringRef ABIName =
         loongarch::getLoongArchABI(D, Args, getToolChain().getTriple());
@@ -2369,6 +2374,12 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
       "i586-suse-linux",     "i686-montavista-linux",
   };
 
+  static const char *const LoongArch32LibDirs[] = {"/lib32", "/lib"};
+  static const char *const LoongArch32Triples[] = {
+      "loongarch32-linux-gnu",    "loongarch32-unknown-linux-gnu",
+      "loongarch32-linux-gnuf32", "loongarch32-unknown-linux-gnuf32",
+      "loongarch32-linux-gnusf",  "loongarch32-unknown-linux-gnusf"};
+
   static const char *const LoongArch64LibDirs[] = {"/lib64", "/lib"};
   static const char *const LoongArch64Triples[] = {
       "loongarch64-linux-gnu", "loongarch64-unknown-linux-gnu"};
@@ -2639,7 +2650,10 @@ void Generic_GCC::GCCInstallationDetector::AddDefaultGCCPrefixes(
       BiarchTripleAliases.append(begin(X32Triples), end(X32Triples));
     }
     break;
-  // TODO: Handle loongarch32.
+  case llvm::Triple::loongarch32:
+    LibDirs.append(begin(LoongArch32LibDirs), end(LoongArch32LibDirs));
+    TripleAliases.append(begin(LoongArch32Triples), end(LoongArch32Triples));
+    break;
   case llvm::Triple::loongarch64:
     LibDirs.append(begin(LoongArch64LibDirs), end(LoongArch64LibDirs));
     TripleAliases.append(begin(LoongArch64Triples), end(LoongArch64Triples));
diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp
index cdbf21fb90263..d525b417b4eae 100644
--- a/clang/lib/Driver/ToolChains/Linux.cpp
+++ b/clang/lib/Driver/ToolChains/Linux.cpp
@@ -206,6 +206,19 @@ static StringRef getOSLibDir(const llvm::Triple &Triple, const ArgList &Args) {
   if (Triple.getArch() == llvm::Triple::riscv32)
     return "lib32";
 
+  if (Triple.getArch() == llvm::Triple::loongarch32) {
+    switch (Triple.getEnvironment()) {
+    default:
+      return "lib32";
+    case llvm::Triple::GNUSF:
+    case llvm::Triple::MuslSF:
+      return "lib32/sf";
+    case llvm::Triple::GNUF32:
+    case llvm::Triple::MuslF32:
+      return "lib32/f32";
+    }
+  }
+
   return Triple.isArch32Bit() ? "lib" : "lib64";
 }
 
diff --git a/clang/test/CodeGen/LoongArch/targetattr-la32.c b/clang/test/CodeGen/LoongArch/targetattr-la32.c
new file mode 100644
index 0000000000000..7483964843ff0
--- /dev/null
+++ b/clang/test/CodeGen/LoongArch/targetattr-la32.c
@@ -0,0 +1,26 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals all --version 5
+// RUN: %clang_cc1 -triple loongarch32 -emit-llvm %s -o - | FileCheck %s
+
+__attribute__((target("arch=la32v1.0")))
+// CHECK-LABEL: define dso_local void @testLa32v10(
+// CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    ret void
+//
+void testLa32v10() {}
+
+__attribute__((target("arch=la32rv1.0")))
+// CHECK-LABEL: define dso_local void @testLa32rv10(
+// CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    ret void
+//
+void testLa32rv10() {}
+
+//.
+// CHECK: attributes #[[ATTR0]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="loongarch32" "target-features"="+32bit,+32s" }
+// CHECK: attributes #[[ATTR1]] = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="loongarch32" "target-features"="+32bit" }
+//.
+// CHECK: [[META0:![0-9]+]] = !{i32 1, !"wchar_size", i32 4}
+// CHECK: [[META1:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"}
+//.
diff --git a/clang/test/CodeGen/LoongArch/targetattr.c b/clang/test/CodeGen/LoongArch/targetattr-la64.c
similarity index 100%
rename from clang/test/CodeGen/LoongArch/targetattr.c
rename to clang/test/CodeGen/LoongArch/targetattr-la64.c
diff --git a/clang/test/Driver/loongarch-march.c b/clang/test/Driver/loongarch-march.c
index 87cdffd8d3a02..046b3b16fa10e 100644
--- a/clang/test/Driver/loongarch-march.c
+++ b/clang/test/Driver/loongarch-march.c
@@ -1,3 +1,11 @@
+// RUN: %clang --target=loongarch32 -march=la32v1.0 -fsyntax-only %s -### 2>&1 | \
+// RUN:   FileCheck %s --check-prefix=CC1-LA32V1P0
+// RUN: %clang --target=loongarch32 -march=la32v1.0 -S -emit-llvm %s -o - | \
+// RUN:   FileCheck %s --check-prefix=IR-LA32V1P0
+// RUN: %clang --target=loongarch32 -march=la32rv1.0 -fsyntax-only %s -### 2>&1 | \
+// RUN:   FileCheck %s --check-prefix=CC1-LA32RV1P0
+// RUN: %clang --target=loongarch32 -march=la32rv1.0 -S -emit-llvm %s -o - | \
+// RUN:   FileCheck %s --check-prefix=IR-LA32RV1P0
 // RUN: %clang --target=loongarch64 -march=loongarch64 -fsyntax-only %s -### 2>&1 | \
 // RUN:   FileCheck %s --check-prefix=CC1-LOONGARCH64
 // RUN: %clang --target=loongarch64 -march=la464 -fsyntax-only %s -### 2>&1 | \
@@ -19,6 +27,18 @@
 // RUN: %clang --target=loongarch64 -march=la664 -S -emit-llvm %s -o - | \
 // RUN:   FileCheck %s --check-prefix=IR-LA664
 
+// CC1-LA32V1P0: "-target-cpu" "loongarch32"
+// CC1-LA32V1P0-NOT: "-target-feature"
+// CC1-LA32V1P0: "-target-feature" "+32bit" "-target-feature" "+32s"
+// CC1-LA32V1P0-NOT: "-target-feature"
+// CC1-LA32V1P0: "-target-abi" "ilp32d"
+
+// CC1-LA32RV1P0: "-target-cpu" "loongarch32"
+// CC1-LA32RV1P0-NOT: "-target-feature"
+// CC1-LA32RV1P0: "-target-feature" "+32bit"
+// CC1-LA32RV1P0-NOT: "-target-feature"
+// CC1-LA32RV1P0: "-target-abi" "ilp32d"
+
 // CC1-LOONGARCH64: "-target-cpu" "loongarch64"
 // CC1-LOONGARCH64-NOT: "-target-feature"
 // CC1-LOONGARCH64: "-target-feature" "+relax" "-target-feature" "+64bit" "-target-feature" "+f" "-target-feature" "+d" "-target-feature" "+ual"
@@ -49,6 +69,9 @@
 // CC1-LA664-NOT: "-target-feature"
 // CC1-LA664: "-target-abi" "lp64d"
 
+// IR-LA32V1P0: attributes #[[#]] ={{.*}}"target-cpu"="loongarch32" {{.*}}"target-features"="+32bit,+32s"
+// IR-LA32RV1P0: attributes #[[#]] ={{.*}}"target-cpu"="loongarch32" {{.*}}"target-features"="+32bit"
+
 // IR-LOONGARCH64: attributes #[[#]] ={{.*}}"target-cpu"="loongarch64" {{.*}}"target-features"="+64bit,+d,+f,+relax,+ual"
 // IR-LA464: attributes #[[#]] ={{.*}}"target-cpu"="la464" {{.*}}"target-features"="+64bit,+d,+f,+lasx,+lsx,+relax,+ual"
 // IR-LA64V1P0: attributes #[[#]] ={{.*}}"target-cpu"="loongarch64" {{.*}}"target-features"="+64bit,+d,+lsx,+relax,+ual"
diff --git a/clang/test/Driver/loongarch-mtune.c b/clang/test/Driver/loongarch-mtune.c
index face12e1a1a82..0f63851efe685 100644
--- a/clang/test/Driver/loongarch-mtune.c
+++ b/clang/test/Driver/loongarch-mtune.c
@@ -1,3 +1,8 @@
+// RUN: %clang --target=loongarch32 -mtune=loongarch32 -fsyntax-only %s -### 2>&1 | \
+// RUN:   FileCheck %s --check-prefix=CC1ARG -DCPU=loongarch32
+// RUN: %clang --target=loongarch32 -mtune=loongarch32 -S -emit-llvm %s -o - | \
+// RUN:   FileCheck %s --check-prefix=IRATTR -DCPU=loongarch32
+
 // RUN: %clang --target=loongarch64 -mtune=loongarch64 -fsyntax-only %s -### 2>&1 | \
 // RUN:   FileCheck %s --check-prefix=CC1ARG -DCPU=loongarch64
 // RUN: %clang --target=loongarch64 -mtune=loongarch64 -S -emit-llvm %s -o - | \
diff --git a/clang/test/Preprocessor/init-loongarch.c b/clang/test/Preprocessor/init-loongarch.c
index b2c0b51464a80..ef9a043a8ddde 100644
--- a/clang/test/Preprocessor/init-loongarch.c
+++ b/clang/test/Preprocessor/init-loongarch.c
@@ -822,6 +822,12 @@
 
 /// Check __loongarch_arch{_tune/_frecipe/_lam_bh/_lamcas/_ld_seq_sa/_div32/_scq}.
 
+// RUN: %clang --target=loongarch32 -x c -E -dM %s -o - | \
+// RUN:   FileCheck --match-full-lines --check-prefix=ARCH-TUNE -DARCH=la32rv1.0 -DTUNE=loongarch32 %s
+// RUN: %clang --target=loongarch32 -x c -E -dM %s -o - -march=la32v1.0 | \
+// RUN:   FileCheck --match-full-lines --check-prefix=ARCH-TUNE -DARCH=la32v1.0 -DTUNE=loongarch32 %s
+// RUN: %clang --target=loongarch32 -x c -E -dM %s -o - -march=la32rv1.0 | \
+// RUN:   FileCheck --match-full-lines --check-prefix=ARCH-TUNE -DARCH=la32rv1.0 -DTUNE=loongarch32 %s
 // RUN: %clang --target=loongarch64 -x c -E -dM %s -o - | \
 // RUN:   FileCheck --match-full-lines --check-prefix=ARCH-TUNE -DARCH=la64v1.0 -DTUNE=loongarch64 %s
 // RUN: %clang --target=loongarch64 -x c -E -dM %s -o - -march=loongarch64 | \
diff --git a/llvm/include/llvm/TargetParser/LoongArchTargetParser.def b/llvm/include/llvm/TargetParser/LoongArchTargetParser.def
index 1bcf65b37f201..e369b0d0d32fe 100644
--- a/llvm/include/llvm/TargetParser/LoongArchTargetParser.def
+++ b/llvm/include/llvm/TargetParser/LoongArchTargetParser.def
@@ -2,6 +2,7 @@
 #define LOONGARCH_FEATURE(NAME, KIND)
 #endif
 
+LOONGARCH_FEATURE("+32bit", FK_32BIT)
 LOONGARCH_FEATURE("+64bit", FK_64BIT)
 LOONGARCH_FEATURE("+f", FK_FP32)
 LOONGARCH_FEATURE("+d", FK_FP64)
@@ -16,6 +17,7 @@ LOONGARCH_FEATURE("+lamcas", FK_LAMCAS)
 LOONGARCH_FEATURE("+ld-seq-sa", FK_LD_SEQ_SA)
 LOONGARCH_FEATURE("+div32", FK_DIV32)
 LOONGARCH_FEATURE("+scq", FK_SCQ)
+LOONGARCH_FEATURE("+32s", FK_32S)
 
 #undef LOONGARCH_FEATURE
 
@@ -23,6 +25,7 @@ LOONGARCH_FEATURE("+scq", FK_SCQ)
 #define LOONGARCH_ARCH(NAME, KIND, FEATURES)
 #endif
 
+LOONGARCH_ARCH("loongarch32", AK_LOONGARCH32, FK_32BIT)
 LOONGARCH_ARCH("loongarch64", AK_LOONGARCH64, FK_64BIT | FK_FP32 | FK_FP64 | FK_UAL)
 LOONGARCH_ARCH("la464", AK_LA464, FK_64BIT | FK_FP32 | FK_FP64 | FK_LSX | FK_LASX | FK_UAL)
 LOONGARCH_ARCH("la664", AK_LA664, FK_64BIT | FK_FP32 | FK_FP64 | FK_LSX | FK_LASX | FK_UAL | FK_FRECIPE | FK_LAM_BH | FK_LAMCAS | FK_LD_SEQ_SA | FK_DIV32 | FK_SCQ)
diff --git a/llvm/include/llvm/TargetParser/LoongArchTargetParser.h b/llvm/include/llvm/TargetParser/LoongArchTargetParser.h
index 1357d74744592..a874fb327bb6c 100644
--- a/llvm/include/llvm/TargetParser/LoongArchTargetParser.h
+++ b/llvm/include/llvm/TargetParser/LoongArchTargetParser.h
@@ -24,6 +24,9 @@ class StringRef;
 namespace LoongArch {
 
 enum FeatureKind : uint32_t {
+  // 32-bit ISA is available.
+  FK_32BIT = 1 << 0,
+
   // 64-bit ISA is available.
   FK_64BIT = 1 << 1,
 
@@ -67,6 +70,9 @@ enum FeatureKind : uint32_t {
 
   // sc.q is available.
   FK_SCQ = 1 << 14,
+
+  // 32-bit standard variant is available.
+  FK_32S = 1 << 15,
 };
 
 struct FeatureInfo {
diff --git a/llvm/lib/Target/LoongArch/LoongArch.td b/llvm/lib/Target/LoongArch/LoongArch.td
index 67f07f0a0370e..577dcbed48eea 100644
--- a/llvm/lib/Target/LoongArch/LoongArch.td
+++ b/llvm/lib/Target/LoongArch/LoongArch.td
@@ -171,6 +171,9 @@ def : ProcessorModel<"generic-la64", NoSchedModel, [Feature64Bit,
                                                     FeatureUAL,
                                                     FeatureExtLSX]>;
 
+// Generic 32-bit processor.
+def : ProcessorModel<"loongarch32", NoSchedModel, [Feature32Bit]>;
+
 // Generic 64-bit processor with double-precision floating-point support.
 def : ProcessorModel<"loongarch64", NoSchedModel, [Feature64Bit,
                                                    FeatureUAL,
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.cpp
index 7cefb3f8119b8..cf8552fde3ca5 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.cpp
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.cpp
@@ -26,18 +26,14 @@ namespace LoongArchABI {
 static ABI checkABIStandardized(ABI Abi) {
   StringRef ABIName;
   switch (Abi) {
-  case ABI_ILP32S:
-    ABIName = "ilp32s";
-    break;
   case ABI_ILP32F:
     ABIName = "ilp32f";
     break;
-  case ABI_ILP32D:
-    ABIName = "ilp32d";
-    break;
   case ABI_LP64F:
     ABIName = "lp64f";
     break;
+  case ABI_ILP32S:
+  case ABI_ILP32D:
   case ABI_LP64S:
   case ABI_LP64D:
     return Abi;
diff --git a/llvm/lib/TargetParser/LoongArchTargetParser.cpp b/llvm/lib/TargetParser/LoongArchTargetParser.cpp
index 572a278a39fce..a568a108aa02f 100644
--- a/llvm/lib/TargetParser/LoongArchTargetParser.cpp
+++ b/llvm/lib/TargetParser/LoongArchTargetParser.cpp
@@ -74,6 +74,13 @@ bool LoongArch::getArchFeatures(StringRef Arch,
     return true;
   }
 
+  if (Arch == "la32v1.0" || Arch == "la32rv1.0") {
+    Features.push_back("+32bit");
+    if (Arch == "la32v1.0")
+      Features.push_back("+32s");
+    return true;
+  }
+
   return false;
 }
 
@@ -85,6 +92,5 @@ void LoongArch::fillValidCPUList(SmallVectorImpl<StringRef> &Values) {
 }
 
 StringRef LoongArch::getDefaultArch(bool Is64Bit) {
-  // TODO: use a real 32-bit arch name.
-  return Is64Bit ? "loongarch64" : "";
+  return Is64Bit ? "loongarch64" : "loongarch32";
 }
diff --git a/llvm/test/CodeGen/LoongArch/target-abi.ll b/llvm/test/CodeGen/LoongArch/target-abi.ll
index de834c152b180..033a47027b85e 100644
--- a/llvm/test/CodeGen/LoongArch/target-abi.ll
+++ b/llvm/test/CodeGen/LoongArch/target-abi.ll
@@ -1,12 +1,12 @@
-; RUN: llc --mtriple=loongarch32 --mattr=+d --target-abi=ilp32s < %s 2>&1 \
-; RUN:   | FileCheck %s -DABI=ilp32s --check-prefixes=CHECK,WARNING
 ; RUN: llc --mtriple=loongarch32 --mattr=+d --target-abi=ilp32f < %s 2>&1 \
 ; RUN:   | FileCheck %s -DABI=ilp32f --check-prefixes=CHECK,WARNING
-; RUN: llc --mtriple=loongarch32 --mattr=+d --target-abi=ilp32d < %s 2>&1 \
-; RUN:   | FileCheck %s -DABI=ilp32d --check-prefixes=CHECK,WARNING
 ; RUN: llc --mtriple=loongarch64 --mattr=+d --target-abi=lp64f < %s 2>&1 \
 ; RUN:   | FileCheck %s -DABI=lp64f --check-prefixes=CHECK,WARNING
 
+; RUN: llc --mtriple=loongarch32 --mattr=+d --target-abi=ilp32s < %s 2>&1 \
+; RUN:   | FileCheck %s --check-prefixes=CHECK,NO-WARNING
+; RUN: llc --mtriple=loongarch32 --mattr=+d --target-abi=ilp32d < %s 2>&1 \
+; RUN:   | FileCheck %s --check-prefixes=CHECK,NO-WARNING
 ; RUN: llc --mtriple=loongarch64 --mattr=+d --target-abi=lp64s < %s 2>&1 \
 ; RUN:   | FileCheck %s --check-prefixes=CHECK,NO-WARNING
 ; RUN: llc --mtriple=loongarch64 --mattr=+d --target-abi=lp64d < %s 2>&1 \



More information about the llvm-branch-commits mailing list