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