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