[llvm] f10d452 - Reland "[JITLink] Add an initial implementation of JITLink for ELF/LoongArch"

via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 18 04:49:35 PST 2023


Author: wanglei
Date: 2023-01-18T20:49:20+08:00
New Revision: f10d452db841f5367f84f6db4841233365a2d03e

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

LOG: Reland "[JITLink] Add an initial implementation of JITLink for ELF/LoongArch"

This implementation supports basic relocation types and adds EHFrame,
Got/Plt handling passes.
This patch also enables JIT support for LoongArch64.

With this patch, I successfully run hello.ll and simple_throw.ll
(which is generated from test-suite/SingleSource/Regression/C++/EH/simple_throw.cpp)
using the `lli` command with options `--jit-kind=orc --jit-linker=jitlink`.

Note: `hasJIT` property of LoongArch32 remains false as there is no
validation environment.

New changes: Since LoongArch does not support RuntimeDyld, JITLink is set
by default.  Add a null-terminator to eh-frame sections. This should fix
the test failure on LoongArch bot.
(https://lab.llvm.org/staging/#/builders/236/builds/896)

Reviewed By: lhames

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

Added: 
    llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h
    llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
    llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp
    llvm/lib/ExecutionEngine/JITLink/loongarch.cpp
    llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch32_relocations.s
    llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_ehframe.s
    llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_relocations.s
    llvm/test/ExecutionEngine/JITLink/LoongArch/lit.local.cfg

Modified: 
    llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
    llvm/lib/ExecutionEngine/JITLink/ELF.cpp
    llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
    llvm/lib/Target/LoongArch/TargetInfo/LoongArchTargetInfo.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h
new file mode 100644
index 0000000000000..4d7655c4b988b
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h
@@ -0,0 +1,39 @@
+//===-- ELF_loongarch.h - JIT link functions for ELF/loongarch -*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+//===----------------------------------------------------------------------===//
+//
+// jit-link functions for ELF/loongarch.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_JITLINK_ELF_LOONGARCH_H
+#define LLVM_EXECUTIONENGINE_JITLINK_ELF_LOONGARCH_H
+
+#include "llvm/ExecutionEngine/JITLink/JITLink.h"
+
+namespace llvm {
+namespace jitlink {
+
+/// Create a LinkGraph from an ELF/loongarch relocatable object
+///
+/// Note: The graph does not take ownership of the underlying buffer, nor copy
+/// its contents. The caller is responsible for ensuring that the object buffer
+/// outlives the graph.
+Expected<std::unique_ptr<LinkGraph>>
+createLinkGraphFromELFObject_loongarch(MemoryBufferRef ObjectBuffer);
+
+/// jit-link the given object buffer, which must be an ELF loongarch object
+/// file.
+void link_ELF_loongarch(std::unique_ptr<LinkGraph> G,
+                        std::unique_ptr<JITLinkContext> Ctx);
+
+} // end namespace jitlink
+} // end namespace llvm
+
+#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_LOONGARCH_H

diff  --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
new file mode 100644
index 0000000000000..bec657723a38c
--- /dev/null
+++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h
@@ -0,0 +1,399 @@
+//= loongarch.h - Generic JITLink loongarch edge kinds, utilities -*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Generic utilities for graphs representing loongarch objects.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_EXECUTIONENGINE_JITLINK_LOONGARCH_H
+#define LLVM_EXECUTIONENGINE_JITLINK_LOONGARCH_H
+
+#include "TableManager.h"
+#include "llvm/ExecutionEngine/JITLink/JITLink.h"
+#include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h"
+
+namespace llvm {
+namespace jitlink {
+namespace loongarch {
+
+/// Represents loongarch fixups.
+enum EdgeKind_loongarch : Edge::Kind {
+  /// A plain 64-bit pointer value relocation.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- Target + Addend : uint64
+  ///
+  Pointer64 = Edge::FirstRelocation,
+
+  /// A plain 32-bit pointer value relocation.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- Target + Addend : uint32
+  ///
+  /// Errors:
+  ///   - The target must reside in the low 32-bits of the address space,
+  ///     otherwise an out-of-range error will be returned.
+  ///
+  Pointer32,
+
+  /// A 26-bit PC-relative branch.
+  ///
+  /// Represents a PC-relative call or branch to a target within +/-128Mb. The
+  /// target must be 4-byte aligned.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- (Target - Fixup + Addend) >> 2 : int26
+  ///
+  /// Notes:
+  ///   The '26' 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 28-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 int26 otherwise an
+  ///     out-of-range error will be returned.
+  ///
+  Branch26PCRel,
+
+  /// A 32-bit delta.
+  ///
+  /// Delta from the fixup to the target.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- Target - Fixup + Addend : int32
+  ///
+  /// Errors:
+  ///   - The result of the fixup expression must fit into an int32, otherwise
+  ///     an out-of-range error will be returned.
+  ///
+  Delta32,
+
+  /// A 32-bit negative delta.
+  ///
+  /// Delta from the target back to the fixup.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- Fixup - Target + Addend : int32
+  ///
+  /// Errors:
+  ///   - The result of the fixup expression must fit into an int32, otherwise
+  ///     an out-of-range error will be returned.
+  ///
+  NegDelta32,
+
+  /// A 64-bit delta.
+  ///
+  /// Delta from the fixup to the target.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- Target - Fixup + Addend : int64
+  ///
+  Delta64,
+
+  /// The signed 20-bit delta from the fixup page to the page containing the
+  /// target.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- (((Target + Addend + ((Target + Addend) & 0x800)) & ~0xfff)
+  //              - (Fixup & ~0xfff)) >> 12 : int20
+  ///
+  /// Notes:
+  ///   For PCALAU12I fixups.
+  ///
+  /// Errors:
+  ///   - The result of the fixup expression must fit into an int20 otherwise an
+  ///     out-of-range error will be returned.
+  ///
+  Page20,
+
+  /// The 12-bit offset of the target within its page.
+  ///
+  /// Typically used to fix up ADDI/LD_W/LD_D immediates.
+  ///
+  /// Fixup expression:
+  ///   Fixup <- ((Target + Addend) >> Shift) & 0xfff : int12
+  ///
+  PageOffset12,
+
+  /// A GOT entry getter/constructor, transformed to Page20 pointing at the GOT
+  /// entry for the original target.
+  ///
+  /// Indicates that this edge should be transformed into a Page20 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.
+  ///
+  RequestGOTAndTransformToPage20,
+
+  /// A GOT entry getter/constructor, transformed to Pageoffset12 pointing at
+  /// the GOT entry for the original target.
+  ///
+  /// Indicates that this edge should be transformed into a PageOffset12
+  /// 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
+  ///
+  RequestGOTAndTransformToPageOffset12,
+};
+
+/// Returns a string name for the given loongarch edge. For debugging purposes
+/// only.
+const char *getEdgeKindName(Edge::Kind K);
+
+// Returns extract bits Val[Hi:Lo].
+inline uint32_t extractBits(uint32_t Val, unsigned Hi, unsigned Lo) {
+  return (Val & (((1UL << (Hi + 1)) - 1))) >> Lo;
+}
+
+/// Apply fixup expression for edge to block content.
+inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
+  using namespace support;
+
+  char *BlockWorkingMem = B.getAlreadyMutableContent().data();
+  char *FixupPtr = BlockWorkingMem + E.getOffset();
+  uint64_t FixupAddress = (B.getAddress() + E.getOffset()).getValue();
+  uint64_t TargetAddress = E.getTarget().getAddress().getValue();
+  int64_t Addend = E.getAddend();
+
+  switch (E.getKind()) {
+  case Pointer64:
+    *(ulittle64_t *)FixupPtr = TargetAddress + Addend;
+    break;
+  case Pointer32: {
+    uint64_t Value = TargetAddress + Addend;
+    if (Value > std::numeric_limits<uint32_t>::max())
+      return makeTargetOutOfRangeError(G, B, E);
+    *(ulittle32_t *)FixupPtr = Value;
+    break;
+  }
+  case Branch26PCRel: {
+    int64_t Value = TargetAddress - FixupAddress + Addend;
+
+    if (!isInt<28>(Value))
+      return makeTargetOutOfRangeError(G, B, E);
+
+    if (!isShiftedInt<26, 2>(Value))
+      return makeAlignmentError(orc::ExecutorAddr(FixupAddress), Value, 4, E);
+
+    uint32_t RawInstr = *(little32_t *)FixupPtr;
+    uint32_t Imm = static_cast<uint32_t>(Value >> 2);
+    uint32_t Imm15_0 = extractBits(Imm, /*Hi=*/15, /*Lo=*/0) << 10;
+    uint32_t Imm25_16 = extractBits(Imm, /*Hi=*/25, /*Lo=*/16);
+    *(little32_t *)FixupPtr = RawInstr | Imm15_0 | Imm25_16;
+    break;
+  }
+  case Delta32: {
+    int64_t Value = TargetAddress - FixupAddress + Addend;
+
+    if (!isInt<32>(Value))
+      return makeTargetOutOfRangeError(G, B, E);
+    *(little32_t *)FixupPtr = Value;
+    break;
+  }
+  case NegDelta32: {
+    int64_t Value = FixupAddress - TargetAddress + Addend;
+    if (!isInt<32>(Value))
+      return makeTargetOutOfRangeError(G, B, E);
+    *(little32_t *)FixupPtr = Value;
+    break;
+  }
+  case Delta64:
+    *(little64_t *)FixupPtr = TargetAddress - FixupAddress + Addend;
+    break;
+  case Page20: {
+    uint64_t Target = TargetAddress + Addend;
+    uint64_t TargetPage =
+        (Target + (Target & 0x800)) & ~static_cast<uint64_t>(0xfff);
+    uint64_t PCPage = FixupAddress & ~static_cast<uint64_t>(0xfff);
+
+    int64_t PageDelta = TargetPage - PCPage;
+    if (!isInt<32>(PageDelta))
+      return makeTargetOutOfRangeError(G, B, E);
+
+    uint32_t RawInstr = *(little32_t *)FixupPtr;
+    uint32_t Imm31_12 = extractBits(PageDelta, /*Hi=*/31, /*Lo=*/12) << 5;
+    *(little32_t *)FixupPtr = RawInstr | Imm31_12;
+    break;
+  }
+  case PageOffset12: {
+    uint64_t TargetOffset = (TargetAddress + Addend) & 0xfff;
+
+    uint32_t RawInstr = *(ulittle32_t *)FixupPtr;
+    uint32_t Imm11_0 = TargetOffset << 10;
+    *(ulittle32_t *)FixupPtr = RawInstr | Imm11_0;
+    break;
+  }
+  default:
+    return make_error<JITLinkError>(
+        "In graph " + G.getName() + ", section " + B.getSection().getName() +
+        " unsupported edge kind " + getEdgeKindName(E.getKind()));
+  }
+
+  return Error::success();
+}
+
+/// loongarch null pointer content.
+extern const char NullPointerContent[8];
+inline ArrayRef<char> getGOTEntryBlockContent(LinkGraph &G) {
+  return {reinterpret_cast<const char *>(NullPointerContent),
+          G.getPointerSize()};
+}
+
+/// loongarch stub content.
+///
+/// Contains the instruction sequence for an indirect jump via an in-memory
+/// pointer:
+///   pcalau12i $t8, %page20(ptr)
+///   ld.[w/d]  $t8, %pageoff12(ptr)
+///   jr        $t8
+constexpr size_t StubEntrySize = 12;
+extern const uint8_t LA64StubContent[StubEntrySize];
+extern const uint8_t LA32StubContent[StubEntrySize];
+inline ArrayRef<char> getStubBlockContent(LinkGraph &G) {
+  auto StubContent =
+      G.getPointerSize() == 8 ? LA64StubContent : LA32StubContent;
+  return {reinterpret_cast<const char *>(StubContent), StubEntrySize};
+}
+
+/// Creates a new pointer block in the given section and returns an
+/// Anonymous symobl pointing to it.
+///
+/// If InitialTarget is given then an Pointer64 relocation will be added to the
+/// block pointing at InitialTarget.
+///
+/// The pointer block will have the following default values:
+///   alignment: PointerSize
+///   alignment-offset: 0
+inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
+                                      Symbol *InitialTarget = nullptr,
+                                      uint64_t InitialAddend = 0) {
+  auto &B = G.createContentBlock(PointerSection, getGOTEntryBlockContent(G),
+                                 orc::ExecutorAddr(), G.getPointerSize(), 0);
+  if (InitialTarget)
+    B.addEdge(G.getPointerSize() == 8 ? Pointer64 : Pointer32, 0,
+              *InitialTarget, InitialAddend);
+  return G.addAnonymousSymbol(B, 0, G.getPointerSize(), false, false);
+}
+
+/// Create a jump stub that jumps via the pointer at the given symbol and
+/// an anonymous symbol pointing to it. Return the anonymous symbol.
+inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
+                                              Section &StubSection,
+                                              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);
+}
+
+/// Global Offset Table Builder.
+class GOTTableManager : public TableManager<GOTTableManager> {
+public:
+  static StringRef getSectionName() { return "$__GOT"; }
+
+  bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
+    Edge::Kind KindToSet = Edge::Invalid;
+    switch (E.getKind()) {
+    case RequestGOTAndTransformToPage20:
+      KindToSet = Page20;
+      break;
+    case RequestGOTAndTransformToPageOffset12:
+      KindToSet = PageOffset12;
+      break;
+    default:
+      return false;
+    }
+    assert(KindToSet != Edge::Invalid &&
+           "Fell through switch, but no new kind to set");
+    DEBUG_WITH_TYPE("jitlink", {
+      dbgs() << "  Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
+             << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
+             << formatv("{0:x}", E.getOffset()) << ")\n";
+    });
+    E.setKind(KindToSet);
+    E.setTarget(getEntryForTarget(G, E.getTarget()));
+    return true;
+  }
+
+  Symbol &createEntry(LinkGraph &G, Symbol &Target) {
+    return createAnonymousPointer(G, getGOTSection(G), &Target);
+  }
+
+private:
+  Section &getGOTSection(LinkGraph &G) {
+    if (!GOTSection)
+      GOTSection = &G.createSection(getSectionName(),
+                                    orc::MemProt::Read | orc::MemProt::Exec);
+    return *GOTSection;
+  }
+
+  Section *GOTSection = nullptr;
+};
+
+/// Procedure Linkage Table Builder.
+class PLTTableManager : public TableManager<PLTTableManager> {
+public:
+  PLTTableManager(GOTTableManager &GOT) : GOT(GOT) {}
+
+  static StringRef getSectionName() { return "$__STUBS"; }
+
+  bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
+    if (E.getKind() == Branch26PCRel && !E.getTarget().isDefined()) {
+      DEBUG_WITH_TYPE("jitlink", {
+        dbgs() << "  Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
+               << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
+               << formatv("{0:x}", E.getOffset()) << ")\n";
+      });
+      E.setTarget(getEntryForTarget(G, E.getTarget()));
+      return true;
+    }
+    return false;
+  }
+
+  Symbol &createEntry(LinkGraph &G, Symbol &Target) {
+    return createAnonymousPointerJumpStub(G, getStubsSection(G),
+                                          GOT.getEntryForTarget(G, Target));
+  }
+
+public:
+  Section &getStubsSection(LinkGraph &G) {
+    if (!StubsSection)
+      StubsSection = &G.createSection(getSectionName(),
+                                      orc::MemProt::Read | orc::MemProt::Exec);
+    return *StubsSection;
+  }
+
+  GOTTableManager &GOT;
+  Section *StubsSection = nullptr;
+};
+
+} // namespace loongarch
+} // namespace jitlink
+} // namespace llvm
+
+#endif

diff  --git a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
index 64a0df57a03d9..52ff5e8370031 100644
--- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
+++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt
@@ -22,6 +22,7 @@ add_llvm_component_library(LLVMJITLink
   ELFLinkGraphBuilder.cpp
   ELF_aarch64.cpp
   ELF_i386.cpp
+  ELF_loongarch.cpp
   ELF_riscv.cpp
   ELF_x86_64.cpp
 
@@ -34,6 +35,7 @@ add_llvm_component_library(LLVMJITLink
   # Architectures:
   aarch64.cpp
   i386.cpp
+  loongarch.cpp
   riscv.cpp
   x86_64.cpp
 

diff  --git a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
index 9d55e6e2bf965..ef0f19a785712 100644
--- a/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF.cpp
@@ -15,6 +15,7 @@
 #include "llvm/BinaryFormat/ELF.h"
 #include "llvm/ExecutionEngine/JITLink/ELF_aarch64.h"
 #include "llvm/ExecutionEngine/JITLink/ELF_i386.h"
+#include "llvm/ExecutionEngine/JITLink/ELF_loongarch.h"
 #include "llvm/ExecutionEngine/JITLink/ELF_riscv.h"
 #include "llvm/ExecutionEngine/JITLink/ELF_x86_64.h"
 #include "llvm/Object/ELF.h"
@@ -68,6 +69,8 @@ createLinkGraphFromELFObject(MemoryBufferRef ObjectBuffer) {
   switch (*TargetMachineArch) {
   case ELF::EM_AARCH64:
     return createLinkGraphFromELFObject_aarch64(ObjectBuffer);
+  case ELF::EM_LOONGARCH:
+    return createLinkGraphFromELFObject_loongarch(ObjectBuffer);
   case ELF::EM_RISCV:
     return createLinkGraphFromELFObject_riscv(ObjectBuffer);
   case ELF::EM_X86_64:
@@ -87,6 +90,10 @@ void link_ELF(std::unique_ptr<LinkGraph> G,
   case Triple::aarch64:
     link_ELF_aarch64(std::move(G), std::move(Ctx));
     return;
+  case Triple::loongarch32:
+  case Triple::loongarch64:
+    link_ELF_loongarch(std::move(G), std::move(Ctx));
+    return;
   case Triple::riscv32:
   case Triple::riscv64:
     link_ELF_riscv(std::move(G), std::move(Ctx));

diff  --git a/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp
new file mode 100644
index 0000000000000..cd70217b4c0ad
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp
@@ -0,0 +1,209 @@
+//===--- ELF_loongarch.cpp - JIT linker implementation for ELF/loongarch --===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// ELF/loongarch jit-link implementation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/JITLink/ELF_loongarch.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h"
+#include "llvm/ExecutionEngine/JITLink/JITLink.h"
+#include "llvm/ExecutionEngine/JITLink/loongarch.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Object/ELFObjectFile.h"
+
+#include "EHFrameSupportImpl.h"
+#include "ELFLinkGraphBuilder.h"
+#include "JITLinkGeneric.h"
+
+#define DEBUG_TYPE "jitlink"
+
+using namespace llvm;
+using namespace llvm::jitlink;
+using namespace llvm::jitlink::loongarch;
+
+namespace {
+
+class ELFJITLinker_loongarch : public JITLinker<ELFJITLinker_loongarch> {
+  friend class JITLinker<ELFJITLinker_loongarch>;
+
+public:
+  ELFJITLinker_loongarch(std::unique_ptr<JITLinkContext> Ctx,
+                         std::unique_ptr<LinkGraph> G,
+                         PassConfiguration PassConfig)
+      : JITLinker(std::move(Ctx), std::move(G), std::move(PassConfig)) {}
+
+private:
+  Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const {
+    return loongarch::applyFixup(G, B, E);
+  }
+};
+
+template <typename ELFT>
+class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
+private:
+  static Expected<loongarch::EdgeKind_loongarch>
+  getRelocationKind(const uint32_t Type) {
+    using namespace loongarch;
+    switch (Type) {
+    case ELF::R_LARCH_64:
+      return Pointer64;
+    case ELF::R_LARCH_32:
+      return Pointer32;
+    case ELF::R_LARCH_32_PCREL:
+      return Delta32;
+    case ELF::R_LARCH_B26:
+      return Branch26PCRel;
+    case ELF::R_LARCH_PCALA_HI20:
+      return Page20;
+    case ELF::R_LARCH_PCALA_LO12:
+      return PageOffset12;
+    case ELF::R_LARCH_GOT_PC_HI20:
+      return RequestGOTAndTransformToPage20;
+    case ELF::R_LARCH_GOT_PC_LO12:
+      return RequestGOTAndTransformToPageOffset12;
+    }
+
+    return make_error<JITLinkError>(
+        "Unsupported loongarch relocation:" + formatv("{0:d}: ", Type) +
+        object::getELFRelocationTypeName(ELF::EM_LOONGARCH, Type));
+  }
+
+  Error addRelocations() override {
+    LLVM_DEBUG(dbgs() << "Processing relocations:\n");
+
+    using Base = ELFLinkGraphBuilder<ELFT>;
+    using Self = ELFLinkGraphBuilder_loongarch<ELFT>;
+    for (const auto &RelSect : Base::Sections)
+      if (Error Err = Base::forEachRelaRelocation(RelSect, this,
+                                                  &Self::addSingleRelocation))
+        return Err;
+
+    return Error::success();
+  }
+
+  Error addSingleRelocation(const typename ELFT::Rela &Rel,
+                            const typename ELFT::Shdr &FixupSect,
+                            Block &BlockToFix) {
+    using Base = ELFLinkGraphBuilder<ELFT>;
+
+    uint32_t SymbolIndex = Rel.getSymbol(false);
+    auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec);
+    if (!ObjSymbol)
+      return ObjSymbol.takeError();
+
+    Symbol *GraphSymbol = Base::getGraphSymbol(SymbolIndex);
+    if (!GraphSymbol)
+      return make_error<StringError>(
+          formatv("Could not find symbol at given index, did you add it to "
+                  "JITSymbolTable? index: {0}, shndx: {1} Size of table: {2}",
+                  SymbolIndex, (*ObjSymbol)->st_shndx,
+                  Base::GraphSymbols.size()),
+          inconvertibleErrorCode());
+
+    uint32_t Type = Rel.getType(false);
+    Expected<loongarch::EdgeKind_loongarch> Kind = getRelocationKind(Type);
+    if (!Kind)
+      return Kind.takeError();
+
+    int64_t Addend = Rel.r_addend;
+    auto FixupAddress = orc::ExecutorAddr(FixupSect.sh_addr) + Rel.r_offset;
+    Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress();
+    Edge GE(*Kind, Offset, *GraphSymbol, Addend);
+    LLVM_DEBUG({
+      dbgs() << "    ";
+      printEdge(dbgs(), BlockToFix, GE, loongarch::getEdgeKindName(*Kind));
+      dbgs() << "\n";
+    });
+
+    BlockToFix.addEdge(std::move(GE));
+
+    return Error::success();
+  }
+
+public:
+  ELFLinkGraphBuilder_loongarch(StringRef FileName,
+                                const object::ELFFile<ELFT> &Obj,
+                                const Triple T)
+      : ELFLinkGraphBuilder<ELFT>(Obj, std::move(T), FileName,
+                                  loongarch::getEdgeKindName) {}
+};
+
+Error buildTables_ELF_loongarch(LinkGraph &G) {
+  LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n");
+
+  GOTTableManager GOT;
+  PLTTableManager PLT(GOT);
+  visitExistingEdges(G, GOT, PLT);
+  return Error::success();
+}
+
+} // namespace
+
+namespace llvm {
+namespace jitlink {
+
+Expected<std::unique_ptr<LinkGraph>>
+createLinkGraphFromELFObject_loongarch(MemoryBufferRef ObjectBuffer) {
+  LLVM_DEBUG({
+    dbgs() << "Building jitlink graph for new input "
+           << ObjectBuffer.getBufferIdentifier() << "...\n";
+  });
+
+  auto ELFObj = object::ObjectFile::createELFObjectFile(ObjectBuffer);
+  if (!ELFObj)
+    return ELFObj.takeError();
+
+  if ((*ELFObj)->getArch() == Triple::loongarch64) {
+    auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF64LE>>(**ELFObj);
+    return ELFLinkGraphBuilder_loongarch<object::ELF64LE>(
+               (*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
+               (*ELFObj)->makeTriple())
+        .buildGraph();
+  }
+
+  assert((*ELFObj)->getArch() == Triple::loongarch32 &&
+         "Invalid triple for LoongArch ELF object file");
+  auto &ELFObjFile = cast<object::ELFObjectFile<object::ELF32LE>>(**ELFObj);
+  return ELFLinkGraphBuilder_loongarch<object::ELF32LE>(
+             (*ELFObj)->getFileName(), ELFObjFile.getELFFile(),
+             (*ELFObj)->makeTriple())
+      .buildGraph();
+}
+
+void link_ELF_loongarch(std::unique_ptr<LinkGraph> G,
+                        std::unique_ptr<JITLinkContext> Ctx) {
+  PassConfiguration Config;
+  const Triple &TT = G->getTargetTriple();
+  if (Ctx->shouldAddDefaultTargetPasses(TT)) {
+    // Add eh-frame passses.
+    Config.PrePrunePasses.push_back(DWARFRecordSectionSplitter(".eh_frame"));
+    Config.PrePrunePasses.push_back(
+        EHFrameEdgeFixer(".eh_frame", G->getPointerSize(), Pointer32, Pointer64,
+                         Delta32, Delta64, NegDelta32));
+    Config.PrePrunePasses.push_back(EHFrameNullTerminator(".eh_frame"));
+
+    // Add a mark-live pass.
+    if (auto MarkLive = Ctx->getMarkLivePass(TT))
+      Config.PrePrunePasses.push_back(std::move(MarkLive));
+    else
+      Config.PrePrunePasses.push_back(markAllSymbolsLive);
+
+    // Add an in-place GOT/PLTStubs build pass.
+    Config.PostPrunePasses.push_back(buildTables_ELF_loongarch);
+  }
+
+  if (auto Err = Ctx->modifyPassConfig(*G, Config))
+    return Ctx->notifyFailed(std::move(Err));
+
+  ELFJITLinker_loongarch::link(std::move(Ctx), std::move(G), std::move(Config));
+}
+
+} // namespace jitlink
+} // namespace llvm

diff  --git a/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp b/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp
new file mode 100644
index 0000000000000..d1e44ec187cc8
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp
@@ -0,0 +1,60 @@
+//===--- loongarch.cpp - Generic JITLink loongarch edge kinds, utilities --===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Generic utilities for graphs representing loongarch objects.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ExecutionEngine/JITLink/loongarch.h"
+
+#define DEBUG_TYPE "jitlink"
+
+namespace llvm {
+namespace jitlink {
+namespace loongarch {
+
+const char NullPointerContent[8] = {0x00, 0x00, 0x00, 0x00,
+                                    0x00, 0x00, 0x00, 0x00};
+
+const uint8_t LA64StubContent[StubEntrySize] = {
+    0x14, 0x00, 0x00, 0x1a, // pcalau12i $t8, %page20(imm)
+    0x94, 0x02, 0xc0, 0x28, // ld.d $t8, $t8, %pageoff12(imm)
+    0x80, 0x02, 0x00, 0x4c  // jr $t8
+};
+
+const uint8_t LA32StubContent[StubEntrySize] = {
+    0x14, 0x00, 0x00, 0x1a, // pcalau12i $t8, %page20(imm)
+    0x94, 0x02, 0x80, 0x28, // ld.w $t8, $t8, %pageoff12(imm)
+    0x80, 0x02, 0x00, 0x4c  // jr $t8
+};
+
+const char *getEdgeKindName(Edge::Kind K) {
+#define KIND_NAME_CASE(K)                                                      \
+  case K:                                                                      \
+    return #K;
+
+  switch (K) {
+    KIND_NAME_CASE(Pointer64)
+    KIND_NAME_CASE(Pointer32)
+    KIND_NAME_CASE(Delta32)
+    KIND_NAME_CASE(NegDelta32)
+    KIND_NAME_CASE(Delta64)
+    KIND_NAME_CASE(Branch26PCRel)
+    KIND_NAME_CASE(Page20)
+    KIND_NAME_CASE(PageOffset12)
+    KIND_NAME_CASE(RequestGOTAndTransformToPage20)
+    KIND_NAME_CASE(RequestGOTAndTransformToPageOffset12)
+  default:
+    return getGenericEdgeKindName(K);
+  }
+#undef KIND_NAME_CASE
+}
+
+} // namespace loongarch
+} // namespace jitlink
+} // namespace llvm

diff  --git a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
index b41cdd3a8e93a..bc84988e32542 100644
--- a/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
+++ b/llvm/lib/ExecutionEngine/Orc/LLJIT.cpp
@@ -716,6 +716,7 @@ Error LLJITBuilderState::prepareForConstruction() {
   if (!CreateObjectLinkingLayer) {
     auto &TT = JTMB->getTargetTriple();
     if (TT.getArch() == Triple::riscv64 ||
+        TT.getArch() == Triple::loongarch64 ||
         (TT.isOSBinFormatMachO() &&
          (TT.getArch() == Triple::aarch64 || TT.getArch() == Triple::x86_64))) {
 

diff  --git a/llvm/lib/Target/LoongArch/TargetInfo/LoongArchTargetInfo.cpp b/llvm/lib/Target/LoongArch/TargetInfo/LoongArchTargetInfo.cpp
index 10654510032f0..1d6be4069b71e 100644
--- a/llvm/lib/Target/LoongArch/TargetInfo/LoongArchTargetInfo.cpp
+++ b/llvm/lib/Target/LoongArch/TargetInfo/LoongArchTargetInfo.cpp
@@ -24,7 +24,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeLoongArchTargetInfo() {
   RegisterTarget<Triple::loongarch32, /*HasJIT=*/false> X(
       getTheLoongArch32Target(), "loongarch32", "32-bit LoongArch",
       "LoongArch");
-  RegisterTarget<Triple::loongarch64, /*HasJIT=*/false> Y(
+  RegisterTarget<Triple::loongarch64, /*HasJIT=*/true> Y(
       getTheLoongArch64Target(), "loongarch64", "64-bit LoongArch",
       "LoongArch");
 }

diff  --git a/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch32_relocations.s b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch32_relocations.s
new file mode 100644
index 0000000000000..23f6acc307b98
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch32_relocations.s
@@ -0,0 +1,113 @@
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc --triple=loongarch32 --filetype=obj -o %t/elf_reloc.o %s
+# RUN: llvm-jitlink --noexec \
+# RUN:              --abs external_data=0xdeadbeef \
+# RUN:              --abs external_func=0xcafef00d \
+# RUN:              --check %s %t/elf_reloc.o
+    .text
+
+    .globl main
+    .p2align 2
+    .type main, at function
+main:
+    ret
+
+    .size main, .-main
+
+## Check R_LARCH_B26 relocation of a local function call.
+
+# jitlink-check: decode_operand(local_func_call26, 0)[27:0] = \
+# jitlink-check:   (local_func - local_func_call26)[27:0]
+# jitlink-check: decode_operand(local_func_jump26, 0)[27:0] = \
+# jitlink-check:   (local_func - local_func_jump26)[27:0]
+    .globl local_func
+    .p2align 2
+    .type local_func, at function
+local_func:
+    ret
+    .size local_func, .-local_func
+
+    .globl local_func_call26
+    .p2align 2
+local_func_call26:
+    bl local_func
+    .size local_func_call26, .-local_func_call26
+
+    .globl local_func_jump26
+    .p2align 2
+local_func_jump26:
+    b local_func
+    .size local_func_jump26, .-local_func_jump26
+
+## 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:      named_data[11:11]
+# jitlink-check: decode_operand(test_addi_pcrel_lo12, 2)[11:0] = \
+# jitlink-check:   (named_data)[11:0]
+    .globl test_pcalau12i_pcrel
+    .p2align 2
+test_pcalau12i_pcrel:
+    pcalau12i $a0, %pc_hi20(named_data)
+    .size test_pcalau12i_pcrel, .-test_pcalau12i_pcrel
+
+    .globl test_addi_pcrel_lo12
+    .p2align 2
+test_addi_pcrel_lo12:
+    addi.w $a0, $a0, %pc_lo12(named_data)
+    .size test_addi_pcrel_lo12, .-test_addi_pcrel_lo12
+
+## Check that calls/jumps to external functions trigger the generation of stubs
+## and GOT entries.
+
+# jitlink-check: *{4}(got_addr(elf_reloc.o, external_func)) = external_func
+# jitlink-check: decode_operand(test_external_call, 0) = \
+# jitlink-check:   (stub_addr(elf_reloc.o, external_func) - \
+# jitlink-check:      test_external_call)[27:0]
+# jitlink-check: decode_operand(test_external_jump, 0) = \
+# jitlink-check:   (stub_addr(elf_reloc.o, external_func) - \
+# jitlink-check:      test_external_jump)[27:0]
+    .globl test_external_call
+    .p2align  2
+test_external_call:
+    bl external_func
+    .size test_external_call, .-test_external_call
+
+    .globl test_external_jump
+    .p2align 2
+test_external_jump:
+    b external_func
+    .size test_external_jump, .-test_external_jump
+
+## 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:   (got_addr(elf_reloc.o, external_data)[31:12] - \
+# jitlink-check:      test_gotpage_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:   got_addr(elf_reloc.o, external_data)[11:0]
+    .globl test_gotpage_external
+    .p2align 2
+test_gotpage_external:
+    pcalau12i $a0, %got_pc_hi20(external_data)
+    .size test_gotpage_external, .-test_gotpage_external
+
+    .globl test_gotoffset12_external
+    .p2align 2
+test_gotoffset12_external:
+    ld.w $a0, $a0, %got_pc_lo12(external_data)
+    .size test_gotoffset12_external, .-test_gotoffset12_external
+
+
+    .globl named_data
+    .p2align 4
+    .type named_data, at object
+named_data:
+    .quad 0x2222222222222222
+    .quad 0x3333333333333333
+    .size named_data, .-named_data

diff  --git a/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_ehframe.s b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_ehframe.s
new file mode 100644
index 0000000000000..4a1a1649508eb
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_ehframe.s
@@ -0,0 +1,72 @@
+# REQUIRES: asserts
+# RUN: llvm-mc --triple=loongarch64-linux-gnu --filetype=obj -o %t %s
+# RUN: llvm-jitlink --noexec --phony-externals --debug-only=jitlink %t 2>&1 | \
+# RUN:   FileCheck %s
+
+## Check that splitting of eh-frame sections works.
+
+# CHECK: DWARFRecordSectionSplitter: Processing .eh_frame...
+# CHECK:  Processing block at
+# CHECK:    Processing CFI record at
+# CHECK:      Extracted {{.*}} section = .eh_frame
+# CHECK:    Processing CFI record at
+# CHECK:      Extracted {{.*}} section = .eh_frame
+# CHECK: EHFrameEdgeFixer: Processing .eh_frame in "{{.*}}"...
+# CHECK:   Processing block at
+# CHECK:     Processing CFI record at
+# CHECK:       Record is CIE
+# CHECK:   Processing block at
+# CHECK:     Processing CFI record at
+# CHECK:       Record is FDE
+# CHECK:         Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK:         Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK:         Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+# CHECK:   Processing block at
+# CHECK:     Processing CFI record at
+# CHECK:       Record is FDE
+# CHECK:         Adding edge at {{.*}} to CIE at: {{.*}}
+# CHECK:         Existing edge at {{.*}} to PC begin at {{.*}}
+# CHECK:         Adding keep-alive edge from target at {{.*}} to FDE at {{.*}}
+
+ .text
+ .globl main
+ .p2align 2
+ .type main, at function
+main:
+ .cfi_startproc
+ addi.d $sp, $sp, -16
+ .cfi_def_cfa_offset 16
+ st.d $ra, $sp, 8
+ .cfi_offset 1, -8
+ ori $a0, $zero, 4
+ bl %plt(__cxa_allocate_exception)
+ ori $a1, $zero, 5
+ st.w $a1, $a0, 0
+ pcalau12i $a1, %got_pc_hi20(_ZTIi)
+ ld.d $a1, $a1, %got_pc_lo12(_ZTIi)
+ move $a2, $zero
+ bl %plt(__cxa_throw)
+.main_end:
+ .size main, .main_end-main
+ .cfi_endproc
+
+ .globl dup
+ .p2align 2
+ .type main, at function
+dup:
+ .cfi_startproc
+ addi.d $sp, $sp, -16
+ .cfi_def_cfa_offset 16
+ st.d $ra, $sp, 8
+ .cfi_offset 1, -8
+ ori $a0, $zero, 4
+ bl %plt(__cxa_allocate_exception)
+ ori $a1, $zero, 5
+ st.w $a1, $a0, 0
+ pcalau12i $a1, %got_pc_hi20(_ZTIi)
+ ld.d $a1, $a1, %got_pc_lo12(_ZTIi)
+ move $a2, $zero
+ bl %plt(__cxa_throw)
+.dup_end:
+ .size main, .dup_end-dup
+ .cfi_endproc

diff  --git a/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_relocations.s b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_relocations.s
new file mode 100644
index 0000000000000..74eb8118d10e3
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_loongarch64_relocations.s
@@ -0,0 +1,113 @@
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: llvm-mc --triple=loongarch64 --filetype=obj -o %t/elf_reloc.o %s
+# RUN: llvm-jitlink --noexec \
+# RUN:              --abs external_data=0xdeadbeef \
+# RUN:              --abs external_func=0xcafef00d \
+# RUN:              --check %s %t/elf_reloc.o
+    .text
+
+    .globl main
+    .p2align 2
+    .type main, at function
+main:
+    ret
+
+    .size main, .-main
+
+## Check R_LARCH_B26 relocation of a local function call.
+
+# jitlink-check: decode_operand(local_func_call26, 0)[27:0] = \
+# jitlink-check:   (local_func - local_func_call26)[27:0]
+# jitlink-check: decode_operand(local_func_jump26, 0)[27:0] = \
+# jitlink-check:   (local_func - local_func_jump26)[27:0]
+    .globl local_func
+    .p2align 2
+    .type local_func, at function
+local_func:
+    ret
+    .size local_func, .-local_func
+
+    .globl local_func_call26
+    .p2align 2
+local_func_call26:
+    bl local_func
+    .size local_func_call26, .-local_func_call26
+
+    .globl local_func_jump26
+    .p2align 2
+local_func_jump26:
+    b local_func
+    .size local_func_jump26, .-local_func_jump26
+
+## 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:      named_data[11:11]
+# jitlink-check: decode_operand(test_addi_pcrel_lo12, 2)[11:0] = \
+# jitlink-check:   (named_data)[11:0]
+    .globl test_pcalau12i_pcrel
+    .p2align 2
+test_pcalau12i_pcrel:
+    pcalau12i $a0, %pc_hi20(named_data)
+    .size test_pcalau12i_pcrel, .-test_pcalau12i_pcrel
+
+    .globl test_addi_pcrel_lo12
+    .p2align 2
+test_addi_pcrel_lo12:
+    addi.d $a0, $a0, %pc_lo12(named_data)
+    .size test_addi_pcrel_lo12, .-test_addi_pcrel_lo12
+
+## Check that calls/jumps to external functions trigger the generation of stubs
+## and GOT entries.
+
+# jitlink-check: *{8}(got_addr(elf_reloc.o, external_func)) = external_func
+# jitlink-check: decode_operand(test_external_call, 0) = \
+# jitlink-check:   (stub_addr(elf_reloc.o, external_func) - \
+# jitlink-check:      test_external_call)[27:0]
+# jitlink-check: decode_operand(test_external_jump, 0) = \
+# jitlink-check:   (stub_addr(elf_reloc.o, external_func) - \
+# jitlink-check:      test_external_jump)[27:0]
+    .globl test_external_call
+    .p2align  2
+test_external_call:
+    bl external_func
+    .size test_external_call, .-test_external_call
+
+    .globl test_external_jump
+    .p2align 2
+test_external_jump:
+    b external_func
+    .size test_external_jump, .-test_external_jump
+
+## 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: *{8}(got_addr(elf_reloc.o, external_data)) = external_data
+# jitlink-check: decode_operand(test_gotpage_external, 1)[19:0] = \
+# jitlink-check:   (got_addr(elf_reloc.o, external_data)[31:12] - \
+# jitlink-check:      test_gotpage_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:   got_addr(elf_reloc.o, external_data)[11:0]
+    .globl test_gotpage_external
+    .p2align 2
+test_gotpage_external:
+    pcalau12i $a0, %got_pc_hi20(external_data)
+    .size test_gotpage_external, .-test_gotpage_external
+
+    .globl test_gotoffset12_external
+    .p2align 2
+test_gotoffset12_external:
+    ld.d $a0, $a0, %got_pc_lo12(external_data)
+    .size test_gotoffset12_external, .-test_gotoffset12_external
+
+
+    .globl named_data
+    .p2align 4
+    .type named_data, at object
+named_data:
+    .quad 0x2222222222222222
+    .quad 0x3333333333333333
+    .size named_data, .-named_data

diff  --git a/llvm/test/ExecutionEngine/JITLink/LoongArch/lit.local.cfg b/llvm/test/ExecutionEngine/JITLink/LoongArch/lit.local.cfg
new file mode 100644
index 0000000000000..a4b6c6f59bdbd
--- /dev/null
+++ b/llvm/test/ExecutionEngine/JITLink/LoongArch/lit.local.cfg
@@ -0,0 +1,2 @@
+if not 'LoongArch' in config.root.targets:
+  config.unsupported = True


        


More information about the llvm-commits mailing list