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