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