brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.7 KiB · 0b26da3 Raw
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