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