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