diff --git a/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp b/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp index 0fb4b59ba6384..1bc3323a2e1d5 100644 --- a/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp +++ b/interpreter/cling/lib/Interpreter/IncrementalJIT.cpp @@ -356,7 +356,8 @@ static bool UseJITLink(const Triple& TT) { // LLJITBuilderState::prepareForConstruction. if (TT.getArch() == Triple::riscv64 || (TT.isOSBinFormatMachO() && - (TT.getArch() == Triple::aarch64 || TT.getArch() == Triple::x86_64))) { + (TT.getArch() == Triple::aarch64 || TT.getArch() == Triple::x86_64)) || + (TT.isOSBinFormatELF() && TT.getArch() == Triple::ppc64le)) { jitLink = true; } // Finally, honor the user's choice by setting an environment variable. diff --git a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/ELF_ppc64.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/ELF_ppc64.h new file mode 100644 index 0000000000000..8db986a4a9fa1 --- /dev/null +++ b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/ELF_ppc64.h @@ -0,0 +1,50 @@ +//===------ ELF_ppc64.h - JIT link functions for ELF/ppc64 ------*- 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/ppc64{le}. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_ELF_PPC64_H +#define LLVM_EXECUTIONENGINE_JITLINK_ELF_PPC64_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" + +namespace llvm::jitlink { + +/// Create a LinkGraph from an ELF/ppc64 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. +/// +/// WARNING: The big-endian backend has not been tested yet. +Expected> +createLinkGraphFromELFObject_ppc64(MemoryBufferRef ObjectBuffer); + +/// Create a LinkGraph from an ELF/ppc64le 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_ppc64le(MemoryBufferRef ObjectBuffer); + +/// jit-link the given object buffer, which must be a ELF ppc64le object file. +/// +/// WARNING: The big-endian backend has not been tested yet. +void link_ELF_ppc64(std::unique_ptr G, + std::unique_ptr Ctx); + +/// jit-link the given object buffer, which must be a ELF ppc64le object file. +void link_ELF_ppc64le(std::unique_ptr G, + std::unique_ptr Ctx); + +} // end namespace llvm::jitlink + +#endif // LLVM_EXECUTIONENGINE_JITLINK_ELF_PPC64_H diff --git a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h index 28996cbea35b1..cc31c1965fc2c 100644 --- a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h +++ b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/TableManager.h @@ -52,6 +52,21 @@ template class TableManager { return *EntryI->second; } + /// Register a pre-existing entry. + /// + /// Objects may include pre-existing table entries (e.g. for GOTs). + /// This method can be used to register those entries so that they will not + /// be duplicated by createEntry the first time that getEntryForTarget is + /// called. + bool registerPreExistingEntry(Symbol &Target, Symbol &Entry) { + assert(Target.hasName() && "Edge cannot point to anonymous target"); + auto Res = Entries.insert({ + Target.getName(), + &Entry, + }); + return Res.second; + } + private: TableManagerImplT &impl() { return static_cast(*this); } DenseMap Entries; diff --git a/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/ppc64.h b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/ppc64.h new file mode 100644 index 0000000000000..2658923826650 --- /dev/null +++ b/interpreter/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/ppc64.h @@ -0,0 +1,498 @@ +//===--- ppc64.h - Generic JITLink ppc64 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 64-bit PowerPC objects. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_EXECUTIONENGINE_JITLINK_PPC64_H +#define LLVM_EXECUTIONENGINE_JITLINK_PPC64_H + +#include "llvm/ExecutionEngine/JITLink/JITLink.h" +#include "llvm/ExecutionEngine/JITLink/TableManager.h" +#include "llvm/Support/Endian.h" + +namespace llvm::jitlink::ppc64 { + +/// Represents ppc64 fixups and other ppc64-specific edge kinds. +enum EdgeKind_ppc64 : Edge::Kind { + Pointer64 = Edge::FirstRelocation, + Pointer32, + Pointer16, + Pointer16DS, + Pointer16HA, + Pointer16HI, + Pointer16HIGH, + Pointer16HIGHA, + Pointer16HIGHER, + Pointer16HIGHERA, + Pointer16HIGHEST, + Pointer16HIGHESTA, + Pointer16LO, + Pointer16LODS, + Pointer14, + Delta64, + Delta34, + Delta32, + NegDelta32, + Delta16, + Delta16HA, + Delta16HI, + Delta16LO, + TOC, + TOCDelta16, + TOCDelta16DS, + TOCDelta16HA, + TOCDelta16HI, + TOCDelta16LO, + TOCDelta16LODS, + RequestGOTAndTransformToDelta34, + CallBranchDelta, + // Need to restore r2 after the bl, suggesting the bl is followed by a nop. + CallBranchDeltaRestoreTOC, + // Request calling function with TOC. + RequestCall, + // Request calling function without TOC. + RequestCallNoTOC, + RequestTLSDescInGOTAndTransformToTOCDelta16HA, + RequestTLSDescInGOTAndTransformToTOCDelta16LO, + RequestTLSDescInGOTAndTransformToDelta34, +}; + +enum PLTCallStubKind { + // Setup function entry(r12) and long branch to target using TOC. + LongBranch, + // Save TOC pointer, setup function entry and long branch to target using TOC. + LongBranchSaveR2, + // Setup function entry(r12) and long branch to target without using TOC. + LongBranchNoTOC, +}; + +extern const char NullPointerContent[8]; +extern const char PointerJumpStubContent_big[20]; +extern const char PointerJumpStubContent_little[20]; +extern const char PointerJumpStubNoTOCContent_big[32]; +extern const char PointerJumpStubNoTOCContent_little[32]; + +struct PLTCallStubReloc { + Edge::Kind K; + size_t Offset; + Edge::AddendT A; +}; + +struct PLTCallStubInfo { + ArrayRef Content; + SmallVector Relocs; +}; + +template +inline PLTCallStubInfo pickStub(PLTCallStubKind StubKind) { + constexpr bool isLE = Endianness == support::endianness::little; + switch (StubKind) { + case LongBranch: { + ArrayRef Content = + isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big; + // Skip save r2. + Content = Content.slice(4); + size_t Offset = isLE ? 0 : 2; + return PLTCallStubInfo{ + Content, + {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 0}}, + }; + } + case LongBranchSaveR2: { + ArrayRef Content = + isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big; + size_t Offset = isLE ? 4 : 6; + return PLTCallStubInfo{ + Content, + {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 0}}, + }; + } + case LongBranchNoTOC: { + ArrayRef Content = isLE ? PointerJumpStubNoTOCContent_little + : PointerJumpStubNoTOCContent_big; + size_t Offset = isLE ? 16 : 18; + Edge::AddendT Addend = isLE ? 8 : 10; + return PLTCallStubInfo{ + Content, + {{Delta16HA, Offset, Addend}, {Delta16LO, Offset + 4, Addend + 4}}, + }; + } + } + llvm_unreachable("Unknown PLTCallStubKind enum"); +} + +inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection, + Symbol *InitialTarget = nullptr, + uint64_t InitialAddend = 0) { + assert(G.getPointerSize() == sizeof(NullPointerContent) && + "LinkGraph's pointer size should be consistent with size of " + "NullPointerContent"); + Block &B = G.createContentBlock(PointerSection, NullPointerContent, + orc::ExecutorAddr(), G.getPointerSize(), 0); + if (InitialTarget) + B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend); + return G.addAnonymousSymbol(B, 0, G.getPointerSize(), false, false); +} + +template +inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G, + Section &StubSection, + Symbol &PointerSymbol, + PLTCallStubKind StubKind) { + PLTCallStubInfo StubInfo = pickStub(StubKind); + Block &B = G.createContentBlock(StubSection, StubInfo.Content, + orc::ExecutorAddr(), 4, 0); + for (auto const &Reloc : StubInfo.Relocs) + B.addEdge(Reloc.K, Reloc.Offset, PointerSymbol, Reloc.A); + return G.addAnonymousSymbol(B, 0, StubInfo.Content.size(), true, false); +} + +template +class TOCTableManager : public TableManager> { +public: + // FIXME: `llvm-jitlink -check` relies this name to be $__GOT. + static StringRef getSectionName() { return "$__GOT"; } + + bool visitEdge(LinkGraph &G, Block *B, Edge &E) { + Edge::Kind K = E.getKind(); + switch (K) { + case TOCDelta16HA: + case TOCDelta16LO: + case TOCDelta16DS: + case TOCDelta16LODS: + case CallBranchDeltaRestoreTOC: + case RequestCall: + // Create TOC section if TOC relocation, PLT or GOT is used. + getOrCreateTOCSection(G); + return false; + case RequestGOTAndTransformToDelta34: + E.setKind(ppc64::Delta34); + E.setTarget(createEntry(G, E.getTarget())); + return true; + default: + return false; + } + } + + Symbol &createEntry(LinkGraph &G, Symbol &Target) { + return createAnonymousPointer(G, getOrCreateTOCSection(G), &Target); + } + +private: + Section &getOrCreateTOCSection(LinkGraph &G) { + TOCSection = G.findSectionByName(getSectionName()); + if (!TOCSection) + TOCSection = &G.createSection(getSectionName(), orc::MemProt::Read); + return *TOCSection; + } + + Section *TOCSection = nullptr; +}; + +template +class PLTTableManager : public TableManager> { +public: + PLTTableManager(TOCTableManager &TOC) : TOC(TOC) {} + + static StringRef getSectionName() { return "$__STUBS"; } + + // FIXME: One external symbol can only have one PLT stub in a object file. + // This is a limitation when we need different PLT stubs for the same symbol. + // For example, we need two different PLT stubs for `bl __tls_get_addr` and + // `bl __tls_get_addr@notoc`. + bool visitEdge(LinkGraph &G, Block *B, Edge &E) { + bool isExternal = E.getTarget().isExternal(); + Edge::Kind K = E.getKind(); + if (K == ppc64::RequestCall) { + if (isExternal) { + E.setKind(ppc64::CallBranchDeltaRestoreTOC); + this->StubKind = LongBranchSaveR2; + // FIXME: We assume the addend to the external target is zero. It's + // quite unusual that the addend of an external target to be non-zero as + // if we have known the layout of the external object. + E.setTarget(this->getEntryForTarget(G, E.getTarget())); + // Addend to the stub is zero. + E.setAddend(0); + } else + // TODO: There are cases a local function call need a call stub. + // 1. Caller uses TOC, the callee doesn't, need a r2 save stub. + // 2. Caller doesn't use TOC, the callee does, need a r12 setup stub. + // 3. Branching target is out of range. + E.setKind(ppc64::CallBranchDelta); + return true; + } + if (K == ppc64::RequestCallNoTOC) { + E.setKind(ppc64::CallBranchDelta); + this->StubKind = LongBranchNoTOC; + E.setTarget(this->getEntryForTarget(G, E.getTarget())); + return true; + } + return false; + } + + Symbol &createEntry(LinkGraph &G, Symbol &Target) { + return createAnonymousPointerJumpStub( + G, getOrCreateStubsSection(G), TOC.getEntryForTarget(G, Target), + this->StubKind); + } + +private: + Section &getOrCreateStubsSection(LinkGraph &G) { + PLTSection = G.findSectionByName(getSectionName()); + if (!PLTSection) + PLTSection = &G.createSection(getSectionName(), + orc::MemProt::Read | orc::MemProt::Exec); + return *PLTSection; + } + + TOCTableManager &TOC; + Section *PLTSection = nullptr; + PLTCallStubKind StubKind; +}; + +/// Returns a string name for the given ppc64 edge. For debugging purposes +/// only. +const char *getEdgeKindName(Edge::Kind K); + +inline static uint16_t ha(uint64_t x) { return (x + 0x8000) >> 16; } +inline static uint64_t lo(uint64_t x) { return x & 0xffff; } +inline static uint16_t hi(uint64_t x) { return x >> 16; } +inline static uint64_t high(uint64_t x) { return (x >> 16) & 0xffff; } +inline static uint64_t higha(uint64_t x) { + return ((x + 0x8000) >> 16) & 0xffff; +} +inline static uint64_t higher(uint64_t x) { return (x >> 32) & 0xffff; } +inline static uint64_t highera(uint64_t x) { + return ((x + 0x8000) >> 32) & 0xffff; +} +inline static uint16_t highest(uint64_t x) { return x >> 48; } +inline static uint16_t highesta(uint64_t x) { return (x + 0x8000) >> 48; } + +// Prefixed instruction introduced in ISAv3.1 consists of two 32-bit words, +// prefix word and suffix word, i.e., prefixed_instruction = concat(prefix_word, +// suffix_word). That's to say, for a prefixed instruction encoded in uint64_t, +// the most significant 32 bits belong to the prefix word. The prefix word is at +// low address for both big/little endian. Byte order in each word still follows +// its endian. +template +inline static uint64_t readPrefixedInstruction(const char *Loc) { + constexpr bool isLE = Endianness == support::endianness::little; + uint64_t Inst = support::endian::read64(Loc); + return isLE ? (Inst << 32) | (Inst >> 32) : Inst; +} + +template +inline static void writePrefixedInstruction(char *Loc, uint64_t Inst) { + constexpr bool isLE = Endianness == support::endianness::little; + Inst = isLE ? (Inst << 32) | (Inst >> 32) : Inst; + support::endian::write64(Loc, Inst); +} + +template +inline Error relocateHalf16(char *FixupPtr, int64_t Value, Edge::Kind K) { + switch (K) { + case Delta16: + case Pointer16: + case TOCDelta16: + support::endian::write16(FixupPtr, Value); + break; + case Pointer16DS: + case TOCDelta16DS: + support::endian::write16(FixupPtr, Value & ~3); + break; + case Delta16HA: + case Pointer16HA: + case TOCDelta16HA: + support::endian::write16(FixupPtr, ha(Value)); + break; + case Delta16HI: + case Pointer16HI: + case TOCDelta16HI: + support::endian::write16(FixupPtr, hi(Value)); + break; + case Pointer16HIGH: + support::endian::write16(FixupPtr, high(Value)); + break; + case Pointer16HIGHA: + support::endian::write16(FixupPtr, higha(Value)); + break; + case Pointer16HIGHER: + support::endian::write16(FixupPtr, higher(Value)); + break; + case Pointer16HIGHERA: + support::endian::write16(FixupPtr, highera(Value)); + break; + case Pointer16HIGHEST: + support::endian::write16(FixupPtr, highest(Value)); + break; + case Pointer16HIGHESTA: + support::endian::write16(FixupPtr, highesta(Value)); + break; + case Delta16LO: + case Pointer16LO: + case TOCDelta16LO: + support::endian::write16(FixupPtr, lo(Value)); + break; + case Pointer16LODS: + case TOCDelta16LODS: + support::endian::write16(FixupPtr, lo(Value) & ~3); + break; + default: + return make_error( + StringRef(getEdgeKindName(K)) + + " relocation does not write at half16 field"); + } + return Error::success(); +} + +/// Apply fixup expression for edge to block content. +template +inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E, + const Symbol *TOCSymbol) { + char *BlockWorkingMem = B.getAlreadyMutableContent().data(); + char *FixupPtr = BlockWorkingMem + E.getOffset(); + orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset(); + int64_t S = E.getTarget().getAddress().getValue(); + int64_t A = E.getAddend(); + int64_t P = FixupAddress.getValue(); + int64_t TOCBase = TOCSymbol ? TOCSymbol->getAddress().getValue() : 0; + Edge::Kind K = E.getKind(); + + DEBUG_WITH_TYPE("jitlink", { + dbgs() << " Applying fixup on " << G.getEdgeKindName(K) + << " edge, (S, A, P, .TOC.) = (" << formatv("{0:x}", S) << ", " + << formatv("{0:x}", A) << ", " << formatv("{0:x}", P) << ", " + << formatv("{0:x}", TOCBase) << ")\n"; + }); + + switch (K) { + case Pointer64: { + uint64_t Value = S + A; + support::endian::write64(FixupPtr, Value); + break; + } + case Delta16: + case Delta16HA: + case Delta16HI: + case Delta16LO: { + int64_t Value = S + A - P; + if (LLVM_UNLIKELY(!isInt<32>(Value))) { + return makeTargetOutOfRangeError(G, B, E); + } + return relocateHalf16(FixupPtr, Value, K); + } + case TOC: + support::endian::write64(FixupPtr, TOCBase); + break; + case Pointer16: + case Pointer16DS: + case Pointer16HA: + case Pointer16HI: + case Pointer16HIGH: + case Pointer16HIGHA: + case Pointer16HIGHER: + case Pointer16HIGHERA: + case Pointer16HIGHEST: + case Pointer16HIGHESTA: + case Pointer16LO: + case Pointer16LODS: { + uint64_t Value = S + A; + if (LLVM_UNLIKELY(!isInt<32>(Value))) { + return makeTargetOutOfRangeError(G, B, E); + } + return relocateHalf16(FixupPtr, Value, K); + } + case Pointer14: { + static const uint32_t Low14Mask = 0xfffc; + uint64_t Value = S + A; + assert((Value & 3) == 0 && "Pointer14 requires 4-byte alignment"); + if (LLVM_UNLIKELY(!isInt<16>(Value))) { + return makeTargetOutOfRangeError(G, B, E); + } + uint32_t Inst = support::endian::read32(FixupPtr); + support::endian::write32(FixupPtr, (Inst & ~Low14Mask) | + (Value & Low14Mask)); + break; + } + case TOCDelta16: + case TOCDelta16DS: + case TOCDelta16HA: + case TOCDelta16HI: + case TOCDelta16LO: + case TOCDelta16LODS: { + int64_t Value = S + A - TOCBase; + if (LLVM_UNLIKELY(!isInt<32>(Value))) { + return makeTargetOutOfRangeError(G, B, E); + } + return relocateHalf16(FixupPtr, Value, K); + } + case CallBranchDeltaRestoreTOC: + case CallBranchDelta: { + int64_t Value = S + A - P; + if (LLVM_UNLIKELY(!isInt<26>(Value))) { + return makeTargetOutOfRangeError(G, B, E); + } + uint32_t Inst = support::endian::read32(FixupPtr); + support::endian::write32(FixupPtr, (Inst & 0xfc000003) | + (Value & 0x03fffffc)); + if (K == CallBranchDeltaRestoreTOC) { + uint32_t NopInst = support::endian::read32(FixupPtr + 4); + assert(NopInst == 0x60000000 && + "NOP should be placed here for restoring r2"); + (void)NopInst; + // Restore r2 by instruction 0xe8410018 which is `ld r2, 24(r1)`. + support::endian::write32(FixupPtr + 4, 0xe8410018); + } + break; + } + case Delta64: { + int64_t Value = S + A - P; + support::endian::write64(FixupPtr, Value); + break; + } + case Delta34: { + int64_t Value = S + A - P; + if (!LLVM_UNLIKELY(isInt<34>(Value))) + return makeTargetOutOfRangeError(G, B, E); + static const uint64_t SI0Mask = 0x00000003ffff0000; + static const uint64_t SI1Mask = 0x000000000000ffff; + static const uint64_t FullMask = 0x0003ffff0000ffff; + uint64_t Inst = readPrefixedInstruction(FixupPtr) & ~FullMask; + writePrefixedInstruction( + FixupPtr, Inst | ((Value & SI0Mask) << 16) | (Value & SI1Mask)); + break; + } + case Delta32: { + int64_t Value = S + A - P; + if (LLVM_UNLIKELY(!isInt<32>(Value))) { + return makeTargetOutOfRangeError(G, B, E); + } + support::endian::write32(FixupPtr, Value); + break; + } + case NegDelta32: { + int64_t Value = P - S + A; + if (LLVM_UNLIKELY(!isInt<32>(Value))) { + return makeTargetOutOfRangeError(G, B, E); + } + support::endian::write32(FixupPtr, Value); + break; + } + default: + return make_error( + "In graph " + G.getName() + ", section " + B.getSection().getName() + + " unsupported edge kind " + getEdgeKindName(E.getKind())); + } + return Error::success(); +} + +} // end namespace llvm::jitlink::ppc64 + +#endif // LLVM_EXECUTIONENGINE_JITLINK_PPC64_H diff --git a/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt b/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt index 52ff5e8370031..9fb33b801fd24 100644 --- a/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/CMakeLists.txt @@ -23,6 +23,7 @@ add_llvm_component_library(LLVMJITLink ELF_aarch64.cpp ELF_i386.cpp ELF_loongarch.cpp + ELF_ppc64.cpp ELF_riscv.cpp ELF_x86_64.cpp @@ -36,6 +37,7 @@ add_llvm_component_library(LLVMJITLink aarch64.cpp i386.cpp loongarch.cpp + ppc64.cpp riscv.cpp x86_64.cpp diff --git a/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/ELF.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/ELF.cpp index ef0f19a785712..b0bca4589ebd3 100644 --- a/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/ELF.cpp +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/ELF.cpp @@ -16,6 +16,7 @@ #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_ppc64.h" #include "llvm/ExecutionEngine/JITLink/ELF_riscv.h" #include "llvm/ExecutionEngine/JITLink/ELF_x86_64.h" #include "llvm/Object/ELF.h" @@ -62,6 +63,7 @@ createLinkGraphFromELFObject(MemoryBufferRef ObjectBuffer) { if (memcmp(Buffer.data(), ELF::ElfMagic, strlen(ELF::ElfMagic)) != 0) return make_error("ELF magic not valid"); + uint8_t DataEncoding = Buffer.data()[ELF::EI_DATA]; Expected TargetMachineArch = readTargetMachineArch(Buffer); if (!TargetMachineArch) return TargetMachineArch.takeError(); @@ -71,6 +73,12 @@ createLinkGraphFromELFObject(MemoryBufferRef ObjectBuffer) { return createLinkGraphFromELFObject_aarch64(ObjectBuffer); case ELF::EM_LOONGARCH: return createLinkGraphFromELFObject_loongarch(ObjectBuffer); + case ELF::EM_PPC64: { + if (DataEncoding == ELF::ELFDATA2LSB) + return createLinkGraphFromELFObject_ppc64le(ObjectBuffer); + else + return createLinkGraphFromELFObject_ppc64(ObjectBuffer); + } case ELF::EM_RISCV: return createLinkGraphFromELFObject_riscv(ObjectBuffer); case ELF::EM_X86_64: @@ -94,6 +102,12 @@ void link_ELF(std::unique_ptr G, case Triple::loongarch64: link_ELF_loongarch(std::move(G), std::move(Ctx)); return; + case Triple::ppc64: + link_ELF_ppc64(std::move(G), std::move(Ctx)); + return; + case Triple::ppc64le: + link_ELF_ppc64le(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/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/ELF_ppc64.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/ELF_ppc64.cpp new file mode 100644 index 0000000000000..9b01c07583671 --- /dev/null +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/ELF_ppc64.cpp @@ -0,0 +1,544 @@ +//===------- ELF_ppc64.cpp -JIT linker implementation for ELF/ppc64 -------===// +// +// 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/ppc64 jit-link implementation. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/ELF_ppc64.h" +#include "llvm/ExecutionEngine/JITLink/DWARFRecordSectionSplitter.h" +#include "llvm/ExecutionEngine/JITLink/TableManager.h" +#include "llvm/ExecutionEngine/JITLink/ppc64.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/Endian.h" + +#include "EHFrameSupportImpl.h" +#include "ELFLinkGraphBuilder.h" +#include "JITLinkGeneric.h" + +#define DEBUG_TYPE "jitlink" + +namespace { + +using namespace llvm; +using namespace llvm::jitlink; + +constexpr StringRef ELFTOCSymbolName = ".TOC."; +constexpr StringRef TOCSymbolAliasIdent = "__TOC__"; +constexpr uint64_t ELFTOCBaseOffset = 0x8000; +constexpr StringRef ELFTLSInfoSectionName = "$__TLSINFO"; + +template +class TLSInfoTableManager_ELF_ppc64 + : public TableManager> { +public: + static const uint8_t TLSInfoEntryContent[16]; + + static StringRef getSectionName() { return ELFTLSInfoSectionName; } + + bool visitEdge(LinkGraph &G, Block *B, Edge &E) { + Edge::Kind K = E.getKind(); + switch (K) { + case ppc64::RequestTLSDescInGOTAndTransformToTOCDelta16HA: + E.setKind(ppc64::TOCDelta16HA); + E.setTarget(this->getEntryForTarget(G, E.getTarget())); + return true; + case ppc64::RequestTLSDescInGOTAndTransformToTOCDelta16LO: + E.setKind(ppc64::TOCDelta16LO); + E.setTarget(this->getEntryForTarget(G, E.getTarget())); + return true; + case ppc64::RequestTLSDescInGOTAndTransformToDelta34: + E.setKind(ppc64::Delta34); + E.setTarget(this->getEntryForTarget(G, E.getTarget())); + return true; + default: + return false; + } + } + + Symbol &createEntry(LinkGraph &G, Symbol &Target) { + // The TLS Info entry's key value will be written by + // `fixTLVSectionsAndEdges`, so create mutable content. + auto &TLSInfoEntry = G.createMutableContentBlock( + getTLSInfoSection(G), G.allocateContent(getTLSInfoEntryContent()), + orc::ExecutorAddr(), 8, 0); + TLSInfoEntry.addEdge(ppc64::Pointer64, 8, Target, 0); + return G.addAnonymousSymbol(TLSInfoEntry, 0, 16, false, false); + } + +private: + Section &getTLSInfoSection(LinkGraph &G) { + if (!TLSInfoTable) + TLSInfoTable = + &G.createSection(ELFTLSInfoSectionName, orc::MemProt::Read); + return *TLSInfoTable; + } + + ArrayRef getTLSInfoEntryContent() const { + return {reinterpret_cast(TLSInfoEntryContent), + sizeof(TLSInfoEntryContent)}; + } + + Section *TLSInfoTable = nullptr; +}; + +template <> +const uint8_t TLSInfoTableManager_ELF_ppc64< + support::endianness::little>::TLSInfoEntryContent[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*pthread key */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*data address*/ +}; + +template <> +const uint8_t TLSInfoTableManager_ELF_ppc64< + support::endianness::big>::TLSInfoEntryContent[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*pthread key */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*data address*/ +}; + +template +Symbol &createELFGOTHeader(LinkGraph &G, + ppc64::TOCTableManager &TOC) { + Symbol *TOCSymbol = nullptr; + + for (Symbol *Sym : G.defined_symbols()) + if (LLVM_UNLIKELY(Sym->getName() == ELFTOCSymbolName)) { + TOCSymbol = Sym; + break; + } + + if (LLVM_LIKELY(TOCSymbol == nullptr)) { + for (Symbol *Sym : G.external_symbols()) + if (Sym->getName() == ELFTOCSymbolName) { + TOCSymbol = Sym; + break; + } + } + + if (!TOCSymbol) + TOCSymbol = &G.addExternalSymbol(ELFTOCSymbolName, 0, false); + + return TOC.getEntryForTarget(G, *TOCSymbol); +} + +// Register preexisting GOT entries with TOC table manager. +template +inline void +registerExistingGOTEntries(LinkGraph &G, + ppc64::TOCTableManager &TOC) { + auto isGOTEntry = [](const Edge &E) { + return E.getKind() == ppc64::Pointer64 && E.getTarget().isExternal(); + }; + if (Section *dotTOCSection = G.findSectionByName(".toc")) { + for (Block *B : dotTOCSection->blocks()) + for (Edge &E : B->edges()) + if (isGOTEntry(E)) + TOC.registerPreExistingEntry(E.getTarget(), + G.addAnonymousSymbol(*B, E.getOffset(), + G.getPointerSize(), + false, false)); + } +} + +template +Error buildTables_ELF_ppc64(LinkGraph &G) { + LLVM_DEBUG(dbgs() << "Visiting edges in graph:\n"); + ppc64::TOCTableManager TOC; + // Before visiting edges, we create a header containing the address of TOC + // base as ELFABIv2 suggests: + // > The GOT consists of an 8-byte header that contains the TOC base (the + // first TOC base when multiple TOCs are present), followed by an array of + // 8-byte addresses. + createELFGOTHeader(G, TOC); + + // There might be compiler-generated GOT entries in ELF relocatable file. + registerExistingGOTEntries(G, TOC); + + ppc64::PLTTableManager PLT(TOC); + TLSInfoTableManager_ELF_ppc64 TLSInfo; + visitExistingEdges(G, TOC, PLT, TLSInfo); + + // After visiting edges in LinkGraph, we have GOT entries built in the + // synthesized section. + // Merge sections included in TOC into synthesized TOC section, + // thus TOC is compact and reducing chances of relocation + // overflow. + if (Section *TOCSection = G.findSectionByName(TOC.getSectionName())) { + // .got and .plt are not normally present in a relocatable object file + // because they are linker generated. + if (Section *gotSection = G.findSectionByName(".got")) + G.mergeSections(*TOCSection, *gotSection); + if (Section *tocSection = G.findSectionByName(".toc")) + G.mergeSections(*TOCSection, *tocSection); + if (Section *sdataSection = G.findSectionByName(".sdata")) + G.mergeSections(*TOCSection, *sdataSection); + if (Section *sbssSection = G.findSectionByName(".sbss")) + G.mergeSections(*TOCSection, *sbssSection); + // .tocbss no longer appears in ELFABIv2. Leave it here to be compatible + // with rtdyld. + if (Section *tocbssSection = G.findSectionByName(".tocbss")) + G.mergeSections(*TOCSection, *tocbssSection); + if (Section *pltSection = G.findSectionByName(".plt")) + G.mergeSections(*TOCSection, *pltSection); + } + + return Error::success(); +} + +} // namespace + +namespace llvm::jitlink { + +template +class ELFLinkGraphBuilder_ppc64 + : public ELFLinkGraphBuilder> { +private: + using ELFT = object::ELFType; + using Base = ELFLinkGraphBuilder; + + using Base::G; // Use LinkGraph pointer from base class. + + Error addRelocations() override { + LLVM_DEBUG(dbgs() << "Processing relocations:\n"); + + using Self = ELFLinkGraphBuilder_ppc64; + for (const auto &RelSect : Base::Sections) { + // Validate the section to read relocation entries from. + if (RelSect.sh_type == ELF::SHT_REL) + return make_error("No SHT_REL in valid " + + G->getTargetTriple().getArchName() + + " ELF object files", + inconvertibleErrorCode()); + + 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 &FixupSection, + Block &BlockToFix) { + using Base = ELFLinkGraphBuilder; + auto ELFReloc = Rel.getType(false); + + // R_PPC64_NONE is a no-op. + if (LLVM_UNLIKELY(ELFReloc == ELF::R_PPC64_NONE)) + return Error::success(); + + // TLS model markers. We only support global-dynamic model now. + if (ELFReloc == ELF::R_PPC64_TLSGD) + return Error::success(); + if (ELFReloc == ELF::R_PPC64_TLSLD) + return make_error("Local-dynamic TLS model is not supported", + inconvertibleErrorCode()); + + if (ELFReloc == ELF::R_PPC64_PCREL_OPT) + // TODO: Support PCREL optimization, now ignore it. + return Error::success(); + + if (ELFReloc == ELF::R_PPC64_TPREL34) + return make_error("Local-exec TLS model is not supported", + inconvertibleErrorCode()); + + auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec); + if (!ObjSymbol) + return ObjSymbol.takeError(); + + uint32_t SymbolIndex = Rel.getSymbol(false); + 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()); + + int64_t Addend = Rel.r_addend; + orc::ExecutorAddr FixupAddress = + orc::ExecutorAddr(FixupSection.sh_addr) + Rel.r_offset; + Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress(); + Edge::Kind Kind = Edge::Invalid; + + switch (ELFReloc) { + default: + return make_error( + "In " + G->getName() + ": Unsupported ppc64 relocation type " + + object::getELFRelocationTypeName(ELF::EM_PPC64, ELFReloc)); + case ELF::R_PPC64_ADDR64: + Kind = ppc64::Pointer64; + break; + case ELF::R_PPC64_ADDR32: + Kind = ppc64::Pointer32; + break; + case ELF::R_PPC64_ADDR16: + Kind = ppc64::Pointer16; + break; + case ELF::R_PPC64_ADDR16_DS: + Kind = ppc64::Pointer16DS; + break; + case ELF::R_PPC64_ADDR16_HA: + Kind = ppc64::Pointer16HA; + break; + case ELF::R_PPC64_ADDR16_HI: + Kind = ppc64::Pointer16HI; + break; + case ELF::R_PPC64_ADDR16_HIGH: + Kind = ppc64::Pointer16HIGH; + break; + case ELF::R_PPC64_ADDR16_HIGHA: + Kind = ppc64::Pointer16HIGHA; + break; + case ELF::R_PPC64_ADDR16_HIGHER: + Kind = ppc64::Pointer16HIGHER; + break; + case ELF::R_PPC64_ADDR16_HIGHERA: + Kind = ppc64::Pointer16HIGHERA; + break; + case ELF::R_PPC64_ADDR16_HIGHEST: + Kind = ppc64::Pointer16HIGHEST; + break; + case ELF::R_PPC64_ADDR16_HIGHESTA: + Kind = ppc64::Pointer16HIGHESTA; + break; + case ELF::R_PPC64_ADDR16_LO: + Kind = ppc64::Pointer16LO; + break; + case ELF::R_PPC64_ADDR16_LO_DS: + Kind = ppc64::Pointer16LODS; + break; + case ELF::R_PPC64_ADDR14: + Kind = ppc64::Pointer14; + break; + case ELF::R_PPC64_TOC: + Kind = ppc64::TOC; + break; + case ELF::R_PPC64_TOC16: + Kind = ppc64::TOCDelta16; + break; + case ELF::R_PPC64_TOC16_HA: + Kind = ppc64::TOCDelta16HA; + break; + case ELF::R_PPC64_TOC16_HI: + Kind = ppc64::TOCDelta16HI; + break; + case ELF::R_PPC64_TOC16_DS: + Kind = ppc64::TOCDelta16DS; + break; + case ELF::R_PPC64_TOC16_LO: + Kind = ppc64::TOCDelta16LO; + break; + case ELF::R_PPC64_TOC16_LO_DS: + Kind = ppc64::TOCDelta16LODS; + break; + case ELF::R_PPC64_REL16: + Kind = ppc64::Delta16; + break; + case ELF::R_PPC64_REL16_HA: + Kind = ppc64::Delta16HA; + break; + case ELF::R_PPC64_REL16_HI: + Kind = ppc64::Delta16HI; + break; + case ELF::R_PPC64_REL16_LO: + Kind = ppc64::Delta16LO; + break; + case ELF::R_PPC64_REL32: + Kind = ppc64::Delta32; + break; + case ELF::R_PPC64_REL24_NOTOC: + Kind = ppc64::RequestCallNoTOC; + break; + case ELF::R_PPC64_REL24: + Kind = ppc64::RequestCall; + // Determining a target is external or not is deferred in PostPrunePass. + // We assume branching to local entry by default, since in PostPrunePass, + // we don't have any context to determine LocalEntryOffset. If it finally + // turns out to be an external call, we'll have a stub for the external + // target, the target of this edge will be the stub and its addend will be + // set 0. + Addend += ELF::decodePPC64LocalEntryOffset((*ObjSymbol)->st_other); + break; + case ELF::R_PPC64_REL64: + Kind = ppc64::Delta64; + break; + case ELF::R_PPC64_PCREL34: + Kind = ppc64::Delta34; + break; + case ELF::R_PPC64_GOT_PCREL34: + Kind = ppc64::RequestGOTAndTransformToDelta34; + break; + case ELF::R_PPC64_GOT_TLSGD16_HA: + Kind = ppc64::RequestTLSDescInGOTAndTransformToTOCDelta16HA; + break; + case ELF::R_PPC64_GOT_TLSGD16_LO: + Kind = ppc64::RequestTLSDescInGOTAndTransformToTOCDelta16LO; + break; + case ELF::R_PPC64_GOT_TLSGD_PCREL34: + Kind = ppc64::RequestTLSDescInGOTAndTransformToDelta34; + break; + } + + Edge GE(Kind, Offset, *GraphSymbol, Addend); + BlockToFix.addEdge(std::move(GE)); + return Error::success(); + } + +public: + ELFLinkGraphBuilder_ppc64(StringRef FileName, + const object::ELFFile &Obj, Triple TT) + : ELFLinkGraphBuilder(Obj, std::move(TT), FileName, + ppc64::getEdgeKindName) {} +}; + +template +class ELFJITLinker_ppc64 : public JITLinker> { + using JITLinkerBase = JITLinker>; + friend JITLinkerBase; + +public: + ELFJITLinker_ppc64(std::unique_ptr Ctx, + std::unique_ptr G, PassConfiguration PassConfig) + : JITLinkerBase(std::move(Ctx), std::move(G), std::move(PassConfig)) { + JITLinkerBase::getPassConfig().PostAllocationPasses.push_back( + [this](LinkGraph &G) { return defineTOCBase(G); }); + } + +private: + Symbol *TOCSymbol = nullptr; + + Error defineTOCBase(LinkGraph &G) { + for (Symbol *Sym : G.defined_symbols()) { + if (LLVM_UNLIKELY(Sym->getName() == ELFTOCSymbolName)) { + TOCSymbol = Sym; + return Error::success(); + } + } + + assert(TOCSymbol == nullptr && + "TOCSymbol should not be defined at this point"); + + for (Symbol *Sym : G.external_symbols()) { + if (Sym->getName() == ELFTOCSymbolName) { + TOCSymbol = Sym; + break; + } + } + + if (Section *TOCSection = G.findSectionByName( + ppc64::TOCTableManager::getSectionName())) { + assert(!TOCSection->blocks().empty() && + "TOC section should have reserved an " + "entry for containing the TOC base"); + + SectionRange SR(*TOCSection); + orc::ExecutorAddr TOCBaseAddr(SR.getFirstBlock()->getAddress() + + ELFTOCBaseOffset); + assert(TOCSymbol && TOCSymbol->isExternal() && + ".TOC. should be a external symbol at this point"); + G.makeAbsolute(*TOCSymbol, TOCBaseAddr); + // Create an alias of .TOC. so that rtdyld checker can recognize. + G.addAbsoluteSymbol(TOCSymbolAliasIdent, TOCSymbol->getAddress(), + TOCSymbol->getSize(), TOCSymbol->getLinkage(), + TOCSymbol->getScope(), TOCSymbol->isLive()); + return Error::success(); + } + + // If TOC section doesn't exist, which means no TOC relocation is found, we + // don't need a TOCSymbol. + return Error::success(); + } + + Error applyFixup(LinkGraph &G, Block &B, const Edge &E) const { + return ppc64::applyFixup(G, B, E, TOCSymbol); + } +}; + +template +Expected> +createLinkGraphFromELFObject_ppc64(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(); + + auto Features = (*ELFObj)->getFeatures(); + if (!Features) + return Features.takeError(); + + using ELFT = object::ELFType; + auto &ELFObjFile = cast>(**ELFObj); + return ELFLinkGraphBuilder_ppc64( + (*ELFObj)->getFileName(), ELFObjFile.getELFFile(), + (*ELFObj)->makeTriple()) + .buildGraph(); +} + +template +void link_ELF_ppc64(std::unique_ptr G, + std::unique_ptr Ctx) { + PassConfiguration Config; + + if (Ctx->shouldAddDefaultTargetPasses(G->getTargetTriple())) { + // Construct a JITLinker and run the link function. + + // Add eh-frame passes. + Config.PrePrunePasses.push_back(DWARFRecordSectionSplitter(".eh_frame")); + Config.PrePrunePasses.push_back(EHFrameEdgeFixer( + ".eh_frame", G->getPointerSize(), ppc64::Pointer32, ppc64::Pointer64, + ppc64::Delta32, ppc64::Delta64, ppc64::NegDelta32)); + Config.PrePrunePasses.push_back(EHFrameNullTerminator(".eh_frame")); + + // Add a mark-live pass. + if (auto MarkLive = Ctx->getMarkLivePass(G->getTargetTriple())) + Config.PrePrunePasses.push_back(std::move(MarkLive)); + else + Config.PrePrunePasses.push_back(markAllSymbolsLive); + } + + Config.PostPrunePasses.push_back(buildTables_ELF_ppc64); + + if (auto Err = Ctx->modifyPassConfig(*G, Config)) + return Ctx->notifyFailed(std::move(Err)); + + ELFJITLinker_ppc64::link(std::move(Ctx), std::move(G), + std::move(Config)); +} + +Expected> +createLinkGraphFromELFObject_ppc64(MemoryBufferRef ObjectBuffer) { + return createLinkGraphFromELFObject_ppc64( + std::move(ObjectBuffer)); +} + +Expected> +createLinkGraphFromELFObject_ppc64le(MemoryBufferRef ObjectBuffer) { + return createLinkGraphFromELFObject_ppc64( + std::move(ObjectBuffer)); +} + +/// jit-link the given object buffer, which must be a ELF ppc64 object file. +void link_ELF_ppc64(std::unique_ptr G, + std::unique_ptr Ctx) { + return link_ELF_ppc64(std::move(G), std::move(Ctx)); +} + +/// jit-link the given object buffer, which must be a ELF ppc64le object file. +void link_ELF_ppc64le(std::unique_ptr G, + std::unique_ptr Ctx) { + return link_ELF_ppc64(std::move(G), std::move(Ctx)); +} + +} // end namespace llvm::jitlink diff --git a/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/ppc64.cpp b/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/ppc64.cpp new file mode 100644 index 0000000000000..27484aaf20590 --- /dev/null +++ b/interpreter/llvm-project/llvm/lib/ExecutionEngine/JITLink/ppc64.cpp @@ -0,0 +1,144 @@ +//===----- ppc64.cpp - Generic JITLink ppc64 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 64-bit PowerPC objects. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ExecutionEngine/JITLink/ppc64.h" + +#define DEBUG_TYPE "jitlink" + +namespace llvm::jitlink::ppc64 { + +const char NullPointerContent[8] = {0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + +const char PointerJumpStubContent_little[20] = { + 0x18, 0x00, 0x41, (char)0xf8, // std r2, 24(r1) + 0x00, 0x00, (char)0x82, 0x3d, // addis r12, r2, OffHa + 0x00, 0x00, (char)0x8c, (char)0xe9, // ld r12, OffLo(r12) + (char)0xa6, 0x03, (char)0x89, 0x7d, // mtctr r12 + 0x20, 0x04, (char)0x80, 0x4e, // bctr +}; + +const char PointerJumpStubContent_big[20] = { + (char)0xf8, 0x41, 0x00, 0x18, // std r2, 24(r1) + 0x3d, (char)0x82, 0x00, 0x00, // addis r12, r2, OffHa + (char)0xe9, (char)0x8c, 0x00, 0x00, // ld r12, OffLo(r12) + 0x7d, (char)0x89, 0x03, (char)0xa6, // mtctr r12 + 0x4e, (char)0x80, 0x04, 0x20, // bctr +}; + +// TODO: We can use prefixed instructions if LLJIT is running on power10. +const char PointerJumpStubNoTOCContent_little[32] = { + (char)0xa6, 0x02, (char)0x88, 0x7d, // mflr 12 + 0x05, (char)0x00, (char)0x9f, 0x42, // bcl 20,31,.+4 + (char)0xa6, 0x02, 0x68, 0x7d, // mflr 11 + (char)0xa6, 0x03, (char)0x88, 0x7d, // mtlr 12 + 0x00, 0x00, (char)0x8b, 0x3d, // addis 12,11,OffHa + 0x00, 0x00, (char)0x8c, (char)0xe9, // ld 12, OffLo(12) + (char)0xa6, 0x03, (char)0x89, 0x7d, // mtctr 12 + 0x20, 0x04, (char)0x80, 0x4e, // bctr +}; + +const char PointerJumpStubNoTOCContent_big[32] = { + 0x7d, (char)0x88, 0x02, (char)0xa6, // mflr 12 + 0x42, (char)0x9f, 0x00, 0x05, // bcl 20,31,.+4 + 0x7d, 0x68, 0x02, (char)0xa6, // mflr 11 + 0x7d, (char)0x88, 0x03, (char)0xa6, // mtlr 12 + 0x3d, (char)0x8b, 0x00, 0x00, // addis 12,11,OffHa + (char)0xe9, (char)0x8c, 0x00, 0x00, // ld 12, OffLo(12) + 0x7d, (char)0x89, 0x03, (char)0xa6, // mtctr 12 + 0x4e, (char)0x80, 0x04, 0x20, // bctr +}; + +const char *getEdgeKindName(Edge::Kind K) { + switch (K) { + case Pointer64: + return "Pointer64"; + case Pointer32: + return "Pointer32"; + case Pointer16: + return "Pointer16"; + case Pointer16DS: + return "Pointer16DS"; + case Pointer16HA: + return "Pointer16HA"; + case Pointer16HI: + return "Pointer16HI"; + case Pointer16HIGH: + return "Pointer16HIGH"; + case Pointer16HIGHA: + return "Pointer16HIGHA"; + case Pointer16HIGHER: + return "Pointer16HIGHER"; + case Pointer16HIGHERA: + return "Pointer16HIGHERA"; + case Pointer16HIGHEST: + return "Pointer16HIGHEST"; + case Pointer16HIGHESTA: + return "Pointer16HIGHESTA"; + case Pointer16LO: + return "Pointer16LO"; + case Pointer16LODS: + return "Pointer16LODS"; + case Pointer14: + return "Pointer14"; + case Delta64: + return "Delta64"; + case Delta34: + return "Delta34"; + case Delta32: + return "Delta32"; + case NegDelta32: + return "NegDelta32"; + case Delta16: + return "Delta16"; + case Delta16HA: + return "Delta16HA"; + case Delta16HI: + return "Delta16HI"; + case Delta16LO: + return "Delta16LO"; + case TOC: + return "TOC"; + case TOCDelta16: + return "TOCDelta16"; + case TOCDelta16DS: + return "TOCDelta16DS"; + case TOCDelta16HA: + return "TOCDelta16HA"; + case TOCDelta16HI: + return "TOCDelta16HI"; + case TOCDelta16LO: + return "TOCDelta16LO"; + case TOCDelta16LODS: + return "TOCDelta16LODS"; + case RequestGOTAndTransformToDelta34: + return "RequestGOTAndTransformToDelta34"; + case CallBranchDelta: + return "CallBranchDelta"; + case CallBranchDeltaRestoreTOC: + return "CallBranchDeltaRestoreTOC"; + case RequestCall: + return "RequestCall"; + case RequestCallNoTOC: + return "RequestCallNoTOC"; + case RequestTLSDescInGOTAndTransformToTOCDelta16HA: + return "RequestTLSDescInGOTAndTransformToTOCDelta16HA"; + case RequestTLSDescInGOTAndTransformToTOCDelta16LO: + return "RequestTLSDescInGOTAndTransformToTOCDelta16LO"; + case RequestTLSDescInGOTAndTransformToDelta34: + return "RequestTLSDescInGOTAndTransformToDelta34"; + default: + return getGenericEdgeKindName(static_cast(K)); + } +} + +} // end namespace llvm::jitlink::ppc64