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