1 //===- bolt/Passes/FrameAnalysis.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 FrameAnalysis class. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "bolt/Passes/FrameAnalysis.h" 14 #include "bolt/Core/ParallelUtilities.h" 15 #include "bolt/Passes/CallGraphWalker.h" 16 #include "llvm/MC/MCRegisterInfo.h" 17 #include "llvm/Support/Timer.h" 18 #include <fstream> 19 #include <stack> 20 21 #define DEBUG_TYPE "fa" 22 23 using namespace llvm; 24 25 namespace opts { 26 extern cl::OptionCategory BoltOptCategory; 27 extern cl::opt<unsigned> Verbosity; 28 29 static cl::list<std::string> 30 FrameOptFunctionNames("funcs-fop", cl::CommaSeparated, 31 cl::desc("list of functions to apply frame opts"), 32 cl::value_desc("func1,func2,func3,...")); 33 34 static cl::opt<std::string> FrameOptFunctionNamesFile( 35 "funcs-file-fop", 36 cl::desc("file with list of functions to frame optimize")); 37 38 static cl::opt<bool> TimeFA("time-fa", cl::desc("time frame analysis steps"), 39 cl::ReallyHidden, cl::cat(BoltOptCategory)); 40 41 bool shouldFrameOptimize(const llvm::bolt::BinaryFunction &Function) { 42 if (Function.hasUnknownControlFlow()) 43 return false; 44 45 if (!FrameOptFunctionNamesFile.empty()) { 46 assert(!FrameOptFunctionNamesFile.empty() && "unexpected empty file name"); 47 std::ifstream FuncsFile(FrameOptFunctionNamesFile, std::ios::in); 48 std::string FuncName; 49 while (std::getline(FuncsFile, FuncName)) 50 FrameOptFunctionNames.push_back(FuncName); 51 FrameOptFunctionNamesFile = ""; 52 } 53 54 bool IsValid = true; 55 if (!FrameOptFunctionNames.empty()) { 56 IsValid = false; 57 for (std::string &Name : FrameOptFunctionNames) { 58 if (Function.hasName(Name)) { 59 IsValid = true; 60 break; 61 } 62 } 63 } 64 if (!IsValid) 65 return false; 66 67 return IsValid; 68 } 69 } // namespace opts 70 71 namespace llvm { 72 namespace bolt { 73 74 raw_ostream &operator<<(raw_ostream &OS, const FrameIndexEntry &FIE) { 75 OS << "FrameIndexEntry<IsLoad: " << FIE.IsLoad << ", IsStore: " << FIE.IsStore 76 << ", IsStoreFromReg: " << FIE.IsStoreFromReg 77 << ", RegOrImm: " << FIE.RegOrImm << ", StackOffset: "; 78 if (FIE.StackOffset < 0) 79 OS << "-" << Twine::utohexstr(-FIE.StackOffset); 80 else 81 OS << "+" << Twine::utohexstr(FIE.StackOffset); 82 OS << ", Size: " << static_cast<int>(FIE.Size) 83 << ", IsSimple: " << FIE.IsSimple << ">"; 84 return OS; 85 } 86 87 namespace { 88 89 /// This class should be used to iterate through basic blocks in layout order 90 /// to analyze instructions for frame accesses. The user should call 91 /// enterNewBB() whenever starting analyzing a new BB and doNext() for each 92 /// instruction. After doNext(), if isValidAccess() returns true, it means the 93 /// current instruction accesses the frame and getFIE() may be used to obtain 94 /// details about this access. 95 class FrameAccessAnalysis { 96 /// We depend on Stack Pointer Tracking to figure out the current SP offset 97 /// value at a given program point 98 StackPointerTracking &SPT; 99 100 /// Context vars 101 const BinaryContext &BC; 102 const BinaryFunction &BF; 103 // Vars used for storing useful CFI info to give us a hint about how the stack 104 // is used in this function 105 int SPOffset{0}; 106 int FPOffset{0}; 107 int64_t CfaOffset{-8}; 108 uint16_t CfaReg{7}; 109 std::stack<std::pair<int64_t, uint16_t>> CFIStack; 110 /// Our pointer to access SPT info 111 const MCInst *Prev{nullptr}; 112 /// Info about the last frame access 113 bool IsValidAccess{false}; 114 FrameIndexEntry FIE; 115 116 bool decodeFrameAccess(const MCInst &Inst) { 117 int32_t SrcImm = 0; 118 MCPhysReg Reg = 0; 119 int64_t StackOffset = 0; 120 bool IsIndexed = false; 121 if (!BC.MIB->isStackAccess( 122 Inst, FIE.IsLoad, FIE.IsStore, FIE.IsStoreFromReg, Reg, SrcImm, 123 FIE.StackPtrReg, StackOffset, FIE.Size, FIE.IsSimple, IsIndexed)) { 124 return true; 125 } 126 127 if (IsIndexed || FIE.Size == 0) { 128 LLVM_DEBUG(dbgs() << "Giving up on indexed memory access/unknown size\n"); 129 LLVM_DEBUG(dbgs() << "Blame insn: "); 130 LLVM_DEBUG(Inst.dump()); 131 return false; 132 } 133 134 assert(FIE.Size != 0); 135 136 FIE.RegOrImm = SrcImm; 137 if (FIE.IsLoad || FIE.IsStoreFromReg) 138 FIE.RegOrImm = Reg; 139 140 if (FIE.StackPtrReg == BC.MIB->getStackPointer() && SPOffset != SPT.EMPTY && 141 SPOffset != SPT.SUPERPOSITION) { 142 LLVM_DEBUG( 143 dbgs() << "Adding access via SP while CFA reg is another one\n"); 144 FIE.StackOffset = SPOffset + StackOffset; 145 } else if (FIE.StackPtrReg == BC.MIB->getFramePointer() && 146 FPOffset != SPT.EMPTY && FPOffset != SPT.SUPERPOSITION) { 147 LLVM_DEBUG( 148 dbgs() << "Adding access via FP while CFA reg is another one\n"); 149 FIE.StackOffset = FPOffset + StackOffset; 150 } else if (FIE.StackPtrReg == 151 *BC.MRI->getLLVMRegNum(CfaReg, /*isEH=*/false)) { 152 FIE.StackOffset = CfaOffset + StackOffset; 153 } else { 154 LLVM_DEBUG( 155 dbgs() << "Found stack access with reg different than cfa reg.\n"); 156 LLVM_DEBUG(dbgs() << "\tCurrent CFA reg: " << CfaReg 157 << "\n\tStack access reg: " << FIE.StackPtrReg << "\n"); 158 LLVM_DEBUG(dbgs() << "Blame insn: "); 159 LLVM_DEBUG(Inst.dump()); 160 return false; 161 } 162 IsValidAccess = true; 163 return true; 164 } 165 166 public: 167 FrameAccessAnalysis(BinaryFunction &BF, StackPointerTracking &SPT) 168 : SPT(SPT), BC(BF.getBinaryContext()), BF(BF) {} 169 170 void enterNewBB() { Prev = nullptr; } 171 const FrameIndexEntry &getFIE() const { return FIE; } 172 int getSPOffset() const { return SPOffset; } 173 bool isValidAccess() const { return IsValidAccess; } 174 175 bool doNext(const BinaryBasicBlock &BB, const MCInst &Inst) { 176 IsValidAccess = false; 177 std::tie(SPOffset, FPOffset) = 178 Prev ? *SPT.getStateAt(*Prev) : *SPT.getStateAt(BB); 179 Prev = &Inst; 180 // Use CFI information to keep track of which register is being used to 181 // access the frame 182 if (BC.MIB->isCFI(Inst)) { 183 const MCCFIInstruction *CFI = BF.getCFIFor(Inst); 184 switch (CFI->getOperation()) { 185 case MCCFIInstruction::OpDefCfa: 186 CfaOffset = CFI->getOffset(); 187 LLVM_FALLTHROUGH; 188 case MCCFIInstruction::OpDefCfaRegister: 189 CfaReg = CFI->getRegister(); 190 break; 191 case MCCFIInstruction::OpDefCfaOffset: 192 CfaOffset = CFI->getOffset(); 193 break; 194 case MCCFIInstruction::OpRememberState: 195 CFIStack.push(std::make_pair(CfaOffset, CfaReg)); 196 break; 197 case MCCFIInstruction::OpRestoreState: { 198 if (CFIStack.empty()) 199 dbgs() << "Assertion is about to fail: " << BF.getPrintName() << "\n"; 200 assert(!CFIStack.empty() && "Corrupt CFI stack"); 201 std::pair<int64_t, uint16_t> &Elem = CFIStack.top(); 202 CFIStack.pop(); 203 CfaOffset = Elem.first; 204 CfaReg = Elem.second; 205 break; 206 } 207 case MCCFIInstruction::OpAdjustCfaOffset: 208 llvm_unreachable("Unhandled AdjustCfaOffset"); 209 break; 210 default: 211 break; 212 } 213 return true; 214 } 215 216 if (BC.MIB->escapesVariable(Inst, SPT.HasFramePointer)) { 217 LLVM_DEBUG( 218 dbgs() << "Leaked stack address, giving up on this function.\n"); 219 LLVM_DEBUG(dbgs() << "Blame insn: "); 220 LLVM_DEBUG(Inst.dump()); 221 return false; 222 } 223 224 return decodeFrameAccess(Inst); 225 } 226 }; 227 228 } // end anonymous namespace 229 230 void FrameAnalysis::addArgAccessesFor(MCInst &Inst, ArgAccesses &&AA) { 231 if (ErrorOr<ArgAccesses &> OldAA = getArgAccessesFor(Inst)) { 232 if (OldAA->AssumeEverything) 233 return; 234 *OldAA = std::move(AA); 235 return; 236 } 237 if (AA.AssumeEverything) { 238 // Index 0 in ArgAccessesVector represents an "assumeeverything" entry 239 BC.MIB->addAnnotation(Inst, "ArgAccessEntry", 0U); 240 return; 241 } 242 BC.MIB->addAnnotation(Inst, "ArgAccessEntry", 243 (unsigned)ArgAccessesVector.size()); 244 ArgAccessesVector.emplace_back(std::move(AA)); 245 } 246 247 void FrameAnalysis::addArgInStackAccessFor(MCInst &Inst, 248 const ArgInStackAccess &Arg) { 249 ErrorOr<ArgAccesses &> AA = getArgAccessesFor(Inst); 250 if (!AA) { 251 addArgAccessesFor(Inst, ArgAccesses(false)); 252 AA = getArgAccessesFor(Inst); 253 assert(AA && "Object setup failed"); 254 } 255 std::set<ArgInStackAccess> &Set = AA->Set; 256 assert(!AA->AssumeEverything && "Adding arg to AssumeEverything set"); 257 Set.emplace(Arg); 258 } 259 260 void FrameAnalysis::addFIEFor(MCInst &Inst, const FrameIndexEntry &FIE) { 261 BC.MIB->addAnnotation(Inst, "FrameAccessEntry", (unsigned)FIEVector.size()); 262 FIEVector.emplace_back(FIE); 263 } 264 265 ErrorOr<ArgAccesses &> FrameAnalysis::getArgAccessesFor(const MCInst &Inst) { 266 if (auto Idx = BC.MIB->tryGetAnnotationAs<unsigned>(Inst, "ArgAccessEntry")) { 267 assert(ArgAccessesVector.size() > *Idx && "Out of bounds"); 268 return ArgAccessesVector[*Idx]; 269 } 270 return make_error_code(errc::result_out_of_range); 271 } 272 273 ErrorOr<const ArgAccesses &> 274 FrameAnalysis::getArgAccessesFor(const MCInst &Inst) const { 275 if (auto Idx = BC.MIB->tryGetAnnotationAs<unsigned>(Inst, "ArgAccessEntry")) { 276 assert(ArgAccessesVector.size() > *Idx && "Out of bounds"); 277 return ArgAccessesVector[*Idx]; 278 } 279 return make_error_code(errc::result_out_of_range); 280 } 281 282 ErrorOr<const FrameIndexEntry &> 283 FrameAnalysis::getFIEFor(const MCInst &Inst) const { 284 if (auto Idx = 285 BC.MIB->tryGetAnnotationAs<unsigned>(Inst, "FrameAccessEntry")) { 286 assert(FIEVector.size() > *Idx && "Out of bounds"); 287 return FIEVector[*Idx]; 288 } 289 return make_error_code(errc::result_out_of_range); 290 } 291 292 void FrameAnalysis::traverseCG(BinaryFunctionCallGraph &CG) { 293 CallGraphWalker CGWalker(CG); 294 295 CGWalker.registerVisitor( 296 [&](BinaryFunction *Func) -> bool { return computeArgsAccessed(*Func); }); 297 298 CGWalker.walk(); 299 300 DEBUG_WITH_TYPE("ra", { 301 for (auto &MapEntry : ArgsTouchedMap) { 302 const BinaryFunction *Func = MapEntry.first; 303 const auto &Set = MapEntry.second; 304 dbgs() << "Args accessed for " << Func->getPrintName() << ": "; 305 if (!Set.empty() && Set.count(std::make_pair(-1, 0))) 306 dbgs() << "assume everything"; 307 else 308 for (const std::pair<int64_t, uint8_t> &Entry : Set) 309 dbgs() << "[" << Entry.first << ", " << (int)Entry.second << "] "; 310 dbgs() << "\n"; 311 } 312 }); 313 } 314 315 bool FrameAnalysis::updateArgsTouchedFor(const BinaryFunction &BF, MCInst &Inst, 316 int CurOffset) { 317 if (!BC.MIB->isCall(Inst)) 318 return false; 319 320 std::set<int64_t> Res; 321 const MCSymbol *TargetSymbol = BC.MIB->getTargetSymbol(Inst); 322 // If indirect call, we conservatively assume it accesses all stack positions 323 if (TargetSymbol == nullptr) { 324 addArgAccessesFor(Inst, ArgAccesses(/*AssumeEverything=*/true)); 325 if (!FunctionsRequireAlignment.count(&BF)) { 326 FunctionsRequireAlignment.insert(&BF); 327 return true; 328 } 329 return false; 330 } 331 332 const BinaryFunction *Function = BC.getFunctionForSymbol(TargetSymbol); 333 // Call to a function without a BinaryFunction object. Conservatively assume 334 // it accesses all stack positions 335 if (Function == nullptr) { 336 addArgAccessesFor(Inst, ArgAccesses(/*AssumeEverything=*/true)); 337 if (!FunctionsRequireAlignment.count(&BF)) { 338 FunctionsRequireAlignment.insert(&BF); 339 return true; 340 } 341 return false; 342 } 343 344 auto Iter = ArgsTouchedMap.find(Function); 345 346 bool Changed = false; 347 if (BC.MIB->isTailCall(Inst) && Iter != ArgsTouchedMap.end()) { 348 // Ignore checking CurOffset because we can't always reliably determine the 349 // offset specially after an epilogue, where tailcalls happen. It should be 350 // -8. 351 for (std::pair<int64_t, uint8_t> Elem : Iter->second) { 352 if (ArgsTouchedMap[&BF].find(Elem) == ArgsTouchedMap[&BF].end()) { 353 ArgsTouchedMap[&BF].emplace(Elem); 354 Changed = true; 355 } 356 } 357 } 358 if (FunctionsRequireAlignment.count(Function) && 359 !FunctionsRequireAlignment.count(&BF)) { 360 Changed = true; 361 FunctionsRequireAlignment.insert(&BF); 362 } 363 if (Iter == ArgsTouchedMap.end()) 364 return Changed; 365 366 if (CurOffset == StackPointerTracking::EMPTY || 367 CurOffset == StackPointerTracking::SUPERPOSITION) { 368 addArgAccessesFor(Inst, ArgAccesses(/*AssumeEverything=*/true)); 369 return Changed; 370 } 371 372 for (std::pair<int64_t, uint8_t> Elem : Iter->second) { 373 if (Elem.first == -1) { 374 addArgAccessesFor(Inst, ArgAccesses(/*AssumeEverything=*/true)); 375 break; 376 } 377 LLVM_DEBUG(dbgs() << "Added arg in stack access annotation " 378 << CurOffset + Elem.first << "\n"); 379 addArgInStackAccessFor( 380 Inst, ArgInStackAccess{/*StackOffset=*/CurOffset + Elem.first, 381 /*Size=*/Elem.second}); 382 } 383 return Changed; 384 } 385 386 bool FrameAnalysis::computeArgsAccessed(BinaryFunction &BF) { 387 if (!BF.isSimple() || !BF.hasCFG()) { 388 LLVM_DEBUG(dbgs() << "Treating " << BF.getPrintName() 389 << " conservatively.\n"); 390 ArgsTouchedMap[&BF].emplace(std::make_pair(-1, 0)); 391 if (!FunctionsRequireAlignment.count(&BF)) { 392 FunctionsRequireAlignment.insert(&BF); 393 return true; 394 } 395 return false; 396 } 397 398 LLVM_DEBUG(dbgs() << "Now computing args accessed for: " << BF.getPrintName() 399 << "\n"); 400 bool UpdatedArgsTouched = false; 401 bool NoInfo = false; 402 FrameAccessAnalysis FAA(BF, getSPT(BF)); 403 404 for (BinaryBasicBlock *BB : BF.layout()) { 405 FAA.enterNewBB(); 406 407 for (MCInst &Inst : *BB) { 408 if (!FAA.doNext(*BB, Inst)) { 409 ArgsTouchedMap[&BF].emplace(std::make_pair(-1, 0)); 410 NoInfo = true; 411 break; 412 } 413 414 // Check for calls -- attach stack accessing info to them regarding their 415 // target 416 if (updateArgsTouchedFor(BF, Inst, FAA.getSPOffset())) 417 UpdatedArgsTouched = true; 418 419 // Check for stack accesses that affect callers 420 if (!FAA.isValidAccess()) 421 continue; 422 423 const FrameIndexEntry &FIE = FAA.getFIE(); 424 if (FIE.StackOffset < 0) 425 continue; 426 if (ArgsTouchedMap[&BF].find(std::make_pair(FIE.StackOffset, FIE.Size)) != 427 ArgsTouchedMap[&BF].end()) 428 continue; 429 430 // Record accesses to the previous stack frame 431 ArgsTouchedMap[&BF].emplace(std::make_pair(FIE.StackOffset, FIE.Size)); 432 UpdatedArgsTouched = true; 433 LLVM_DEBUG({ 434 dbgs() << "Arg access offset " << FIE.StackOffset << " added to:\n"; 435 BC.printInstruction(dbgs(), Inst, 0, &BF, true); 436 }); 437 } 438 if (NoInfo) 439 break; 440 } 441 if (FunctionsRequireAlignment.count(&BF)) 442 return UpdatedArgsTouched; 443 444 if (NoInfo) { 445 FunctionsRequireAlignment.insert(&BF); 446 return true; 447 } 448 449 for (BinaryBasicBlock &BB : BF) { 450 for (MCInst &Inst : BB) { 451 if (BC.MIB->requiresAlignedAddress(Inst)) { 452 FunctionsRequireAlignment.insert(&BF); 453 return true; 454 } 455 } 456 } 457 return UpdatedArgsTouched; 458 } 459 460 bool FrameAnalysis::restoreFrameIndex(BinaryFunction &BF) { 461 FrameAccessAnalysis FAA(BF, getSPT(BF)); 462 463 LLVM_DEBUG(dbgs() << "Restoring frame indices for \"" << BF.getPrintName() 464 << "\"\n"); 465 for (BinaryBasicBlock *BB : BF.layout()) { 466 LLVM_DEBUG(dbgs() << "\tNow at BB " << BB->getName() << "\n"); 467 FAA.enterNewBB(); 468 469 for (MCInst &Inst : *BB) { 470 if (!FAA.doNext(*BB, Inst)) 471 return false; 472 LLVM_DEBUG({ 473 dbgs() << "\t\tNow at "; 474 Inst.dump(); 475 dbgs() << "\t\t\tSP offset is " << FAA.getSPOffset() << "\n"; 476 }); 477 478 if (!FAA.isValidAccess()) 479 continue; 480 481 const FrameIndexEntry &FIE = FAA.getFIE(); 482 483 addFIEFor(Inst, FIE); 484 LLVM_DEBUG({ 485 dbgs() << "Frame index annotation " << FIE << " added to:\n"; 486 BC.printInstruction(dbgs(), Inst, 0, &BF, true); 487 }); 488 } 489 } 490 return true; 491 } 492 493 void FrameAnalysis::cleanAnnotations() { 494 NamedRegionTimer T("cleanannotations", "clean annotations", "FA", 495 "FA breakdown", opts::TimeFA); 496 497 ParallelUtilities::WorkFuncTy CleanFunction = [&](BinaryFunction &BF) { 498 for (BinaryBasicBlock &BB : BF) { 499 for (MCInst &Inst : BB) { 500 BC.MIB->removeAnnotation(Inst, "ArgAccessEntry"); 501 BC.MIB->removeAnnotation(Inst, "FrameAccessEntry"); 502 } 503 } 504 }; 505 506 ParallelUtilities::runOnEachFunction( 507 BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, CleanFunction, 508 ParallelUtilities::PredicateTy(nullptr), "cleanAnnotations"); 509 } 510 511 FrameAnalysis::FrameAnalysis(BinaryContext &BC, BinaryFunctionCallGraph &CG) 512 : BC(BC) { 513 // Position 0 of the vector should be always associated with "assume access 514 // everything". 515 ArgAccessesVector.emplace_back(ArgAccesses(/*AssumeEverything*/ true)); 516 517 if (!opts::NoThreads) { 518 NamedRegionTimer T1("precomputespt", "pre-compute spt", "FA", 519 "FA breakdown", opts::TimeFA); 520 preComputeSPT(); 521 } 522 523 { 524 NamedRegionTimer T1("traversecg", "traverse call graph", "FA", 525 "FA breakdown", opts::TimeFA); 526 traverseCG(CG); 527 } 528 529 for (auto &I : BC.getBinaryFunctions()) { 530 CountDenominator += I.second.getFunctionScore(); 531 532 // "shouldOptimize" for passes that run after finalize 533 if (!(I.second.isSimple() && I.second.hasCFG() && !I.second.isIgnored()) || 534 !opts::shouldFrameOptimize(I.second)) { 535 ++NumFunctionsNotOptimized; 536 continue; 537 } 538 539 { 540 NamedRegionTimer T1("restorefi", "restore frame index", "FA", 541 "FA breakdown", opts::TimeFA); 542 if (!restoreFrameIndex(I.second)) { 543 ++NumFunctionsFailedRestoreFI; 544 CountFunctionsFailedRestoreFI += I.second.getFunctionScore(); 545 continue; 546 } 547 } 548 AnalyzedFunctions.insert(&I.second); 549 } 550 551 { 552 NamedRegionTimer T1("clearspt", "clear spt", "FA", "FA breakdown", 553 opts::TimeFA); 554 clearSPTMap(); 555 556 // Clean up memory allocated for annotation values 557 if (!opts::NoThreads) 558 for (MCPlusBuilder::AllocatorIdTy Id : SPTAllocatorsId) 559 BC.MIB->freeValuesAllocator(Id); 560 } 561 } 562 563 void FrameAnalysis::printStats() { 564 outs() << "BOLT-INFO: FRAME ANALYSIS: " << NumFunctionsNotOptimized 565 << " function(s) were not optimized.\n" 566 << "BOLT-INFO: FRAME ANALYSIS: " << NumFunctionsFailedRestoreFI 567 << " function(s) " 568 << format("(%.1lf%% dyn cov)", 569 (100.0 * CountFunctionsFailedRestoreFI / CountDenominator)) 570 << " could not have its frame indices restored.\n"; 571 } 572 573 void FrameAnalysis::clearSPTMap() { 574 if (opts::NoThreads) { 575 SPTMap.clear(); 576 return; 577 } 578 579 ParallelUtilities::WorkFuncTy ClearFunctionSPT = [&](BinaryFunction &BF) { 580 std::unique_ptr<StackPointerTracking> &SPTPtr = SPTMap.find(&BF)->second; 581 SPTPtr.reset(); 582 }; 583 584 ParallelUtilities::PredicateTy SkipFunc = [&](const BinaryFunction &BF) { 585 return !BF.isSimple() || !BF.hasCFG(); 586 }; 587 588 ParallelUtilities::runOnEachFunction( 589 BC, ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, ClearFunctionSPT, 590 SkipFunc, "clearSPTMap"); 591 592 SPTMap.clear(); 593 } 594 595 void FrameAnalysis::preComputeSPT() { 596 // Make sure that the SPTMap is empty 597 assert(SPTMap.size() == 0); 598 599 // Create map entries to allow lock-free parallel execution 600 for (auto &BFI : BC.getBinaryFunctions()) { 601 BinaryFunction &BF = BFI.second; 602 if (!BF.isSimple() || !BF.hasCFG()) 603 continue; 604 SPTMap.emplace(&BF, std::unique_ptr<StackPointerTracking>()); 605 } 606 607 // Create an index for the SPT annotation to allow lock-free parallel 608 // execution 609 BC.MIB->getOrCreateAnnotationIndex("StackPointerTracking"); 610 611 // Run SPT in parallel 612 ParallelUtilities::WorkFuncWithAllocTy ProcessFunction = 613 [&](BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId) { 614 std::unique_ptr<StackPointerTracking> &SPTPtr = 615 SPTMap.find(&BF)->second; 616 SPTPtr = std::make_unique<StackPointerTracking>(BF, AllocId); 617 SPTPtr->run(); 618 }; 619 620 ParallelUtilities::PredicateTy SkipPredicate = [&](const BinaryFunction &BF) { 621 return !BF.isSimple() || !BF.hasCFG(); 622 }; 623 624 ParallelUtilities::runOnEachFunctionWithUniqueAllocId( 625 BC, ParallelUtilities::SchedulingPolicy::SP_BB_QUADRATIC, ProcessFunction, 626 SkipPredicate, "preComputeSPT"); 627 } 628 629 } // namespace bolt 630 } // namespace llvm 631