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