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