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