106c3fb27SDimitry Andric //===--- ppc64.h - Generic JITLink ppc64 edge kinds, utilities --*- C++ -*-===//
206c3fb27SDimitry Andric //
306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606c3fb27SDimitry Andric //
706c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
806c3fb27SDimitry Andric //
906c3fb27SDimitry Andric // Generic utilities for graphs representing 64-bit PowerPC objects.
1006c3fb27SDimitry Andric //
1106c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
1206c3fb27SDimitry Andric 
1306c3fb27SDimitry Andric #ifndef LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
1406c3fb27SDimitry Andric #define LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
1506c3fb27SDimitry Andric 
1606c3fb27SDimitry Andric #include "llvm/ExecutionEngine/JITLink/JITLink.h"
1706c3fb27SDimitry Andric #include "llvm/ExecutionEngine/JITLink/TableManager.h"
1806c3fb27SDimitry Andric #include "llvm/Support/Endian.h"
1906c3fb27SDimitry Andric 
2006c3fb27SDimitry Andric namespace llvm::jitlink::ppc64 {
2106c3fb27SDimitry Andric 
2206c3fb27SDimitry Andric /// Represents ppc64 fixups and other ppc64-specific edge kinds.
2306c3fb27SDimitry Andric enum EdgeKind_ppc64 : Edge::Kind {
2406c3fb27SDimitry Andric   Pointer64 = Edge::FirstRelocation,
2506c3fb27SDimitry Andric   Pointer32,
26*5f757f3fSDimitry Andric   Pointer16,
27*5f757f3fSDimitry Andric   Pointer16DS,
28*5f757f3fSDimitry Andric   Pointer16HA,
29*5f757f3fSDimitry Andric   Pointer16HI,
30*5f757f3fSDimitry Andric   Pointer16HIGH,
31*5f757f3fSDimitry Andric   Pointer16HIGHA,
32*5f757f3fSDimitry Andric   Pointer16HIGHER,
33*5f757f3fSDimitry Andric   Pointer16HIGHERA,
34*5f757f3fSDimitry Andric   Pointer16HIGHEST,
35*5f757f3fSDimitry Andric   Pointer16HIGHESTA,
36*5f757f3fSDimitry Andric   Pointer16LO,
37*5f757f3fSDimitry Andric   Pointer16LODS,
38*5f757f3fSDimitry Andric   Pointer14,
3906c3fb27SDimitry Andric   Delta64,
40*5f757f3fSDimitry Andric   Delta34,
4106c3fb27SDimitry Andric   Delta32,
4206c3fb27SDimitry Andric   NegDelta32,
4306c3fb27SDimitry Andric   Delta16,
4406c3fb27SDimitry Andric   Delta16HA,
45*5f757f3fSDimitry Andric   Delta16HI,
4606c3fb27SDimitry Andric   Delta16LO,
47*5f757f3fSDimitry Andric   TOC,
48*5f757f3fSDimitry Andric   TOCDelta16,
4906c3fb27SDimitry Andric   TOCDelta16DS,
50*5f757f3fSDimitry Andric   TOCDelta16HA,
51*5f757f3fSDimitry Andric   TOCDelta16HI,
52*5f757f3fSDimitry Andric   TOCDelta16LO,
5306c3fb27SDimitry Andric   TOCDelta16LODS,
54*5f757f3fSDimitry Andric   RequestGOTAndTransformToDelta34,
5506c3fb27SDimitry Andric   CallBranchDelta,
5606c3fb27SDimitry Andric   // Need to restore r2 after the bl, suggesting the bl is followed by a nop.
5706c3fb27SDimitry Andric   CallBranchDeltaRestoreTOC,
58*5f757f3fSDimitry Andric   // Request calling function with TOC.
59*5f757f3fSDimitry Andric   RequestCall,
60*5f757f3fSDimitry Andric   // Request calling function without TOC.
61*5f757f3fSDimitry Andric   RequestCallNoTOC,
62*5f757f3fSDimitry Andric   RequestTLSDescInGOTAndTransformToTOCDelta16HA,
63*5f757f3fSDimitry Andric   RequestTLSDescInGOTAndTransformToTOCDelta16LO,
64*5f757f3fSDimitry Andric   RequestTLSDescInGOTAndTransformToDelta34,
6506c3fb27SDimitry Andric };
6606c3fb27SDimitry Andric 
6706c3fb27SDimitry Andric enum PLTCallStubKind {
68*5f757f3fSDimitry Andric   // Setup function entry(r12) and long branch to target using TOC.
6906c3fb27SDimitry Andric   LongBranch,
70*5f757f3fSDimitry Andric   // Save TOC pointer, setup function entry and long branch to target using TOC.
7106c3fb27SDimitry Andric   LongBranchSaveR2,
72*5f757f3fSDimitry Andric   // Setup function entry(r12) and long branch to target without using TOC.
7306c3fb27SDimitry Andric   LongBranchNoTOC,
7406c3fb27SDimitry Andric };
7506c3fb27SDimitry Andric 
7606c3fb27SDimitry Andric extern const char NullPointerContent[8];
7706c3fb27SDimitry Andric extern const char PointerJumpStubContent_big[20];
7806c3fb27SDimitry Andric extern const char PointerJumpStubContent_little[20];
7906c3fb27SDimitry Andric extern const char PointerJumpStubNoTOCContent_big[32];
8006c3fb27SDimitry Andric extern const char PointerJumpStubNoTOCContent_little[32];
8106c3fb27SDimitry Andric 
8206c3fb27SDimitry Andric struct PLTCallStubReloc {
8306c3fb27SDimitry Andric   Edge::Kind K;
8406c3fb27SDimitry Andric   size_t Offset;
8506c3fb27SDimitry Andric   Edge::AddendT A;
8606c3fb27SDimitry Andric };
8706c3fb27SDimitry Andric 
8806c3fb27SDimitry Andric struct PLTCallStubInfo {
8906c3fb27SDimitry Andric   ArrayRef<char> Content;
9006c3fb27SDimitry Andric   SmallVector<PLTCallStubReloc, 2> Relocs;
9106c3fb27SDimitry Andric };
9206c3fb27SDimitry Andric 
93*5f757f3fSDimitry Andric template <llvm::endianness Endianness>
pickStub(PLTCallStubKind StubKind)9406c3fb27SDimitry Andric inline PLTCallStubInfo pickStub(PLTCallStubKind StubKind) {
95*5f757f3fSDimitry Andric   constexpr bool isLE = Endianness == llvm::endianness::little;
9606c3fb27SDimitry Andric   switch (StubKind) {
9706c3fb27SDimitry Andric   case LongBranch: {
9806c3fb27SDimitry Andric     ArrayRef<char> Content =
9906c3fb27SDimitry Andric         isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
10006c3fb27SDimitry Andric     // Skip save r2.
10106c3fb27SDimitry Andric     Content = Content.slice(4);
102*5f757f3fSDimitry Andric     size_t Offset = isLE ? 0 : 2;
10306c3fb27SDimitry Andric     return PLTCallStubInfo{
10406c3fb27SDimitry Andric         Content,
105*5f757f3fSDimitry Andric         {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 0}},
10606c3fb27SDimitry Andric     };
10706c3fb27SDimitry Andric   }
10806c3fb27SDimitry Andric   case LongBranchSaveR2: {
10906c3fb27SDimitry Andric     ArrayRef<char> Content =
11006c3fb27SDimitry Andric         isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
111*5f757f3fSDimitry Andric     size_t Offset = isLE ? 4 : 6;
11206c3fb27SDimitry Andric     return PLTCallStubInfo{
11306c3fb27SDimitry Andric         Content,
114*5f757f3fSDimitry Andric         {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 0}},
11506c3fb27SDimitry Andric     };
11606c3fb27SDimitry Andric   }
11706c3fb27SDimitry Andric   case LongBranchNoTOC: {
11806c3fb27SDimitry Andric     ArrayRef<char> Content = isLE ? PointerJumpStubNoTOCContent_little
11906c3fb27SDimitry Andric                                   : PointerJumpStubNoTOCContent_big;
120*5f757f3fSDimitry Andric     size_t Offset = isLE ? 16 : 18;
121*5f757f3fSDimitry Andric     Edge::AddendT Addend = isLE ? 8 : 10;
12206c3fb27SDimitry Andric     return PLTCallStubInfo{
12306c3fb27SDimitry Andric         Content,
124*5f757f3fSDimitry Andric         {{Delta16HA, Offset, Addend}, {Delta16LO, Offset + 4, Addend + 4}},
12506c3fb27SDimitry Andric     };
12606c3fb27SDimitry Andric   }
12706c3fb27SDimitry Andric   }
12806c3fb27SDimitry Andric   llvm_unreachable("Unknown PLTCallStubKind enum");
12906c3fb27SDimitry Andric }
13006c3fb27SDimitry Andric 
13106c3fb27SDimitry Andric inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
13206c3fb27SDimitry Andric                                       Symbol *InitialTarget = nullptr,
13306c3fb27SDimitry Andric                                       uint64_t InitialAddend = 0) {
13406c3fb27SDimitry Andric   assert(G.getPointerSize() == sizeof(NullPointerContent) &&
13506c3fb27SDimitry Andric          "LinkGraph's pointer size should be consistent with size of "
13606c3fb27SDimitry Andric          "NullPointerContent");
13706c3fb27SDimitry Andric   Block &B = G.createContentBlock(PointerSection, NullPointerContent,
13806c3fb27SDimitry Andric                                   orc::ExecutorAddr(), G.getPointerSize(), 0);
13906c3fb27SDimitry Andric   if (InitialTarget)
14006c3fb27SDimitry Andric     B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend);
14106c3fb27SDimitry Andric   return G.addAnonymousSymbol(B, 0, G.getPointerSize(), false, false);
14206c3fb27SDimitry Andric }
14306c3fb27SDimitry Andric 
144*5f757f3fSDimitry Andric template <llvm::endianness Endianness>
createAnonymousPointerJumpStub(LinkGraph & G,Section & StubSection,Symbol & PointerSymbol,PLTCallStubKind StubKind)14506c3fb27SDimitry Andric inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
14606c3fb27SDimitry Andric                                               Section &StubSection,
14706c3fb27SDimitry Andric                                               Symbol &PointerSymbol,
14806c3fb27SDimitry Andric                                               PLTCallStubKind StubKind) {
14906c3fb27SDimitry Andric   PLTCallStubInfo StubInfo = pickStub<Endianness>(StubKind);
15006c3fb27SDimitry Andric   Block &B = G.createContentBlock(StubSection, StubInfo.Content,
15106c3fb27SDimitry Andric                                   orc::ExecutorAddr(), 4, 0);
15206c3fb27SDimitry Andric   for (auto const &Reloc : StubInfo.Relocs)
15306c3fb27SDimitry Andric     B.addEdge(Reloc.K, Reloc.Offset, PointerSymbol, Reloc.A);
15406c3fb27SDimitry Andric   return G.addAnonymousSymbol(B, 0, StubInfo.Content.size(), true, false);
15506c3fb27SDimitry Andric }
15606c3fb27SDimitry Andric 
157*5f757f3fSDimitry Andric template <llvm::endianness Endianness>
15806c3fb27SDimitry Andric class TOCTableManager : public TableManager<TOCTableManager<Endianness>> {
15906c3fb27SDimitry Andric public:
16006c3fb27SDimitry Andric   // FIXME: `llvm-jitlink -check` relies this name to be $__GOT.
getSectionName()16106c3fb27SDimitry Andric   static StringRef getSectionName() { return "$__GOT"; }
16206c3fb27SDimitry Andric 
visitEdge(LinkGraph & G,Block * B,Edge & E)16306c3fb27SDimitry Andric   bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
16406c3fb27SDimitry Andric     Edge::Kind K = E.getKind();
16506c3fb27SDimitry Andric     switch (K) {
16606c3fb27SDimitry Andric     case TOCDelta16HA:
16706c3fb27SDimitry Andric     case TOCDelta16LO:
16806c3fb27SDimitry Andric     case TOCDelta16DS:
16906c3fb27SDimitry Andric     case TOCDelta16LODS:
17006c3fb27SDimitry Andric     case CallBranchDeltaRestoreTOC:
171*5f757f3fSDimitry Andric     case RequestCall:
17206c3fb27SDimitry Andric       // Create TOC section if TOC relocation, PLT or GOT is used.
17306c3fb27SDimitry Andric       getOrCreateTOCSection(G);
17406c3fb27SDimitry Andric       return false;
175*5f757f3fSDimitry Andric     case RequestGOTAndTransformToDelta34:
176*5f757f3fSDimitry Andric       E.setKind(ppc64::Delta34);
177*5f757f3fSDimitry Andric       E.setTarget(createEntry(G, E.getTarget()));
178*5f757f3fSDimitry Andric       return true;
17906c3fb27SDimitry Andric     default:
18006c3fb27SDimitry Andric       return false;
18106c3fb27SDimitry Andric     }
18206c3fb27SDimitry Andric   }
18306c3fb27SDimitry Andric 
createEntry(LinkGraph & G,Symbol & Target)18406c3fb27SDimitry Andric   Symbol &createEntry(LinkGraph &G, Symbol &Target) {
18506c3fb27SDimitry Andric     return createAnonymousPointer(G, getOrCreateTOCSection(G), &Target);
18606c3fb27SDimitry Andric   }
18706c3fb27SDimitry Andric 
18806c3fb27SDimitry Andric private:
getOrCreateTOCSection(LinkGraph & G)18906c3fb27SDimitry Andric   Section &getOrCreateTOCSection(LinkGraph &G) {
19006c3fb27SDimitry Andric     TOCSection = G.findSectionByName(getSectionName());
19106c3fb27SDimitry Andric     if (!TOCSection)
19206c3fb27SDimitry Andric       TOCSection = &G.createSection(getSectionName(), orc::MemProt::Read);
19306c3fb27SDimitry Andric     return *TOCSection;
19406c3fb27SDimitry Andric   }
19506c3fb27SDimitry Andric 
19606c3fb27SDimitry Andric   Section *TOCSection = nullptr;
19706c3fb27SDimitry Andric };
19806c3fb27SDimitry Andric 
199*5f757f3fSDimitry Andric template <llvm::endianness Endianness>
20006c3fb27SDimitry Andric class PLTTableManager : public TableManager<PLTTableManager<Endianness>> {
20106c3fb27SDimitry Andric public:
PLTTableManager(TOCTableManager<Endianness> & TOC)20206c3fb27SDimitry Andric   PLTTableManager(TOCTableManager<Endianness> &TOC) : TOC(TOC) {}
20306c3fb27SDimitry Andric 
getSectionName()20406c3fb27SDimitry Andric   static StringRef getSectionName() { return "$__STUBS"; }
20506c3fb27SDimitry Andric 
206*5f757f3fSDimitry Andric   // FIXME: One external symbol can only have one PLT stub in a object file.
207*5f757f3fSDimitry Andric   // This is a limitation when we need different PLT stubs for the same symbol.
208*5f757f3fSDimitry Andric   // For example, we need two different PLT stubs for `bl __tls_get_addr` and
209*5f757f3fSDimitry Andric   // `bl __tls_get_addr@notoc`.
visitEdge(LinkGraph & G,Block * B,Edge & E)21006c3fb27SDimitry Andric   bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
211*5f757f3fSDimitry Andric     bool isExternal = E.getTarget().isExternal();
21206c3fb27SDimitry Andric     Edge::Kind K = E.getKind();
213*5f757f3fSDimitry Andric     if (K == ppc64::RequestCall) {
214*5f757f3fSDimitry Andric       if (isExternal) {
21506c3fb27SDimitry Andric         E.setKind(ppc64::CallBranchDeltaRestoreTOC);
21606c3fb27SDimitry Andric         this->StubKind = LongBranchSaveR2;
217*5f757f3fSDimitry Andric         // FIXME: We assume the addend to the external target is zero. It's
218*5f757f3fSDimitry Andric         // quite unusual that the addend of an external target to be non-zero as
219*5f757f3fSDimitry Andric         // if we have known the layout of the external object.
22006c3fb27SDimitry Andric         E.setTarget(this->getEntryForTarget(G, E.getTarget()));
221*5f757f3fSDimitry Andric         // Addend to the stub is zero.
222*5f757f3fSDimitry Andric         E.setAddend(0);
223*5f757f3fSDimitry Andric       } else
224*5f757f3fSDimitry Andric         // TODO: There are cases a local function call need a call stub.
225*5f757f3fSDimitry Andric         // 1. Caller uses TOC, the callee doesn't, need a r2 save stub.
226*5f757f3fSDimitry Andric         // 2. Caller doesn't use TOC, the callee does, need a r12 setup stub.
227*5f757f3fSDimitry Andric         // 3. Branching target is out of range.
228*5f757f3fSDimitry Andric         E.setKind(ppc64::CallBranchDelta);
22906c3fb27SDimitry Andric       return true;
23006c3fb27SDimitry Andric     }
231*5f757f3fSDimitry Andric     if (K == ppc64::RequestCallNoTOC) {
23206c3fb27SDimitry Andric       E.setKind(ppc64::CallBranchDelta);
23306c3fb27SDimitry Andric       this->StubKind = LongBranchNoTOC;
23406c3fb27SDimitry Andric       E.setTarget(this->getEntryForTarget(G, E.getTarget()));
23506c3fb27SDimitry Andric       return true;
23606c3fb27SDimitry Andric     }
23706c3fb27SDimitry Andric     return false;
23806c3fb27SDimitry Andric   }
23906c3fb27SDimitry Andric 
createEntry(LinkGraph & G,Symbol & Target)24006c3fb27SDimitry Andric   Symbol &createEntry(LinkGraph &G, Symbol &Target) {
24106c3fb27SDimitry Andric     return createAnonymousPointerJumpStub<Endianness>(
24206c3fb27SDimitry Andric         G, getOrCreateStubsSection(G), TOC.getEntryForTarget(G, Target),
24306c3fb27SDimitry Andric         this->StubKind);
24406c3fb27SDimitry Andric   }
24506c3fb27SDimitry Andric 
24606c3fb27SDimitry Andric private:
getOrCreateStubsSection(LinkGraph & G)24706c3fb27SDimitry Andric   Section &getOrCreateStubsSection(LinkGraph &G) {
24806c3fb27SDimitry Andric     PLTSection = G.findSectionByName(getSectionName());
24906c3fb27SDimitry Andric     if (!PLTSection)
25006c3fb27SDimitry Andric       PLTSection = &G.createSection(getSectionName(),
25106c3fb27SDimitry Andric                                     orc::MemProt::Read | orc::MemProt::Exec);
25206c3fb27SDimitry Andric     return *PLTSection;
25306c3fb27SDimitry Andric   }
25406c3fb27SDimitry Andric 
25506c3fb27SDimitry Andric   TOCTableManager<Endianness> &TOC;
25606c3fb27SDimitry Andric   Section *PLTSection = nullptr;
25706c3fb27SDimitry Andric   PLTCallStubKind StubKind;
25806c3fb27SDimitry Andric };
25906c3fb27SDimitry Andric 
26006c3fb27SDimitry Andric /// Returns a string name for the given ppc64 edge. For debugging purposes
26106c3fb27SDimitry Andric /// only.
26206c3fb27SDimitry Andric const char *getEdgeKindName(Edge::Kind K);
26306c3fb27SDimitry Andric 
ha(uint64_t x)264*5f757f3fSDimitry Andric inline static uint16_t ha(uint64_t x) { return (x + 0x8000) >> 16; }
lo(uint64_t x)265*5f757f3fSDimitry Andric inline static uint64_t lo(uint64_t x) { return x & 0xffff; }
hi(uint64_t x)266*5f757f3fSDimitry Andric inline static uint16_t hi(uint64_t x) { return x >> 16; }
high(uint64_t x)267*5f757f3fSDimitry Andric inline static uint64_t high(uint64_t x) { return (x >> 16) & 0xffff; }
higha(uint64_t x)268*5f757f3fSDimitry Andric inline static uint64_t higha(uint64_t x) {
269*5f757f3fSDimitry Andric   return ((x + 0x8000) >> 16) & 0xffff;
270*5f757f3fSDimitry Andric }
higher(uint64_t x)271*5f757f3fSDimitry Andric inline static uint64_t higher(uint64_t x) { return (x >> 32) & 0xffff; }
highera(uint64_t x)272*5f757f3fSDimitry Andric inline static uint64_t highera(uint64_t x) {
273*5f757f3fSDimitry Andric   return ((x + 0x8000) >> 32) & 0xffff;
274*5f757f3fSDimitry Andric }
highest(uint64_t x)275*5f757f3fSDimitry Andric inline static uint16_t highest(uint64_t x) { return x >> 48; }
highesta(uint64_t x)276*5f757f3fSDimitry Andric inline static uint16_t highesta(uint64_t x) { return (x + 0x8000) >> 48; }
27706c3fb27SDimitry Andric 
278*5f757f3fSDimitry Andric // Prefixed instruction introduced in ISAv3.1 consists of two 32-bit words,
279*5f757f3fSDimitry Andric // prefix word and suffix word, i.e., prefixed_instruction = concat(prefix_word,
280*5f757f3fSDimitry Andric // suffix_word). That's to say, for a prefixed instruction encoded in uint64_t,
281*5f757f3fSDimitry Andric // the most significant 32 bits belong to the prefix word. The prefix word is at
282*5f757f3fSDimitry Andric // low address for both big/little endian. Byte order in each word still follows
283*5f757f3fSDimitry Andric // its endian.
284*5f757f3fSDimitry Andric template <llvm::endianness Endianness>
readPrefixedInstruction(const char * Loc)285*5f757f3fSDimitry Andric inline static uint64_t readPrefixedInstruction(const char *Loc) {
286*5f757f3fSDimitry Andric   constexpr bool isLE = Endianness == llvm::endianness::little;
287*5f757f3fSDimitry Andric   uint64_t Inst = support::endian::read64<Endianness>(Loc);
288*5f757f3fSDimitry Andric   return isLE ? (Inst << 32) | (Inst >> 32) : Inst;
289*5f757f3fSDimitry Andric }
290*5f757f3fSDimitry Andric 
291*5f757f3fSDimitry Andric template <llvm::endianness Endianness>
writePrefixedInstruction(char * Loc,uint64_t Inst)292*5f757f3fSDimitry Andric inline static void writePrefixedInstruction(char *Loc, uint64_t Inst) {
293*5f757f3fSDimitry Andric   constexpr bool isLE = Endianness == llvm::endianness::little;
294*5f757f3fSDimitry Andric   Inst = isLE ? (Inst << 32) | (Inst >> 32) : Inst;
295*5f757f3fSDimitry Andric   support::endian::write64<Endianness>(Loc, Inst);
296*5f757f3fSDimitry Andric }
297*5f757f3fSDimitry Andric 
298*5f757f3fSDimitry Andric template <llvm::endianness Endianness>
relocateHalf16(char * FixupPtr,int64_t Value,Edge::Kind K)299*5f757f3fSDimitry Andric inline Error relocateHalf16(char *FixupPtr, int64_t Value, Edge::Kind K) {
300*5f757f3fSDimitry Andric   switch (K) {
301*5f757f3fSDimitry Andric   case Delta16:
302*5f757f3fSDimitry Andric   case Pointer16:
303*5f757f3fSDimitry Andric   case TOCDelta16:
304*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, Value);
305*5f757f3fSDimitry Andric     break;
306*5f757f3fSDimitry Andric   case Pointer16DS:
307*5f757f3fSDimitry Andric   case TOCDelta16DS:
308*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, Value & ~3);
309*5f757f3fSDimitry Andric     break;
310*5f757f3fSDimitry Andric   case Delta16HA:
311*5f757f3fSDimitry Andric   case Pointer16HA:
312*5f757f3fSDimitry Andric   case TOCDelta16HA:
313*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, ha(Value));
314*5f757f3fSDimitry Andric     break;
315*5f757f3fSDimitry Andric   case Delta16HI:
316*5f757f3fSDimitry Andric   case Pointer16HI:
317*5f757f3fSDimitry Andric   case TOCDelta16HI:
318*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, hi(Value));
319*5f757f3fSDimitry Andric     break;
320*5f757f3fSDimitry Andric   case Pointer16HIGH:
321*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, high(Value));
322*5f757f3fSDimitry Andric     break;
323*5f757f3fSDimitry Andric   case Pointer16HIGHA:
324*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, higha(Value));
325*5f757f3fSDimitry Andric     break;
326*5f757f3fSDimitry Andric   case Pointer16HIGHER:
327*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, higher(Value));
328*5f757f3fSDimitry Andric     break;
329*5f757f3fSDimitry Andric   case Pointer16HIGHERA:
330*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, highera(Value));
331*5f757f3fSDimitry Andric     break;
332*5f757f3fSDimitry Andric   case Pointer16HIGHEST:
333*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, highest(Value));
334*5f757f3fSDimitry Andric     break;
335*5f757f3fSDimitry Andric   case Pointer16HIGHESTA:
336*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, highesta(Value));
337*5f757f3fSDimitry Andric     break;
338*5f757f3fSDimitry Andric   case Delta16LO:
339*5f757f3fSDimitry Andric   case Pointer16LO:
340*5f757f3fSDimitry Andric   case TOCDelta16LO:
341*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, lo(Value));
342*5f757f3fSDimitry Andric     break;
343*5f757f3fSDimitry Andric   case Pointer16LODS:
344*5f757f3fSDimitry Andric   case TOCDelta16LODS:
345*5f757f3fSDimitry Andric     support::endian::write16<Endianness>(FixupPtr, lo(Value) & ~3);
346*5f757f3fSDimitry Andric     break;
347*5f757f3fSDimitry Andric   default:
348*5f757f3fSDimitry Andric     return make_error<JITLinkError>(
349*5f757f3fSDimitry Andric         StringRef(getEdgeKindName(K)) +
350*5f757f3fSDimitry Andric         " relocation does not write at half16 field");
351*5f757f3fSDimitry Andric   }
352*5f757f3fSDimitry Andric   return Error::success();
353*5f757f3fSDimitry Andric }
35406c3fb27SDimitry Andric 
35506c3fb27SDimitry Andric /// Apply fixup expression for edge to block content.
356*5f757f3fSDimitry Andric template <llvm::endianness Endianness>
applyFixup(LinkGraph & G,Block & B,const Edge & E,const Symbol * TOCSymbol)35706c3fb27SDimitry Andric inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
35806c3fb27SDimitry Andric                         const Symbol *TOCSymbol) {
35906c3fb27SDimitry Andric   char *BlockWorkingMem = B.getAlreadyMutableContent().data();
36006c3fb27SDimitry Andric   char *FixupPtr = BlockWorkingMem + E.getOffset();
36106c3fb27SDimitry Andric   orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
36206c3fb27SDimitry Andric   int64_t S = E.getTarget().getAddress().getValue();
36306c3fb27SDimitry Andric   int64_t A = E.getAddend();
36406c3fb27SDimitry Andric   int64_t P = FixupAddress.getValue();
36506c3fb27SDimitry Andric   int64_t TOCBase = TOCSymbol ? TOCSymbol->getAddress().getValue() : 0;
36606c3fb27SDimitry Andric   Edge::Kind K = E.getKind();
36706c3fb27SDimitry Andric 
36806c3fb27SDimitry Andric   DEBUG_WITH_TYPE("jitlink", {
36906c3fb27SDimitry Andric     dbgs() << "    Applying fixup on " << G.getEdgeKindName(K)
37006c3fb27SDimitry Andric            << " edge, (S, A, P, .TOC.) = (" << formatv("{0:x}", S) << ", "
37106c3fb27SDimitry Andric            << formatv("{0:x}", A) << ", " << formatv("{0:x}", P) << ", "
37206c3fb27SDimitry Andric            << formatv("{0:x}", TOCBase) << ")\n";
37306c3fb27SDimitry Andric   });
37406c3fb27SDimitry Andric 
37506c3fb27SDimitry Andric   switch (K) {
37606c3fb27SDimitry Andric   case Pointer64: {
37706c3fb27SDimitry Andric     uint64_t Value = S + A;
37806c3fb27SDimitry Andric     support::endian::write64<Endianness>(FixupPtr, Value);
37906c3fb27SDimitry Andric     break;
38006c3fb27SDimitry Andric   }
381*5f757f3fSDimitry Andric   case Delta16:
38206c3fb27SDimitry Andric   case Delta16HA:
383*5f757f3fSDimitry Andric   case Delta16HI:
38406c3fb27SDimitry Andric   case Delta16LO: {
38506c3fb27SDimitry Andric     int64_t Value = S + A - P;
38606c3fb27SDimitry Andric     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
38706c3fb27SDimitry Andric       return makeTargetOutOfRangeError(G, B, E);
38806c3fb27SDimitry Andric     }
389*5f757f3fSDimitry Andric     return relocateHalf16<Endianness>(FixupPtr, Value, K);
39006c3fb27SDimitry Andric   }
391*5f757f3fSDimitry Andric   case TOC:
392*5f757f3fSDimitry Andric     support::endian::write64<Endianness>(FixupPtr, TOCBase);
393*5f757f3fSDimitry Andric     break;
394*5f757f3fSDimitry Andric   case Pointer16:
395*5f757f3fSDimitry Andric   case Pointer16DS:
396*5f757f3fSDimitry Andric   case Pointer16HA:
397*5f757f3fSDimitry Andric   case Pointer16HI:
398*5f757f3fSDimitry Andric   case Pointer16HIGH:
399*5f757f3fSDimitry Andric   case Pointer16HIGHA:
400*5f757f3fSDimitry Andric   case Pointer16HIGHER:
401*5f757f3fSDimitry Andric   case Pointer16HIGHERA:
402*5f757f3fSDimitry Andric   case Pointer16HIGHEST:
403*5f757f3fSDimitry Andric   case Pointer16HIGHESTA:
404*5f757f3fSDimitry Andric   case Pointer16LO:
405*5f757f3fSDimitry Andric   case Pointer16LODS: {
406*5f757f3fSDimitry Andric     uint64_t Value = S + A;
40706c3fb27SDimitry Andric     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
40806c3fb27SDimitry Andric       return makeTargetOutOfRangeError(G, B, E);
40906c3fb27SDimitry Andric     }
410*5f757f3fSDimitry Andric     return relocateHalf16<Endianness>(FixupPtr, Value, K);
411*5f757f3fSDimitry Andric   }
412*5f757f3fSDimitry Andric   case Pointer14: {
413*5f757f3fSDimitry Andric     static const uint32_t Low14Mask = 0xfffc;
414*5f757f3fSDimitry Andric     uint64_t Value = S + A;
415*5f757f3fSDimitry Andric     assert((Value & 3) == 0 && "Pointer14 requires 4-byte alignment");
416*5f757f3fSDimitry Andric     if (LLVM_UNLIKELY(!isInt<16>(Value))) {
417*5f757f3fSDimitry Andric       return makeTargetOutOfRangeError(G, B, E);
418*5f757f3fSDimitry Andric     }
419*5f757f3fSDimitry Andric     uint32_t Inst = support::endian::read32<Endianness>(FixupPtr);
420*5f757f3fSDimitry Andric     support::endian::write32<Endianness>(FixupPtr, (Inst & ~Low14Mask) |
421*5f757f3fSDimitry Andric                                                        (Value & Low14Mask));
42206c3fb27SDimitry Andric     break;
42306c3fb27SDimitry Andric   }
424*5f757f3fSDimitry Andric   case TOCDelta16:
42506c3fb27SDimitry Andric   case TOCDelta16DS:
426*5f757f3fSDimitry Andric   case TOCDelta16HA:
427*5f757f3fSDimitry Andric   case TOCDelta16HI:
428*5f757f3fSDimitry Andric   case TOCDelta16LO:
42906c3fb27SDimitry Andric   case TOCDelta16LODS: {
43006c3fb27SDimitry Andric     int64_t Value = S + A - TOCBase;
43106c3fb27SDimitry Andric     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
43206c3fb27SDimitry Andric       return makeTargetOutOfRangeError(G, B, E);
43306c3fb27SDimitry Andric     }
434*5f757f3fSDimitry Andric     return relocateHalf16<Endianness>(FixupPtr, Value, K);
43506c3fb27SDimitry Andric   }
43606c3fb27SDimitry Andric   case CallBranchDeltaRestoreTOC:
43706c3fb27SDimitry Andric   case CallBranchDelta: {
43806c3fb27SDimitry Andric     int64_t Value = S + A - P;
43906c3fb27SDimitry Andric     if (LLVM_UNLIKELY(!isInt<26>(Value))) {
44006c3fb27SDimitry Andric       return makeTargetOutOfRangeError(G, B, E);
44106c3fb27SDimitry Andric     }
44206c3fb27SDimitry Andric     uint32_t Inst = support::endian::read32<Endianness>(FixupPtr);
44306c3fb27SDimitry Andric     support::endian::write32<Endianness>(FixupPtr, (Inst & 0xfc000003) |
44406c3fb27SDimitry Andric                                                        (Value & 0x03fffffc));
44506c3fb27SDimitry Andric     if (K == CallBranchDeltaRestoreTOC) {
44606c3fb27SDimitry Andric       uint32_t NopInst = support::endian::read32<Endianness>(FixupPtr + 4);
44706c3fb27SDimitry Andric       assert(NopInst == 0x60000000 &&
44806c3fb27SDimitry Andric              "NOP should be placed here for restoring r2");
44906c3fb27SDimitry Andric       (void)NopInst;
45006c3fb27SDimitry Andric       // Restore r2 by instruction 0xe8410018 which is `ld r2, 24(r1)`.
45106c3fb27SDimitry Andric       support::endian::write32<Endianness>(FixupPtr + 4, 0xe8410018);
45206c3fb27SDimitry Andric     }
45306c3fb27SDimitry Andric     break;
45406c3fb27SDimitry Andric   }
45506c3fb27SDimitry Andric   case Delta64: {
45606c3fb27SDimitry Andric     int64_t Value = S + A - P;
45706c3fb27SDimitry Andric     support::endian::write64<Endianness>(FixupPtr, Value);
45806c3fb27SDimitry Andric     break;
45906c3fb27SDimitry Andric   }
460*5f757f3fSDimitry Andric   case Delta34: {
461*5f757f3fSDimitry Andric     int64_t Value = S + A - P;
462*5f757f3fSDimitry Andric     if (!LLVM_UNLIKELY(isInt<34>(Value)))
463*5f757f3fSDimitry Andric       return makeTargetOutOfRangeError(G, B, E);
464*5f757f3fSDimitry Andric     static const uint64_t SI0Mask = 0x00000003ffff0000;
465*5f757f3fSDimitry Andric     static const uint64_t SI1Mask = 0x000000000000ffff;
466*5f757f3fSDimitry Andric     static const uint64_t FullMask = 0x0003ffff0000ffff;
467*5f757f3fSDimitry Andric     uint64_t Inst = readPrefixedInstruction<Endianness>(FixupPtr) & ~FullMask;
468*5f757f3fSDimitry Andric     writePrefixedInstruction<Endianness>(
469*5f757f3fSDimitry Andric         FixupPtr, Inst | ((Value & SI0Mask) << 16) | (Value & SI1Mask));
470*5f757f3fSDimitry Andric     break;
471*5f757f3fSDimitry Andric   }
47206c3fb27SDimitry Andric   case Delta32: {
47306c3fb27SDimitry Andric     int64_t Value = S + A - P;
47406c3fb27SDimitry Andric     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
47506c3fb27SDimitry Andric       return makeTargetOutOfRangeError(G, B, E);
47606c3fb27SDimitry Andric     }
47706c3fb27SDimitry Andric     support::endian::write32<Endianness>(FixupPtr, Value);
47806c3fb27SDimitry Andric     break;
47906c3fb27SDimitry Andric   }
48006c3fb27SDimitry Andric   case NegDelta32: {
48106c3fb27SDimitry Andric     int64_t Value = P - S + A;
48206c3fb27SDimitry Andric     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
48306c3fb27SDimitry Andric       return makeTargetOutOfRangeError(G, B, E);
48406c3fb27SDimitry Andric     }
48506c3fb27SDimitry Andric     support::endian::write32<Endianness>(FixupPtr, Value);
48606c3fb27SDimitry Andric     break;
48706c3fb27SDimitry Andric   }
48806c3fb27SDimitry Andric   default:
48906c3fb27SDimitry Andric     return make_error<JITLinkError>(
49006c3fb27SDimitry Andric         "In graph " + G.getName() + ", section " + B.getSection().getName() +
49106c3fb27SDimitry Andric         " unsupported edge kind " + getEdgeKindName(E.getKind()));
49206c3fb27SDimitry Andric   }
49306c3fb27SDimitry Andric   return Error::success();
49406c3fb27SDimitry Andric }
49506c3fb27SDimitry Andric 
49606c3fb27SDimitry Andric } // end namespace llvm::jitlink::ppc64
49706c3fb27SDimitry Andric 
49806c3fb27SDimitry Andric #endif // LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
499