[llvm] c2ee21c - [LoongArch] Add some fixups and relocations

Weining Lu via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 19 20:46:23 PDT 2022


Author: wanglei
Date: 2022-08-20T11:19:24+08:00
New Revision: c2ee21cf3f863dbe48bd6decf896b41891808647

URL: https://github.com/llvm/llvm-project/commit/c2ee21cf3f863dbe48bd6decf896b41891808647
DIFF: https://github.com/llvm/llvm-project/commit/c2ee21cf3f863dbe48bd6decf896b41891808647.diff

LOG: [LoongArch] Add some fixups and relocations

This patch only add %pc_hi20/%pc_lo12/%plt relocations in order
to be able to generate gnu ld linkable relocation file for the
`hello world` IR :
```
@.str = private unnamed_addr constant [14 x i8] c"Hello world!\0A\00", align 1

define dso_local signext i32 @main() nounwind {
entry:
  %call = call signext i32 (ptr, ...) @printf(ptr noundef @.str)
  ret i32 0
}

declare dso_local signext i32 @printf(ptr noundef, ...)
```

This patch also updates some test cases due to new modifiers introduced.
New test: test/MC/LoongArch/Relocations/relocations.s

Differential Revision: https://reviews.llvm.org/D132108

Added: 
    llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h
    llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp
    llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h
    llvm/test/MC/LoongArch/Relocations/relocations.s

Modified: 
    llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
    llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
    llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
    llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp
    llvm/lib/Target/LoongArch/MCTargetDesc/CMakeLists.txt
    llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp
    llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h
    llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h
    llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFObjectWriter.cpp
    llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp
    llvm/test/CodeGen/LoongArch/analyze-branch.ll
    llvm/test/CodeGen/LoongArch/bnez-beqz.ll
    llvm/test/CodeGen/LoongArch/calling-conv-lp64d.ll
    llvm/test/CodeGen/LoongArch/eh-dwarf-cfa.ll
    llvm/test/CodeGen/LoongArch/frame.ll
    llvm/test/CodeGen/LoongArch/fsqrt.ll
    llvm/test/CodeGen/LoongArch/ir-instruction/call.ll
    llvm/test/CodeGen/LoongArch/ir-instruction/double-convert.ll
    llvm/test/CodeGen/LoongArch/ir-instruction/float-convert.ll
    llvm/test/CodeGen/LoongArch/ir-instruction/load-store-atomic.ll
    llvm/test/CodeGen/LoongArch/ir-instruction/load-store.ll
    llvm/test/CodeGen/LoongArch/ir-instruction/sdiv-udiv-srem-urem.ll
    llvm/test/CodeGen/LoongArch/vararg.ll
    llvm/test/MC/LoongArch/Basic/Integer/invalid.s
    llvm/test/MC/LoongArch/Basic/Integer/invalid64.s
    llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/loongarch_generated_funcs.ll.generated.expected
    llvm/test/tools/UpdateTestChecks/update_llc_test_checks/Inputs/loongarch_generated_funcs.ll.nogenerated.expected

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
index 9793c7bc3532c..2559069e3813b 100644
--- a/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
+++ b/llvm/lib/Target/LoongArch/AsmParser/LoongArchAsmParser.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "MCTargetDesc/LoongArchInstPrinter.h"
+#include "MCTargetDesc/LoongArchMCExpr.h"
 #include "MCTargetDesc/LoongArchMCTargetDesc.h"
 #include "TargetInfo/LoongArchTargetInfo.h"
 #include "llvm/MC/MCContext.h"
@@ -17,6 +18,7 @@
 #include "llvm/MC/MCRegisterInfo.h"
 #include "llvm/MC/MCStreamer.h"
 #include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/MC/MCValue.h"
 #include "llvm/MC/TargetRegistry.h"
 #include "llvm/Support/Casting.h"
 
@@ -62,6 +64,8 @@ class LoongArchAsmParser : public MCTargetAsmParser {
 
   OperandMatchResultTy parseRegister(OperandVector &Operands);
   OperandMatchResultTy parseImmediate(OperandVector &Operands);
+  OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands);
+  OperandMatchResultTy parseSImm26Operand(OperandVector &Operands);
 
   bool parseOperand(OperandVector &Operands, StringRef Mnemonic);
 
@@ -75,6 +79,9 @@ class LoongArchAsmParser : public MCTargetAsmParser {
 #undef GET_OPERAND_DIAGNOSTIC_TYPES
   };
 
+  static bool classifySymbolRef(const MCExpr *Expr,
+                                LoongArchMCExpr::VariantKind &Kind);
+
   LoongArchAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser,
                      const MCInstrInfo &MII, const MCTargetOptions &Options)
       : MCTargetAsmParser(Options, STI, MII) {
@@ -120,7 +127,13 @@ class LoongArchOperand : public MCParsedAsmOperand {
   bool isMem() const override { return false; }
   void setReg(MCRegister PhysReg) { Reg.RegNum = PhysReg; }
 
-  static bool evaluateConstantImm(const MCExpr *Expr, int64_t &Imm) {
+  static bool evaluateConstantImm(const MCExpr *Expr, int64_t &Imm,
+                                  LoongArchMCExpr::VariantKind &VK) {
+    if (auto *LE = dyn_cast<LoongArchMCExpr>(Expr)) {
+      VK = LE->getKind();
+      return false;
+    }
+
     if (auto CE = dyn_cast<MCConstantExpr>(Expr)) {
       Imm = CE->getValue();
       return true;
@@ -134,8 +147,10 @@ class LoongArchOperand : public MCParsedAsmOperand {
       return false;
 
     int64_t Imm;
-    bool IsConstantImm = evaluateConstantImm(getImm(), Imm);
-    return IsConstantImm && isUInt<N>(Imm - P);
+    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
+    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
+    return IsConstantImm && isUInt<N>(Imm - P) &&
+           VK == LoongArchMCExpr::VK_LoongArch_None;
   }
 
   template <unsigned N, unsigned S = 0> bool isSImm() const {
@@ -143,8 +158,10 @@ class LoongArchOperand : public MCParsedAsmOperand {
       return false;
 
     int64_t Imm;
-    bool IsConstantImm = evaluateConstantImm(getImm(), Imm);
-    return IsConstantImm && isShiftedInt<N, S>(Imm);
+    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
+    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
+    return IsConstantImm && isShiftedInt<N, S>(Imm) &&
+           VK == LoongArchMCExpr::VK_LoongArch_None;
   }
 
   bool isUImm2() const { return isUImm<2>(); }
@@ -156,13 +173,59 @@ class LoongArchOperand : public MCParsedAsmOperand {
   bool isUImm12() const { return isUImm<12>(); }
   bool isUImm14() const { return isUImm<14>(); }
   bool isUImm15() const { return isUImm<15>(); }
-  bool isSImm12() const { return isSImm<12>(); }
+
+  bool isSImm12() const {
+    if (!isImm())
+      return false;
+
+    int64_t Imm;
+    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
+    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
+    bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_PCREL_LO;
+
+    return IsConstantImm
+               ? (isInt<12>(Imm) &&
+                  (VK == LoongArchMCExpr::VK_LoongArch_None || IsValidKind))
+               : (LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
+                  IsValidKind);
+  }
+
   bool isSImm14lsl2() const { return isSImm<14, 2>(); }
   bool isSImm16() const { return isSImm<16>(); }
   bool isSImm16lsl2() const { return isSImm<16, 2>(); }
   bool isSImm20() const { return isSImm<20>(); }
+
+  bool isSImm20pcalau12i() const {
+    if (!isImm())
+      return false;
+
+    int64_t Imm;
+    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
+    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
+    bool IsValidKind = VK == LoongArchMCExpr::VK_LoongArch_PCREL_HI;
+
+    return IsConstantImm
+               ? (isInt<20>(Imm) &&
+                  (VK == LoongArchMCExpr::VK_LoongArch_None || IsValidKind))
+               : (LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
+                  IsValidKind);
+  }
+
   bool isSImm21lsl2() const { return isSImm<21, 2>(); }
-  bool isSImm26lsl2() const { return isSImm<26, 2>(); }
+
+  bool isSImm26Operand() const {
+    int64_t Imm;
+    LoongArchMCExpr::VariantKind VK = LoongArchMCExpr::VK_LoongArch_None;
+    bool IsConstantImm = evaluateConstantImm(getImm(), Imm, VK);
+    bool IsValidKind = (VK == LoongArchMCExpr::VK_LoongArch_CALL ||
+                        VK == LoongArchMCExpr::VK_LoongArch_CALL_PLT);
+
+    return IsConstantImm
+               ? (isShiftedInt<26, 2>(Imm) &&
+                  (VK == LoongArchMCExpr::VK_LoongArch_None || IsValidKind))
+               : (LoongArchAsmParser::classifySymbolRef(getImm(), VK) &&
+                  IsValidKind);
+  }
 
   /// Gets location of the first token of this operand.
   SMLoc getStartLoc() const override { return StartLoc; }
@@ -289,6 +352,21 @@ OperandMatchResultTy LoongArchAsmParser::tryParseRegister(unsigned &RegNo,
   llvm_unreachable("Unimplemented function.");
 }
 
+bool LoongArchAsmParser::classifySymbolRef(const MCExpr *Expr,
+                                           LoongArchMCExpr::VariantKind &Kind) {
+  Kind = LoongArchMCExpr::VK_LoongArch_None;
+
+  if (const LoongArchMCExpr *RE = dyn_cast<LoongArchMCExpr>(Expr)) {
+    Kind = RE->getKind();
+    Expr = RE->getSubExpr();
+  }
+
+  MCValue Res;
+  if (Expr->evaluateAsRelocatable(Res, nullptr, nullptr))
+    return Res.getRefKind() == LoongArchMCExpr::VK_LoongArch_None;
+  return false;
+}
+
 OperandMatchResultTy
 LoongArchAsmParser::parseRegister(OperandVector &Operands) {
   if (getLexer().getTok().isNot(AsmToken::Dollar))
@@ -319,9 +397,91 @@ LoongArchAsmParser::parseImmediate(OperandVector &Operands) {
   SMLoc E;
   const MCExpr *Res;
 
-  if (getParser().parseExpression(Res, E))
+  switch (getLexer().getKind()) {
+  default:
+    return MatchOperand_NoMatch;
+  case AsmToken::LParen:
+  case AsmToken::Dot:
+  case AsmToken::Minus:
+  case AsmToken::Plus:
+  case AsmToken::Exclaim:
+  case AsmToken::Tilde:
+  case AsmToken::Integer:
+  case AsmToken::String:
+  case AsmToken::Identifier:
+    if (getParser().parseExpression(Res, E))
+      return MatchOperand_ParseFail;
+    break;
+  case AsmToken::Percent:
+    return parseOperandWithModifier(Operands);
+  }
+
+  Operands.push_back(LoongArchOperand::createImm(Res, S, E));
+  return MatchOperand_Success;
+}
+
+OperandMatchResultTy
+LoongArchAsmParser::parseOperandWithModifier(OperandVector &Operands) {
+  SMLoc S = getLoc();
+  SMLoc E;
+
+  if (getLexer().getKind() != AsmToken::Percent) {
+    Error(getLoc(), "expected '%' for operand modifier");
+    return MatchOperand_ParseFail;
+  }
+
+  getParser().Lex(); // Eat '%'
+
+  if (getLexer().getKind() != AsmToken::Identifier) {
+    Error(getLoc(), "expected valid identifier for operand modifier");
+    return MatchOperand_ParseFail;
+  }
+  StringRef Identifier = getParser().getTok().getIdentifier();
+  LoongArchMCExpr::VariantKind VK =
+      LoongArchMCExpr::getVariantKindForName(Identifier);
+  if (VK == LoongArchMCExpr::VK_LoongArch_Invalid) {
+    Error(getLoc(), "unrecognized operand modifier");
+    return MatchOperand_ParseFail;
+  }
+
+  getParser().Lex(); // Eat the identifier
+  if (getLexer().getKind() != AsmToken::LParen) {
+    Error(getLoc(), "expected '('");
+    return MatchOperand_ParseFail;
+  }
+  getParser().Lex(); // Eat '('
+
+  const MCExpr *SubExpr;
+  if (getParser().parseParenExpression(SubExpr, E)) {
+    return MatchOperand_ParseFail;
+  }
+
+  const MCExpr *ModExpr = LoongArchMCExpr::create(SubExpr, VK, getContext());
+  Operands.push_back(LoongArchOperand::createImm(ModExpr, S, E));
+  return MatchOperand_Success;
+}
+
+OperandMatchResultTy
+LoongArchAsmParser::parseSImm26Operand(OperandVector &Operands) {
+  SMLoc S = getLoc();
+  const MCExpr *Res;
+
+  if (getLexer().getKind() == AsmToken::Percent)
+    return parseOperandWithModifier(Operands);
+
+  if (getLexer().getKind() != AsmToken::Identifier)
+    return MatchOperand_NoMatch;
+
+  StringRef Identifier;
+  if (getParser().parseIdentifier(Identifier))
     return MatchOperand_ParseFail;
 
+  SMLoc E = SMLoc::getFromPointer(S.getPointer() + Identifier.size());
+
+  MCSymbol *Sym = getContext().getOrCreateSymbol(Identifier);
+  Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext());
+  Res = LoongArchMCExpr::create(Res, LoongArchMCExpr::VK_LoongArch_CALL,
+                                getContext());
   Operands.push_back(LoongArchOperand::createImm(Res, S, E));
   return MatchOperand_Success;
 }
@@ -330,6 +490,15 @@ LoongArchAsmParser::parseImmediate(OperandVector &Operands) {
 /// information, adding to Operands. Return true upon an error.
 bool LoongArchAsmParser::parseOperand(OperandVector &Operands,
                                       StringRef Mnemonic) {
+  // Check if the current operand has a custom associated parser, if so, try to
+  // custom parse the operand, or fallback to the general approach.
+  OperandMatchResultTy Result =
+      MatchOperandParserImpl(Operands, Mnemonic, /*ParseForAllFeatures=*/true);
+  if (Result == MatchOperand_Success)
+    return false;
+  if (Result == MatchOperand_ParseFail)
+    return true;
+
   if (parseRegister(Operands) == MatchOperand_Success ||
       parseImmediate(Operands) == MatchOperand_Success)
     return false;
@@ -522,8 +691,11 @@ bool LoongArchAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
     return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/0,
                                       /*Upper=*/(1 << 15) - 1);
   case Match_InvalidSImm12:
-    return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 11),
-                                      /*Upper=*/(1 << 11) - 1);
+    return generateImmOutOfRangeError(
+        Operands, ErrorInfo, /*Lower=*/-(1 << 11),
+        /*Upper=*/(1 << 11) - 1,
+        "operand must be a symbol with modifier (e.g. %pc_lo12) or an integer "
+        "in the range");
   case Match_InvalidSImm14lsl2:
     return generateImmOutOfRangeError(
         Operands, ErrorInfo, /*Lower=*/-(1 << 15), /*Upper=*/(1 << 15) - 4,
@@ -538,14 +710,21 @@ bool LoongArchAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
   case Match_InvalidSImm20:
     return generateImmOutOfRangeError(Operands, ErrorInfo, /*Lower=*/-(1 << 19),
                                       /*Upper=*/(1 << 19) - 1);
+  case Match_InvalidSImm20pcalau12i:
+    return generateImmOutOfRangeError(
+        Operands, ErrorInfo, /*Lower=*/-(1 << 19),
+        /*Upper=*/(1 << 19) - 1,
+        "operand must be a symbol with modifier (e.g. %pc_hi20) or an integer "
+        "in the range");
   case Match_InvalidSImm21lsl2:
     return generateImmOutOfRangeError(
         Operands, ErrorInfo, /*Lower=*/-(1 << 22), /*Upper=*/(1 << 22) - 4,
         "immediate must be a multiple of 4 in the range");
-  case Match_InvalidSImm26lsl2:
+  case Match_InvalidSImm26Operand:
     return generateImmOutOfRangeError(
         Operands, ErrorInfo, /*Lower=*/-(1 << 27), /*Upper=*/(1 << 27) - 4,
-        "immediate must be a multiple of 4 in the range");
+        "operand must be a bare symbol name or an immediate must be a multiple "
+        "of 4 in the range");
   }
   llvm_unreachable("Unknown match type detected!");
 }

diff  --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
index de6c758bdfdda..63ffe125b5418 100644
--- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp
@@ -17,6 +17,7 @@
 #include "LoongArchRegisterInfo.h"
 #include "LoongArchSubtarget.h"
 #include "LoongArchTargetMachine.h"
+#include "MCTargetDesc/LoongArchBaseInfo.h"
 #include "MCTargetDesc/LoongArchMCTargetDesc.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/CodeGen/ISDOpcodes.h"
@@ -278,10 +279,13 @@ SDValue LoongArchTargetLowering::lowerGlobalAddress(SDValue Op,
 
   // TODO: Support dso_preemptable and target flags.
   if (GV->isDSOLocal()) {
-    SDValue GA = DAG.getTargetGlobalAddress(GV, DL, Ty);
-    SDValue AddrHi(DAG.getMachineNode(LoongArch::PCALAU12I, DL, Ty, GA), 0);
-    SDValue Addr(DAG.getMachineNode(ADDIOp, DL, Ty, AddrHi, GA), 0);
-    return Addr;
+    SDValue GAHi =
+        DAG.getTargetGlobalAddress(GV, DL, Ty, 0, LoongArchII::MO_PCREL_HI);
+    SDValue GALo =
+        DAG.getTargetGlobalAddress(GV, DL, Ty, 0, LoongArchII::MO_PCREL_LO);
+    SDValue AddrHi(DAG.getMachineNode(LoongArch::PCALAU12I, DL, Ty, GAHi), 0);
+
+    return SDValue(DAG.getMachineNode(ADDIOp, DL, Ty, AddrHi, GALo), 0);
   }
   report_fatal_error("Unable to lowerGlobalAddress");
 }
@@ -1601,11 +1605,20 @@ 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.
-  // FIXME: Add target flags for relocation.
-  if (GlobalAddressSDNode *S = dyn_cast<GlobalAddressSDNode>(Callee))
-    Callee = DAG.getTargetGlobalAddress(S->getGlobal(), DL, PtrVT);
-  else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee))
-    Callee = DAG.getTargetExternalSymbol(S->getSymbol(), PtrVT);
+  if (GlobalAddressSDNode *S = dyn_cast<GlobalAddressSDNode>(Callee)) {
+    const GlobalValue *GV = S->getGlobal();
+    unsigned OpFlags =
+        getTargetMachine().shouldAssumeDSOLocal(*GV->getParent(), GV)
+            ? LoongArchII::MO_CALL
+            : LoongArchII::MO_CALL_PLT;
+    Callee = DAG.getTargetGlobalAddress(S->getGlobal(), DL, PtrVT, 0, OpFlags);
+  } else if (ExternalSymbolSDNode *S = dyn_cast<ExternalSymbolSDNode>(Callee)) {
+    unsigned OpFlags = getTargetMachine().shouldAssumeDSOLocal(
+                           *MF.getFunction().getParent(), nullptr)
+                           ? LoongArchII::MO_CALL
+                           : LoongArchII::MO_CALL_PLT;
+    Callee = DAG.getTargetExternalSymbol(S->getSymbol(), PtrVT, OpFlags);
+  }
 
   // The first call operand is the chain and the second is the target address.
   SmallVector<SDValue> Ops;

diff  --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
index fa73ea2fbc64b..29c3255aa9189 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
@@ -150,19 +150,42 @@ def simm16_lsl2_br : Operand<OtherVT> {
   let DecoderMethod = "decodeSImmOperand<16, 2>";
 }
 
-def simm20 : Operand<GRLenVT> {
-  let ParserMatchClass = SImmAsmOperand<20>;
+class SImm20Operand : Operand<GRLenVT> {
   let DecoderMethod = "decodeSImmOperand<20>";
 }
 
+def simm20 : SImm20Operand {
+  let ParserMatchClass = SImmAsmOperand<20>;
+}
+
+def simm20_pcalau12i : SImm20Operand {
+  let ParserMatchClass = SImmAsmOperand<20, "pcalau12i">;
+}
+
 def simm21_lsl2 : Operand<OtherVT> {
   let ParserMatchClass = SImmAsmOperand<21, "lsl2">;
   let EncoderMethod = "getImmOpValueAsr2";
   let DecoderMethod = "decodeSImmOperand<21, 2>";
 }
 
-def simm26_lsl2 : Operand<OtherVT> {
-  let ParserMatchClass = SImmAsmOperand<26, "lsl2">;
+// TODO: Need split the ParserMethod/PredicateMethod for call/jump/tailcall.
+def SImm26Operand: AsmOperandClass {
+  let Name = "SImm26Operand";
+  let RenderMethod = "addImmOperands";
+  let DiagnosticType = "InvalidSImm26Operand";
+  let ParserMethod = "parseSImm26Operand";
+}
+
+// A symbol or an imm used in B/PseudoBR.
+def simm26_b : Operand<OtherVT> {
+  let ParserMatchClass = SImm26Operand;
+  let EncoderMethod = "getImmOpValueAsr2";
+  let DecoderMethod = "decodeSImmOperand<26, 2>";
+}
+
+// A symbol or an imm used in BL/PseudoCALL.
+def simm26_bl : Operand<GRLenVT> {
+  let ParserMatchClass = SImm26Operand;
   let EncoderMethod = "getImmOpValueAsr2";
   let DecoderMethod = "decodeSImmOperand<26, 2>";
 }
@@ -190,17 +213,6 @@ def ImmSubFrom32 : SDNodeXForm<imm, [{
                                    N->getValueType(0));
 }]>;
 
-def CallSymbol: AsmOperandClass {
-  let Name = "CallSymbol";
-  let RenderMethod = "addImmOperands";
-  let PredicateMethod = "isImm";
-}
-
-// A bare symbol used in call only.
-def call_symbol : Operand<iPTR> {
-  let ParserMatchClass = CallSymbol;
-}
-
 def BaseAddr : ComplexPattern<iPTR, 1, "SelectBaseAddr">;
 
 //===----------------------------------------------------------------------===//
@@ -259,7 +271,7 @@ class BrCCZ_1RI21<bits<6> op, string opstr>
   let isTerminator = 1;
 }
 class Br_I26<bits<6> op, string opstr>
-    : FmtI26<op, (outs), (ins simm26_lsl2:$imm26), opstr, "$imm26"> {
+    : FmtI26<op, (outs), (ins simm26_b:$imm26), opstr, "$imm26"> {
   let isBranch = 1;
   let isTerminator = 1;
 }
@@ -323,7 +335,7 @@ def SLTI  : ALU_2RI12<0b0000001000, "slti", simm12>;
 def SLTUI : ALU_2RI12<0b0000001001, "sltui", simm12>;
 def PCADDI    : ALU_1RI20<0b0001100, "pcaddi", simm20>;
 def PCADDU12I : ALU_1RI20<0b0001110, "pcaddu12i", simm20>;
-def PCALAU12I : ALU_1RI20<0b0001101, "pcalau12i", simm20>;
+def PCALAU12I : ALU_1RI20<0b0001101, "pcalau12i", simm20_pcalau12i>;
 def AND  : ALU_3R<0b00000000000101001, "and">;
 def OR   : ALU_3R<0b00000000000101010, "or">;
 def NOR  : ALU_3R<0b00000000000101000, "nor">;
@@ -387,8 +399,8 @@ def BEQZ : BrCCZ_1RI21<0b010000, "beqz">;
 def BNEZ : BrCCZ_1RI21<0b010001, "bnez">;
 def B : Br_I26<0b010100, "b">;
 
-let isCall = 1 in
-def BL : FmtI26<0b010101, (outs), (ins simm26_lsl2:$imm26), "bl", "$imm26">;
+let isCall = 1, Defs=[R1] in
+def BL : FmtI26<0b010101, (outs), (ins simm26_bl:$imm26), "bl", "$imm26">;
 def JIRL : Fmt2RI16<0b010011, (outs GPR:$rd),
                     (ins GPR:$rj, simm16_lsl2:$imm16), "jirl",
                     "$rd, $rj, $imm16">;
@@ -827,8 +839,8 @@ def : Pat<(brcond (GRLenVT (setne GPR:$rj, 0)), bb:$imm21),
           (BNEZ GPR:$rj, bb:$imm21)>;
 
 let isBarrier = 1, isBranch = 1, isTerminator = 1 in
-def PseudoBR : Pseudo<(outs), (ins simm26_lsl2:$imm26), [(br bb:$imm26)]>,
-               PseudoInstExpansion<(B simm26_lsl2:$imm26)>;
+def PseudoBR : Pseudo<(outs), (ins simm26_b:$imm26), [(br bb:$imm26)]>,
+               PseudoInstExpansion<(B simm26_b:$imm26)>;
 
 let isBarrier = 1, isBranch = 1, isIndirectBranch = 1, isTerminator = 1 in
 def PseudoBRIND : Pseudo<(outs), (ins GPR:$rj, simm16_lsl2:$imm16), []>,
@@ -839,7 +851,7 @@ def : Pat<(brind (add GPR:$rj, simm16_lsl2:$imm16)),
           (PseudoBRIND GPR:$rj, simm16_lsl2:$imm16)>;
 
 let isCall = 1, Defs = [R1] in
-def PseudoCALL : Pseudo<(outs), (ins call_symbol:$func), []> {
+def PseudoCALL : Pseudo<(outs), (ins simm26_bl:$func), []> {
   let AsmString = "bl\t$func";
 }
 

diff  --git a/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp b/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp
index 488c66f47863c..328cbb56aabf5 100644
--- a/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp
+++ b/llvm/lib/Target/LoongArch/LoongArchMCInstLower.cpp
@@ -13,6 +13,8 @@
 
 #include "LoongArch.h"
 #include "LoongArchSubtarget.h"
+#include "MCTargetDesc/LoongArchBaseInfo.h"
+#include "MCTargetDesc/LoongArchMCExpr.h"
 #include "llvm/CodeGen/AsmPrinter.h"
 #include "llvm/CodeGen/MachineBasicBlock.h"
 #include "llvm/CodeGen/MachineInstr.h"
@@ -25,8 +27,28 @@ using namespace llvm;
 static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym,
                                     const AsmPrinter &AP) {
   MCContext &Ctx = AP.OutContext;
+  LoongArchMCExpr::VariantKind Kind;
 
-  // TODO: Processing target flags.
+  switch (MO.getTargetFlags()) {
+  default:
+    llvm_unreachable("Unknown target flag on GV operand");
+  case LoongArchII::MO_None:
+    Kind = LoongArchMCExpr::VK_LoongArch_None;
+    break;
+  case LoongArchII::MO_CALL:
+    Kind = LoongArchMCExpr::VK_LoongArch_CALL;
+    break;
+  case LoongArchII::MO_CALL_PLT:
+    Kind = LoongArchMCExpr::VK_LoongArch_CALL_PLT;
+    break;
+  case LoongArchII::MO_PCREL_HI:
+    Kind = LoongArchMCExpr::VK_LoongArch_PCREL_HI;
+    break;
+  case LoongArchII::MO_PCREL_LO:
+    Kind = LoongArchMCExpr::VK_LoongArch_PCREL_LO;
+    break;
+    // TODO: Handle more target-flags.
+  }
 
   const MCExpr *ME =
       MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Ctx);
@@ -35,6 +57,8 @@ static MCOperand lowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym,
     ME = MCBinaryExpr::createAdd(
         ME, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
 
+  if (Kind != LoongArchMCExpr::VK_LoongArch_None)
+    ME = LoongArchMCExpr::create(ME, Kind, Ctx);
   return MCOperand::createExpr(ME);
 }
 

diff  --git a/llvm/lib/Target/LoongArch/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/LoongArch/MCTargetDesc/CMakeLists.txt
index 42832d0ffe8eb..6b572a93e38ef 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/CMakeLists.txt
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/CMakeLists.txt
@@ -5,8 +5,9 @@ add_llvm_component_library(LLVMLoongArchDesc
   LoongArchELFStreamer.cpp
   LoongArchInstPrinter.cpp
   LoongArchMCAsmInfo.cpp
-  LoongArchMCTargetDesc.cpp
   LoongArchMCCodeEmitter.cpp
+  LoongArchMCExpr.cpp
+  LoongArchMCTargetDesc.cpp
   LoongArchMatInt.cpp
   LoongArchTargetStreamer.cpp
 

diff  --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp
index 94a068897f8c5..ea2c92b553810 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp
@@ -22,6 +22,30 @@
 
 using namespace llvm;
 
+const MCFixupKindInfo &
+LoongArchAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
+  const static MCFixupKindInfo Infos[] = {
+      // This table *must* be in the order that the fixup_* kinds are defined in
+      // LoongArchFixupKinds.h.
+      //
+      // {name, offset, bits, flags}
+      {"fixup_loongarch_b26", 0, 26, 0},
+      {"fixup_loongarch_pcala_hi20", 5, 20, MCFixupKindInfo::FKF_IsPCRel},
+      {"fixup_loongarch_pcala_lo12", 10, 12, MCFixupKindInfo::FKF_IsPCRel},
+      // TODO: Add more fixup kinds.
+  };
+
+  static_assert((array_lengthof(Infos)) == LoongArch::NumTargetFixupKinds,
+                "Not all fixup kinds added to Infos array");
+
+  if (Kind < FirstTargetFixupKind)
+    return MCAsmBackend::getFixupKindInfo(Kind);
+
+  assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() &&
+         "Invalid kind!");
+  return Infos[Kind - FirstTargetFixupKind];
+}
+
 void LoongArchAsmBackend::applyFixup(const MCAssembler &Asm,
                                      const MCFixup &Fixup,
                                      const MCValue &Target,

diff  --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h
index a5f0b816c972c..449554f55a774 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h
@@ -14,6 +14,7 @@
 #define LLVM_LIB_TARGET_LOONGARCH_MCTARGETDESC_LOONGARCHASMBACKEND_H
 
 #include "MCTargetDesc/LoongArchBaseInfo.h"
+#include "MCTargetDesc/LoongArchFixupKinds.h"
 #include "MCTargetDesc/LoongArchMCTargetDesc.h"
 #include "llvm/MC/MCAsmBackend.h"
 #include "llvm/MC/MCFixupKindInfo.h"
@@ -45,10 +46,11 @@ class LoongArchAsmBackend : public MCAsmBackend {
   }
 
   unsigned getNumFixupKinds() const override {
-    // FIXME: Implement this when we define fixup kind
-    return 0;
+    return LoongArch::NumTargetFixupKinds;
   }
 
+  const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
+
   void relaxInstruction(MCInst &Inst,
                         const MCSubtargetInfo &STI) const override {}
 

diff  --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h
index fee247a0c02c6..72afab3ec3051 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchBaseInfo.h
@@ -22,6 +22,19 @@
 
 namespace llvm {
 
+// This namespace holds all of the target specific flags that instruction info
+// tracks.
+namespace LoongArchII {
+enum {
+  MO_None,
+  MO_CALL,
+  MO_CALL_PLT,
+  MO_PCREL_HI,
+  MO_PCREL_LO,
+  // TODO: Add more flags.
+};
+} // end namespace LoongArchII
+
 namespace LoongArchABI {
 enum ABI {
   ABI_ILP32S,

diff  --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFObjectWriter.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFObjectWriter.cpp
index 1850b0d8a756c..3c09b30cebe1e 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFObjectWriter.cpp
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchELFObjectWriter.cpp
@@ -6,7 +6,9 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "MCTargetDesc/LoongArchFixupKinds.h"
 #include "MCTargetDesc/LoongArchMCTargetDesc.h"
+#include "llvm/BinaryFormat/ELF.h"
 #include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCELFObjectWriter.h"
 #include "llvm/MC/MCFixup.h"
@@ -52,9 +54,26 @@ unsigned LoongArchELFObjectWriter::getRelocType(MCContext &Ctx,
     return Kind - FirstLiteralRelocationKind;
 
   switch (Kind) {
-  // TODO: Implement this when we defined fixup kind.
   default:
+    Ctx.reportError(Fixup.getLoc(), "Unsupported relocation type");
     return ELF::R_LARCH_NONE;
+  case FK_Data_1:
+    Ctx.reportError(Fixup.getLoc(), "1-byte data relocations not supported");
+    return ELF::R_LARCH_NONE;
+  case FK_Data_2:
+    Ctx.reportError(Fixup.getLoc(), "2-byte data relocations not supported");
+    return ELF::R_LARCH_NONE;
+  case FK_Data_4:
+    return ELF::R_LARCH_32;
+  case FK_Data_8:
+    return ELF::R_LARCH_64;
+  case LoongArch::fixup_loongarch_pcala_hi20:
+    return ELF::R_LARCH_PCALA_HI20;
+  case LoongArch::fixup_loongarch_pcala_lo12:
+    return ELF::R_LARCH_PCALA_LO12;
+  case LoongArch::fixup_loongarch_b26:
+    return ELF::R_LARCH_B26;
+    // TODO: Handle more fixup-kinds.
   }
 }
 

diff  --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h
new file mode 100644
index 0000000000000..46d40d304c918
--- /dev/null
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchFixupKinds.h
@@ -0,0 +1,39 @@
+//===- LoongArchFixupKinds.h - LoongArch Specific Fixup Entries -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_LOONGARCH_MCTARGETDESC_LOONGARCHFIXUPKINDS_H
+#define LLVM_LIB_TARGET_LOONGARCH_MCTARGETDESC_LOONGARCHFIXUPKINDS_H
+
+#include "llvm/MC/MCFixup.h"
+
+#undef LoongArch
+
+namespace llvm {
+namespace LoongArch {
+//
+// This table *must* be in the same order of
+// MCFixupKindInfo Infos[LoongArch::NumTargetFixupKinds] in
+// LoongArchAsmBackend.cpp.
+//
+enum Fixups {
+  // 26-bit fixup for symbol references in the b/bl instructions.
+  fixup_loongarch_b26 = FirstTargetFixupKind,
+  // 20-bit fixup corresponding to %pc_hi20(foo) for instruction pcalau12i.
+  fixup_loongarch_pcala_hi20,
+  // 12-bit fixup corresponding to %pc_lo12(foo) for instructions addi.w/d.
+  fixup_loongarch_pcala_lo12,
+  // TODO: Add more fixup kind.
+
+  // Used as a sentinel, must be the last.
+  fixup_loongarch_invalid,
+  NumTargetFixupKinds = fixup_loongarch_invalid - FirstTargetFixupKind
+};
+} // end namespace LoongArch
+} // end namespace llvm
+
+#endif

diff  --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp
index 01a370a90403c..1a08f352b52fb 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp
@@ -10,13 +10,16 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "LoongArchFixupKinds.h"
 #include "MCTargetDesc/LoongArchBaseInfo.h"
+#include "MCTargetDesc/LoongArchMCExpr.h"
 #include "MCTargetDesc/LoongArchMCTargetDesc.h"
 #include "llvm/MC/MCCodeEmitter.h"
 #include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCInstBuilder.h"
 #include "llvm/MC/MCInstrInfo.h"
 #include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/EndianStream.h"
 
 using namespace llvm;
@@ -40,6 +43,10 @@ class LoongArchMCCodeEmitter : public MCCodeEmitter {
                          SmallVectorImpl<MCFixup> &Fixups,
                          const MCSubtargetInfo &STI) const override;
 
+  void expandFunctionCall(const MCInst &MI, raw_ostream &OS,
+                          SmallVectorImpl<MCFixup> &Fixups,
+                          const MCSubtargetInfo &STI) const;
+
   /// TableGen'erated function for getting the binary encoding for an
   /// instruction.
   uint64_t getBinaryCodeForInstr(const MCInst &MI,
@@ -68,6 +75,10 @@ class LoongArchMCCodeEmitter : public MCCodeEmitter {
   unsigned getImmOpValueAsr2(const MCInst &MI, unsigned OpNo,
                              SmallVectorImpl<MCFixup> &Fixups,
                              const MCSubtargetInfo &STI) const;
+
+  unsigned getExprOpValue(const MCInst &MI, const MCOperand &MO,
+                          SmallVectorImpl<MCFixup> &Fixups,
+                          const MCSubtargetInfo &STI) const;
 };
 } // end namespace
 
@@ -82,7 +93,9 @@ LoongArchMCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCOperand &MO,
   if (MO.isImm())
     return static_cast<unsigned>(MO.getImm());
 
-  llvm_unreachable("Unhandled expression!");
+  // MO must be an Expr.
+  assert(MO.isExpr());
+  return getExprOpValue(MI, MO, Fixups, STI);
 }
 
 unsigned
@@ -96,9 +109,62 @@ unsigned
 LoongArchMCCodeEmitter::getImmOpValueAsr2(const MCInst &MI, unsigned OpNo,
                                           SmallVectorImpl<MCFixup> &Fixups,
                                           const MCSubtargetInfo &STI) const {
-  unsigned Res = MI.getOperand(OpNo).getImm();
-  assert((Res & 3) == 0 && "lowest 2 bits are non-zero");
-  return Res >> 2;
+  const MCOperand &MO = MI.getOperand(OpNo);
+
+  if (MO.isImm()) {
+    unsigned Res = MI.getOperand(OpNo).getImm();
+    assert((Res & 3) == 0 && "lowest 2 bits are non-zero");
+    return Res >> 2;
+  }
+
+  return getExprOpValue(MI, MO, Fixups, STI);
+}
+
+unsigned
+LoongArchMCCodeEmitter::getExprOpValue(const MCInst &MI, const MCOperand &MO,
+                                       SmallVectorImpl<MCFixup> &Fixups,
+                                       const MCSubtargetInfo &STI) const {
+  assert(MO.isExpr() && "getExprOpValue expects only expressions");
+  const MCExpr *Expr = MO.getExpr();
+  MCExpr::ExprKind Kind = Expr->getKind();
+  LoongArch::Fixups FixupKind = LoongArch::fixup_loongarch_invalid;
+  if (Kind == MCExpr::Target) {
+    const LoongArchMCExpr *LAExpr = cast<LoongArchMCExpr>(Expr);
+
+    switch (LAExpr->getKind()) {
+    case LoongArchMCExpr::VK_LoongArch_None:
+    case LoongArchMCExpr::VK_LoongArch_Invalid:
+      llvm_unreachable("Unhandled fixup kind!");
+    case LoongArchMCExpr::VK_LoongArch_PCREL_HI:
+      FixupKind = LoongArch::fixup_loongarch_pcala_hi20;
+      break;
+    case LoongArchMCExpr::VK_LoongArch_PCREL_LO:
+      FixupKind = LoongArch::fixup_loongarch_pcala_lo12;
+      break;
+    case LoongArchMCExpr::VK_LoongArch_CALL:
+    case LoongArchMCExpr::VK_LoongArch_CALL_PLT:
+      FixupKind = LoongArch::fixup_loongarch_b26;
+      break;
+    }
+  }
+
+  assert(FixupKind != LoongArch::fixup_loongarch_invalid &&
+         "Unhandled expression!");
+
+  Fixups.push_back(
+      MCFixup::create(0, Expr, MCFixupKind(FixupKind), MI.getLoc()));
+  return 0;
+}
+
+void LoongArchMCCodeEmitter::expandFunctionCall(
+    const MCInst &MI, raw_ostream &OS, SmallVectorImpl<MCFixup> &Fixups,
+    const MCSubtargetInfo &STI) const {
+  MCOperand Func = MI.getOperand(0);
+  MCInst TmpInst = Func.isExpr()
+                       ? MCInstBuilder(LoongArch::BL).addExpr(Func.getExpr())
+                       : MCInstBuilder(LoongArch::BL).addImm(Func.getImm());
+  uint32_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI);
+  support::endian::write(OS, Binary, support::little);
 }
 
 void LoongArchMCCodeEmitter::encodeInstruction(
@@ -108,6 +174,9 @@ void LoongArchMCCodeEmitter::encodeInstruction(
   // Get byte count of instruction.
   unsigned Size = Desc.getSize();
 
+  if (MI.getOpcode() == LoongArch::PseudoCALL)
+    return expandFunctionCall(MI, OS, Fixups, STI);
+
   switch (Size) {
   default:
     llvm_unreachable("Unhandled encodeInstruction length!");

diff  --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp
new file mode 100644
index 0000000000000..bbf6de6375569
--- /dev/null
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.cpp
@@ -0,0 +1,82 @@
+//===-- LoongArchMCExpr.cpp - LoongArch specific MC expression classes ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the implementation of the assembly expression modifiers
+// accepted by the LoongArch architecture.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LoongArchMCExpr.h"
+#include "LoongArchAsmBackend.h"
+#include "LoongArchFixupKinds.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/MC/MCValue.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "loongarch-mcexpr"
+
+const LoongArchMCExpr *
+LoongArchMCExpr::create(const MCExpr *Expr, VariantKind Kind, MCContext &Ctx) {
+  return new (Ctx) LoongArchMCExpr(Expr, Kind);
+}
+
+void LoongArchMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const {
+  VariantKind Kind = getKind();
+  bool HasVariant =
+      ((Kind != VK_LoongArch_None) && (Kind != VK_LoongArch_CALL));
+
+  if (HasVariant)
+    OS << '%' << getVariantKindName(getKind()) << '(';
+  Expr->print(OS, MAI);
+  if (HasVariant)
+    OS << ')';
+}
+
+bool LoongArchMCExpr::evaluateAsRelocatableImpl(MCValue &Res,
+                                                const MCAsmLayout *Layout,
+                                                const MCFixup *Fixup) const {
+  // Explicitly drop the layout and assembler to prevent any symbolic folding in
+  // the expression handling.  This is required to preserve symbolic 
diff erence
+  // expressions to emit the paired relocations.
+  if (!getSubExpr()->evaluateAsRelocatable(Res, nullptr, nullptr))
+    return false;
+
+  Res =
+      MCValue::get(Res.getSymA(), Res.getSymB(), Res.getConstant(), getKind());
+  // Custom fixup types are not valid with symbol 
diff erence expressions.
+  return Res.getSymB() ? getKind() == VK_LoongArch_None : true;
+}
+
+void LoongArchMCExpr::visitUsedExpr(MCStreamer &Streamer) const {
+  Streamer.visitUsedExpr(*getSubExpr());
+}
+
+StringRef LoongArchMCExpr::getVariantKindName(VariantKind Kind) {
+  switch (Kind) {
+  default:
+    llvm_unreachable("Invalid ELF symbol kind");
+  case VK_LoongArch_CALL_PLT:
+    return "plt";
+  case VK_LoongArch_PCREL_HI:
+    return "pc_hi20";
+  case VK_LoongArch_PCREL_LO:
+    return "pc_lo12";
+  }
+}
+
+LoongArchMCExpr::VariantKind
+LoongArchMCExpr::getVariantKindForName(StringRef name) {
+  return StringSwitch<LoongArchMCExpr::VariantKind>(name)
+      .Case("pc_hi20", VK_LoongArch_PCREL_HI)
+      .Case("pc_lo12", VK_LoongArch_PCREL_LO)
+      .Case("plt", VK_LoongArch_CALL_PLT)
+      .Default(VK_LoongArch_Invalid);
+}

diff  --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h
new file mode 100644
index 0000000000000..e37c625382d22
--- /dev/null
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCExpr.h
@@ -0,0 +1,69 @@
+//= LoongArchMCExpr.h - LoongArch specific MC expression classes -*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file describes LoongArch-specific MCExprs, used for modifiers like
+// "%pc_hi20" or "%pc_lo12" etc.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_LOONGARCH_MCTARGETDESC_LOONGARCHMCEXPR_H
+#define LLVM_LIB_TARGET_LOONGARCH_MCTARGETDESC_LOONGARCHMCEXPR_H
+
+#include "llvm/MC/MCExpr.h"
+
+namespace llvm {
+
+class StringRef;
+
+class LoongArchMCExpr : public MCTargetExpr {
+public:
+  enum VariantKind {
+    // TODO: Add more target kinds.
+    VK_LoongArch_None,
+    VK_LoongArch_CALL,
+    VK_LoongArch_CALL_PLT,
+    VK_LoongArch_PCREL_HI,
+    VK_LoongArch_PCREL_LO,
+    VK_LoongArch_Invalid // Must be the last item.
+  };
+
+private:
+  const MCExpr *Expr;
+  const VariantKind Kind;
+
+  explicit LoongArchMCExpr(const MCExpr *Expr, VariantKind Kind)
+      : Expr(Expr), Kind(Kind) {}
+
+public:
+  static const LoongArchMCExpr *create(const MCExpr *Expr, VariantKind Kind,
+                                       MCContext &Ctx);
+
+  VariantKind getKind() const { return Kind; }
+  const MCExpr *getSubExpr() const { return Expr; }
+
+  void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override;
+  bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout,
+                                 const MCFixup *Fixup) const override;
+  void visitUsedExpr(MCStreamer &Streamer) const override;
+  MCFragment *findAssociatedFragment() const override {
+    return getSubExpr()->findAssociatedFragment();
+  }
+
+  void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override {}
+
+  static bool classof(const MCExpr *E) {
+    return E->getKind() == MCExpr::Target;
+  }
+
+  static StringRef getVariantKindName(VariantKind Kind);
+  static VariantKind getVariantKindForName(StringRef name);
+};
+
+} // end namespace llvm
+
+#endif

diff  --git a/llvm/test/CodeGen/LoongArch/analyze-branch.ll b/llvm/test/CodeGen/LoongArch/analyze-branch.ll
index 7ce61f84df384..9b61cd7f40053 100644
--- a/llvm/test/CodeGen/LoongArch/analyze-branch.ll
+++ b/llvm/test/CodeGen/LoongArch/analyze-branch.ll
@@ -19,13 +19,13 @@ define void @test_bcc_fallthrough_taken(i64 %in) nounwind {
 ; CHECK-NEXT:    ori $a1, $zero, 42
 ; CHECK-NEXT:    bne $a0, $a1, .LBB0_3
 ; CHECK-NEXT:  # %bb.1: # %true
-; CHECK-NEXT:    bl test_true
+; CHECK-NEXT:    bl %plt(test_true)
 ; CHECK-NEXT:  .LBB0_2: # %true
 ; CHECK-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 16
 ; CHECK-NEXT:    ret
 ; CHECK-NEXT:  .LBB0_3: # %false
-; CHECK-NEXT:    bl test_false
+; CHECK-NEXT:    bl %plt(test_false)
 ; CHECK-NEXT:    b .LBB0_2
   %tst = icmp eq i64 %in, 42
   br i1 %tst, label %true, label %false, !prof !0
@@ -51,13 +51,13 @@ define void @test_bcc_fallthrough_nottaken(i64 %in) nounwind {
 ; CHECK-NEXT:    ori $a1, $zero, 42
 ; CHECK-NEXT:    beq $a0, $a1, .LBB1_1
 ; CHECK-NEXT:  # %bb.3: # %false
-; CHECK-NEXT:    bl test_false
+; CHECK-NEXT:    bl %plt(test_false)
 ; CHECK-NEXT:  .LBB1_2: # %true
 ; CHECK-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 16
 ; CHECK-NEXT:    ret
 ; CHECK-NEXT:  .LBB1_1: # %true
-; CHECK-NEXT:    bl test_true
+; CHECK-NEXT:    bl %plt(test_true)
 ; CHECK-NEXT:    b .LBB1_2
   %tst = icmp eq i64 %in, 42
   br i1 %tst, label %true, label %false, !prof !1

diff  --git a/llvm/test/CodeGen/LoongArch/bnez-beqz.ll b/llvm/test/CodeGen/LoongArch/bnez-beqz.ll
index 782dd121e666a..b4b2c3e160e2a 100644
--- a/llvm/test/CodeGen/LoongArch/bnez-beqz.ll
+++ b/llvm/test/CodeGen/LoongArch/bnez-beqz.ll
@@ -11,7 +11,7 @@ define void @bnez_i32(i32 signext %0) nounwind {
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-NEXT:    bnez $a0, .LBB0_2
 ; LA32-NEXT:  # %bb.1: # %t
-; LA32-NEXT:    bl bar
+; LA32-NEXT:    bl %plt(bar)
 ; LA32-NEXT:  .LBB0_2: # %f
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
@@ -23,7 +23,7 @@ define void @bnez_i32(i32 signext %0) nounwind {
 ; LA64-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
 ; LA64-NEXT:    bnez $a0, .LBB0_2
 ; LA64-NEXT:  # %bb.1: # %t
-; LA64-NEXT:    bl bar
+; LA64-NEXT:    bl %plt(bar)
 ; LA64-NEXT:  .LBB0_2: # %f
 ; LA64-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64-NEXT:    addi.d $sp, $sp, 16
@@ -47,7 +47,7 @@ define void @beqz_i32(i32 signext %0) nounwind {
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-NEXT:    beqz $a0, .LBB1_2
 ; LA32-NEXT:  # %bb.1: # %t
-; LA32-NEXT:    bl bar
+; LA32-NEXT:    bl %plt(bar)
 ; LA32-NEXT:  .LBB1_2: # %f
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
@@ -59,7 +59,7 @@ define void @beqz_i32(i32 signext %0) nounwind {
 ; LA64-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
 ; LA64-NEXT:    beqz $a0, .LBB1_2
 ; LA64-NEXT:  # %bb.1: # %t
-; LA64-NEXT:    bl bar
+; LA64-NEXT:    bl %plt(bar)
 ; LA64-NEXT:  .LBB1_2: # %f
 ; LA64-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64-NEXT:    addi.d $sp, $sp, 16
@@ -84,7 +84,7 @@ define void @bnez_i64(i64 %0) nounwind {
 ; LA32-NEXT:    or $a0, $a0, $a1
 ; LA32-NEXT:    bnez $a0, .LBB2_2
 ; LA32-NEXT:  # %bb.1: # %t
-; LA32-NEXT:    bl bar
+; LA32-NEXT:    bl %plt(bar)
 ; LA32-NEXT:  .LBB2_2: # %f
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
@@ -96,7 +96,7 @@ define void @bnez_i64(i64 %0) nounwind {
 ; LA64-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
 ; LA64-NEXT:    bnez $a0, .LBB2_2
 ; LA64-NEXT:  # %bb.1: # %t
-; LA64-NEXT:    bl bar
+; LA64-NEXT:    bl %plt(bar)
 ; LA64-NEXT:  .LBB2_2: # %f
 ; LA64-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64-NEXT:    addi.d $sp, $sp, 16
@@ -121,7 +121,7 @@ define void @beqz_i64(i64 %0) nounwind {
 ; LA32-NEXT:    or $a0, $a0, $a1
 ; LA32-NEXT:    beqz $a0, .LBB3_2
 ; LA32-NEXT:  # %bb.1: # %t
-; LA32-NEXT:    bl bar
+; LA32-NEXT:    bl %plt(bar)
 ; LA32-NEXT:  .LBB3_2: # %f
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
@@ -133,7 +133,7 @@ define void @beqz_i64(i64 %0) nounwind {
 ; LA64-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
 ; LA64-NEXT:    beqz $a0, .LBB3_2
 ; LA64-NEXT:  # %bb.1: # %t
-; LA64-NEXT:    bl bar
+; LA64-NEXT:    bl %plt(bar)
 ; LA64-NEXT:  .LBB3_2: # %f
 ; LA64-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64-NEXT:    addi.d $sp, $sp, 16

diff  --git a/llvm/test/CodeGen/LoongArch/calling-conv-lp64d.ll b/llvm/test/CodeGen/LoongArch/calling-conv-lp64d.ll
index 5876786720351..28f571e5c6289 100644
--- a/llvm/test/CodeGen/LoongArch/calling-conv-lp64d.ll
+++ b/llvm/test/CodeGen/LoongArch/calling-conv-lp64d.ll
@@ -21,7 +21,7 @@ define i64 @caller_i128_in_regs() nounwind {
 ; CHECK-NEXT:    ori $a0, $zero, 1
 ; CHECK-NEXT:    ori $a1, $zero, 2
 ; CHECK-NEXT:    move $a2, $zero
-; CHECK-NEXT:    bl callee_i128_in_regs
+; CHECK-NEXT:    bl %plt(callee_i128_in_regs)
 ; CHECK-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 16
 ; CHECK-NEXT:    ret
@@ -79,7 +79,7 @@ define i64 @caller_many_scalars() nounwind {
 ; CHECK-NEXT:    ori $a6, $zero, 6
 ; CHECK-NEXT:    ori $a7, $zero, 7
 ; CHECK-NEXT:    move $a5, $zero
-; CHECK-NEXT:    bl callee_many_scalars
+; CHECK-NEXT:    bl %plt(callee_many_scalars)
 ; CHECK-NEXT:    ld.d $ra, $sp, 24 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 32
 ; CHECK-NEXT:    ret
@@ -131,7 +131,7 @@ define i64 @caller_large_scalars() nounwind {
 ; CHECK-NEXT:    st.d $a0, $sp, 32
 ; CHECK-NEXT:    addi.d $a0, $sp, 32
 ; CHECK-NEXT:    addi.d $a1, $sp, 0
-; CHECK-NEXT:    bl callee_large_scalars
+; CHECK-NEXT:    bl %plt(callee_large_scalars)
 ; CHECK-NEXT:    ld.d $ra, $sp, 72 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 80
 ; CHECK-NEXT:    ret
@@ -196,7 +196,7 @@ define i64 @caller_large_scalars_exhausted_regs() nounwind {
 ; CHECK-NEXT:    ori $a5, $zero, 6
 ; CHECK-NEXT:    ori $a6, $zero, 7
 ; CHECK-NEXT:    addi.d $a7, $sp, 48
-; CHECK-NEXT:    bl callee_large_scalars_exhausted_regs
+; CHECK-NEXT:    bl %plt(callee_large_scalars_exhausted_regs)
 ; CHECK-NEXT:    ld.d $ra, $sp, 88 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 96
 ; CHECK-NEXT:    ret
@@ -243,7 +243,7 @@ define i64 @caller_large_struct() nounwind {
 ; CHECK-NEXT:    st.d $a0, $sp, 64
 ; CHECK-NEXT:    st.d $a0, $sp, 32
 ; CHECK-NEXT:    addi.d $a0, $sp, 8
-; CHECK-NEXT:    bl callee_large_struct
+; CHECK-NEXT:    bl %plt(callee_large_struct)
 ; CHECK-NEXT:    ld.d $ra, $sp, 72 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 80
 ; CHECK-NEXT:    ret
@@ -276,7 +276,7 @@ define i64 @caller_small_scalar_ret() nounwind {
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    addi.d $sp, $sp, -16
 ; CHECK-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; CHECK-NEXT:    bl callee_small_scalar_ret
+; CHECK-NEXT:    bl %plt(callee_small_scalar_ret)
 ; CHECK-NEXT:    addi.w $a2, $zero, -2
 ; CHECK-NEXT:    xor $a0, $a0, $a2
 ; CHECK-NEXT:    orn $a0, $a0, $a1
@@ -308,7 +308,7 @@ define i64 @caller_small_struct_ret() nounwind {
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    addi.d $sp, $sp, -16
 ; CHECK-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; CHECK-NEXT:    bl callee_small_struct_ret
+; CHECK-NEXT:    bl %plt(callee_small_struct_ret)
 ; CHECK-NEXT:    add.d $a0, $a0, $a1
 ; CHECK-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 16
@@ -343,7 +343,7 @@ define void @caller_large_scalar_ret() nounwind {
 ; CHECK-NEXT:    addi.d $sp, $sp, -48
 ; CHECK-NEXT:    st.d $ra, $sp, 40 # 8-byte Folded Spill
 ; CHECK-NEXT:    addi.d $a0, $sp, 0
-; CHECK-NEXT:    bl callee_large_scalar_ret
+; CHECK-NEXT:    bl %plt(callee_large_scalar_ret)
 ; CHECK-NEXT:    ld.d $ra, $sp, 40 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 48
 ; CHECK-NEXT:    ret
@@ -386,7 +386,7 @@ define i64 @caller_large_struct_ret() nounwind {
 ; CHECK-NEXT:    addi.d $sp, $sp, -48
 ; CHECK-NEXT:    st.d $ra, $sp, 40 # 8-byte Folded Spill
 ; CHECK-NEXT:    addi.d $a0, $sp, 8
-; CHECK-NEXT:    bl callee_large_struct_ret
+; CHECK-NEXT:    bl %plt(callee_large_struct_ret)
 ; CHECK-NEXT:    ld.d $a0, $sp, 32
 ; CHECK-NEXT:    ld.d $a1, $sp, 8
 ; CHECK-NEXT:    add.d $a0, $a1, $a0
@@ -430,7 +430,7 @@ define i64 @caller_float_in_fpr() nounwind {
 ; CHECK-NEXT:    ori $a0, $zero, 1
 ; CHECK-NEXT:    movgr2fr.w $fa0, $zero
 ; CHECK-NEXT:    movgr2fr.d $fa1, $zero
-; CHECK-NEXT:    bl callee_float_in_fpr
+; CHECK-NEXT:    bl %plt(callee_float_in_fpr)
 ; CHECK-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 16
 ; CHECK-NEXT:    ret
@@ -489,7 +489,7 @@ define i64 @caller_double_in_gpr_exhausted_fprs() nounwind {
 ; CHECK-NEXT:    ori $a0, $zero, 0
 ; CHECK-NEXT:    lu32i.d $a0, 131072
 ; CHECK-NEXT:    lu52i.d $a0, $a0, 1026
-; CHECK-NEXT:    bl callee_double_in_gpr_exhausted_fprs
+; CHECK-NEXT:    bl %plt(callee_double_in_gpr_exhausted_fprs)
 ; CHECK-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 16
 ; CHECK-NEXT:    ret
@@ -516,7 +516,7 @@ define i64 @caller_double_ret() nounwind {
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    addi.d $sp, $sp, -16
 ; CHECK-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; CHECK-NEXT:    bl callee_double_ret
+; CHECK-NEXT:    bl %plt(callee_double_ret)
 ; CHECK-NEXT:    movfr2gr.d $a0, $fa0
 ; CHECK-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 16

diff  --git a/llvm/test/CodeGen/LoongArch/eh-dwarf-cfa.ll b/llvm/test/CodeGen/LoongArch/eh-dwarf-cfa.ll
index 83c47fea79108..796ada3a1a024 100644
--- a/llvm/test/CodeGen/LoongArch/eh-dwarf-cfa.ll
+++ b/llvm/test/CodeGen/LoongArch/eh-dwarf-cfa.ll
@@ -10,7 +10,7 @@ define void @dwarf() {
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-NEXT:    .cfi_offset 1, -4
 ; LA32-NEXT:    addi.w $a0, $sp, 16
-; LA32-NEXT:    bl foo
+; LA32-NEXT:    bl %plt(foo)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret
@@ -22,7 +22,7 @@ define void @dwarf() {
 ; LA64-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
 ; LA64-NEXT:    .cfi_offset 1, -8
 ; LA64-NEXT:    addi.d $a0, $sp, 16
-; LA64-NEXT:    bl foo
+; LA64-NEXT:    bl %plt(foo)
 ; LA64-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64-NEXT:    addi.d $sp, $sp, 16
 ; LA64-NEXT:    ret

diff  --git a/llvm/test/CodeGen/LoongArch/frame.ll b/llvm/test/CodeGen/LoongArch/frame.ll
index d80c6dd63f676..a3806bcd57a48 100644
--- a/llvm/test/CodeGen/LoongArch/frame.ll
+++ b/llvm/test/CodeGen/LoongArch/frame.ll
@@ -12,7 +12,7 @@ define i32 @test() nounwind {
 ; CHECK-NEXT:    st.d $zero, $sp, 8
 ; CHECK-NEXT:    st.d $zero, $sp, 0
 ; CHECK-NEXT:    addi.d $a0, $sp, 4
-; CHECK-NEXT:    bl test1
+; CHECK-NEXT:    bl %plt(test1)
 ; CHECK-NEXT:    move $a0, $zero
 ; CHECK-NEXT:    ld.d $ra, $sp, 24 # 8-byte Folded Reload
 ; CHECK-NEXT:    addi.d $sp, $sp, 32

diff  --git a/llvm/test/CodeGen/LoongArch/fsqrt.ll b/llvm/test/CodeGen/LoongArch/fsqrt.ll
index 296c3c01bd8a1..776de7f729ec4 100644
--- a/llvm/test/CodeGen/LoongArch/fsqrt.ll
+++ b/llvm/test/CodeGen/LoongArch/fsqrt.ll
@@ -36,7 +36,7 @@ define double @fsqrt_f64(double %a) nounwind {
 ; LA32F:       # %bb.0:
 ; LA32F-NEXT:    addi.w $sp, $sp, -16
 ; LA32F-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32F-NEXT:    bl sqrt
+; LA32F-NEXT:    bl %plt(sqrt)
 ; LA32F-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32F-NEXT:    addi.w $sp, $sp, 16
 ; LA32F-NEXT:    ret
@@ -50,7 +50,7 @@ define double @fsqrt_f64(double %a) nounwind {
 ; LA64F:       # %bb.0:
 ; LA64F-NEXT:    addi.d $sp, $sp, -16
 ; LA64F-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; LA64F-NEXT:    bl sqrt
+; LA64F-NEXT:    bl %plt(sqrt)
 ; LA64F-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64F-NEXT:    addi.d $sp, $sp, 16
 ; LA64F-NEXT:    ret
@@ -93,12 +93,12 @@ define double @frsqrt_f64(double %a) nounwind {
 ; LA32F:       # %bb.0:
 ; LA32F-NEXT:    addi.w $sp, $sp, -16
 ; LA32F-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32F-NEXT:    bl sqrt
+; LA32F-NEXT:    bl %plt(sqrt)
 ; LA32F-NEXT:    move $a2, $a0
 ; LA32F-NEXT:    move $a3, $a1
 ; LA32F-NEXT:    lu12i.w $a1, 261888
 ; LA32F-NEXT:    move $a0, $zero
-; LA32F-NEXT:    bl __divdf3
+; LA32F-NEXT:    bl %plt(__divdf3)
 ; LA32F-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32F-NEXT:    addi.w $sp, $sp, 16
 ; LA32F-NEXT:    ret
@@ -112,10 +112,10 @@ define double @frsqrt_f64(double %a) nounwind {
 ; LA64F:       # %bb.0:
 ; LA64F-NEXT:    addi.d $sp, $sp, -16
 ; LA64F-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; LA64F-NEXT:    bl sqrt
+; LA64F-NEXT:    bl %plt(sqrt)
 ; LA64F-NEXT:    move $a1, $a0
 ; LA64F-NEXT:    lu52i.d $a0, $zero, 1023
-; LA64F-NEXT:    bl __divdf3
+; LA64F-NEXT:    bl %plt(__divdf3)
 ; LA64F-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64F-NEXT:    addi.d $sp, $sp, 16
 ; LA64F-NEXT:    ret

diff  --git a/llvm/test/CodeGen/LoongArch/ir-instruction/call.ll b/llvm/test/CodeGen/LoongArch/ir-instruction/call.ll
index 5b499fa21690c..90ee9490de74e 100644
--- a/llvm/test/CodeGen/LoongArch/ir-instruction/call.ll
+++ b/llvm/test/CodeGen/LoongArch/ir-instruction/call.ll
@@ -9,7 +9,7 @@ define i32 @test_call_external(i32 %a) nounwind {
 ; LA32:       # %bb.0:
 ; LA32-NEXT:    addi.w $sp, $sp, -16
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32-NEXT:    bl external_function
+; LA32-NEXT:    bl %plt(external_function)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret
@@ -18,7 +18,7 @@ define i32 @test_call_external(i32 %a) nounwind {
 ; LA64:       # %bb.0:
 ; LA64-NEXT:    addi.d $sp, $sp, -16
 ; LA64-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; LA64-NEXT:    bl external_function
+; LA64-NEXT:    bl %plt(external_function)
 ; LA64-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64-NEXT:    addi.d $sp, $sp, 16
 ; LA64-NEXT:    ret
@@ -45,7 +45,7 @@ define i32 @test_call_defined(i32 %a) nounwind {
 ; LA32:       # %bb.0:
 ; LA32-NEXT:    addi.w $sp, $sp, -16
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32-NEXT:    bl defined_function
+; LA32-NEXT:    bl %plt(defined_function)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret
@@ -54,7 +54,7 @@ define i32 @test_call_defined(i32 %a) nounwind {
 ; LA64:       # %bb.0:
 ; LA64-NEXT:    addi.d $sp, $sp, -16
 ; LA64-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
-; LA64-NEXT:    bl defined_function
+; LA64-NEXT:    bl %plt(defined_function)
 ; LA64-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64-NEXT:    addi.d $sp, $sp, 16
 ; LA64-NEXT:    ret

diff  --git a/llvm/test/CodeGen/LoongArch/ir-instruction/double-convert.ll b/llvm/test/CodeGen/LoongArch/ir-instruction/double-convert.ll
index d1b83cdff06bf..eaa3feba36205 100644
--- a/llvm/test/CodeGen/LoongArch/ir-instruction/double-convert.ll
+++ b/llvm/test/CodeGen/LoongArch/ir-instruction/double-convert.ll
@@ -83,7 +83,7 @@ define double @convert_i64_to_double(i64 %a) nounwind {
 ; LA32:       # %bb.0:
 ; LA32-NEXT:    addi.w $sp, $sp, -16
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32-NEXT:    bl __floatdidf
+; LA32-NEXT:    bl %plt(__floatdidf)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret
@@ -147,7 +147,7 @@ define i64 @convert_double_to_i64(double %a) nounwind {
 ; LA32:       # %bb.0:
 ; LA32-NEXT:    addi.w $sp, $sp, -16
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32-NEXT:    bl __fixdfdi
+; LA32-NEXT:    bl %plt(__fixdfdi)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret
@@ -166,7 +166,7 @@ define i64 @convert_double_to_u64(double %a) nounwind {
 ; LA32:       # %bb.0:
 ; LA32-NEXT:    addi.w $sp, $sp, -16
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32-NEXT:    bl __fixunsdfdi
+; LA32-NEXT:    bl %plt(__fixunsdfdi)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret
@@ -262,7 +262,7 @@ define double @convert_u64_to_double(i64 %a) nounwind {
 ; LA32:       # %bb.0:
 ; LA32-NEXT:    addi.w $sp, $sp, -16
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32-NEXT:    bl __floatundidf
+; LA32-NEXT:    bl %plt(__floatundidf)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret

diff  --git a/llvm/test/CodeGen/LoongArch/ir-instruction/float-convert.ll b/llvm/test/CodeGen/LoongArch/ir-instruction/float-convert.ll
index 4a0f2ff685f3d..3661d709fb042 100644
--- a/llvm/test/CodeGen/LoongArch/ir-instruction/float-convert.ll
+++ b/llvm/test/CodeGen/LoongArch/ir-instruction/float-convert.ll
@@ -93,7 +93,7 @@ define i64 @convert_float_to_i64(float %a) nounwind {
 ; LA32F:       # %bb.0:
 ; LA32F-NEXT:    addi.w $sp, $sp, -16
 ; LA32F-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32F-NEXT:    bl __fixsfdi
+; LA32F-NEXT:    bl %plt(__fixsfdi)
 ; LA32F-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32F-NEXT:    addi.w $sp, $sp, 16
 ; LA32F-NEXT:    ret
@@ -102,7 +102,7 @@ define i64 @convert_float_to_i64(float %a) nounwind {
 ; LA32D:       # %bb.0:
 ; LA32D-NEXT:    addi.w $sp, $sp, -16
 ; LA32D-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32D-NEXT:    bl __fixsfdi
+; LA32D-NEXT:    bl %plt(__fixsfdi)
 ; LA32D-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32D-NEXT:    addi.w $sp, $sp, 16
 ; LA32D-NEXT:    ret
@@ -250,7 +250,7 @@ define i64 @convert_float_to_u64(float %a) nounwind {
 ; LA32F:       # %bb.0:
 ; LA32F-NEXT:    addi.w $sp, $sp, -16
 ; LA32F-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32F-NEXT:    bl __fixunssfdi
+; LA32F-NEXT:    bl %plt(__fixunssfdi)
 ; LA32F-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32F-NEXT:    addi.w $sp, $sp, 16
 ; LA32F-NEXT:    ret
@@ -259,7 +259,7 @@ define i64 @convert_float_to_u64(float %a) nounwind {
 ; LA32D:       # %bb.0:
 ; LA32D-NEXT:    addi.w $sp, $sp, -16
 ; LA32D-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32D-NEXT:    bl __fixunssfdi
+; LA32D-NEXT:    bl %plt(__fixunssfdi)
 ; LA32D-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32D-NEXT:    addi.w $sp, $sp, 16
 ; LA32D-NEXT:    ret
@@ -396,7 +396,7 @@ define float @convert_i64_to_float(i64 %a) nounwind {
 ; LA32F:       # %bb.0:
 ; LA32F-NEXT:    addi.w $sp, $sp, -16
 ; LA32F-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32F-NEXT:    bl __floatdisf
+; LA32F-NEXT:    bl %plt(__floatdisf)
 ; LA32F-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32F-NEXT:    addi.w $sp, $sp, 16
 ; LA32F-NEXT:    ret
@@ -405,7 +405,7 @@ define float @convert_i64_to_float(i64 %a) nounwind {
 ; LA32D:       # %bb.0:
 ; LA32D-NEXT:    addi.w $sp, $sp, -16
 ; LA32D-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32D-NEXT:    bl __floatdisf
+; LA32D-NEXT:    bl %plt(__floatdisf)
 ; LA32D-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32D-NEXT:    addi.w $sp, $sp, 16
 ; LA32D-NEXT:    ret
@@ -552,7 +552,7 @@ define float @convert_u64_to_float(i64 %a) nounwind {
 ; LA32F:       # %bb.0:
 ; LA32F-NEXT:    addi.w $sp, $sp, -16
 ; LA32F-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32F-NEXT:    bl __floatundisf
+; LA32F-NEXT:    bl %plt(__floatundisf)
 ; LA32F-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32F-NEXT:    addi.w $sp, $sp, 16
 ; LA32F-NEXT:    ret
@@ -561,7 +561,7 @@ define float @convert_u64_to_float(i64 %a) nounwind {
 ; LA32D:       # %bb.0:
 ; LA32D-NEXT:    addi.w $sp, $sp, -16
 ; LA32D-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
-; LA32D-NEXT:    bl __floatundisf
+; LA32D-NEXT:    bl %plt(__floatundisf)
 ; LA32D-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32D-NEXT:    addi.w $sp, $sp, 16
 ; LA32D-NEXT:    ret

diff  --git a/llvm/test/CodeGen/LoongArch/ir-instruction/load-store-atomic.ll b/llvm/test/CodeGen/LoongArch/ir-instruction/load-store-atomic.ll
index 8dde4251fb262..9600bf0762ff7 100644
--- a/llvm/test/CodeGen/LoongArch/ir-instruction/load-store-atomic.ll
+++ b/llvm/test/CodeGen/LoongArch/ir-instruction/load-store-atomic.ll
@@ -58,7 +58,7 @@ define i64 @load_acquire_i64(ptr %ptr) {
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-NEXT:    .cfi_offset 1, -4
 ; LA32-NEXT:    ori $a1, $zero, 2
-; LA32-NEXT:    bl __atomic_load_8
+; LA32-NEXT:    bl %plt(__atomic_load_8)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret
@@ -128,7 +128,7 @@ define void @store_release_i64(ptr %ptr, i64 %v) {
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-NEXT:    .cfi_offset 1, -4
 ; LA32-NEXT:    ori $a3, $zero, 3
-; LA32-NEXT:    bl __atomic_store_8
+; LA32-NEXT:    bl %plt(__atomic_store_8)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret

diff  --git a/llvm/test/CodeGen/LoongArch/ir-instruction/load-store.ll b/llvm/test/CodeGen/LoongArch/ir-instruction/load-store.ll
index 4564b8ed1c005..37a5e64b4200d 100644
--- a/llvm/test/CodeGen/LoongArch/ir-instruction/load-store.ll
+++ b/llvm/test/CodeGen/LoongArch/ir-instruction/load-store.ll
@@ -11,18 +11,18 @@ define i32 @load_store_global() nounwind {
 ; ALL-LABEL:      load_store_global:
 ; ALL:            # %bb.0:
 
-; LA32NOPIC-NEXT:   pcalau12i $a0, G
-; LA32NOPIC-NEXT:   addi.w $a1, $a0, G
-; LA32PIC-NEXT:     pcalau12i $a0, .LG$local
-; LA32PIC-NEXT:     addi.w $a1, $a0, .LG$local
+; LA32NOPIC-NEXT:   pcalau12i $a0, %pc_hi20(G)
+; LA32NOPIC-NEXT:   addi.w $a1, $a0, %pc_lo12(G)
+; LA32PIC-NEXT:     pcalau12i $a0, %pc_hi20(.LG$local)
+; LA32PIC-NEXT:     addi.w $a1, $a0, %pc_lo12(.LG$local)
 ; LA32-NEXT:        ld.w $a0, $a1, 0
 ; LA32-NEXT:        addi.w $a0, $a0, 1
 ; LA32-NEXT:        st.w $a0, $a1, 0
 
-; LA64NOPIC-NEXT:   pcalau12i $a0, G
-; LA64NOPIC-NEXT:   addi.d $a1, $a0, G
-; LA64PIC-NEXT:     pcalau12i $a0, .LG$local
-; LA64PIC-NEXT:     addi.d $a1, $a0, .LG$local
+; LA64NOPIC-NEXT:   pcalau12i $a0, %pc_hi20(G)
+; LA64NOPIC-NEXT:   addi.d $a1, $a0, %pc_lo12(G)
+; LA64PIC-NEXT:     pcalau12i $a0, %pc_hi20(.LG$local)
+; LA64PIC-NEXT:     addi.d $a1, $a0, %pc_lo12(.LG$local)
 ; LA64-NEXT:        ld.w $a0, $a1, 0
 ; LA64-NEXT:        addi.d $a0, $a0, 1
 ; LA64-NEXT:        st.w $a0, $a1, 0
@@ -39,10 +39,10 @@ define i32 @load_store_global_array(i32 %a) nounwind {
 ; ALL-LABEL: load_store_global_array:
 ; ALL:       # %bb.0:
 
-; LA32NOPIC-NEXT:   pcalau12i $a1, arr
-; LA32NOPIC-NEXT:   addi.w $a2, $a1, arr
-; LA32PIC-NEXT:     pcalau12i $a1, .Larr$local
-; LA32PIC-NEXT:     addi.w $a2, $a1, .Larr$local
+; LA32NOPIC-NEXT:   pcalau12i $a1, %pc_hi20(arr)
+; LA32NOPIC-NEXT:   addi.w $a2, $a1, %pc_lo12(arr)
+; LA32PIC-NEXT:     pcalau12i $a1, %pc_hi20(.Larr$local)
+; LA32PIC-NEXT:     addi.w $a2, $a1, %pc_lo12(.Larr$local)
 ; LA32-NEXT:        ld.w $a1, $a2, 0
 ; LA32-NEXT:        st.w $a0, $a2, 0
 ; LA32NOPIC-NEXT:   ld.w $a3, $a2, 36
@@ -50,10 +50,10 @@ define i32 @load_store_global_array(i32 %a) nounwind {
 ; LA32PIC-NEXT:     ld.w $a3, $a2, 36
 ; LA32PIC-NEXT:     st.w $a0, $a2, 36
 
-; LA64NOPIC-NEXT:   pcalau12i $a1, arr
-; LA64NOPIC-NEXT:   addi.d $a2, $a1, arr
-; LA64PIC-NEXT:     pcalau12i $a1, .Larr$local
-; LA64PIC-NEXT:     addi.d $a2, $a1, .Larr$local
+; LA64NOPIC-NEXT:   pcalau12i $a1, %pc_hi20(arr)
+; LA64NOPIC-NEXT:   addi.d $a2, $a1, %pc_lo12(arr)
+; LA64PIC-NEXT:     pcalau12i $a1, %pc_hi20(.Larr$local)
+; LA64PIC-NEXT:     addi.d $a2, $a1, %pc_lo12(.Larr$local)
 ; LA64-NEXT:        ld.w $a1, $a2, 0
 ; LA64-NEXT:        st.w $a0, $a2, 0
 ; LA64NOPIC-NEXT:   ld.w $a3, $a2, 36

diff  --git a/llvm/test/CodeGen/LoongArch/ir-instruction/sdiv-udiv-srem-urem.ll b/llvm/test/CodeGen/LoongArch/ir-instruction/sdiv-udiv-srem-urem.ll
index a5d9214597bae..9c94bfeeadc06 100644
--- a/llvm/test/CodeGen/LoongArch/ir-instruction/sdiv-udiv-srem-urem.ll
+++ b/llvm/test/CodeGen/LoongArch/ir-instruction/sdiv-udiv-srem-urem.ll
@@ -155,7 +155,7 @@ define i64 @sdiv_i64(i64 %a, i64 %b) {
 ; LA32-NEXT:    .cfi_def_cfa_offset 16
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-NEXT:    .cfi_offset 1, -4
-; LA32-NEXT:    bl __divdi3
+; LA32-NEXT:    bl %plt(__divdi3)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret
@@ -171,7 +171,7 @@ define i64 @sdiv_i64(i64 %a, i64 %b) {
 ; LA32-TRAP-NEXT:    .cfi_def_cfa_offset 16
 ; LA32-TRAP-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-TRAP-NEXT:    .cfi_offset 1, -4
-; LA32-TRAP-NEXT:    bl __divdi3
+; LA32-TRAP-NEXT:    bl %plt(__divdi3)
 ; LA32-TRAP-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-TRAP-NEXT:    addi.w $sp, $sp, 16
 ; LA32-TRAP-NEXT:    ret
@@ -336,7 +336,7 @@ define i64 @udiv_i64(i64 %a, i64 %b) {
 ; LA32-NEXT:    .cfi_def_cfa_offset 16
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-NEXT:    .cfi_offset 1, -4
-; LA32-NEXT:    bl __udivdi3
+; LA32-NEXT:    bl %plt(__udivdi3)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret
@@ -352,7 +352,7 @@ define i64 @udiv_i64(i64 %a, i64 %b) {
 ; LA32-TRAP-NEXT:    .cfi_def_cfa_offset 16
 ; LA32-TRAP-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-TRAP-NEXT:    .cfi_offset 1, -4
-; LA32-TRAP-NEXT:    bl __udivdi3
+; LA32-TRAP-NEXT:    bl %plt(__udivdi3)
 ; LA32-TRAP-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-TRAP-NEXT:    addi.w $sp, $sp, 16
 ; LA32-TRAP-NEXT:    ret
@@ -521,7 +521,7 @@ define i64 @srem_i64(i64 %a, i64 %b) {
 ; LA32-NEXT:    .cfi_def_cfa_offset 16
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-NEXT:    .cfi_offset 1, -4
-; LA32-NEXT:    bl __moddi3
+; LA32-NEXT:    bl %plt(__moddi3)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret
@@ -537,7 +537,7 @@ define i64 @srem_i64(i64 %a, i64 %b) {
 ; LA32-TRAP-NEXT:    .cfi_def_cfa_offset 16
 ; LA32-TRAP-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-TRAP-NEXT:    .cfi_offset 1, -4
-; LA32-TRAP-NEXT:    bl __moddi3
+; LA32-TRAP-NEXT:    bl %plt(__moddi3)
 ; LA32-TRAP-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-TRAP-NEXT:    addi.w $sp, $sp, 16
 ; LA32-TRAP-NEXT:    ret
@@ -706,7 +706,7 @@ define i64 @urem_i64(i64 %a, i64 %b) {
 ; LA32-NEXT:    .cfi_def_cfa_offset 16
 ; LA32-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-NEXT:    .cfi_offset 1, -4
-; LA32-NEXT:    bl __umoddi3
+; LA32-NEXT:    bl %plt(__umoddi3)
 ; LA32-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-NEXT:    addi.w $sp, $sp, 16
 ; LA32-NEXT:    ret
@@ -722,7 +722,7 @@ define i64 @urem_i64(i64 %a, i64 %b) {
 ; LA32-TRAP-NEXT:    .cfi_def_cfa_offset 16
 ; LA32-TRAP-NEXT:    st.w $ra, $sp, 12 # 4-byte Folded Spill
 ; LA32-TRAP-NEXT:    .cfi_offset 1, -4
-; LA32-TRAP-NEXT:    bl __umoddi3
+; LA32-TRAP-NEXT:    bl %plt(__umoddi3)
 ; LA32-TRAP-NEXT:    ld.w $ra, $sp, 12 # 4-byte Folded Reload
 ; LA32-TRAP-NEXT:    addi.w $sp, $sp, 16
 ; LA32-TRAP-NEXT:    ret

diff  --git a/llvm/test/CodeGen/LoongArch/vararg.ll b/llvm/test/CodeGen/LoongArch/vararg.ll
index 94c6f93cc24dc..90881e2aa4cbd 100644
--- a/llvm/test/CodeGen/LoongArch/vararg.ll
+++ b/llvm/test/CodeGen/LoongArch/vararg.ll
@@ -132,7 +132,7 @@ define i64 @va1_va_arg_alloca(ptr %fmt, ...) nounwind {
 ; LA64-FPELIM-NEXT:    st.d $s0, $fp, 8
 ; LA64-FPELIM-NEXT:    sub.d $a0, $sp, $a0
 ; LA64-FPELIM-NEXT:    move $sp, $a0
-; LA64-FPELIM-NEXT:    bl notdead
+; LA64-FPELIM-NEXT:    bl %plt(notdead)
 ; LA64-FPELIM-NEXT:    move $a0, $s0
 ; LA64-FPELIM-NEXT:    addi.d $sp, $fp, -32
 ; LA64-FPELIM-NEXT:    ld.d $s0, $sp, 8 # 8-byte Folded Reload
@@ -163,7 +163,7 @@ define i64 @va1_va_arg_alloca(ptr %fmt, ...) nounwind {
 ; LA64-WITHFP-NEXT:    st.d $s0, $fp, 8
 ; LA64-WITHFP-NEXT:    sub.d $a0, $sp, $a0
 ; LA64-WITHFP-NEXT:    move $sp, $a0
-; LA64-WITHFP-NEXT:    bl notdead
+; LA64-WITHFP-NEXT:    bl %plt(notdead)
 ; LA64-WITHFP-NEXT:    move $a0, $s0
 ; LA64-WITHFP-NEXT:    addi.d $sp, $fp, -32
 ; LA64-WITHFP-NEXT:    ld.d $s0, $sp, 8 # 8-byte Folded Reload
@@ -187,7 +187,7 @@ define void @va1_caller() nounwind {
 ; LA64-FPELIM-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
 ; LA64-FPELIM-NEXT:    lu52i.d $a1, $zero, 1023
 ; LA64-FPELIM-NEXT:    ori $a2, $zero, 2
-; LA64-FPELIM-NEXT:    bl va1
+; LA64-FPELIM-NEXT:    bl %plt(va1)
 ; LA64-FPELIM-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64-FPELIM-NEXT:    addi.d $sp, $sp, 16
 ; LA64-FPELIM-NEXT:    ret
@@ -200,7 +200,7 @@ define void @va1_caller() nounwind {
 ; LA64-WITHFP-NEXT:    addi.d $fp, $sp, 16
 ; LA64-WITHFP-NEXT:    lu52i.d $a1, $zero, 1023
 ; LA64-WITHFP-NEXT:    ori $a2, $zero, 2
-; LA64-WITHFP-NEXT:    bl va1
+; LA64-WITHFP-NEXT:    bl %plt(va1)
 ; LA64-WITHFP-NEXT:    ld.d $fp, $sp, 0 # 8-byte Folded Reload
 ; LA64-WITHFP-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64-WITHFP-NEXT:    addi.d $sp, $sp, 16
@@ -230,7 +230,7 @@ define void @va_aligned_register_caller() nounwind {
 ; LA64-FPELIM-NEXT:    ori $a0, $zero, 2
 ; LA64-FPELIM-NEXT:    ori $a1, $zero, 1111
 ; LA64-FPELIM-NEXT:    move $a2, $zero
-; LA64-FPELIM-NEXT:    bl va_aligned_register
+; LA64-FPELIM-NEXT:    bl %plt(va_aligned_register)
 ; LA64-FPELIM-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64-FPELIM-NEXT:    addi.d $sp, $sp, 16
 ; LA64-FPELIM-NEXT:    ret
@@ -252,7 +252,7 @@ define void @va_aligned_register_caller() nounwind {
 ; LA64-WITHFP-NEXT:    ori $a0, $zero, 2
 ; LA64-WITHFP-NEXT:    ori $a1, $zero, 1111
 ; LA64-WITHFP-NEXT:    move $a2, $zero
-; LA64-WITHFP-NEXT:    bl va_aligned_register
+; LA64-WITHFP-NEXT:    bl %plt(va_aligned_register)
 ; LA64-WITHFP-NEXT:    ld.d $fp, $sp, 0 # 8-byte Folded Reload
 ; LA64-WITHFP-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
 ; LA64-WITHFP-NEXT:    addi.d $sp, $sp, 16
@@ -302,7 +302,7 @@ define void @va_aligned_stack_caller() nounwind {
 ; LA64-FPELIM-NEXT:    ori $a0, $zero, 1
 ; LA64-FPELIM-NEXT:    move $a6, $zero
 ; LA64-FPELIM-NEXT:    move $a7, $a0
-; LA64-FPELIM-NEXT:    bl va_aligned_stack_callee
+; LA64-FPELIM-NEXT:    bl %plt(va_aligned_stack_callee)
 ; LA64-FPELIM-NEXT:    ld.d $ra, $sp, 104 # 8-byte Folded Reload
 ; LA64-FPELIM-NEXT:    addi.d $sp, $sp, 112
 ; LA64-FPELIM-NEXT:    ret
@@ -343,7 +343,7 @@ define void @va_aligned_stack_caller() nounwind {
 ; LA64-WITHFP-NEXT:    ori $a0, $zero, 1
 ; LA64-WITHFP-NEXT:    move $a6, $zero
 ; LA64-WITHFP-NEXT:    move $a7, $a0
-; LA64-WITHFP-NEXT:    bl va_aligned_stack_callee
+; LA64-WITHFP-NEXT:    bl %plt(va_aligned_stack_callee)
 ; LA64-WITHFP-NEXT:    ld.d $fp, $sp, 96 # 8-byte Folded Reload
 ; LA64-WITHFP-NEXT:    ld.d $ra, $sp, 104 # 8-byte Folded Reload
 ; LA64-WITHFP-NEXT:    addi.d $sp, $sp, 112

diff  --git a/llvm/test/MC/LoongArch/Basic/Integer/invalid.s b/llvm/test/MC/LoongArch/Basic/Integer/invalid.s
index 94b3976f5bfd0..861d382688c11 100644
--- a/llvm/test/MC/LoongArch/Basic/Integer/invalid.s
+++ b/llvm/test/MC/LoongArch/Basic/Integer/invalid.s
@@ -44,29 +44,29 @@ xori $a0, $a0, 4096
 
 ## simm12
 addi.w $a0, $a0, -2049
-# CHECK: :[[#@LINE-1]]:18: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:18: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 slti $a0, $a0, -2049
-# CHECK: :[[#@LINE-1]]:16: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 sltui $a0, $a0, 2048
-# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:17: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 preld 0, $a0, 2048
-# CHECK: :[[#@LINE-1]]:15: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:15: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 ld.b $a0, $a0, 2048
-# CHECK: :[[#@LINE-1]]:16: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 ld.h $a0, $a0, 2048
-# CHECK: :[[#@LINE-1]]:16: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 ld.w $a0, $a0, 2048
-# CHECK: :[[#@LINE-1]]:16: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 ld.bu $a0, $a0, -2049
-# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:17: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 ld.hu $a0, $a0, -2049
-# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:17: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 st.b $a0, $a0, 2048
-# CHECK: :[[#@LINE-1]]:16: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 st.h $a0, $a0, 2048
-# CHECK: :[[#@LINE-1]]:16: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 st.w $a0, $a0, -2049
-# CHECK: :[[#@LINE-1]]:16: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 
 ## simm14_lsl2
 ll.w $a0, $a0, -32772
@@ -101,8 +101,10 @@ pcaddi $a0, -0x80001
 # CHECK: :[[#@LINE-1]]:13: error: immediate must be an integer in the range [-524288, 524287]
 pcaddu12i $a0, 0x80000
 # CHECK: :[[#@LINE-1]]:16: error: immediate must be an integer in the range [-524288, 524287]
+
+## simm20_pcalau12i
 pcalau12i $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. %pc_hi20) or an integer in the range [-524288, 524287]
 
 ## simm21_lsl2
 beqz $a0, -0x400001
@@ -116,13 +118,13 @@ bnez $a0, 0x400000
 
 ## simm26_lsl2
 b -0x8000001
-# CHECK: :[[#@LINE-1]]:3: error: immediate must be a multiple of 4 in the range [-134217728, 134217724]
+# CHECK: :[[#@LINE-1]]:3: error: operand must be a bare symbol name or an immediate must be a multiple of 4 in the range [-134217728, 134217724]
 b 0x1
-# CHECK: :[[#@LINE-1]]:3: error: immediate must be a multiple of 4 in the range [-134217728, 134217724]
+# CHECK: :[[#@LINE-1]]:3: error: operand must be a bare symbol name or an immediate must be a multiple of 4 in the range [-134217728, 134217724]
 bl 0x7FFFFFF
-# CHECK: :[[#@LINE-1]]:4: error: immediate must be a multiple of 4 in the range [-134217728, 134217724]
+# CHECK: :[[#@LINE-1]]:4: error: operand must be a bare symbol name or an immediate must be a multiple of 4 in the range [-134217728, 134217724]
 bl 0x8000000
-# CHECK: :[[#@LINE-1]]:4: error: immediate must be a multiple of 4 in the range [-134217728, 134217724]
+# CHECK: :[[#@LINE-1]]:4: error: operand must be a bare symbol name or an immediate must be a multiple of 4 in the range [-134217728, 134217724]
 
 ## Invalid mnemonics
 nori $a0, $a0, 0

diff  --git a/llvm/test/MC/LoongArch/Basic/Integer/invalid64.s b/llvm/test/MC/LoongArch/Basic/Integer/invalid64.s
index a8b175a886cc3..e910a3b086ff9 100644
--- a/llvm/test/MC/LoongArch/Basic/Integer/invalid64.s
+++ b/llvm/test/MC/LoongArch/Basic/Integer/invalid64.s
@@ -31,15 +31,15 @@ bstrpick.d $a0, $a0, 64, 0
 
 ## simm12
 addi.d $a0, $a0, -2049
-# CHECK: :[[#@LINE-1]]:18: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:18: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 lu52i.d $a0, $a0, -2049
-# CHECK: :[[#@LINE-1]]:19: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:19: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 ld.wu $a0, $a0, 2048
-# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:17: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 ld.d $a0, $a0, 2048
-# CHECK: :[[#@LINE-1]]:16: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 st.d $a0, $a0, 2048
-# CHECK: :[[#@LINE-1]]:16: error: immediate must be an integer in the range [-2048, 2047]
+# CHECK: :[[#@LINE-1]]:16: error: operand must be a symbol with modifier (e.g. %pc_lo12) or an integer in the range [-2048, 2047]
 
 ## simm14_lsl2
 ldptr.w $a0, $a0, -32772

diff  --git a/llvm/test/MC/LoongArch/Relocations/relocations.s b/llvm/test/MC/LoongArch/Relocations/relocations.s
new file mode 100644
index 0000000000000..a6bc7c80471a1
--- /dev/null
+++ b/llvm/test/MC/LoongArch/Relocations/relocations.s
@@ -0,0 +1,55 @@
+# RUN: llvm-mc --triple=loongarch64 < %s --show-encoding \
+# RUN:     | FileCheck --check-prefixes=INSTR,FIXUP %s
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 < %s \
+# RUN:     | llvm-readobj -r - | FileCheck --check-prefix=RELOC %s
+
+## Check prefixes:
+## RELOC - Check the relocation in the object.
+## FIXUP - Check the fixup on the instruction.
+## INSTR - Check the instruction is handled properly by the ASMPrinter.
+
+.long foo
+# RELOC: R_LARCH_32 foo
+
+.quad foo
+# RELOC: R_LARCH_64 foo
+
+pcalau12i $t1, %pc_hi20(foo)
+# RELOC: R_LARCH_PCALA_HI20 foo 0x0
+# INSTR: pcalau12i $t1, %pc_hi20(foo)
+# FIXUP: fixup A - offset: 0, value: %pc_hi20(foo), kind: fixup_loongarch_pcala_hi20
+
+pcalau12i $t1, %pc_hi20(foo+4)
+# RELOC: R_LARCH_PCALA_HI20 foo 0x4
+# INSTR: pcalau12i $t1, %pc_hi20(foo+4)
+# FIXUP: fixup A - offset: 0, value: %pc_hi20(foo+4), kind: fixup_loongarch_pcala_hi20
+
+addi.d $t1, $t1, %pc_lo12(foo)
+# RELOC: R_LARCH_PCALA_LO12 foo 0x0
+# INSTR: addi.d  $t1, $t1, %pc_lo12(foo)
+# FIXUP: fixup A - offset: 0, value: %pc_lo12(foo), kind: fixup_loongarch_pcala_lo12
+
+addi.d $t1, $t1, %pc_lo12(foo+4)
+# RELOC: R_LARCH_PCALA_LO12 foo 0x4
+# INSTR: addi.d  $t1, $t1, %pc_lo12(foo+4)
+# FIXUP: fixup A - offset: 0, value: %pc_lo12(foo+4), kind: fixup_loongarch_pcala_lo12
+
+st.b $t1, $a2, %pc_lo12(foo)
+# RELOC: R_LARCH_PCALA_LO12 foo 0x0
+# INSTR: st.b  $t1, $a2, %pc_lo12(foo)
+# FIXUP: fixup A - offset: 0, value: %pc_lo12(foo), kind: fixup_loongarch_pcala_lo12
+
+st.b $t1, $a2, %pc_lo12(foo+4)
+# RELOC: R_LARCH_PCALA_LO12 foo 0x4
+# INSTR: st.b  $t1, $a2, %pc_lo12(foo+4)
+# FIXUP: fixup A - offset: 0, value: %pc_lo12(foo+4), kind: fixup_loongarch_pcala_lo12
+
+bl %plt(foo)
+# RELOC: R_LARCH_B26
+# INSTR: bl  %plt(foo)
+# FIXUP: fixup A - offset: 0, value: %plt(foo), kind: fixup_loongarch_b26
+
+bl foo
+# RELOC: R_LARCH_B26
+# INSTR: bl  foo
+# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_loongarch_b26

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 b2ba71c8292d7..5aef9c356c655 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,8 +121,8 @@ attributes #0 = { noredzone nounwind ssp uwtable "frame-pointer"="all" }
 ; CHECK-NEXT:    .cfi_offset 22, -8
 ; CHECK-NEXT:    addi.w $fp, $sp, 32
 ; CHECK-NEXT:    .cfi_def_cfa 22, 0
-; CHECK-NEXT:    pcalau12i $a0, x
-; CHECK-NEXT:    addi.w $a0, $a0, x
+; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(x)
+; CHECK-NEXT:    addi.w $a0, $a0, %pc_lo12(x)
 ; CHECK-NEXT:    ori $a1, $zero, 1
 ; CHECK-NEXT:    st.w $a1, $a0, 0
 ; CHECK-NEXT:    st.w $zero, $fp, -12

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 acccb9923b7d6..d816f29355d4f 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,8 +98,8 @@ define dso_local i32 @main() #0 {
 ; CHECK-NEXT:    .cfi_offset 22, -8
 ; CHECK-NEXT:    addi.w $fp, $sp, 32
 ; CHECK-NEXT:    .cfi_def_cfa 22, 0
-; CHECK-NEXT:    pcalau12i $a0, x
-; CHECK-NEXT:    addi.w $a0, $a0, x
+; CHECK-NEXT:    pcalau12i $a0, %pc_hi20(x)
+; CHECK-NEXT:    addi.w $a0, $a0, %pc_lo12(x)
 ; CHECK-NEXT:    ori $a1, $zero, 1
 ; CHECK-NEXT:    st.w $a1, $a0, 0
 ; CHECK-NEXT:    st.w $zero, $fp, -12


        


More information about the llvm-commits mailing list