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