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