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