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