xref: /llvm-project/llvm/lib/CodeGen/StackFrameLayoutAnalysisPass.cpp (revision e09032f7a36ffb5eb8638a3933aeca7015a9579a)
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 #include <sstream>
38 
39 using namespace llvm;
40 
41 #define DEBUG_TYPE "stack-frame-layout"
42 
43 namespace {
44 
45 /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a
46 /// MachineFunction.
47 ///
48 struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {
49   using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>;
50   static char ID;
51 
52   enum SlotType {
53     Spill,          // a Spill slot
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     int Offset;
64     SlotType SlotTy;
65     bool Scalable;
66 
67     SlotData(const MachineFrameInfo &MFI, const int ValOffset, const int Idx)
68         : Slot(Idx), Size(MFI.getObjectSize(Idx)),
69           Align(MFI.getObjectAlign(Idx).value()),
70           Offset(MFI.getObjectOffset(Idx) - ValOffset), SlotTy(Invalid),
71           Scalable(false) {
72       Scalable = MFI.getStackID(Idx) == TargetStackID::ScalableVector;
73       if (MFI.isSpillSlotObjectIndex(Idx))
74         SlotTy = SlotType::Spill;
75       else if (Idx == MFI.getStackProtectorIndex())
76         SlotTy = SlotType::StackProtector;
77       else
78         SlotTy = SlotType::Variable;
79     }
80 
81     // We use this to sort in reverse order, so that the layout is displayed
82     // correctly. Scalable slots are sorted to the end of the list.
83     bool operator<(const SlotData &Rhs) const {
84       return std::make_tuple(!Scalable, Offset) >
85              std::make_tuple(!Rhs.Scalable, Rhs.Offset);
86     }
87   };
88 
89   StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {}
90 
91   StringRef getPassName() const override {
92     return "Stack Frame Layout Analysis";
93   }
94 
95   void getAnalysisUsage(AnalysisUsage &AU) const override {
96     AU.setPreservesAll();
97     MachineFunctionPass::getAnalysisUsage(AU);
98     AU.addRequired<MachineOptimizationRemarkEmitterPass>();
99   }
100 
101   bool runOnMachineFunction(MachineFunction &MF) override {
102     // TODO: We should implement a similar filter for remarks:
103     //   -Rpass-func-filter=<regex>
104     if (!isFunctionInPrintList(MF.getName()))
105       return false;
106 
107     LLVMContext &Ctx = MF.getFunction().getContext();
108     if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE))
109       return false;
110 
111     MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout",
112                                           MF.getFunction().getSubprogram(),
113                                           &MF.front());
114     Rem << ("\nFunction: " + MF.getName()).str();
115     emitStackFrameLayoutRemarks(MF, Rem);
116     getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE().emit(Rem);
117     return false;
118   }
119 
120   std::string getTypeString(SlotType Ty) {
121     switch (Ty) {
122     case SlotType::Spill:
123       return "Spill";
124     case SlotType::StackProtector:
125       return "Protector";
126     case SlotType::Variable:
127       return "Variable";
128     default:
129       llvm_unreachable("bad slot type for stack layout");
130     }
131   }
132 
133   void emitStackSlotRemark(const MachineFunction &MF, const SlotData &D,
134                            MachineOptimizationRemarkAnalysis &Rem) {
135     // To make it easy to understand the stack layout from the CLI, we want to
136     // print each slot like the following:
137     //
138     //   Offset: [SP+8], Type: Spill, Align: 8, Size: 16
139     //       foo @ /path/to/file.c:25
140     //       bar @ /path/to/file.c:35
141     //
142     // Which prints the size, alignment, and offset from the SP at function
143     // entry.
144     //
145     // But we also want the machine readable remarks data to be nicely
146     // organized. So we print some additional data as strings for the CLI
147     // output, but maintain more structured data for the YAML.
148     //
149     // For example we store the Offset in YAML as:
150     //    ...
151     //    - Offset: -8
152     //
153     // But we print it to the CLI as
154     //   Offset: [SP-8]
155 
156     // Negative offsets will print a leading `-`, so only add `+`
157     std::string Prefix =
158         formatv("\nOffset: [SP{0}", (D.Offset < 0) ? "" : "+").str();
159     Rem << Prefix << ore::NV("Offset", D.Offset)
160         << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy))
161         << ", Align: " << ore::NV("Align", D.Align)
162         << ", Size: " << ore::NV("Size", ElementCount::get(D.Size, D.Scalable));
163   }
164 
165   void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N,
166                            MachineOptimizationRemarkAnalysis &Rem) {
167     std::string Loc =
168         formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine())
169             .str();
170     Rem << "\n    " << ore::NV("DataLoc", Loc);
171   }
172 
173   void emitStackFrameLayoutRemarks(MachineFunction &MF,
174                                    MachineOptimizationRemarkAnalysis &Rem) {
175     const MachineFrameInfo &MFI = MF.getFrameInfo();
176     if (!MFI.hasStackObjects())
177       return;
178 
179     // ValOffset is the offset to the local area from the SP at function entry.
180     // To display the true offset from SP, we need to subtract ValOffset from
181     // MFI's ObjectOffset.
182     const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering();
183     const int ValOffset = (FI ? FI->getOffsetOfLocalArea() : 0);
184 
185     LLVM_DEBUG(dbgs() << "getStackProtectorIndex =="
186                       << MFI.getStackProtectorIndex() << "\n");
187 
188     std::vector<SlotData> SlotInfo;
189 
190     const unsigned int NumObj = MFI.getNumObjects();
191     SlotInfo.reserve(NumObj);
192     // initialize slot info
193     for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd();
194          Idx != EndIdx; ++Idx) {
195       if (MFI.isDeadObjectIndex(Idx))
196         continue;
197       SlotInfo.emplace_back(MFI, ValOffset, Idx);
198     }
199 
200     // sort the ordering, to match the actual layout in memory
201     llvm::sort(SlotInfo);
202 
203     SlotDbgMap SlotMap = genSlotDbgMapping(MF);
204 
205     for (const SlotData &Info : SlotInfo) {
206       emitStackSlotRemark(MF, Info, Rem);
207       for (const DILocalVariable *N : SlotMap[Info.Slot])
208         emitSourceLocRemark(MF, N, Rem);
209     }
210   }
211 
212   // We need to generate a mapping of slots to the values that are stored to
213   // them. This information is lost by the time we need to print out the frame,
214   // so we reconstruct it here by walking the CFG, and generating the mapping.
215   SlotDbgMap genSlotDbgMapping(MachineFunction &MF) {
216     SlotDbgMap SlotDebugMap;
217 
218     // add variables to the map
219     for (MachineFunction::VariableDbgInfo &DI :
220          MF.getInStackSlotVariableDbgInfo())
221       SlotDebugMap[DI.getStackSlot()].insert(DI.Var);
222 
223     // Then add all the spills that have debug data
224     for (MachineBasicBlock &MBB : MF) {
225       for (MachineInstr &MI : MBB) {
226         for (MachineMemOperand *MO : MI.memoperands()) {
227           if (!MO->isStore())
228             continue;
229           auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>(
230               MO->getPseudoValue());
231           if (!FI)
232             continue;
233           int FrameIdx = FI->getFrameIndex();
234           SmallVector<MachineInstr *> Dbg;
235           MI.collectDebugValues(Dbg);
236 
237           for (MachineInstr *MI : Dbg)
238             SlotDebugMap[FrameIdx].insert(MI->getDebugVariable());
239         }
240       }
241     }
242 
243     return SlotDebugMap;
244   }
245 };
246 
247 char StackFrameLayoutAnalysisPass::ID = 0;
248 } // namespace
249 
250 char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID;
251 INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout",
252                 "Stack Frame Layout", false, false)
253 
254 namespace llvm {
255 /// Returns a newly-created StackFrameLayout pass.
256 MachineFunctionPass *createStackFrameLayoutAnalysisPass() {
257   return new StackFrameLayoutAnalysisPass();
258 }
259 
260 } // namespace llvm
261