Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-10 08:43:50

0001 //===--- ppc64.h - Generic JITLink ppc64 edge kinds, utilities --*- C++ -*-===//
0002 //
0003 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
0004 // See https://llvm.org/LICENSE.txt for license information.
0005 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
0006 //
0007 //===----------------------------------------------------------------------===//
0008 //
0009 // Generic utilities for graphs representing 64-bit PowerPC objects.
0010 //
0011 //===----------------------------------------------------------------------===//
0012 
0013 #ifndef LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
0014 #define LLVM_EXECUTIONENGINE_JITLINK_PPC64_H
0015 
0016 #include "llvm/ExecutionEngine/JITLink/JITLink.h"
0017 #include "llvm/ExecutionEngine/JITLink/TableManager.h"
0018 #include "llvm/Support/Endian.h"
0019 
0020 namespace llvm::jitlink::ppc64 {
0021 
0022 /// Represents ppc64 fixups and other ppc64-specific edge kinds.
0023 enum EdgeKind_ppc64 : Edge::Kind {
0024   Pointer64 = Edge::FirstRelocation,
0025   Pointer32,
0026   Pointer16,
0027   Pointer16DS,
0028   Pointer16HA,
0029   Pointer16HI,
0030   Pointer16HIGH,
0031   Pointer16HIGHA,
0032   Pointer16HIGHER,
0033   Pointer16HIGHERA,
0034   Pointer16HIGHEST,
0035   Pointer16HIGHESTA,
0036   Pointer16LO,
0037   Pointer16LODS,
0038   Pointer14,
0039   Delta64,
0040   Delta34,
0041   Delta32,
0042   NegDelta32,
0043   Delta16,
0044   Delta16HA,
0045   Delta16HI,
0046   Delta16LO,
0047   TOC,
0048   TOCDelta16,
0049   TOCDelta16DS,
0050   TOCDelta16HA,
0051   TOCDelta16HI,
0052   TOCDelta16LO,
0053   TOCDelta16LODS,
0054   RequestGOTAndTransformToDelta34,
0055   CallBranchDelta,
0056   // Need to restore r2 after the bl, suggesting the bl is followed by a nop.
0057   CallBranchDeltaRestoreTOC,
0058   // Request calling function with TOC.
0059   RequestCall,
0060   // Request calling function without TOC.
0061   RequestCallNoTOC,
0062   RequestTLSDescInGOTAndTransformToTOCDelta16HA,
0063   RequestTLSDescInGOTAndTransformToTOCDelta16LO,
0064   RequestTLSDescInGOTAndTransformToDelta34,
0065 };
0066 
0067 enum PLTCallStubKind {
0068   // Setup function entry(r12) and long branch to target using TOC.
0069   LongBranch,
0070   // Save TOC pointer, setup function entry and long branch to target using TOC.
0071   LongBranchSaveR2,
0072   // Setup function entry(r12) and long branch to target without using TOC.
0073   LongBranchNoTOC,
0074 };
0075 
0076 extern const char NullPointerContent[8];
0077 extern const char PointerJumpStubContent_big[20];
0078 extern const char PointerJumpStubContent_little[20];
0079 extern const char PointerJumpStubNoTOCContent_big[32];
0080 extern const char PointerJumpStubNoTOCContent_little[32];
0081 
0082 struct PLTCallStubReloc {
0083   Edge::Kind K;
0084   size_t Offset;
0085   Edge::AddendT A;
0086 };
0087 
0088 struct PLTCallStubInfo {
0089   ArrayRef<char> Content;
0090   SmallVector<PLTCallStubReloc, 2> Relocs;
0091 };
0092 
0093 template <llvm::endianness Endianness>
0094 inline PLTCallStubInfo pickStub(PLTCallStubKind StubKind) {
0095   constexpr bool isLE = Endianness == llvm::endianness::little;
0096   switch (StubKind) {
0097   case LongBranch: {
0098     ArrayRef<char> Content =
0099         isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
0100     // Skip save r2.
0101     Content = Content.slice(4);
0102     size_t Offset = isLE ? 0 : 2;
0103     return PLTCallStubInfo{
0104         Content,
0105         {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 0}},
0106     };
0107   }
0108   case LongBranchSaveR2: {
0109     ArrayRef<char> Content =
0110         isLE ? PointerJumpStubContent_little : PointerJumpStubContent_big;
0111     size_t Offset = isLE ? 4 : 6;
0112     return PLTCallStubInfo{
0113         Content,
0114         {{TOCDelta16HA, Offset, 0}, {TOCDelta16LO, Offset + 4, 0}},
0115     };
0116   }
0117   case LongBranchNoTOC: {
0118     ArrayRef<char> Content = isLE ? PointerJumpStubNoTOCContent_little
0119                                   : PointerJumpStubNoTOCContent_big;
0120     size_t Offset = isLE ? 16 : 18;
0121     Edge::AddendT Addend = isLE ? 8 : 10;
0122     return PLTCallStubInfo{
0123         Content,
0124         {{Delta16HA, Offset, Addend}, {Delta16LO, Offset + 4, Addend + 4}},
0125     };
0126   }
0127   }
0128   llvm_unreachable("Unknown PLTCallStubKind enum");
0129 }
0130 
0131 inline Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
0132                                       Symbol *InitialTarget = nullptr,
0133                                       uint64_t InitialAddend = 0) {
0134   assert(G.getPointerSize() == sizeof(NullPointerContent) &&
0135          "LinkGraph's pointer size should be consistent with size of "
0136          "NullPointerContent");
0137   Block &B = G.createContentBlock(PointerSection, NullPointerContent,
0138                                   orc::ExecutorAddr(), G.getPointerSize(), 0);
0139   if (InitialTarget)
0140     B.addEdge(Pointer64, 0, *InitialTarget, InitialAddend);
0141   return G.addAnonymousSymbol(B, 0, G.getPointerSize(), false, false);
0142 }
0143 
0144 template <llvm::endianness Endianness>
0145 inline Symbol &createAnonymousPointerJumpStub(LinkGraph &G,
0146                                               Section &StubSection,
0147                                               Symbol &PointerSymbol,
0148                                               PLTCallStubKind StubKind) {
0149   PLTCallStubInfo StubInfo = pickStub<Endianness>(StubKind);
0150   Block &B = G.createContentBlock(StubSection, StubInfo.Content,
0151                                   orc::ExecutorAddr(), 4, 0);
0152   for (auto const &Reloc : StubInfo.Relocs)
0153     B.addEdge(Reloc.K, Reloc.Offset, PointerSymbol, Reloc.A);
0154   return G.addAnonymousSymbol(B, 0, StubInfo.Content.size(), true, false);
0155 }
0156 
0157 template <llvm::endianness Endianness>
0158 class TOCTableManager : public TableManager<TOCTableManager<Endianness>> {
0159 public:
0160   // FIXME: `llvm-jitlink -check` relies this name to be $__GOT.
0161   static StringRef getSectionName() { return "$__GOT"; }
0162 
0163   bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
0164     Edge::Kind K = E.getKind();
0165     switch (K) {
0166     case TOCDelta16HA:
0167     case TOCDelta16LO:
0168     case TOCDelta16DS:
0169     case TOCDelta16LODS:
0170     case CallBranchDeltaRestoreTOC:
0171     case RequestCall:
0172       // Create TOC section if TOC relocation, PLT or GOT is used.
0173       getOrCreateTOCSection(G);
0174       return false;
0175     case RequestGOTAndTransformToDelta34:
0176       E.setKind(ppc64::Delta34);
0177       E.setTarget(createEntry(G, E.getTarget()));
0178       return true;
0179     default:
0180       return false;
0181     }
0182   }
0183 
0184   Symbol &createEntry(LinkGraph &G, Symbol &Target) {
0185     return createAnonymousPointer(G, getOrCreateTOCSection(G), &Target);
0186   }
0187 
0188 private:
0189   Section &getOrCreateTOCSection(LinkGraph &G) {
0190     TOCSection = G.findSectionByName(getSectionName());
0191     if (!TOCSection)
0192       TOCSection = &G.createSection(getSectionName(), orc::MemProt::Read);
0193     return *TOCSection;
0194   }
0195 
0196   Section *TOCSection = nullptr;
0197 };
0198 
0199 template <llvm::endianness Endianness>
0200 class PLTTableManager : public TableManager<PLTTableManager<Endianness>> {
0201 public:
0202   PLTTableManager(TOCTableManager<Endianness> &TOC) : TOC(TOC) {}
0203 
0204   static StringRef getSectionName() { return "$__STUBS"; }
0205 
0206   // FIXME: One external symbol can only have one PLT stub in a object file.
0207   // This is a limitation when we need different PLT stubs for the same symbol.
0208   // For example, we need two different PLT stubs for `bl __tls_get_addr` and
0209   // `bl __tls_get_addr@notoc`.
0210   bool visitEdge(LinkGraph &G, Block *B, Edge &E) {
0211     bool isExternal = E.getTarget().isExternal();
0212     Edge::Kind K = E.getKind();
0213     if (K == ppc64::RequestCall) {
0214       if (isExternal) {
0215         E.setKind(ppc64::CallBranchDeltaRestoreTOC);
0216         this->StubKind = LongBranchSaveR2;
0217         // FIXME: We assume the addend to the external target is zero. It's
0218         // quite unusual that the addend of an external target to be non-zero as
0219         // if we have known the layout of the external object.
0220         E.setTarget(this->getEntryForTarget(G, E.getTarget()));
0221         // Addend to the stub is zero.
0222         E.setAddend(0);
0223       } else
0224         // TODO: There are cases a local function call need a call stub.
0225         // 1. Caller uses TOC, the callee doesn't, need a r2 save stub.
0226         // 2. Caller doesn't use TOC, the callee does, need a r12 setup stub.
0227         // 3. Branching target is out of range.
0228         E.setKind(ppc64::CallBranchDelta);
0229       return true;
0230     }
0231     if (K == ppc64::RequestCallNoTOC) {
0232       E.setKind(ppc64::CallBranchDelta);
0233       this->StubKind = LongBranchNoTOC;
0234       E.setTarget(this->getEntryForTarget(G, E.getTarget()));
0235       return true;
0236     }
0237     return false;
0238   }
0239 
0240   Symbol &createEntry(LinkGraph &G, Symbol &Target) {
0241     return createAnonymousPointerJumpStub<Endianness>(
0242         G, getOrCreateStubsSection(G), TOC.getEntryForTarget(G, Target),
0243         this->StubKind);
0244   }
0245 
0246 private:
0247   Section &getOrCreateStubsSection(LinkGraph &G) {
0248     PLTSection = G.findSectionByName(getSectionName());
0249     if (!PLTSection)
0250       PLTSection = &G.createSection(getSectionName(),
0251                                     orc::MemProt::Read | orc::MemProt::Exec);
0252     return *PLTSection;
0253   }
0254 
0255   TOCTableManager<Endianness> &TOC;
0256   Section *PLTSection = nullptr;
0257   PLTCallStubKind StubKind;
0258 };
0259 
0260 /// Returns a string name for the given ppc64 edge. For debugging purposes
0261 /// only.
0262 const char *getEdgeKindName(Edge::Kind K);
0263 
0264 inline static uint16_t ha(uint64_t x) { return (x + 0x8000) >> 16; }
0265 inline static uint64_t lo(uint64_t x) { return x & 0xffff; }
0266 inline static uint16_t hi(uint64_t x) { return x >> 16; }
0267 inline static uint64_t high(uint64_t x) { return (x >> 16) & 0xffff; }
0268 inline static uint64_t higha(uint64_t x) {
0269   return ((x + 0x8000) >> 16) & 0xffff;
0270 }
0271 inline static uint64_t higher(uint64_t x) { return (x >> 32) & 0xffff; }
0272 inline static uint64_t highera(uint64_t x) {
0273   return ((x + 0x8000) >> 32) & 0xffff;
0274 }
0275 inline static uint16_t highest(uint64_t x) { return x >> 48; }
0276 inline static uint16_t highesta(uint64_t x) { return (x + 0x8000) >> 48; }
0277 
0278 // Prefixed instruction introduced in ISAv3.1 consists of two 32-bit words,
0279 // prefix word and suffix word, i.e., prefixed_instruction = concat(prefix_word,
0280 // suffix_word). That's to say, for a prefixed instruction encoded in uint64_t,
0281 // the most significant 32 bits belong to the prefix word. The prefix word is at
0282 // low address for both big/little endian. Byte order in each word still follows
0283 // its endian.
0284 template <llvm::endianness Endianness>
0285 inline static uint64_t readPrefixedInstruction(const char *Loc) {
0286   constexpr bool isLE = Endianness == llvm::endianness::little;
0287   uint64_t Inst = support::endian::read64<Endianness>(Loc);
0288   return isLE ? (Inst << 32) | (Inst >> 32) : Inst;
0289 }
0290 
0291 template <llvm::endianness Endianness>
0292 inline static void writePrefixedInstruction(char *Loc, uint64_t Inst) {
0293   constexpr bool isLE = Endianness == llvm::endianness::little;
0294   Inst = isLE ? (Inst << 32) | (Inst >> 32) : Inst;
0295   support::endian::write64<Endianness>(Loc, Inst);
0296 }
0297 
0298 template <llvm::endianness Endianness>
0299 inline Error relocateHalf16(char *FixupPtr, int64_t Value, Edge::Kind K) {
0300   switch (K) {
0301   case Delta16:
0302   case Pointer16:
0303   case TOCDelta16:
0304     support::endian::write16<Endianness>(FixupPtr, Value);
0305     break;
0306   case Pointer16DS:
0307   case TOCDelta16DS:
0308     support::endian::write16<Endianness>(FixupPtr, Value & ~3);
0309     break;
0310   case Delta16HA:
0311   case Pointer16HA:
0312   case TOCDelta16HA:
0313     support::endian::write16<Endianness>(FixupPtr, ha(Value));
0314     break;
0315   case Delta16HI:
0316   case Pointer16HI:
0317   case TOCDelta16HI:
0318     support::endian::write16<Endianness>(FixupPtr, hi(Value));
0319     break;
0320   case Pointer16HIGH:
0321     support::endian::write16<Endianness>(FixupPtr, high(Value));
0322     break;
0323   case Pointer16HIGHA:
0324     support::endian::write16<Endianness>(FixupPtr, higha(Value));
0325     break;
0326   case Pointer16HIGHER:
0327     support::endian::write16<Endianness>(FixupPtr, higher(Value));
0328     break;
0329   case Pointer16HIGHERA:
0330     support::endian::write16<Endianness>(FixupPtr, highera(Value));
0331     break;
0332   case Pointer16HIGHEST:
0333     support::endian::write16<Endianness>(FixupPtr, highest(Value));
0334     break;
0335   case Pointer16HIGHESTA:
0336     support::endian::write16<Endianness>(FixupPtr, highesta(Value));
0337     break;
0338   case Delta16LO:
0339   case Pointer16LO:
0340   case TOCDelta16LO:
0341     support::endian::write16<Endianness>(FixupPtr, lo(Value));
0342     break;
0343   case Pointer16LODS:
0344   case TOCDelta16LODS:
0345     support::endian::write16<Endianness>(FixupPtr, lo(Value) & ~3);
0346     break;
0347   default:
0348     return make_error<JITLinkError>(
0349         StringRef(getEdgeKindName(K)) +
0350         " relocation does not write at half16 field");
0351   }
0352   return Error::success();
0353 }
0354 
0355 /// Apply fixup expression for edge to block content.
0356 template <llvm::endianness Endianness>
0357 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
0358                         const Symbol *TOCSymbol) {
0359   char *BlockWorkingMem = B.getAlreadyMutableContent().data();
0360   char *FixupPtr = BlockWorkingMem + E.getOffset();
0361   orc::ExecutorAddr FixupAddress = B.getAddress() + E.getOffset();
0362   int64_t S = E.getTarget().getAddress().getValue();
0363   int64_t A = E.getAddend();
0364   int64_t P = FixupAddress.getValue();
0365   int64_t TOCBase = TOCSymbol ? TOCSymbol->getAddress().getValue() : 0;
0366   Edge::Kind K = E.getKind();
0367 
0368   DEBUG_WITH_TYPE("jitlink", {
0369     dbgs() << "    Applying fixup on " << G.getEdgeKindName(K)
0370            << " edge, (S, A, P, .TOC.) = (" << formatv("{0:x}", S) << ", "
0371            << formatv("{0:x}", A) << ", " << formatv("{0:x}", P) << ", "
0372            << formatv("{0:x}", TOCBase) << ")\n";
0373   });
0374 
0375   switch (K) {
0376   case Pointer64: {
0377     uint64_t Value = S + A;
0378     support::endian::write64<Endianness>(FixupPtr, Value);
0379     break;
0380   }
0381   case Delta16:
0382   case Delta16HA:
0383   case Delta16HI:
0384   case Delta16LO: {
0385     int64_t Value = S + A - P;
0386     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
0387       return makeTargetOutOfRangeError(G, B, E);
0388     }
0389     return relocateHalf16<Endianness>(FixupPtr, Value, K);
0390   }
0391   case TOC:
0392     support::endian::write64<Endianness>(FixupPtr, TOCBase);
0393     break;
0394   case Pointer16:
0395   case Pointer16DS:
0396   case Pointer16HA:
0397   case Pointer16HI:
0398   case Pointer16HIGH:
0399   case Pointer16HIGHA:
0400   case Pointer16HIGHER:
0401   case Pointer16HIGHERA:
0402   case Pointer16HIGHEST:
0403   case Pointer16HIGHESTA:
0404   case Pointer16LO:
0405   case Pointer16LODS: {
0406     uint64_t Value = S + A;
0407     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
0408       return makeTargetOutOfRangeError(G, B, E);
0409     }
0410     return relocateHalf16<Endianness>(FixupPtr, Value, K);
0411   }
0412   case Pointer14: {
0413     static const uint32_t Low14Mask = 0xfffc;
0414     uint64_t Value = S + A;
0415     assert((Value & 3) == 0 && "Pointer14 requires 4-byte alignment");
0416     if (LLVM_UNLIKELY(!isInt<16>(Value))) {
0417       return makeTargetOutOfRangeError(G, B, E);
0418     }
0419     uint32_t Inst = support::endian::read32<Endianness>(FixupPtr);
0420     support::endian::write32<Endianness>(FixupPtr, (Inst & ~Low14Mask) |
0421                                                        (Value & Low14Mask));
0422     break;
0423   }
0424   case TOCDelta16:
0425   case TOCDelta16DS:
0426   case TOCDelta16HA:
0427   case TOCDelta16HI:
0428   case TOCDelta16LO:
0429   case TOCDelta16LODS: {
0430     int64_t Value = S + A - TOCBase;
0431     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
0432       return makeTargetOutOfRangeError(G, B, E);
0433     }
0434     return relocateHalf16<Endianness>(FixupPtr, Value, K);
0435   }
0436   case CallBranchDeltaRestoreTOC:
0437   case CallBranchDelta: {
0438     int64_t Value = S + A - P;
0439     if (LLVM_UNLIKELY(!isInt<26>(Value))) {
0440       return makeTargetOutOfRangeError(G, B, E);
0441     }
0442     uint32_t Inst = support::endian::read32<Endianness>(FixupPtr);
0443     support::endian::write32<Endianness>(FixupPtr, (Inst & 0xfc000003) |
0444                                                        (Value & 0x03fffffc));
0445     if (K == CallBranchDeltaRestoreTOC) {
0446       uint32_t NopInst = support::endian::read32<Endianness>(FixupPtr + 4);
0447       assert(NopInst == 0x60000000 &&
0448              "NOP should be placed here for restoring r2");
0449       (void)NopInst;
0450       // Restore r2 by instruction 0xe8410018 which is `ld r2, 24(r1)`.
0451       support::endian::write32<Endianness>(FixupPtr + 4, 0xe8410018);
0452     }
0453     break;
0454   }
0455   case Delta64: {
0456     int64_t Value = S + A - P;
0457     support::endian::write64<Endianness>(FixupPtr, Value);
0458     break;
0459   }
0460   case Delta34: {
0461     int64_t Value = S + A - P;
0462     if (!LLVM_UNLIKELY(isInt<34>(Value)))
0463       return makeTargetOutOfRangeError(G, B, E);
0464     static const uint64_t SI0Mask = 0x00000003ffff0000;
0465     static const uint64_t SI1Mask = 0x000000000000ffff;
0466     static const uint64_t FullMask = 0x0003ffff0000ffff;
0467     uint64_t Inst = readPrefixedInstruction<Endianness>(FixupPtr) & ~FullMask;
0468     writePrefixedInstruction<Endianness>(
0469         FixupPtr, Inst | ((Value & SI0Mask) << 16) | (Value & SI1Mask));
0470     break;
0471   }
0472   case Delta32: {
0473     int64_t Value = S + A - P;
0474     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
0475       return makeTargetOutOfRangeError(G, B, E);
0476     }
0477     support::endian::write32<Endianness>(FixupPtr, Value);
0478     break;
0479   }
0480   case NegDelta32: {
0481     int64_t Value = P - S + A;
0482     if (LLVM_UNLIKELY(!isInt<32>(Value))) {
0483       return makeTargetOutOfRangeError(G, B, E);
0484     }
0485     support::endian::write32<Endianness>(FixupPtr, Value);
0486     break;
0487   }
0488   default:
0489     return make_error<JITLinkError>(
0490         "In graph " + G.getName() + ", section " + B.getSection().getName() +
0491         " unsupported edge kind " + getEdgeKindName(E.getKind()));
0492   }
0493   return Error::success();
0494 }
0495 
0496 } // end namespace llvm::jitlink::ppc64
0497 
0498 #endif // LLVM_EXECUTIONENGINE_JITLINK_PPC64_H