LLVM 24.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.
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
93
94template <llvm::endianness Endianness>
96 constexpr bool isLE = Endianness == llvm::endianness::little;
97 switch (StubKind) {
98 case LongBranch: {
99 ArrayRef<char> Content =
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: {
110 ArrayRef<char> Content =
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
158// LongBranchSaveR2 is the default for external calls: saves the TOC
159// pointer (r2) before branching, as required when the callee sets its
160// own TOC. Callers needing a different stub kind (e.g. LongBranchNoTOC)
161// should call createAnonymousPointerJumpStub directly with the desired
162// PLTCallStubKind.
163template <llvm::endianness Endianness>
165 Section &StubSection,
166 Symbol &PointerSymbol) {
168 G, StubSection, PointerSymbol, LongBranchSaveR2);
169}
170
171template <llvm::endianness Endianness>
172class TOCTableManager : public TableManager<TOCTableManager<Endianness>> {
173public:
174 // FIXME: `llvm-jitlink -check` relies this name to be $__GOT.
175 static StringRef getSectionName() { return "$__GOT"; }
176
178 Edge::Kind K = E.getKind();
179 switch (K) {
180 case TOCDelta16HA:
181 case TOCDelta16LO:
182 case TOCDelta16DS:
183 case TOCDelta16LODS:
185 case RequestCall:
186 // Create TOC section if TOC relocation, PLT or GOT is used.
187 getOrCreateTOCSection(G);
188 return false;
190 E.setKind(ppc64::Delta34);
191 E.setTarget(createEntry(G, E.getTarget()));
192 return true;
193 default:
194 return false;
195 }
196 }
197
199 return createAnonymousPointer(G, getOrCreateTOCSection(G), &Target);
200 }
201
202private:
203 Section &getOrCreateTOCSection(LinkGraph &G) {
204 TOCSection = G.findSectionByName(getSectionName());
205 if (!TOCSection)
206 TOCSection = &G.createSection(getSectionName(), orc::MemProt::Read);
207 return *TOCSection;
208 }
209
210 Section *TOCSection = nullptr;
211};
212
213template <llvm::endianness Endianness>
214class PLTTableManager : public TableManager<PLTTableManager<Endianness>> {
215public:
217
218 static StringRef getSectionName() { return "$__STUBS"; }
219
220 // FIXME: One external symbol can only have one PLT stub in a object file.
221 // This is a limitation when we need different PLT stubs for the same symbol.
222 // For example, we need two different PLT stubs for `bl __tls_get_addr` and
223 // `bl __tls_get_addr@notoc`.
225 bool isExternal = E.getTarget().isExternal();
226 Edge::Kind K = E.getKind();
227 if (K == ppc64::RequestCall) {
228 if (isExternal) {
230 this->StubKind = LongBranchSaveR2;
231 // FIXME: We assume the addend to the external target is zero. It's
232 // quite unusual that the addend of an external target to be non-zero as
233 // if we have known the layout of the external object.
234 E.setTarget(this->getEntryForTarget(G, E.getTarget()));
235 // Addend to the stub is zero.
236 E.setAddend(0);
237 } else
238 // TODO: There are cases a local function call need a call stub.
239 // 1. Caller uses TOC, the callee doesn't, need a r2 save stub.
240 // 2. Caller doesn't use TOC, the callee does, need a r12 setup stub.
241 // 3. Branching target is out of range.
242 E.setKind(ppc64::CallBranchDelta);
243 return true;
244 }
245 if (K == ppc64::RequestCallNoTOC) {
246 E.setKind(ppc64::CallBranchDelta);
247 this->StubKind = LongBranchNoTOC;
248 E.setTarget(this->getEntryForTarget(G, E.getTarget()));
249 return true;
250 }
251 return false;
252 }
253
256 G, getOrCreateStubsSection(G), TOC.getEntryForTarget(G, Target),
257 this->StubKind);
258 }
259
260private:
261 Section &getOrCreateStubsSection(LinkGraph &G) {
262 PLTSection = G.findSectionByName(getSectionName());
263 if (!PLTSection)
264 PLTSection = &G.createSection(getSectionName(),
266 return *PLTSection;
267 }
268
269 TOCTableManager<Endianness> &TOC;
270 Section *PLTSection = nullptr;
271 PLTCallStubKind StubKind;
272};
273
274/// Returns a string name for the given ppc64 edge. For debugging purposes
275/// only.
276LLVM_ABI const char *getEdgeKindName(Edge::Kind K);
277
278inline static uint16_t ha(uint64_t x) { return (x + 0x8000) >> 16; }
279inline static uint64_t lo(uint64_t x) { return x & 0xffff; }
280inline static uint16_t hi(uint64_t x) { return x >> 16; }
281inline static uint64_t high(uint64_t x) { return (x >> 16) & 0xffff; }
282inline static uint64_t higha(uint64_t x) {
283 return ((x + 0x8000) >> 16) & 0xffff;
284}
285inline static uint64_t higher(uint64_t x) { return (x >> 32) & 0xffff; }
286inline static uint64_t highera(uint64_t x) {
287 return ((x + 0x8000) >> 32) & 0xffff;
288}
289inline static uint16_t highest(uint64_t x) { return x >> 48; }
290inline static uint16_t highesta(uint64_t x) { return (x + 0x8000) >> 48; }
291
292// Prefixed instruction introduced in ISAv3.1 consists of two 32-bit words,
293// prefix word and suffix word, i.e., prefixed_instruction = concat(prefix_word,
294// suffix_word). That's to say, for a prefixed instruction encoded in uint64_t,
295// the most significant 32 bits belong to the prefix word. The prefix word is at
296// low address for both big/little endian. Byte order in each word still follows
297// its endian.
298template <llvm::endianness Endianness>
299inline static uint64_t readPrefixedInstruction(const char *Loc) {
300 constexpr bool isLE = Endianness == llvm::endianness::little;
302 return isLE ? (Inst << 32) | (Inst >> 32) : Inst;
303}
304
305template <llvm::endianness Endianness>
306inline static void writePrefixedInstruction(char *Loc, uint64_t Inst) {
307 constexpr bool isLE = Endianness == llvm::endianness::little;
308 Inst = isLE ? (Inst << 32) | (Inst >> 32) : Inst;
310}
311
312template <llvm::endianness Endianness>
313inline Error relocateHalf16(char *FixupPtr, int64_t Value, Edge::Kind K) {
314 switch (K) {
315 case Delta16:
316 case Pointer16:
317 case TOCDelta16:
319 break;
320 case Pointer16DS:
321 case TOCDelta16DS:
323 break;
324 case Delta16HA:
325 case Pointer16HA:
326 case TOCDelta16HA:
328 break;
329 case Delta16HI:
330 case Pointer16HI:
331 case TOCDelta16HI:
333 break;
334 case Pointer16HIGH:
336 break;
337 case Pointer16HIGHA:
339 break;
340 case Pointer16HIGHER:
342 break;
343 case Pointer16HIGHERA:
345 break;
346 case Pointer16HIGHEST:
348 break;
351 break;
352 case Delta16LO:
353 case Pointer16LO:
354 case TOCDelta16LO:
356 break;
357 case Pointer16LODS:
358 case TOCDelta16LODS:
360 break;
361 default:
364 " relocation does not write at half16 field");
365 }
366 return Error::success();
367}
368
369/// Apply fixup expression for edge to block content.
370template <llvm::endianness Endianness>
372 const Symbol *TOCSymbol) {
373 char *BlockWorkingMem = B.getAlreadyMutableContent().data();
374 char *FixupPtr = BlockWorkingMem + E.getOffset();
375 orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
376 int64_t S = E.getTarget().getAddress().getValue();
377 int64_t A = E.getAddend();
378 int64_t P = FixupAddress.getValue();
379 int64_t TOCBase = TOCSymbol ? TOCSymbol->getAddress().getValue() : 0;
380 Edge::Kind K = E.getKind();
381
382 DEBUG_WITH_TYPE("jitlink", {
383 dbgs() << " Applying fixup on " << G.getEdgeKindName(K)
384 << " edge, (S, A, P, .TOC.) = (" << formatv("{0:x}", S) << ", "
385 << formatv("{0:x}", A) << ", " << formatv("{0:x}", P) << ", "
386 << formatv("{0:x}", TOCBase) << ")\n";
387 });
388
389 switch (K) {
390 case Pointer64: {
391 uint64_t Value = S + A;
393 break;
394 }
395 case Delta16:
396 case Delta16HA:
397 case Delta16HI:
398 case Delta16LO: {
399 int64_t Value = S + A - P;
401 return makeTargetOutOfRangeError(G, B, E);
402 }
403 return relocateHalf16<Endianness>(FixupPtr, Value, K);
404 }
405 case TOC:
406 support::endian::write64<Endianness>(FixupPtr, TOCBase);
407 break;
408 case Pointer16:
409 case Pointer16DS:
410 case Pointer16HA:
411 case Pointer16HI:
412 case Pointer16HIGH:
413 case Pointer16HIGHA:
414 case Pointer16HIGHER:
415 case Pointer16HIGHERA:
416 case Pointer16HIGHEST:
418 case Pointer16LO:
419 case Pointer16LODS: {
420 uint64_t Value = S + A;
422 return makeTargetOutOfRangeError(G, B, E);
423 }
424 return relocateHalf16<Endianness>(FixupPtr, Value, K);
425 }
426 case Pointer14: {
427 static const uint32_t Low14Mask = 0xfffc;
428 uint64_t Value = S + A;
429 assert((Value & 3) == 0 && "Pointer14 requires 4-byte alignment");
431 return makeTargetOutOfRangeError(G, B, E);
432 }
434 support::endian::write32<Endianness>(FixupPtr, (Inst & ~Low14Mask) |
435 (Value & Low14Mask));
436 break;
437 }
438 case TOCDelta16:
439 case TOCDelta16DS:
440 case TOCDelta16HA:
441 case TOCDelta16HI:
442 case TOCDelta16LO:
443 case TOCDelta16LODS: {
444 int64_t Value = S + A - TOCBase;
446 return makeTargetOutOfRangeError(G, B, E);
447 }
448 return relocateHalf16<Endianness>(FixupPtr, Value, K);
449 }
451 case CallBranchDelta: {
452 int64_t Value = S + A - P;
454 return makeTargetOutOfRangeError(G, B, E);
455 }
457 support::endian::write32<Endianness>(FixupPtr, (Inst & 0xfc000003) |
458 (Value & 0x03fffffc));
459 if (K == CallBranchDeltaRestoreTOC) {
460 uint32_t NopInst = support::endian::read32<Endianness>(FixupPtr + 4);
461 assert(NopInst == 0x60000000 &&
462 "NOP should be placed here for restoring r2");
463 (void)NopInst;
464 // Restore r2 by instruction 0xe8410018 which is `ld r2, 24(r1)`.
465 support::endian::write32<Endianness>(FixupPtr + 4, 0xe8410018);
466 }
467 break;
468 }
469 case Delta64: {
470 int64_t Value = S + A - P;
472 break;
473 }
474 case Delta34: {
475 int64_t Value = S + A - P;
477 return makeTargetOutOfRangeError(G, B, E);
478 static const uint64_t SI0Mask = 0x00000003ffff0000;
479 static const uint64_t SI1Mask = 0x000000000000ffff;
480 static const uint64_t FullMask = 0x0003ffff0000ffff;
481 uint64_t Inst = readPrefixedInstruction<Endianness>(FixupPtr) & ~FullMask;
483 FixupPtr, Inst | ((Value & SI0Mask) << 16) | (Value & SI1Mask));
484 break;
485 }
486 case Delta32: {
487 int64_t Value = S + A - P;
489 return makeTargetOutOfRangeError(G, B, E);
490 }
492 break;
493 }
494 case NegDelta32: {
495 int64_t Value = P - S + A;
497 return makeTargetOutOfRangeError(G, B, E);
498 }
500 break;
501 }
502 default:
504 "In graph " + G.getName() + ", section " + B.getSection().getName() +
505 " unsupported edge kind " + getEdgeKindName(E.getKind()));
506 }
507 return Error::success();
508}
509
510} // end namespace llvm::jitlink::ppc64
511
512#endif // LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
#define LLVM_UNLIKELY(EXPR)
Definition Compiler.h:344
#define LLVM_ABI
Definition Compiler.h:215
#define G(x, y, z)
Definition MD5.cpp:55
#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:72
Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition ArrayRef.h:40
size_t size() const
Get the array size.
Definition ArrayRef.h:141
ArrayRef< T > slice(size_t N, size_t M) const
slice(n, m) - Chop off the first N elements of the array, and keep M elements in the array.
Definition ArrayRef.h:185
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.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
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.
uint32_t read32(const void *P, endianness E)
Definition Endian.h:392
void write32(void *P, uint32_t V, endianness E)
Definition Endian.h:435
uint64_t read64(const void *P, endianness E)
Definition Endian.h:395
void write16(void *P, uint16_t V, endianness E)
Definition Endian.h:432
void write64(void *P, uint64_t V, endianness E)
Definition Endian.h:438
@ Offset
Definition DWP.cpp:578
constexpr bool isInt(int64_t x)
Checks if an integer fits into the given bit width.
Definition MathExtras.h:166
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:209
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340