LLVM 22.0.0git
ppc64.h
Go to the documentation of this file.
1//===--- ppc64.h - Generic JITLink ppc64 edge kinds, utilities --*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Generic utilities for graphs representing 64-bit PowerPC objects.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
14#define LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
15
19#include "llvm/Support/Endian.h"
20
22
23/// Represents ppc64 fixups and other ppc64-specific edge kinds.
24enum EdgeKind_ppc64 : Edge::Kind {
25 Pointer64 = Edge::FirstRelocation,
57 // Need to restore r2 after the bl, suggesting the bl is followed by a nop.
59 // Request calling function with TOC.
61 // Request calling function without TOC.
66};
67
69 // Setup function entry(r12) and long branch to target using TOC.
71 // Save TOC pointer, setup function entry and long branch to target using TOC.
73 // Setup function entry(r12) and long branch to target without using TOC.
75};
76
77LLVM_ABI extern const char NullPointerContent[8];
78LLVM_ABI extern const char PointerJumpStubContent_big[20];
79LLVM_ABI extern const char PointerJumpStubContent_little[20];
80LLVM_ABI extern const char PointerJumpStubNoTOCContent_big[32];
82
84 Edge::Kind K;
85 size_t Offset;
86 Edge::AddendT A;
87};
88
92};
93
94template <llvm::endianness Endianness>
96 constexpr bool isLE = Endianness == llvm::endianness::little;
97 switch (StubKind) {
98 case LongBranch: {
101 // Skip save r2.
102 Content = Content.slice(4);
103 size_t Offset = isLE ? 0 : 2;
104 return PLTCallStubInfo{
105 Content,
106 {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 0}},
107 };
108 }
109 case LongBranchSaveR2: {
112 size_t Offset = isLE ? 4 : 6;
113 return PLTCallStubInfo{
114 Content,
115 {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 0}},
116 };
117 }
118 case LongBranchNoTOC: {
121 size_t Offset = isLE ? 16 : 18;
122 Edge::AddendT Addend = isLE ? 8 : 10;
123 return PLTCallStubInfo{
124 Content,
125 {{Delta16HA, Offset, Addend}, {Delta16LO, Offset + 4, Addend + 4}},
126 };
127 }
128 }
129 llvm_unreachable("Unknown PLTCallStubKind enum");
130}
131
133 Symbol *InitialTarget = nullptr,
134 uint64_t InitialAddend = 0) {
135 assert(G.getPointerSize() == sizeof(NullPointerContent) &&
136 "LinkGraph's pointer size should be consistent with size of "
137 "NullPointerContent");
138 Block &B = G.createContentBlock(PointerSection, NullPointerContent,
139 orc::ExecutorAddr(), G.getPointerSize(), 0);
140 if (InitialTarget)
141 B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend);
142 return G.addAnonymousSymbol(B, 0, G.getPointerSize(), false, false);
143}
144
145template <llvm::endianness Endianness>
147 Section &StubSection,
148 Symbol &PointerSymbol,
149 PLTCallStubKind StubKind) {
150 PLTCallStubInfo StubInfo = pickStub<Endianness>(StubKind);
151 Block &B = G.createContentBlock(StubSection, StubInfo.Content,
152 orc::ExecutorAddr(), 4, 0);
153 for (auto const &Reloc : StubInfo.Relocs)
154 B.addEdge(Reloc.K, Reloc.Offset, PointerSymbol, Reloc.A);
155 return G.addAnonymousSymbol(B, 0, StubInfo.Content.size(), true, false);
156}
157
158template <llvm::endianness Endianness>
159class TOCTableManager : public TableManager<TOCTableManager<Endianness>> {
160public:
161 // FIXME: `llvm-jitlink -check` relies this name to be $__GOT.
162 static StringRef getSectionName() { return "$__GOT"; }
163
165 Edge::Kind K = E.getKind();
166 switch (K) {
167 case TOCDelta16HA:
168 case TOCDelta16LO:
169 case TOCDelta16DS:
170 case TOCDelta16LODS:
172 case RequestCall:
173 // Create TOC section if TOC relocation, PLT or GOT is used.
174 getOrCreateTOCSection(G);
175 return false;
177 E.setKind(ppc64::Delta34);
178 E.setTarget(createEntry(G, E.getTarget()));
179 return true;
180 default:
181 return false;
182 }
183 }
184
186 return createAnonymousPointer(G, getOrCreateTOCSection(G), &Target);
187 }
188
189private:
190 Section &getOrCreateTOCSection(LinkGraph &G) {
191 TOCSection = G.findSectionByName(getSectionName());
192 if (!TOCSection)
193 TOCSection = &G.createSection(getSectionName(), orc::MemProt::Read);
194 return *TOCSection;
195 }
196
197 Section *TOCSection = nullptr;
198};
199
200template <llvm::endianness Endianness>
201class PLTTableManager : public TableManager<PLTTableManager<Endianness>> {
202public:
204
205 static StringRef getSectionName() { return "$__STUBS"; }
206
207 // FIXME: One external symbol can only have one PLT stub in a object file.
208 // This is a limitation when we need different PLT stubs for the same symbol.
209 // For example, we need two different PLT stubs for `bl __tls_get_addr` and
210 // `bl __tls_get_addr@notoc`.
212 bool isExternal = E.getTarget().isExternal();
213 Edge::Kind K = E.getKind();
214 if (K == ppc64::RequestCall) {
215 if (isExternal) {
217 this->StubKind = LongBranchSaveR2;
218 // FIXME: We assume the addend to the external target is zero. It's
219 // quite unusual that the addend of an external target to be non-zero as
220 // if we have known the layout of the external object.
221 E.setTarget(this->getEntryForTarget(G, E.getTarget()));
222 // Addend to the stub is zero.
223 E.setAddend(0);
224 } else
225 // TODO: There are cases a local function call need a call stub.
226 // 1. Caller uses TOC, the callee doesn't, need a r2 save stub.
227 // 2. Caller doesn't use TOC, the callee does, need a r12 setup stub.
228 // 3. Branching target is out of range.
229 E.setKind(ppc64::CallBranchDelta);
230 return true;
231 }
232 if (K == ppc64::RequestCallNoTOC) {
233 E.setKind(ppc64::CallBranchDelta);
234 this->StubKind = LongBranchNoTOC;
235 E.setTarget(this->getEntryForTarget(G, E.getTarget()));
236 return true;
237 }
238 return false;
239 }
240
242 return createAnonymousPointerJumpStub<Endianness>(
243 G, getOrCreateStubsSection(G), TOC.getEntryForTarget(G, Target),
244 this->StubKind);
245 }
246
247private:
248 Section &getOrCreateStubsSection(LinkGraph &G) {
249 PLTSection = G.findSectionByName(getSectionName());
250 if (!PLTSection)
251 PLTSection = &G.createSection(getSectionName(),
253 return *PLTSection;
254 }
255
256 TOCTableManager<Endianness> &TOC;
257 Section *PLTSection = nullptr;
258 PLTCallStubKind StubKind;
259};
260
261/// Returns a string name for the given ppc64 edge. For debugging purposes
262/// only.
263LLVM_ABI const char *getEdgeKindName(Edge::Kind K);
264
265inline static uint16_t ha(uint64_t x) { return (x + 0x8000) >> 16; }
266inline static uint64_t lo(uint64_t x) { return x & 0xffff; }
267inline static uint16_t hi(uint64_t x) { return x >> 16; }
268inline static uint64_t high(uint64_t x) { return (x >> 16) & 0xffff; }
269inline static uint64_t higha(uint64_t x) {
270 return ((x + 0x8000) >> 16) & 0xffff;
271}
272inline static uint64_t higher(uint64_t x) { return (x >> 32) & 0xffff; }
273inline static uint64_t highera(uint64_t x) {
274 return ((x + 0x8000) >> 32) & 0xffff;
275}
276inline static uint16_t highest(uint64_t x) { return x >> 48; }
277inline static uint16_t highesta(uint64_t x) { return (x + 0x8000) >> 48; }
278
279// Prefixed instruction introduced in ISAv3.1 consists of two 32-bit words,
280// prefix word and suffix word, i.e., prefixed_instruction = concat(prefix_word,
281// suffix_word). That's to say, for a prefixed instruction encoded in uint64_t,
282// the most significant 32 bits belong to the prefix word. The prefix word is at
283// low address for both big/little endian. Byte order in each word still follows
284// its endian.
285template <llvm::endianness Endianness>
286inline static uint64_t readPrefixedInstruction(const char *Loc) {
287 constexpr bool isLE = Endianness == llvm::endianness::little;
288 uint64_t Inst = support::endian::read64<Endianness>(Loc);
289 return isLE ? (Inst << 32) | (Inst >> 32) : Inst;
290}
291
292template <llvm::endianness Endianness>
293inline static void writePrefixedInstruction(char *Loc, uint64_t Inst) {
294 constexpr bool isLE = Endianness == llvm::endianness::little;
295 Inst = isLE ? (Inst << 32) | (Inst >> 32) : Inst;
296 support::endian::write64<Endianness>(Loc, Inst);
297}
298
299template <llvm::endianness Endianness>
300inline Error relocateHalf16(char *FixupPtr, int64_t Value, Edge::Kind K) {
301 switch (K) {
302 case Delta16:
303 case Pointer16:
304 case TOCDelta16:
305 support::endian::write16<Endianness>(FixupPtr, Value);
306 break;
307 case Pointer16DS:
308 case TOCDelta16DS:
309 support::endian::write16<Endianness>(FixupPtr, Value & ~3);
310 break;
311 case Delta16HA:
312 case Pointer16HA:
313 case TOCDelta16HA:
314 support::endian::write16<Endianness>(FixupPtr, ha(Value));
315 break;
316 case Delta16HI:
317 case Pointer16HI:
318 case TOCDelta16HI:
319 support::endian::write16<Endianness>(FixupPtr, hi(Value));
320 break;
321 case Pointer16HIGH:
322 support::endian::write16<Endianness>(FixupPtr, high(Value));
323 break;
324 case Pointer16HIGHA:
325 support::endian::write16<Endianness>(FixupPtr, higha(Value));
326 break;
327 case Pointer16HIGHER:
328 support::endian::write16<Endianness>(FixupPtr, higher(Value));
329 break;
330 case Pointer16HIGHERA:
331 support::endian::write16<Endianness>(FixupPtr, highera(Value));
332 break;
333 case Pointer16HIGHEST:
334 support::endian::write16<Endianness>(FixupPtr, highest(Value));
335 break;
337 support::endian::write16<Endianness>(FixupPtr, highesta(Value));
338 break;
339 case Delta16LO:
340 case Pointer16LO:
341 case TOCDelta16LO:
342 support::endian::write16<Endianness>(FixupPtr, lo(Value));
343 break;
344 case Pointer16LODS:
345 case TOCDelta16LODS:
346 support::endian::write16<Endianness>(FixupPtr, lo(Value) & ~3);
347 break;
348 default:
349 return make_error<JITLinkError>(
351 " relocation does not write at half16 field");
352 }
353 return Error::success();
354}
355
356/// Apply fixup expression for edge to block content.
357template <llvm::endianness Endianness>
359 const Symbol *TOCSymbol) {
360 char *BlockWorkingMem = B.getAlreadyMutableContent().data();
361 char *FixupPtr = BlockWorkingMem + E.getOffset();
362 orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
363 int64_t S = E.getTarget().getAddress().getValue();
364 int64_t A = E.getAddend();
365 int64_t P = FixupAddress.getValue();
366 int64_t TOCBase = TOCSymbol ? TOCSymbol->getAddress().getValue() : 0;
367 Edge::Kind K = E.getKind();
368
369 DEBUG_WITH_TYPE("jitlink", {
370 dbgs() << " Applying fixup on " << G.getEdgeKindName(K)
371 << " edge, (S, A, P, .TOC.) = (" << formatv("{0:x}", S) << ", "
372 << formatv("{0:x}", A) << ", " << formatv("{0:x}", P) << ", "
373 << formatv("{0:x}", TOCBase) << ")\n";
374 });
375
376 switch (K) {
377 case Pointer64: {
378 uint64_t Value = S + A;
379 support::endian::write64<Endianness>(FixupPtr, Value);
380 break;
381 }
382 case Delta16:
383 case Delta16HA:
384 case Delta16HI:
385 case Delta16LO: {
386 int64_t Value = S + A - P;
387 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
388 return makeTargetOutOfRangeError(G, B, E);
389 }
390 return relocateHalf16<Endianness>(FixupPtr, Value, K);
391 }
392 case TOC:
393 support::endian::write64<Endianness>(FixupPtr, TOCBase);
394 break;
395 case Pointer16:
396 case Pointer16DS:
397 case Pointer16HA:
398 case Pointer16HI:
399 case Pointer16HIGH:
400 case Pointer16HIGHA:
401 case Pointer16HIGHER:
402 case Pointer16HIGHERA:
403 case Pointer16HIGHEST:
405 case Pointer16LO:
406 case Pointer16LODS: {
407 uint64_t Value = S + A;
408 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
409 return makeTargetOutOfRangeError(G, B, E);
410 }
411 return relocateHalf16<Endianness>(FixupPtr, Value, K);
412 }
413 case Pointer14: {
414 static const uint32_t Low14Mask = 0xfffc;
415 uint64_t Value = S + A;
416 assert((Value & 3) == 0 && "Pointer14 requires 4-byte alignment");
417 if (LLVM_UNLIKELY(!isInt<16>(Value))) {
418 return makeTargetOutOfRangeError(G, B, E);
419 }
420 uint32_t Inst = support::endian::read32<Endianness>(FixupPtr);
421 support::endian::write32<Endianness>(FixupPtr, (Inst & ~Low14Mask) |
422 (Value & Low14Mask));
423 break;
424 }
425 case TOCDelta16:
426 case TOCDelta16DS:
427 case TOCDelta16HA:
428 case TOCDelta16HI:
429 case TOCDelta16LO:
430 case TOCDelta16LODS: {
431 int64_t Value = S + A - TOCBase;
432 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
433 return makeTargetOutOfRangeError(G, B, E);
434 }
435 return relocateHalf16<Endianness>(FixupPtr, Value, K);
436 }
438 case CallBranchDelta: {
439 int64_t Value = S + A - P;
440 if (LLVM_UNLIKELY(!isInt<26>(Value))) {
441 return makeTargetOutOfRangeError(G, B, E);
442 }
443 uint32_t Inst = support::endian::read32<Endianness>(FixupPtr);
444 support::endian::write32<Endianness>(FixupPtr, (Inst & 0xfc000003) |
445 (Value & 0x03fffffc));
446 if (K == CallBranchDeltaRestoreTOC) {
447 uint32_t NopInst = support::endian::read32<Endianness>(FixupPtr + 4);
448 assert(NopInst == 0x60000000 &&
449 "NOP should be placed here for restoring r2");
450 (void)NopInst;
451 // Restore r2 by instruction 0xe8410018 which is `ld r2, 24(r1)`.
452 support::endian::write32<Endianness>(FixupPtr + 4, 0xe8410018);
453 }
454 break;
455 }
456 case Delta64: {
457 int64_t Value = S + A - P;
458 support::endian::write64<Endianness>(FixupPtr, Value);
459 break;
460 }
461 case Delta34: {
462 int64_t Value = S + A - P;
463 if (!LLVM_UNLIKELY(isInt<34>(Value)))
464 return makeTargetOutOfRangeError(G, B, E);
465 static const uint64_t SI0Mask = 0x00000003ffff0000;
466 static const uint64_t SI1Mask = 0x000000000000ffff;
467 static const uint64_t FullMask = 0x0003ffff0000ffff;
468 uint64_t Inst = readPrefixedInstruction<Endianness>(FixupPtr) & ~FullMask;
469 writePrefixedInstruction<Endianness>(
470 FixupPtr, Inst | ((Value & SI0Mask) << 16) | (Value & SI1Mask));
471 break;
472 }
473 case Delta32: {
474 int64_t Value = S + A - P;
475 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
476 return makeTargetOutOfRangeError(G, B, E);
477 }
478 support::endian::write32<Endianness>(FixupPtr, Value);
479 break;
480 }
481 case NegDelta32: {
482 int64_t Value = P - S + A;
483 if (LLVM_UNLIKELY(!isInt<32>(Value))) {
484 return makeTargetOutOfRangeError(G, B, E);
485 }
486 support::endian::write32<Endianness>(FixupPtr, Value);
487 break;
488 }
489 default:
490 return make_error<JITLinkError>(
491 "In graph " + G.getName() + ", section " + B.getSection().getName() +
492 " unsupported edge kind " + getEdgeKindName(E.getKind()));
493 }
494 return Error::success();
495}
496
497} // end namespace llvm::jitlink::ppc64
498
499#endif // LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define LLVM_UNLIKELY(EXPR)
Definition: Compiler.h:336
#define LLVM_ABI
Definition: Compiler.h:213
T Content
#define G(x, y, z)
Definition: MD5.cpp:56
#define P(N)
#define DEBUG_WITH_TYPE(TYPE,...)
DEBUG_WITH_TYPE macro - This macro should be used by passes to emit debug information.
Definition: Debug.h:77
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
size_t size() const
size - Get the array size.
Definition: ArrayRef.h:147
Lightweight error class with error context and mandatory checking.
Definition: Error.h:159
static ErrorSuccess success()
Create a success value.
Definition: Error.h:336
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1197
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:55
Target - Wrapper for Target specific information.
LLVM Value Representation.
Definition: Value.h:75
Represents an address in the executor process.
uint64_t getValue() const
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ Offset
Definition: DWP.cpp:477
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:207