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/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 | |
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 | |
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(is&: FuncsFile, str&: FuncName)) |
55 | FrameOptFunctionNames.push_back(value: FuncName); |
56 | FrameOptFunctionNamesFile = ""; |
57 | } |
58 | |
59 | if (FrameOptFunctionNames.empty()) |
60 | return true; |
61 | return llvm::any_of(Range&: FrameOptFunctionNames, P: [&](std::string &Name) { |
62 | return Function.hasName(FunctionName: Name); |
63 | }); |
64 | } |
65 | } // namespace opts |
66 | |
67 | namespace llvm { |
68 | namespace bolt { |
69 | |
70 | raw_ostream &operator<<(raw_ostream &OS, const FrameIndexEntry &FIE) { |
71 | OS << "FrameIndexEntry<IsLoad: "<< FIE.IsLoad << ", IsStore: "<< FIE.IsStore |
72 | << ", IsStoreFromReg: "<< FIE.IsStoreFromReg |
73 | << ", RegOrImm: "<< FIE.RegOrImm << ", StackOffset: "; |
74 | if (FIE.StackOffset < 0) |
75 | OS << "-"<< Twine::utohexstr(Val: -FIE.StackOffset); |
76 | else |
77 | OS << "+"<< Twine::utohexstr(Val: FIE.StackOffset); |
78 | OS << ", Size: "<< static_cast<int>(FIE.Size) |
79 | << ", IsSimple: "<< FIE.IsSimple << ">"; |
80 | return OS; |
81 | } |
82 | |
83 | namespace { |
84 | |
85 | /// This class should be used to iterate through basic blocks in layout order |
86 | /// to analyze instructions for frame accesses. The user should call |
87 | /// enterNewBB() whenever starting analyzing a new BB and doNext() for each |
88 | /// instruction. After doNext(), if isValidAccess() returns true, it means the |
89 | /// current instruction accesses the frame and getFIE() may be used to obtain |
90 | /// details about this access. |
91 | class FrameAccessAnalysis { |
92 | /// We depend on Stack Pointer Tracking to figure out the current SP offset |
93 | /// value at a given program point |
94 | StackPointerTracking &SPT; |
95 | |
96 | /// Context vars |
97 | const BinaryContext &BC; |
98 | const BinaryFunction &BF; |
99 | // Vars used for storing useful CFI info to give us a hint about how the stack |
100 | // is used in this function |
101 | 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 info |
107 | const MCInst *Prev{nullptr}; |
108 | /// Info about the last frame access |
109 | 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, IsLoad&: FIE.IsLoad, IsStore&: FIE.IsStore, IsStoreFromReg&: FIE.IsStoreFromReg, Reg, SrcImm, |
120 | StackPtrReg&: FIE.StackPtrReg, StackOffset, Size&: FIE.Size, IsSimple&: 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(RegNum: 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: "<< CfaReg |
155 | << "\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 | |
164 | public: |
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(args&: SPOffset, args&: FPOffset) = |
178 | Prev ? *SPT.getStateAt(Point: *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(Instr: 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(x: std::make_pair(x&: CfaOffset, y&: 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, HasFramePointer: 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 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, Name: "ArgAccessEntry", Val: 0U); |
243 | return; |
244 | } |
245 | BC.MIB->addAnnotation(Inst, Name: "ArgAccessEntry", |
246 | Val: (unsigned)ArgAccessesVector.size()); |
247 | ArgAccessesVector.emplace_back(args: 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, AA: 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(args: Arg); |
261 | } |
262 | |
263 | void FrameAnalysis::addFIEFor(MCInst &Inst, const FrameIndexEntry &FIE) { |
264 | BC.MIB->addAnnotation(Inst, Name: "FrameAccessEntry", Val: (unsigned)FIEVector.size()); |
265 | FIEVector.emplace_back(args: FIE); |
266 | } |
267 | |
268 | ErrorOr<ArgAccesses &> FrameAnalysis::getArgAccessesFor(const MCInst &Inst) { |
269 | if (auto Idx = BC.MIB->tryGetAnnotationAs<unsigned>(Inst, Name: "ArgAccessEntry")) { |
270 | assert(ArgAccessesVector.size() > *Idx && "Out of bounds"); |
271 | return ArgAccessesVector[*Idx]; |
272 | } |
273 | return make_error_code(E: 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, Name: "ArgAccessEntry")) { |
279 | assert(ArgAccessesVector.size() > *Idx && "Out of bounds"); |
280 | return ArgAccessesVector[*Idx]; |
281 | } |
282 | return make_error_code(E: 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, Name: "FrameAccessEntry")) { |
289 | assert(FIEVector.size() > *Idx && "Out of bounds"); |
290 | return FIEVector[*Idx]; |
291 | } |
292 | return make_error_code(E: errc::result_out_of_range); |
293 | } |
294 | |
295 | void FrameAnalysis::traverseCG(BinaryFunctionCallGraph &CG) { |
296 | CallGraphWalker CGWalker(CG); |
297 | |
298 | CGWalker.registerVisitor( |
299 | Callback: [&](BinaryFunction *Func) -> bool { return computeArgsAccessed(BF&: *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 | const MCSymbol *TargetSymbol = BC.MIB->getTargetSymbol(Inst); |
324 | // If indirect call, we conservatively assume it accesses all stack positions |
325 | if (TargetSymbol == nullptr) { |
326 | addArgAccessesFor(Inst, AA: ArgAccesses(/*AssumeEverything=*/true)); |
327 | if (!FunctionsRequireAlignment.count(V: &BF)) { |
328 | FunctionsRequireAlignment.insert(V: &BF); |
329 | return true; |
330 | } |
331 | return false; |
332 | } |
333 | |
334 | const BinaryFunction *Function = BC.getFunctionForSymbol(Symbol: TargetSymbol); |
335 | // Call to a function without a BinaryFunction object. Conservatively assume |
336 | // it accesses all stack positions |
337 | if (Function == nullptr) { |
338 | addArgAccessesFor(Inst, AA: ArgAccesses(/*AssumeEverything=*/true)); |
339 | if (!FunctionsRequireAlignment.count(V: &BF)) { |
340 | FunctionsRequireAlignment.insert(V: &BF); |
341 | return true; |
342 | } |
343 | return false; |
344 | } |
345 | |
346 | auto Iter = ArgsTouchedMap.find(x: 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 the |
351 | // offset specially after an epilogue, where tailcalls happen. It should be |
352 | // -8. |
353 | for (std::pair<int64_t, uint8_t> Elem : Iter->second) { |
354 | if (!llvm::is_contained(Range&: ArgsTouchedMap[&BF], Element: Elem)) { |
355 | ArgsTouchedMap[&BF].emplace(args&: Elem); |
356 | Changed = true; |
357 | } |
358 | } |
359 | } |
360 | if (FunctionsRequireAlignment.count(V: Function) && |
361 | !FunctionsRequireAlignment.count(V: &BF)) { |
362 | Changed = true; |
363 | FunctionsRequireAlignment.insert(V: &BF); |
364 | } |
365 | if (Iter == ArgsTouchedMap.end()) |
366 | return Changed; |
367 | |
368 | if (CurOffset == StackPointerTracking::EMPTY || |
369 | CurOffset == StackPointerTracking::SUPERPOSITION) { |
370 | addArgAccessesFor(Inst, AA: 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, AA: 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, Arg: ArgInStackAccess{/*StackOffset=*/CurOffset + Elem.first, |
383 | /*Size=*/Elem.second}); |
384 | } |
385 | return Changed; |
386 | } |
387 | |
388 | bool FrameAnalysis::computeArgsAccessed(BinaryFunction &BF) { |
389 | if (!BF.isSimple() || !BF.hasCFG()) { |
390 | LLVM_DEBUG(dbgs() << "Treating "<< BF.getPrintName() |
391 | << " conservatively.\n"); |
392 | ArgsTouchedMap[&BF].emplace(args: std::make_pair(x: -1, y: 0)); |
393 | if (!FunctionsRequireAlignment.count(V: &BF)) { |
394 | FunctionsRequireAlignment.insert(V: &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: *BB, Inst) || FAA.doesEscapeStackAddress()) { |
411 | ArgsTouchedMap[&BF].emplace(args: std::make_pair(x: -1, y: 0)); |
412 | NoInfo = true; |
413 | break; |
414 | } |
415 | |
416 | // Check for calls -- attach stack accessing info to them regarding their |
417 | // target |
418 | if (updateArgsTouchedFor(BF, Inst, CurOffset: FAA.getSPOffset())) |
419 | UpdatedArgsTouched = true; |
420 | |
421 | // Check for stack accesses that affect callers |
422 | if (!FAA.isValidAccess()) |
423 | continue; |
424 | |
425 | const FrameIndexEntry &FIE = FAA.getFIE(); |
426 | if (FIE.StackOffset < 0) |
427 | continue; |
428 | if (ArgsTouchedMap[&BF].find(x: std::make_pair(x: FIE.StackOffset, y: FIE.Size)) != |
429 | ArgsTouchedMap[&BF].end()) |
430 | continue; |
431 | |
432 | // Record accesses to the previous stack frame |
433 | ArgsTouchedMap[&BF].emplace(args: std::make_pair(x: FIE.StackOffset, y: 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(V: &BF)) |
444 | return UpdatedArgsTouched; |
445 | |
446 | if (NoInfo) { |
447 | FunctionsRequireAlignment.insert(V: &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(V: &BF); |
455 | return true; |
456 | } |
457 | } |
458 | } |
459 | return UpdatedArgsTouched; |
460 | } |
461 | |
462 | bool 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: *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(V: &BF)) |
482 | FunctionsWithStackArithmetic.insert(V: &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 | |
501 | void 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, Name: "ArgAccessEntry"); |
509 | BC.MIB->removeAnnotation(Inst, Name: "FrameAccessEntry"); |
510 | } |
511 | } |
512 | }; |
513 | |
514 | ParallelUtilities::runOnEachFunction( |
515 | BC, SchedPolicy: ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFunction: CleanFunction, |
516 | SkipPredicate: ParallelUtilities::PredicateTy(nullptr), LogName: "cleanAnnotations"); |
517 | } |
518 | |
519 | FrameAnalysis::FrameAnalysis(BinaryContext &BC, BinaryFunctionCallGraph &CG) |
520 | : BC(BC) { |
521 | // Position 0 of the vector should be always associated with "assume access |
522 | // everything". |
523 | ArgAccessesVector.emplace_back(args: 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 finalize |
541 | if (!(I.second.isSimple() && I.second.hasCFG() && !I.second.isIgnored()) || |
542 | !opts::shouldFrameOptimize(Function: 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(BF&: I.second)) { |
551 | ++NumFunctionsFailedRestoreFI; |
552 | CountFunctionsFailedRestoreFI += I.second.getFunctionScore(); |
553 | continue; |
554 | } |
555 | } |
556 | AnalyzedFunctions.insert(V: &I.second); |
557 | } |
558 | |
559 | { |
560 | NamedRegionTimer T1("clearspt", "clear spt", "FA", "FA breakdown", |
561 | opts::TimeFA); |
562 | clearSPTMap(); |
563 | } |
564 | } |
565 | |
566 | void FrameAnalysis::printStats() { |
567 | BC.outs() << "BOLT-INFO: FRAME ANALYSIS: "<< NumFunctionsNotOptimized |
568 | << " function(s) were not optimized.\n" |
569 | << "BOLT-INFO: FRAME ANALYSIS: "<< NumFunctionsFailedRestoreFI |
570 | << " function(s) " |
571 | << format( |
572 | Fmt: "(%.1lf%% dyn cov)", |
573 | Vals: (100.0 * CountFunctionsFailedRestoreFI / CountDenominator)) |
574 | << " could not have its frame indices restored.\n"; |
575 | } |
576 | |
577 | void 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(x: &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, SchedPolicy: ParallelUtilities::SchedulingPolicy::SP_INST_LINEAR, WorkFunction: ClearFunctionSPT, |
594 | SkipPredicate: SkipFunc, LogName: "clearSPTMap"); |
595 | |
596 | SPTMap.clear(); |
597 | } |
598 | |
599 | void FrameAnalysis::preComputeSPT() { |
600 | // Make sure that the SPTMap is empty |
601 | assert(SPTMap.size() == 0); |
602 | |
603 | // Create map entries to allow lock-free parallel execution |
604 | for (auto &BFI : BC.getBinaryFunctions()) { |
605 | BinaryFunction &BF = BFI.second; |
606 | if (!BF.isSimple() || !BF.hasCFG()) |
607 | continue; |
608 | SPTMap.emplace(args: &BF, args: std::unique_ptr<StackPointerTracking>()); |
609 | } |
610 | |
611 | // Create an index for the SPT annotation to allow lock-free parallel |
612 | // execution |
613 | BC.MIB->getOrCreateAnnotationIndex(Name: "StackPointerTracking"); |
614 | |
615 | // Run SPT in parallel |
616 | ParallelUtilities::WorkFuncWithAllocTy ProcessFunction = |
617 | [&](BinaryFunction &BF, MCPlusBuilder::AllocatorIdTy AllocId) { |
618 | std::unique_ptr<StackPointerTracking> &SPTPtr = |
619 | SPTMap.find(x: &BF)->second; |
620 | SPTPtr = std::make_unique<StackPointerTracking>(args&: BF, args&: 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, SchedPolicy: ParallelUtilities::SchedulingPolicy::SP_BB_QUADRATIC, WorkFunction: ProcessFunction, |
630 | SkipPredicate, LogName: "preComputeSPT"); |
631 | } |
632 | |
633 | } // namespace bolt |
634 | } // namespace llvm |
635 |
Definitions
- FrameOptFunctionNames
- FrameOptFunctionNamesFile
- TimeFA
- ExperimentalSW
- shouldFrameOptimize
- operator<<
- FrameAccessAnalysis
- decodeFrameAccess
- FrameAccessAnalysis
- enterNewBB
- getFIE
- getSPOffset
- isValidAccess
- doesEscapeStackAddress
- doNext
- addArgAccessesFor
- addArgInStackAccessFor
- addFIEFor
- getArgAccessesFor
- getArgAccessesFor
- getFIEFor
- traverseCG
- updateArgsTouchedFor
- computeArgsAccessed
- restoreFrameIndex
- cleanAnnotations
- FrameAnalysis
- printStats
- clearSPTMap
Improve your Profiling and Debugging skills
Find out more