[llvm] 28b6f38 - [JITLink] Add an initial implementation of JITLink for ELF/LoongArch
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jan 17 22:29:57 PST 2023
Author: wanglei
Date: 2023-01-18T14:29:45+08:00
New Revision: 28b6f38d0a841451af7b4893368543382dd5e630
URL: https://github.com/llvm/llvm-project/commit/28b6f38d0a841451af7b4893368543382dd5e630
DIFF: https://github.com/llvm/llvm-project/commit/28b6f38d0a841451af7b4893368543382dd5e630.diff
LOG: [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.
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/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..aa6247f551f0e
--- /dev/null
+++ b/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp
@@ -0,0 +1,208 @@
+//===--- 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));
+
+ // 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/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