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