xref: /llvm-project/bolt/lib/Passes/LongJmp.cpp (revision cb9bacf57d5c58eba28a76fd07ea2d4f9a0da847)
1 //===- bolt/Passes/LongJmp.cpp --------------------------------------------===//
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 // This file implements the LongJmpPass class.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "bolt/Passes/LongJmp.h"
14 
15 #define DEBUG_TYPE "longjmp"
16 
17 using namespace llvm;
18 
19 namespace opts {
20 extern cl::OptionCategory BoltOptCategory;
21 extern llvm::cl::opt<unsigned> AlignText;
22 extern cl::opt<unsigned> AlignFunctions;
23 extern cl::opt<bool> UseOldText;
24 extern cl::opt<bool> HotFunctionsAtEnd;
25 
26 static cl::opt<bool> GroupStubs("group-stubs",
27                                 cl::desc("share stubs across functions"),
28                                 cl::init(true), cl::cat(BoltOptCategory));
29 }
30 
31 namespace llvm {
32 namespace bolt {
33 
34 constexpr unsigned ColdFragAlign = 16;
35 
36 static void relaxStubToShortJmp(BinaryBasicBlock &StubBB, const MCSymbol *Tgt) {
37   const BinaryContext &BC = StubBB.getFunction()->getBinaryContext();
38   InstructionListType Seq;
39   BC.MIB->createShortJmp(Seq, Tgt, BC.Ctx.get());
40   StubBB.clear();
41   StubBB.addInstructions(Seq.begin(), Seq.end());
42 }
43 
44 static void relaxStubToLongJmp(BinaryBasicBlock &StubBB, const MCSymbol *Tgt) {
45   const BinaryContext &BC = StubBB.getFunction()->getBinaryContext();
46   InstructionListType Seq;
47   BC.MIB->createLongJmp(Seq, Tgt, BC.Ctx.get());
48   StubBB.clear();
49   StubBB.addInstructions(Seq.begin(), Seq.end());
50 }
51 
52 static BinaryBasicBlock *getBBAtHotColdSplitPoint(BinaryFunction &Func) {
53   if (!Func.isSplit() || Func.empty())
54     return nullptr;
55 
56   assert(!(*Func.begin()).isCold() && "Entry cannot be cold");
57   for (auto I = Func.getLayout().block_begin(),
58             E = Func.getLayout().block_end();
59        I != E; ++I) {
60     auto Next = std::next(I);
61     if (Next != E && (*Next)->isCold())
62       return *I;
63   }
64   llvm_unreachable("No hot-colt split point found");
65 }
66 
67 static bool shouldInsertStub(const BinaryContext &BC, const MCInst &Inst) {
68   return (BC.MIB->isBranch(Inst) || BC.MIB->isCall(Inst)) &&
69          !BC.MIB->isIndirectBranch(Inst) && !BC.MIB->isIndirectCall(Inst);
70 }
71 
72 std::pair<std::unique_ptr<BinaryBasicBlock>, MCSymbol *>
73 LongJmpPass::createNewStub(BinaryBasicBlock &SourceBB, const MCSymbol *TgtSym,
74                            bool TgtIsFunc, uint64_t AtAddress) {
75   BinaryFunction &Func = *SourceBB.getFunction();
76   const BinaryContext &BC = Func.getBinaryContext();
77   const bool IsCold = SourceBB.isCold();
78   MCSymbol *StubSym = BC.Ctx->createNamedTempSymbol("Stub");
79   std::unique_ptr<BinaryBasicBlock> StubBB = Func.createBasicBlock(StubSym);
80   MCInst Inst;
81   BC.MIB->createUncondBranch(Inst, TgtSym, BC.Ctx.get());
82   if (TgtIsFunc)
83     BC.MIB->convertJmpToTailCall(Inst);
84   StubBB->addInstruction(Inst);
85   StubBB->setExecutionCount(0);
86 
87   // Register this in stubs maps
88   auto registerInMap = [&](StubGroupsTy &Map) {
89     StubGroupTy &StubGroup = Map[TgtSym];
90     StubGroup.insert(
91         llvm::lower_bound(
92             StubGroup, std::make_pair(AtAddress, nullptr),
93             [&](const std::pair<uint64_t, BinaryBasicBlock *> &LHS,
94                 const std::pair<uint64_t, BinaryBasicBlock *> &RHS) {
95               return LHS.first < RHS.first;
96             }),
97         std::make_pair(AtAddress, StubBB.get()));
98   };
99 
100   Stubs[&Func].insert(StubBB.get());
101   StubBits[StubBB.get()] = BC.MIB->getUncondBranchEncodingSize();
102   if (IsCold) {
103     registerInMap(ColdLocalStubs[&Func]);
104     if (opts::GroupStubs && TgtIsFunc)
105       registerInMap(ColdStubGroups);
106     ++NumColdStubs;
107   } else {
108     registerInMap(HotLocalStubs[&Func]);
109     if (opts::GroupStubs && TgtIsFunc)
110       registerInMap(HotStubGroups);
111     ++NumHotStubs;
112   }
113 
114   return std::make_pair(std::move(StubBB), StubSym);
115 }
116 
117 BinaryBasicBlock *LongJmpPass::lookupStubFromGroup(
118     const StubGroupsTy &StubGroups, const BinaryFunction &Func,
119     const MCInst &Inst, const MCSymbol *TgtSym, uint64_t DotAddress) const {
120   const BinaryContext &BC = Func.getBinaryContext();
121   auto CandidatesIter = StubGroups.find(TgtSym);
122   if (CandidatesIter == StubGroups.end())
123     return nullptr;
124   const StubGroupTy &Candidates = CandidatesIter->second;
125   if (Candidates.empty())
126     return nullptr;
127   auto Cand = llvm::lower_bound(
128       Candidates, std::make_pair(DotAddress, nullptr),
129       [&](const std::pair<uint64_t, BinaryBasicBlock *> &LHS,
130           const std::pair<uint64_t, BinaryBasicBlock *> &RHS) {
131         return LHS.first < RHS.first;
132       });
133   if (Cand == Candidates.end())
134     return nullptr;
135   if (Cand != Candidates.begin()) {
136     const StubTy *LeftCand = std::prev(Cand);
137     if (Cand->first - DotAddress > DotAddress - LeftCand->first)
138       Cand = LeftCand;
139   }
140   int BitsAvail = BC.MIB->getPCRelEncodingSize(Inst) - 1;
141   assert(BitsAvail < 63 && "PCRelEncodingSize is too large to use int64_t to"
142                            "check for out-of-bounds.");
143   int64_t MaxVal = (1ULL << BitsAvail) - 1;
144   int64_t MinVal = -(1ULL << BitsAvail);
145   uint64_t PCRelTgtAddress = Cand->first;
146   int64_t PCOffset = (int64_t)(PCRelTgtAddress - DotAddress);
147 
148   LLVM_DEBUG({
149     if (Candidates.size() > 1)
150       dbgs() << "Considering stub group with " << Candidates.size()
151              << " candidates. DotAddress is " << Twine::utohexstr(DotAddress)
152              << ", chosen candidate address is "
153              << Twine::utohexstr(Cand->first) << "\n";
154   });
155   return (PCOffset < MinVal || PCOffset > MaxVal) ? nullptr : Cand->second;
156 }
157 
158 BinaryBasicBlock *
159 LongJmpPass::lookupGlobalStub(const BinaryBasicBlock &SourceBB,
160                               const MCInst &Inst, const MCSymbol *TgtSym,
161                               uint64_t DotAddress) const {
162   const BinaryFunction &Func = *SourceBB.getFunction();
163   const StubGroupsTy &StubGroups =
164       SourceBB.isCold() ? ColdStubGroups : HotStubGroups;
165   return lookupStubFromGroup(StubGroups, Func, Inst, TgtSym, DotAddress);
166 }
167 
168 BinaryBasicBlock *LongJmpPass::lookupLocalStub(const BinaryBasicBlock &SourceBB,
169                                                const MCInst &Inst,
170                                                const MCSymbol *TgtSym,
171                                                uint64_t DotAddress) const {
172   const BinaryFunction &Func = *SourceBB.getFunction();
173   const DenseMap<const BinaryFunction *, StubGroupsTy> &StubGroups =
174       SourceBB.isCold() ? ColdLocalStubs : HotLocalStubs;
175   const auto Iter = StubGroups.find(&Func);
176   if (Iter == StubGroups.end())
177     return nullptr;
178   return lookupStubFromGroup(Iter->second, Func, Inst, TgtSym, DotAddress);
179 }
180 
181 std::unique_ptr<BinaryBasicBlock>
182 LongJmpPass::replaceTargetWithStub(BinaryBasicBlock &BB, MCInst &Inst,
183                                    uint64_t DotAddress,
184                                    uint64_t StubCreationAddress) {
185   const BinaryFunction &Func = *BB.getFunction();
186   const BinaryContext &BC = Func.getBinaryContext();
187   std::unique_ptr<BinaryBasicBlock> NewBB;
188   const MCSymbol *TgtSym = BC.MIB->getTargetSymbol(Inst);
189   assert(TgtSym && "getTargetSymbol failed");
190 
191   BinaryBasicBlock::BinaryBranchInfo BI{0, 0};
192   BinaryBasicBlock *TgtBB = BB.getSuccessor(TgtSym, BI);
193   auto LocalStubsIter = Stubs.find(&Func);
194 
195   // If already using stub and the stub is from another function, create a local
196   // stub, since the foreign stub is now out of range
197   if (!TgtBB) {
198     auto SSIter = SharedStubs.find(TgtSym);
199     if (SSIter != SharedStubs.end()) {
200       TgtSym = BC.MIB->getTargetSymbol(*SSIter->second->begin());
201       --NumSharedStubs;
202     }
203   } else if (LocalStubsIter != Stubs.end() &&
204              LocalStubsIter->second.count(TgtBB)) {
205     // The TgtBB and TgtSym now are the local out-of-range stub and its label.
206     // So, we are attempting to restore BB to its previous state without using
207     // this stub.
208     TgtSym = BC.MIB->getTargetSymbol(*TgtBB->begin());
209     assert(TgtSym &&
210            "First instruction is expected to contain a target symbol.");
211     BinaryBasicBlock *TgtBBSucc = TgtBB->getSuccessor(TgtSym, BI);
212 
213     // TgtBB might have no successor. e.g. a stub for a function call.
214     if (TgtBBSucc) {
215       BB.replaceSuccessor(TgtBB, TgtBBSucc, BI.Count, BI.MispredictedCount);
216       assert(TgtBB->getExecutionCount() >= BI.Count &&
217              "At least equal or greater than the branch count.");
218       TgtBB->setExecutionCount(TgtBB->getExecutionCount() - BI.Count);
219     }
220 
221     TgtBB = TgtBBSucc;
222   }
223 
224   BinaryBasicBlock *StubBB = lookupLocalStub(BB, Inst, TgtSym, DotAddress);
225   // If not found, look it up in globally shared stub maps if it is a function
226   // call (TgtBB is not set)
227   if (!StubBB && !TgtBB) {
228     StubBB = lookupGlobalStub(BB, Inst, TgtSym, DotAddress);
229     if (StubBB) {
230       SharedStubs[StubBB->getLabel()] = StubBB;
231       ++NumSharedStubs;
232     }
233   }
234   MCSymbol *StubSymbol = StubBB ? StubBB->getLabel() : nullptr;
235 
236   if (!StubBB) {
237     std::tie(NewBB, StubSymbol) =
238         createNewStub(BB, TgtSym, /*is func?*/ !TgtBB, StubCreationAddress);
239     StubBB = NewBB.get();
240   }
241 
242   // Local branch
243   if (TgtBB) {
244     uint64_t OrigCount = BI.Count;
245     uint64_t OrigMispreds = BI.MispredictedCount;
246     BB.replaceSuccessor(TgtBB, StubBB, OrigCount, OrigMispreds);
247     StubBB->setExecutionCount(StubBB->getExecutionCount() + OrigCount);
248     if (NewBB) {
249       StubBB->addSuccessor(TgtBB, OrigCount, OrigMispreds);
250       StubBB->setIsCold(BB.isCold());
251     }
252     // Call / tail call
253   } else {
254     StubBB->setExecutionCount(StubBB->getExecutionCount() +
255                               BB.getExecutionCount());
256     if (NewBB) {
257       assert(TgtBB == nullptr);
258       StubBB->setIsCold(BB.isCold());
259       // Set as entry point because this block is valid but we have no preds
260       StubBB->getFunction()->addEntryPoint(*StubBB);
261     }
262   }
263   BC.MIB->replaceBranchTarget(Inst, StubSymbol, BC.Ctx.get());
264 
265   return NewBB;
266 }
267 
268 void LongJmpPass::updateStubGroups() {
269   auto update = [&](StubGroupsTy &StubGroups) {
270     for (auto &KeyVal : StubGroups) {
271       for (StubTy &Elem : KeyVal.second)
272         Elem.first = BBAddresses[Elem.second];
273       llvm::sort(KeyVal.second, llvm::less_first());
274     }
275   };
276 
277   for (auto &KeyVal : HotLocalStubs)
278     update(KeyVal.second);
279   for (auto &KeyVal : ColdLocalStubs)
280     update(KeyVal.second);
281   update(HotStubGroups);
282   update(ColdStubGroups);
283 }
284 
285 void LongJmpPass::tentativeBBLayout(const BinaryFunction &Func) {
286   const BinaryContext &BC = Func.getBinaryContext();
287   uint64_t HotDot = HotAddresses[&Func];
288   uint64_t ColdDot = ColdAddresses[&Func];
289   bool Cold = false;
290   for (const BinaryBasicBlock *BB : Func.getLayout().blocks()) {
291     if (Cold || BB->isCold()) {
292       Cold = true;
293       BBAddresses[BB] = ColdDot;
294       ColdDot += BC.computeCodeSize(BB->begin(), BB->end());
295     } else {
296       BBAddresses[BB] = HotDot;
297       HotDot += BC.computeCodeSize(BB->begin(), BB->end());
298     }
299   }
300 }
301 
302 uint64_t LongJmpPass::tentativeLayoutRelocColdPart(
303     const BinaryContext &BC, std::vector<BinaryFunction *> &SortedFunctions,
304     uint64_t DotAddress) {
305   DotAddress = alignTo(DotAddress, llvm::Align(opts::AlignFunctions));
306   for (BinaryFunction *Func : SortedFunctions) {
307     if (!Func->isSplit())
308       continue;
309     DotAddress = alignTo(DotAddress, Func->getMinAlignment());
310     uint64_t Pad =
311         offsetToAlignment(DotAddress, llvm::Align(Func->getAlignment()));
312     if (Pad <= Func->getMaxColdAlignmentBytes())
313       DotAddress += Pad;
314     ColdAddresses[Func] = DotAddress;
315     LLVM_DEBUG(dbgs() << Func->getPrintName() << " cold tentative: "
316                       << Twine::utohexstr(DotAddress) << "\n");
317     DotAddress += Func->estimateColdSize();
318     DotAddress = alignTo(DotAddress, Func->getConstantIslandAlignment());
319     DotAddress += Func->estimateConstantIslandSize();
320   }
321   return DotAddress;
322 }
323 
324 uint64_t LongJmpPass::tentativeLayoutRelocMode(
325     const BinaryContext &BC, std::vector<BinaryFunction *> &SortedFunctions,
326     uint64_t DotAddress) {
327   // Compute hot cold frontier
328   int64_t LastHotIndex = -1u;
329   uint32_t CurrentIndex = 0;
330   if (opts::HotFunctionsAtEnd) {
331     for (BinaryFunction *BF : SortedFunctions) {
332       if (BF->hasValidIndex()) {
333         LastHotIndex = CurrentIndex;
334         break;
335       }
336 
337       ++CurrentIndex;
338     }
339   } else {
340     for (BinaryFunction *BF : SortedFunctions) {
341       if (!BF->hasValidIndex()) {
342         LastHotIndex = CurrentIndex;
343         break;
344       }
345 
346       ++CurrentIndex;
347     }
348   }
349 
350   // Hot
351   CurrentIndex = 0;
352   bool ColdLayoutDone = false;
353   auto runColdLayout = [&]() {
354     DotAddress = tentativeLayoutRelocColdPart(BC, SortedFunctions, DotAddress);
355     ColdLayoutDone = true;
356     if (opts::HotFunctionsAtEnd)
357       DotAddress = alignTo(DotAddress, opts::AlignText);
358   };
359   for (BinaryFunction *Func : SortedFunctions) {
360     if (!BC.shouldEmit(*Func)) {
361       HotAddresses[Func] = Func->getAddress();
362       continue;
363     }
364 
365     if (!ColdLayoutDone && CurrentIndex >= LastHotIndex)
366       runColdLayout();
367 
368     DotAddress = alignTo(DotAddress, Func->getMinAlignment());
369     uint64_t Pad =
370         offsetToAlignment(DotAddress, llvm::Align(Func->getAlignment()));
371     if (Pad <= Func->getMaxAlignmentBytes())
372       DotAddress += Pad;
373     HotAddresses[Func] = DotAddress;
374     LLVM_DEBUG(dbgs() << Func->getPrintName() << " tentative: "
375                       << Twine::utohexstr(DotAddress) << "\n");
376     if (!Func->isSplit())
377       DotAddress += Func->estimateSize();
378     else
379       DotAddress += Func->estimateHotSize();
380 
381     DotAddress = alignTo(DotAddress, Func->getConstantIslandAlignment());
382     DotAddress += Func->estimateConstantIslandSize();
383     ++CurrentIndex;
384   }
385 
386   // Ensure that tentative code layout always runs for cold blocks.
387   if (!ColdLayoutDone)
388     runColdLayout();
389 
390   // BBs
391   for (BinaryFunction *Func : SortedFunctions)
392     tentativeBBLayout(*Func);
393 
394   return DotAddress;
395 }
396 
397 void LongJmpPass::tentativeLayout(
398     const BinaryContext &BC, std::vector<BinaryFunction *> &SortedFunctions) {
399   uint64_t DotAddress = BC.LayoutStartAddress;
400 
401   if (!BC.HasRelocations) {
402     for (BinaryFunction *Func : SortedFunctions) {
403       HotAddresses[Func] = Func->getAddress();
404       DotAddress = alignTo(DotAddress, ColdFragAlign);
405       ColdAddresses[Func] = DotAddress;
406       if (Func->isSplit())
407         DotAddress += Func->estimateColdSize();
408       tentativeBBLayout(*Func);
409     }
410 
411     return;
412   }
413 
414   // Relocation mode
415   uint64_t EstimatedTextSize = 0;
416   if (opts::UseOldText) {
417     EstimatedTextSize = tentativeLayoutRelocMode(BC, SortedFunctions, 0);
418 
419     // Initial padding
420     if (EstimatedTextSize <= BC.OldTextSectionSize) {
421       DotAddress = BC.OldTextSectionAddress;
422       uint64_t Pad =
423           offsetToAlignment(DotAddress, llvm::Align(opts::AlignText));
424       if (Pad + EstimatedTextSize <= BC.OldTextSectionSize) {
425         DotAddress += Pad;
426       }
427     }
428   }
429 
430   if (!EstimatedTextSize || EstimatedTextSize > BC.OldTextSectionSize)
431     DotAddress = alignTo(BC.LayoutStartAddress, opts::AlignText);
432 
433   tentativeLayoutRelocMode(BC, SortedFunctions, DotAddress);
434 }
435 
436 bool LongJmpPass::usesStub(const BinaryFunction &Func,
437                            const MCInst &Inst) const {
438   const MCSymbol *TgtSym = Func.getBinaryContext().MIB->getTargetSymbol(Inst);
439   const BinaryBasicBlock *TgtBB = Func.getBasicBlockForLabel(TgtSym);
440   auto Iter = Stubs.find(&Func);
441   if (Iter != Stubs.end())
442     return Iter->second.count(TgtBB);
443   return false;
444 }
445 
446 uint64_t LongJmpPass::getSymbolAddress(const BinaryContext &BC,
447                                        const MCSymbol *Target,
448                                        const BinaryBasicBlock *TgtBB) const {
449   if (TgtBB) {
450     auto Iter = BBAddresses.find(TgtBB);
451     assert(Iter != BBAddresses.end() && "Unrecognized BB");
452     return Iter->second;
453   }
454   uint64_t EntryID = 0;
455   const BinaryFunction *TargetFunc = BC.getFunctionForSymbol(Target, &EntryID);
456   auto Iter = HotAddresses.find(TargetFunc);
457   if (Iter == HotAddresses.end() || (TargetFunc && EntryID)) {
458     // Look at BinaryContext's resolution for this symbol - this is a symbol not
459     // mapped to a BinaryFunction
460     ErrorOr<uint64_t> ValueOrError = BC.getSymbolValue(*Target);
461     assert(ValueOrError && "Unrecognized symbol");
462     return *ValueOrError;
463   }
464   return Iter->second;
465 }
466 
467 Error LongJmpPass::relaxStub(BinaryBasicBlock &StubBB, bool &Modified) {
468   const BinaryFunction &Func = *StubBB.getFunction();
469   const BinaryContext &BC = Func.getBinaryContext();
470   const int Bits = StubBits[&StubBB];
471   // Already working with the largest range?
472   if (Bits == static_cast<int>(BC.AsmInfo->getCodePointerSize() * 8))
473     return Error::success();
474 
475   const static int RangeShortJmp = BC.MIB->getShortJmpEncodingSize();
476   const static int RangeSingleInstr = BC.MIB->getUncondBranchEncodingSize();
477   const static uint64_t ShortJmpMask = ~((1ULL << RangeShortJmp) - 1);
478   const static uint64_t SingleInstrMask =
479       ~((1ULL << (RangeSingleInstr - 1)) - 1);
480 
481   const MCSymbol *RealTargetSym = BC.MIB->getTargetSymbol(*StubBB.begin());
482   const BinaryBasicBlock *TgtBB = Func.getBasicBlockForLabel(RealTargetSym);
483   uint64_t TgtAddress = getSymbolAddress(BC, RealTargetSym, TgtBB);
484   uint64_t DotAddress = BBAddresses[&StubBB];
485   uint64_t PCRelTgtAddress = DotAddress > TgtAddress ? DotAddress - TgtAddress
486                                                      : TgtAddress - DotAddress;
487   // If it fits in one instruction, do not relax
488   if (!(PCRelTgtAddress & SingleInstrMask))
489     return Error::success();
490 
491   // Fits short jmp
492   if (!(PCRelTgtAddress & ShortJmpMask)) {
493     if (Bits >= RangeShortJmp)
494       return Error::success();
495 
496     LLVM_DEBUG(dbgs() << "Relaxing stub to short jump. PCRelTgtAddress = "
497                       << Twine::utohexstr(PCRelTgtAddress)
498                       << " RealTargetSym = " << RealTargetSym->getName()
499                       << "\n");
500     relaxStubToShortJmp(StubBB, RealTargetSym);
501     StubBits[&StubBB] = RangeShortJmp;
502     Modified = true;
503     return Error::success();
504   }
505 
506   // The long jmp uses absolute address on AArch64
507   // So we could not use it for PIC binaries
508   if (BC.isAArch64() && !BC.HasFixedLoadAddress)
509     return createFatalBOLTError(
510         "BOLT-ERROR: Unable to relax stub for PIC binary\n");
511 
512   LLVM_DEBUG(dbgs() << "Relaxing stub to long jump. PCRelTgtAddress = "
513                     << Twine::utohexstr(PCRelTgtAddress)
514                     << " RealTargetSym = " << RealTargetSym->getName() << "\n");
515   relaxStubToLongJmp(StubBB, RealTargetSym);
516   StubBits[&StubBB] = static_cast<int>(BC.AsmInfo->getCodePointerSize() * 8);
517   Modified = true;
518   return Error::success();
519 }
520 
521 bool LongJmpPass::needsStub(const BinaryBasicBlock &BB, const MCInst &Inst,
522                             uint64_t DotAddress) const {
523   const BinaryFunction &Func = *BB.getFunction();
524   const BinaryContext &BC = Func.getBinaryContext();
525   const MCSymbol *TgtSym = BC.MIB->getTargetSymbol(Inst);
526   assert(TgtSym && "getTargetSymbol failed");
527 
528   const BinaryBasicBlock *TgtBB = Func.getBasicBlockForLabel(TgtSym);
529   // Check for shared stubs from foreign functions
530   if (!TgtBB) {
531     auto SSIter = SharedStubs.find(TgtSym);
532     if (SSIter != SharedStubs.end())
533       TgtBB = SSIter->second;
534   }
535 
536   int BitsAvail = BC.MIB->getPCRelEncodingSize(Inst) - 1;
537   assert(BitsAvail < 63 && "PCRelEncodingSize is too large to use int64_t to"
538                            "check for out-of-bounds.");
539   int64_t MaxVal = (1ULL << BitsAvail) - 1;
540   int64_t MinVal = -(1ULL << BitsAvail);
541 
542   uint64_t PCRelTgtAddress = getSymbolAddress(BC, TgtSym, TgtBB);
543   int64_t PCOffset = (int64_t)(PCRelTgtAddress - DotAddress);
544 
545   return PCOffset < MinVal || PCOffset > MaxVal;
546 }
547 
548 Error LongJmpPass::relax(BinaryFunction &Func, bool &Modified) {
549   const BinaryContext &BC = Func.getBinaryContext();
550 
551   assert(BC.isAArch64() && "Unsupported arch");
552   constexpr int InsnSize = 4; // AArch64
553   std::vector<std::pair<BinaryBasicBlock *, std::unique_ptr<BinaryBasicBlock>>>
554       Insertions;
555 
556   BinaryBasicBlock *Frontier = getBBAtHotColdSplitPoint(Func);
557   uint64_t FrontierAddress = Frontier ? BBAddresses[Frontier] : 0;
558   if (FrontierAddress)
559     FrontierAddress += Frontier->getNumNonPseudos() * InsnSize;
560 
561   // Add necessary stubs for branch targets we know we can't fit in the
562   // instruction
563   for (BinaryBasicBlock &BB : Func) {
564     uint64_t DotAddress = BBAddresses[&BB];
565     // Stubs themselves are relaxed on the next loop
566     if (Stubs[&Func].count(&BB))
567       continue;
568 
569     for (MCInst &Inst : BB) {
570       if (BC.MIB->isPseudo(Inst))
571         continue;
572 
573       if (!shouldInsertStub(BC, Inst)) {
574         DotAddress += InsnSize;
575         continue;
576       }
577 
578       // Check and relax direct branch or call
579       if (!needsStub(BB, Inst, DotAddress)) {
580         DotAddress += InsnSize;
581         continue;
582       }
583       Modified = true;
584 
585       // Insert stubs close to the patched BB if call, but far away from the
586       // hot path if a branch, since this branch target is the cold region
587       // (but first check that the far away stub will be in range).
588       BinaryBasicBlock *InsertionPoint = &BB;
589       if (Func.isSimple() && !BC.MIB->isCall(Inst) && FrontierAddress &&
590           !BB.isCold()) {
591         int BitsAvail = BC.MIB->getPCRelEncodingSize(Inst) - 1;
592         uint64_t Mask = ~((1ULL << BitsAvail) - 1);
593         assert(FrontierAddress > DotAddress &&
594                "Hot code should be before the frontier");
595         uint64_t PCRelTgt = FrontierAddress - DotAddress;
596         if (!(PCRelTgt & Mask))
597           InsertionPoint = Frontier;
598       }
599       // Always put stubs at the end of the function if non-simple. We can't
600       // change the layout of non-simple functions because it has jump tables
601       // that we do not control.
602       if (!Func.isSimple())
603         InsertionPoint = &*std::prev(Func.end());
604 
605       // Create a stub to handle a far-away target
606       Insertions.emplace_back(InsertionPoint,
607                               replaceTargetWithStub(BB, Inst, DotAddress,
608                                                     InsertionPoint == Frontier
609                                                         ? FrontierAddress
610                                                         : DotAddress));
611 
612       DotAddress += InsnSize;
613     }
614   }
615 
616   // Relax stubs if necessary
617   for (BinaryBasicBlock &BB : Func) {
618     if (!Stubs[&Func].count(&BB) || !BB.isValid())
619       continue;
620 
621     if (auto E = relaxStub(BB, Modified))
622       return Error(std::move(E));
623   }
624 
625   for (std::pair<BinaryBasicBlock *, std::unique_ptr<BinaryBasicBlock>> &Elmt :
626        Insertions) {
627     if (!Elmt.second)
628       continue;
629     std::vector<std::unique_ptr<BinaryBasicBlock>> NewBBs;
630     NewBBs.emplace_back(std::move(Elmt.second));
631     Func.insertBasicBlocks(Elmt.first, std::move(NewBBs), true);
632   }
633 
634   return Error::success();
635 }
636 
637 Error LongJmpPass::runOnFunctions(BinaryContext &BC) {
638   BC.outs() << "BOLT-INFO: Starting stub-insertion pass\n";
639   std::vector<BinaryFunction *> Sorted = BC.getSortedFunctions();
640   bool Modified;
641   uint32_t Iterations = 0;
642   do {
643     ++Iterations;
644     Modified = false;
645     tentativeLayout(BC, Sorted);
646     updateStubGroups();
647     for (BinaryFunction *Func : Sorted) {
648       if (auto E = relax(*Func, Modified))
649         return Error(std::move(E));
650       // Don't ruin non-simple functions, they can't afford to have the layout
651       // changed.
652       if (Modified && Func->isSimple())
653         Func->fixBranches();
654     }
655   } while (Modified);
656   BC.outs() << "BOLT-INFO: Inserted " << NumHotStubs
657             << " stubs in the hot area and " << NumColdStubs
658             << " stubs in the cold area. Shared " << NumSharedStubs
659             << " times, iterated " << Iterations << " times.\n";
660   return Error::success();
661 }
662 } // namespace bolt
663 } // namespace llvm
664