diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h new file mode 100644 --- /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> +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 G, + std::unique_ptr 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 --- /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::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(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(0xfff); + uint64_t PCPage = FixupAddress & ~static_cast(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( + "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 getGOTEntryBlockContent(LinkGraph &G) { + return {reinterpret_cast(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 getStubBlockContent(LinkGraph &G) { + auto StubContent = + G.getPointerSize() == 8 ? LA64StubContent : LA32StubContent; + return {reinterpret_cast(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 { +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 { +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 --- a/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt +++ b/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt @@ -22,6 +22,7 @@ ELFLinkGraphBuilder.cpp ELF_aarch64.cpp ELF_i386.cpp + ELF_loongarch.cpp ELF_riscv.cpp ELF_x86_64.cpp @@ -34,6 +35,7 @@ # 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 --- 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 @@ 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 @@ 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 --- /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 { + friend class JITLinker; + +public: + ELFJITLinker_loongarch(std::unique_ptr Ctx, + std::unique_ptr 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 +class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder { +private: + static Expected + 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( + "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; + using Self = ELFLinkGraphBuilder_loongarch; + 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; + + 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( + 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 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 &Obj, + const Triple T) + : ELFLinkGraphBuilder(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> +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>(**ELFObj); + return ELFLinkGraphBuilder_loongarch( + (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), + (*ELFObj)->makeTriple()) + .buildGraph(); + } + + assert((*ELFObj)->getArch() == Triple::loongarch32 && + "Invalid triple for LoongArch ELF object file"); + auto &ELFObjFile = cast>(**ELFObj); + return ELFLinkGraphBuilder_loongarch( + (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), + (*ELFObj)->makeTriple()) + .buildGraph(); +} + +void link_ELF_loongarch(std::unique_ptr G, + std::unique_ptr 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 --- /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 --- a/llvm/lib/Target/LoongArch/TargetInfo/LoongArchTargetInfo.cpp +++ b/llvm/lib/Target/LoongArch/TargetInfo/LoongArchTargetInfo.cpp @@ -24,7 +24,7 @@ RegisterTarget X( getTheLoongArch32Target(), "loongarch32", "32-bit LoongArch", "LoongArch"); - RegisterTarget Y( + RegisterTarget 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 --- /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,@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,@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,@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 --- /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,@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,@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 --- /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,@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,@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,@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 --- /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