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