1 //===- bolt/Passes/FrameAnalysis.h ------------------------------*- C++ -*-===//
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 #ifndef BOLT_PASSES_FRAMEANALYSIS_H
10 #define BOLT_PASSES_FRAMEANALYSIS_H
11 
12 #include "bolt/Passes/StackPointerTracking.h"
13 
14 namespace llvm {
15 namespace bolt {
16 class BinaryFunctionCallGraph;
17 
18 /// Alias analysis information attached to each instruction that accesses a
19 /// frame position. This is called a "frame index" by LLVM Target libs when
20 /// it is building a MachineFunction frame, and we use the same name here
21 /// because we are essentially doing the job of frame reconstruction.
22 struct FrameIndexEntry {
23   /// If both IsLoad and IsStore are set, it means this is an instruction that
24   /// reads and updates this frame location.
25   bool IsLoad;
26   bool IsStore;
27   /// If a store, this controls whether the store uses a register os an imm
28   /// as the source value.
29   bool IsStoreFromReg;
30   /// If load, this holds the destination register. If store, this holds
31   /// either the source register or source immediate.
32   int32_t RegOrImm;
33 
34   /// StackOffset and Size are the two aspects that identify this frame access
35   /// for the purposes of alias analysis.
36   int64_t StackOffset;
37   uint8_t Size;
38 
39   /// If this is false, we will never atempt to remove or optimize this
40   /// instruction. We just use it to keep track of stores we don't fully
41   /// understand but we know it may write to a frame position.
42   bool IsSimple;
43 
44   uint16_t StackPtrReg;
45 };
46 
47 /// Record an access to an argument in stack. This should be attached to
48 /// call instructions, so StackOffset and Size are determined in the context
49 /// of the caller. This information helps the caller understand how the callee
50 /// may access its private stack.
51 struct ArgInStackAccess {
52   int64_t StackOffset;
53   uint8_t Size;
54 
55   bool operator<(const ArgInStackAccess &RHS) const {
56     if (StackOffset != RHS.StackOffset)
57       return StackOffset < RHS.StackOffset;
58     return Size < RHS.Size;
59   }
60 };
61 
62 /// The set of all args-in-stack accesses for a given instruction. If
63 /// AssumeEverything is true, then the set should be ignored and the
64 /// corresponding instruction should be treated as accessing the entire
65 /// stack for the purposes of analysis and optimization.
66 struct ArgAccesses {
67   bool AssumeEverything;
68   std::set<ArgInStackAccess> Set;
69 
ArgAccessesArgAccesses70   explicit ArgAccesses(bool AssumeEverything)
71       : AssumeEverything(AssumeEverything) {}
72 };
73 
74 raw_ostream &operator<<(raw_ostream &OS, const FrameIndexEntry &FIE);
75 
76 /// This pass attaches stack access information to instructions. If a load/store
77 /// instruction accesses a stack position, it will identify the CFA offset and
78 /// size information of this access, where CFA is the Canonical Frame Address
79 /// (using DWARF terminology).
80 ///
81 /// This pass also computes frame usage information obtained by a bottom-up call
82 /// graph traversal: which registers are clobbered by functions (including their
83 /// callees as determined by the call graph), whether a function accesses its
84 /// caller's stack frame and whether a function demands its stack to be aligned
85 /// due to the use of SSE aligned load/store operations present in itself or any
86 /// of its direct or indirect callees.
87 ///
88 /// Initialization:
89 ///
90 ///   FrameAnalysis FA(PrintPass);
91 ///   FA.runOnFunctions(BC);
92 ///
93 /// Usage (fetching frame access information about a given instruction):
94 ///
95 ///   auto FIE = FA.getFIEFor(BC, Instruction);
96 ///   if (FIE && FIE->IsSimple) {
97 ///     ... = FIE->StackOffset
98 ///     ... = FIE->Size
99 ///   }
100 ///
101 /// Usage (determining the set of stack positions accessed by the target of a
102 /// call:
103 ///
104 ///    auto Args = FA.getArgAccessesFor(BC, CallInst);
105 ///    if (Args && Args->AssumeEverything) {
106 ///      ... callee may access any position of our current stack frame
107 ///    }
108 ///
109 class FrameAnalysis {
110   BinaryContext &BC;
111 
112   /// Map functions to the set of <stack offsets, size> tuples representing
113   /// accesses to stack positions that belongs to caller
114   std::map<const BinaryFunction *, std::set<std::pair<int64_t, uint8_t>>>
115       ArgsTouchedMap;
116 
117   /// The set of functions we were able to perform the full analysis up to
118   /// restoring frame indexes for all load/store instructions.
119   DenseSet<const BinaryFunction *> AnalyzedFunctions;
120 
121   /// Set of functions that require the stack to be 16B aligned
122   DenseSet<const BinaryFunction *> FunctionsRequireAlignment;
123 
124   /// Set of functions that performs computations with stack addresses and
125   /// complicates our understanding of aliasing of stack spaces.
126   DenseSet<const BinaryFunction *> FunctionsWithStackArithmetic;
127 
128   /// Owns ArgAccesses for all instructions. References to elements are
129   /// attached to instructions as indexes to this vector, in MCAnnotations.
130   std::vector<ArgAccesses> ArgAccessesVector;
131   /// Same for FrameIndexEntries.
132   std::vector<FrameIndexEntry> FIEVector;
133 
134   /// Analysis stats counters
135   uint64_t NumFunctionsNotOptimized{0};
136   uint64_t NumFunctionsFailedRestoreFI{0};
137   uint64_t CountFunctionsFailedRestoreFI{0};
138   uint64_t CountDenominator{0};
139 
140   /// Convenience functions for appending MCAnnotations to instructions with
141   /// our specific data
142   void addArgAccessesFor(MCInst &Inst, ArgAccesses &&AA);
143   void addArgInStackAccessFor(MCInst &Inst, const ArgInStackAccess &Arg);
144   void addFIEFor(MCInst &Inst, const FrameIndexEntry &FIE);
145 
146   /// Perform the step of building the set of registers clobbered by each
147   /// function execution, populating RegsKilledMap and RegsGenMap.
148   void traverseCG(BinaryFunctionCallGraph &CG);
149 
150   /// Analyzes an instruction and if it is a call, checks the called function
151   /// to record which args in stack are accessed, if any. Returns true if
152   /// the args data associated with this instruction were updated.
153   bool updateArgsTouchedFor(const BinaryFunction &BF, MCInst &Inst,
154                             int CurOffset);
155 
156   /// Performs a pass over \p BF to check for accesses to arguments in stack,
157   /// flagging those as accessing the caller stack frame. All functions called
158   /// by \p BF must have been previously analyzed. Returns true if updated
159   /// args data about this function.
160   bool computeArgsAccessed(BinaryFunction &BF);
161 
162   /// Alias analysis to disambiguate which frame position is accessed by each
163   /// instruction in function \p BF. Add MCAnnotation<FrameIndexEntry> to
164   /// instructions that access a frame position. Return false if it failed
165   /// to analyze and this information can't be safely determined for \p BF.
166   bool restoreFrameIndex(BinaryFunction &BF);
167 
168   /// A store for SPT info per function
169   std::unordered_map<const BinaryFunction *,
170                      std::unique_ptr<StackPointerTracking>>
171       SPTMap;
172 
173   /// A vector that stores ids of the allocators that are used in SPT
174   /// computation
175   std::vector<MCPlusBuilder::AllocatorIdTy> SPTAllocatorsId;
176 
177 public:
178   explicit FrameAnalysis(BinaryContext &BC, BinaryFunctionCallGraph &CG);
179 
180   /// Return true if we could fully analyze \p Func
hasFrameInfo(const BinaryFunction & Func)181   bool hasFrameInfo(const BinaryFunction &Func) const {
182     return AnalyzedFunctions.count(&Func);
183   }
184 
185   /// Return true if \p Func cannot operate with a misaligned CFA
requiresAlignment(const BinaryFunction & Func)186   bool requiresAlignment(const BinaryFunction &Func) const {
187     return FunctionsRequireAlignment.count(&Func);
188   }
189 
190   /// Return true if \p Func does computation with the address of any stack
191   /// position, meaning we have limited alias analysis on this function.
hasStackArithmetic(const BinaryFunction & Func)192   bool hasStackArithmetic(const BinaryFunction &Func) const {
193     return FunctionsWithStackArithmetic.count(&Func);
194   }
195 
196   /// Functions for retrieving our specific MCAnnotation data from instructions
197   ErrorOr<ArgAccesses &> getArgAccessesFor(const MCInst &Inst);
198 
199   ErrorOr<const ArgAccesses &> getArgAccessesFor(const MCInst &Inst) const;
200 
201   ErrorOr<const FrameIndexEntry &> getFIEFor(const MCInst &Inst) const;
202 
203   /// Remove all MCAnnotations attached by this pass
204   void cleanAnnotations();
205 
~FrameAnalysis()206   ~FrameAnalysis() { cleanAnnotations(); }
207 
208   /// Print to standard output statistics about the analysis performed by this
209   /// pass
210   void printStats();
211 
212   /// Get or create an SPT object and run the analysis
getSPT(BinaryFunction & BF)213   StackPointerTracking &getSPT(BinaryFunction &BF) {
214     if (!SPTMap.count(&BF)) {
215       SPTMap.emplace(&BF, std::make_unique<StackPointerTracking>(BF));
216       auto Iter = SPTMap.find(&BF);
217       assert(Iter != SPTMap.end() && "item should exist");
218       Iter->second->run();
219       return *Iter->second;
220     }
221 
222     auto Iter = SPTMap.find(&BF);
223     assert(Iter != SPTMap.end() && "item should exist");
224     return *Iter->second;
225   }
226 
227   /// Clean and de-allocate all SPT objects
228   void clearSPTMap();
229 
230   /// Perform SPT analysis for all functions in parallel
231   void preComputeSPT();
232 };
233 
234 } // namespace bolt
235 } // namespace llvm
236 
237 #endif
238