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 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 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 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 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 *> 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 120 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 * 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 // 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 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 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 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 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 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 418 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 428 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 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 502 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 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 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