xref: /llvm-project/llvm/lib/CodeGen/StackFrameLayoutAnalysisPass.cpp (revision 735ab61ac828bd61398e6847d60e308fdf2b54ec)
1 //===-- StackFrameLayoutAnalysisPass.cpp
2 //------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // StackFrameLayoutAnalysisPass implementation. Outputs information about the
11 // layout of the stack frame, using the remarks interface. On the CLI it prints
12 // a textual representation of the stack frame. When possible it prints the
13 // values that occupy a stack slot using any available debug information. Since
14 // output is remarks based, it is also available in a machine readable file
15 // format, such as YAML.
16 //
17 //===----------------------------------------------------------------------===//
18 
19 #include "llvm/ADT/SetVector.h"
20 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
21 #include "llvm/CodeGen/MachineFrameInfo.h"
22 #include "llvm/CodeGen/MachineFunction.h"
23 #include "llvm/CodeGen/MachineFunctionPass.h"
24 #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
25 #include "llvm/CodeGen/Passes.h"
26 #include "llvm/CodeGen/SlotIndexes.h"
27 #include "llvm/CodeGen/StackProtector.h"
28 #include "llvm/CodeGen/TargetFrameLowering.h"
29 #include "llvm/CodeGen/TargetSubtargetInfo.h"
30 #include "llvm/IR/DebugInfoMetadata.h"
31 #include "llvm/IR/PrintPasses.h"
32 #include "llvm/InitializePasses.h"
33 #include "llvm/Support/Debug.h"
34 #include "llvm/Support/FormatVariadic.h"
35 #include "llvm/Support/raw_ostream.h"
36 
37 using namespace llvm;
38 
39 #define DEBUG_TYPE "stack-frame-layout"
40 
41 namespace {
42 
43 /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a
44 /// MachineFunction.
45 ///
46 struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {
47   using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>;
48   static char ID;
49 
50   enum SlotType {
51     Spill,          // a Spill slot
52     Fixed,          // a Fixed slot (e.g. arguments passed on the stack)
53     VariableSized,  // a variable sized object
54     StackProtector, // Stack Protector slot
55     Variable,       // a slot used to store a local data (could be a tmp)
56     Invalid         // It's an error for a slot to have this type
57   };
58 
59   struct SlotData {
60     int Slot;
61     int Size;
62     int Align;
63     StackOffset Offset;
64     SlotType SlotTy;
65     bool Scalable;
66 
67     SlotData(const MachineFrameInfo &MFI, const StackOffset Offset,
68              const int Idx)
69         : Slot(Idx), Size(MFI.getObjectSize(Idx)),
70           Align(MFI.getObjectAlign(Idx).value()), Offset(Offset),
71           SlotTy(Invalid), Scalable(false) {
72       Scalable = MFI.getStackID(Idx) == TargetStackID::ScalableVector;
73       if (MFI.isSpillSlotObjectIndex(Idx))
74         SlotTy = SlotType::Spill;
75       else if (MFI.isFixedObjectIndex(Idx))
76         SlotTy = SlotType::Fixed;
77       else if (MFI.isVariableSizedObjectIndex(Idx))
78         SlotTy = SlotType::VariableSized;
79       else if (MFI.hasStackProtectorIndex() &&
80                Idx == MFI.getStackProtectorIndex())
81         SlotTy = SlotType::StackProtector;
82       else
83         SlotTy = SlotType::Variable;
84     }
85 
86     bool isVarSize() const { return SlotTy == SlotType::VariableSized; }
87 
88     // We use this to sort in reverse order, so that the layout is displayed
89     // correctly. Variable sized slots are sorted to the end of the list, as
90     // offsets are currently incorrect for these but they reside at the end of
91     // the stack frame. The Slot index is used to ensure deterministic order
92     // when offsets are equal.
93     bool operator<(const SlotData &Rhs) const {
94       return std::make_tuple(!isVarSize(),
95                              Offset.getFixed() + Offset.getScalable(), Slot) >
96              std::make_tuple(!Rhs.isVarSize(),
97                              Rhs.Offset.getFixed() + Rhs.Offset.getScalable(),
98                              Rhs.Slot);
99     }
100   };
101 
102   StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {}
103 
104   StringRef getPassName() const override {
105     return "Stack Frame Layout Analysis";
106   }
107 
108   void getAnalysisUsage(AnalysisUsage &AU) const override {
109     AU.setPreservesAll();
110     MachineFunctionPass::getAnalysisUsage(AU);
111     AU.addRequired<MachineOptimizationRemarkEmitterPass>();
112   }
113 
114   bool runOnMachineFunction(MachineFunction &MF) override {
115     // TODO: We should implement a similar filter for remarks:
116     //   -Rpass-func-filter=<regex>
117     if (!isFunctionInPrintList(MF.getName()))
118       return false;
119 
120     LLVMContext &Ctx = MF.getFunction().getContext();
121     if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE))
122       return false;
123 
124     MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout",
125                                           MF.getFunction().getSubprogram(),
126                                           &MF.front());
127     Rem << ("\nFunction: " + MF.getName()).str();
128     emitStackFrameLayoutRemarks(MF, Rem);
129     getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE().emit(Rem);
130     return false;
131   }
132 
133   std::string getTypeString(SlotType Ty) {
134     switch (Ty) {
135     case SlotType::Spill:
136       return "Spill";
137     case SlotType::Fixed:
138       return "Fixed";
139     case SlotType::VariableSized:
140       return "VariableSized";
141     case SlotType::StackProtector:
142       return "Protector";
143     case SlotType::Variable:
144       return "Variable";
145     default:
146       llvm_unreachable("bad slot type for stack layout");
147     }
148   }
149 
150   void emitStackSlotRemark(const MachineFunction &MF, const SlotData &D,
151                            MachineOptimizationRemarkAnalysis &Rem) {
152     // To make it easy to understand the stack layout from the CLI, we want to
153     // print each slot like the following:
154     //
155     //   Offset: [SP+8], Type: Spill, Align: 8, Size: 16
156     //       foo @ /path/to/file.c:25
157     //       bar @ /path/to/file.c:35
158     //
159     // Which prints the size, alignment, and offset from the SP at function
160     // entry.
161     //
162     // But we also want the machine readable remarks data to be nicely
163     // organized. So we print some additional data as strings for the CLI
164     // output, but maintain more structured data for the YAML.
165     //
166     // For example we store the Offset in YAML as:
167     //    ...
168     //    - Offset: -8
169     //    - ScalableOffset: -16
170     // Note: the ScalableOffset entries are added only for slots with non-zero
171     // scalable offsets.
172     //
173     // But we print it to the CLI as:
174     //   Offset: [SP-8]
175     //
176     // Or with non-zero scalable offset:
177     //   Offset: [SP-8-16 x vscale]
178 
179     // Negative offsets will print a leading `-`, so only add `+`
180     std::string Prefix =
181         formatv("\nOffset: [SP{0}", (D.Offset.getFixed() < 0) ? "" : "+").str();
182     Rem << Prefix << ore::NV("Offset", D.Offset.getFixed());
183 
184     if (D.Offset.getScalable()) {
185       Rem << ((D.Offset.getScalable() < 0) ? "" : "+")
186           << ore::NV("ScalableOffset", D.Offset.getScalable()) << " x vscale";
187     }
188 
189     Rem << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy))
190         << ", Align: " << ore::NV("Align", D.Align)
191         << ", Size: " << ore::NV("Size", ElementCount::get(D.Size, D.Scalable));
192   }
193 
194   void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N,
195                            MachineOptimizationRemarkAnalysis &Rem) {
196     std::string Loc =
197         formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine())
198             .str();
199     Rem << "\n    " << ore::NV("DataLoc", Loc);
200   }
201 
202   StackOffset getStackOffset(const MachineFunction &MF,
203                              const MachineFrameInfo &MFI,
204                              const TargetFrameLowering *FI, int FrameIdx) {
205     if (!FI)
206       return StackOffset::getFixed(MFI.getObjectOffset(FrameIdx));
207 
208     return FI->getFrameIndexReferenceFromSP(MF, FrameIdx);
209   }
210 
211   void emitStackFrameLayoutRemarks(MachineFunction &MF,
212                                    MachineOptimizationRemarkAnalysis &Rem) {
213     const MachineFrameInfo &MFI = MF.getFrameInfo();
214     if (!MFI.hasStackObjects())
215       return;
216 
217     const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering();
218 
219     LLVM_DEBUG(dbgs() << "getStackProtectorIndex =="
220                       << MFI.getStackProtectorIndex() << "\n");
221 
222     std::vector<SlotData> SlotInfo;
223 
224     const unsigned int NumObj = MFI.getNumObjects();
225     SlotInfo.reserve(NumObj);
226     // initialize slot info
227     for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd();
228          Idx != EndIdx; ++Idx) {
229       if (MFI.isDeadObjectIndex(Idx))
230         continue;
231       SlotInfo.emplace_back(MFI, getStackOffset(MF, MFI, FI, Idx), Idx);
232     }
233 
234     // sort the ordering, to match the actual layout in memory
235     llvm::sort(SlotInfo);
236 
237     SlotDbgMap SlotMap = genSlotDbgMapping(MF);
238 
239     for (const SlotData &Info : SlotInfo) {
240       emitStackSlotRemark(MF, Info, Rem);
241       for (const DILocalVariable *N : SlotMap[Info.Slot])
242         emitSourceLocRemark(MF, N, Rem);
243     }
244   }
245 
246   // We need to generate a mapping of slots to the values that are stored to
247   // them. This information is lost by the time we need to print out the frame,
248   // so we reconstruct it here by walking the CFG, and generating the mapping.
249   SlotDbgMap genSlotDbgMapping(MachineFunction &MF) {
250     SlotDbgMap SlotDebugMap;
251 
252     // add variables to the map
253     for (MachineFunction::VariableDbgInfo &DI :
254          MF.getInStackSlotVariableDbgInfo())
255       SlotDebugMap[DI.getStackSlot()].insert(DI.Var);
256 
257     // Then add all the spills that have debug data
258     for (MachineBasicBlock &MBB : MF) {
259       for (MachineInstr &MI : MBB) {
260         for (MachineMemOperand *MO : MI.memoperands()) {
261           if (!MO->isStore())
262             continue;
263           auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>(
264               MO->getPseudoValue());
265           if (!FI)
266             continue;
267           int FrameIdx = FI->getFrameIndex();
268           SmallVector<MachineInstr *> Dbg;
269           MI.collectDebugValues(Dbg);
270 
271           for (MachineInstr *MI : Dbg)
272             SlotDebugMap[FrameIdx].insert(MI->getDebugVariable());
273         }
274       }
275     }
276 
277     return SlotDebugMap;
278   }
279 };
280 
281 char StackFrameLayoutAnalysisPass::ID = 0;
282 } // namespace
283 
284 char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID;
285 INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout",
286                 "Stack Frame Layout", false, false)
287 
288 namespace llvm {
289 /// Returns a newly-created StackFrameLayout pass.
290 MachineFunctionPass *createStackFrameLayoutAnalysisPass() {
291   return new StackFrameLayoutAnalysisPass();
292 }
293 
294 } // namespace llvm
295