xref: /llvm-project/llvm/lib/CodeGen/StackFrameLayoutAnalysisPass.cpp (revision 735ab61ac828bd61398e6847d60e308fdf2b54ec)
1557a5bc3SPaul Kirth //===-- StackFrameLayoutAnalysisPass.cpp
2557a5bc3SPaul Kirth //------------------------------------===//
3557a5bc3SPaul Kirth //
4557a5bc3SPaul Kirth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5557a5bc3SPaul Kirth // See https://llvm.org/LICENSE.txt for license information.
6557a5bc3SPaul Kirth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7557a5bc3SPaul Kirth //
8557a5bc3SPaul Kirth //===----------------------------------------------------------------------===//
9557a5bc3SPaul Kirth //
10557a5bc3SPaul Kirth // StackFrameLayoutAnalysisPass implementation. Outputs information about the
11557a5bc3SPaul Kirth // layout of the stack frame, using the remarks interface. On the CLI it prints
12557a5bc3SPaul Kirth // a textual representation of the stack frame. When possible it prints the
13557a5bc3SPaul Kirth // values that occupy a stack slot using any available debug information. Since
14557a5bc3SPaul Kirth // output is remarks based, it is also available in a machine readable file
15557a5bc3SPaul Kirth // format, such as YAML.
16557a5bc3SPaul Kirth //
17557a5bc3SPaul Kirth //===----------------------------------------------------------------------===//
18557a5bc3SPaul Kirth 
19af9a452eSPaul Kirth #include "llvm/ADT/SetVector.h"
20557a5bc3SPaul Kirth #include "llvm/Analysis/OptimizationRemarkEmitter.h"
21557a5bc3SPaul Kirth #include "llvm/CodeGen/MachineFrameInfo.h"
22557a5bc3SPaul Kirth #include "llvm/CodeGen/MachineFunction.h"
23557a5bc3SPaul Kirth #include "llvm/CodeGen/MachineFunctionPass.h"
24557a5bc3SPaul Kirth #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
25557a5bc3SPaul Kirth #include "llvm/CodeGen/Passes.h"
26557a5bc3SPaul Kirth #include "llvm/CodeGen/SlotIndexes.h"
27557a5bc3SPaul Kirth #include "llvm/CodeGen/StackProtector.h"
28557a5bc3SPaul Kirth #include "llvm/CodeGen/TargetFrameLowering.h"
29557a5bc3SPaul Kirth #include "llvm/CodeGen/TargetSubtargetInfo.h"
30557a5bc3SPaul Kirth #include "llvm/IR/DebugInfoMetadata.h"
31557a5bc3SPaul Kirth #include "llvm/IR/PrintPasses.h"
32557a5bc3SPaul Kirth #include "llvm/InitializePasses.h"
33557a5bc3SPaul Kirth #include "llvm/Support/Debug.h"
34557a5bc3SPaul Kirth #include "llvm/Support/FormatVariadic.h"
35557a5bc3SPaul Kirth #include "llvm/Support/raw_ostream.h"
36557a5bc3SPaul Kirth 
37557a5bc3SPaul Kirth using namespace llvm;
38557a5bc3SPaul Kirth 
39557a5bc3SPaul Kirth #define DEBUG_TYPE "stack-frame-layout"
40557a5bc3SPaul Kirth 
41557a5bc3SPaul Kirth namespace {
42557a5bc3SPaul Kirth 
43557a5bc3SPaul Kirth /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a
44557a5bc3SPaul Kirth /// MachineFunction.
45557a5bc3SPaul Kirth ///
46557a5bc3SPaul Kirth struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {
47af9a452eSPaul Kirth   using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>;
48557a5bc3SPaul Kirth   static char ID;
49557a5bc3SPaul Kirth 
50557a5bc3SPaul Kirth   enum SlotType {
51557a5bc3SPaul Kirth     Spill,          // a Spill slot
52*e31794f9SHari Limaye     Fixed,          // a Fixed slot (e.g. arguments passed on the stack)
53*e31794f9SHari Limaye     VariableSized,  // a variable sized object
54557a5bc3SPaul Kirth     StackProtector, // Stack Protector slot
55557a5bc3SPaul Kirth     Variable,       // a slot used to store a local data (could be a tmp)
56557a5bc3SPaul Kirth     Invalid         // It's an error for a slot to have this type
57557a5bc3SPaul Kirth   };
58557a5bc3SPaul Kirth 
59557a5bc3SPaul Kirth   struct SlotData {
60557a5bc3SPaul Kirth     int Slot;
61557a5bc3SPaul Kirth     int Size;
62557a5bc3SPaul Kirth     int Align;
63dc1c00f6SHari Limaye     StackOffset Offset;
64557a5bc3SPaul Kirth     SlotType SlotTy;
65e09032f7SDavid Green     bool Scalable;
66557a5bc3SPaul Kirth 
67dc1c00f6SHari Limaye     SlotData(const MachineFrameInfo &MFI, const StackOffset Offset,
68dc1c00f6SHari Limaye              const int Idx)
69557a5bc3SPaul Kirth         : Slot(Idx), Size(MFI.getObjectSize(Idx)),
70dc1c00f6SHari Limaye           Align(MFI.getObjectAlign(Idx).value()), Offset(Offset),
71dc1c00f6SHari Limaye           SlotTy(Invalid), Scalable(false) {
72e09032f7SDavid Green       Scalable = MFI.getStackID(Idx) == TargetStackID::ScalableVector;
73557a5bc3SPaul Kirth       if (MFI.isSpillSlotObjectIndex(Idx))
74557a5bc3SPaul Kirth         SlotTy = SlotType::Spill;
75*e31794f9SHari Limaye       else if (MFI.isFixedObjectIndex(Idx))
76*e31794f9SHari Limaye         SlotTy = SlotType::Fixed;
77*e31794f9SHari Limaye       else if (MFI.isVariableSizedObjectIndex(Idx))
78*e31794f9SHari Limaye         SlotTy = SlotType::VariableSized;
79*e31794f9SHari Limaye       else if (MFI.hasStackProtectorIndex() &&
80*e31794f9SHari Limaye                Idx == MFI.getStackProtectorIndex())
81557a5bc3SPaul Kirth         SlotTy = SlotType::StackProtector;
82557a5bc3SPaul Kirth       else
83557a5bc3SPaul Kirth         SlotTy = SlotType::Variable;
84557a5bc3SPaul Kirth     }
85557a5bc3SPaul Kirth 
86*e31794f9SHari Limaye     bool isVarSize() const { return SlotTy == SlotType::VariableSized; }
87*e31794f9SHari Limaye 
88e09032f7SDavid Green     // We use this to sort in reverse order, so that the layout is displayed
89*e31794f9SHari Limaye     // correctly. Variable sized slots are sorted to the end of the list, as
90*e31794f9SHari Limaye     // offsets are currently incorrect for these but they reside at the end of
91*e31794f9SHari Limaye     // the stack frame. The Slot index is used to ensure deterministic order
92*e31794f9SHari Limaye     // when offsets are equal.
93e09032f7SDavid Green     bool operator<(const SlotData &Rhs) const {
94*e31794f9SHari Limaye       return std::make_tuple(!isVarSize(),
95*e31794f9SHari Limaye                              Offset.getFixed() + Offset.getScalable(), Slot) >
96*e31794f9SHari Limaye              std::make_tuple(!Rhs.isVarSize(),
97*e31794f9SHari Limaye                              Rhs.Offset.getFixed() + Rhs.Offset.getScalable(),
98*e31794f9SHari Limaye                              Rhs.Slot);
99e09032f7SDavid Green     }
100557a5bc3SPaul Kirth   };
101557a5bc3SPaul Kirth 
102557a5bc3SPaul Kirth   StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {}
103557a5bc3SPaul Kirth 
104557a5bc3SPaul Kirth   StringRef getPassName() const override {
105557a5bc3SPaul Kirth     return "Stack Frame Layout Analysis";
106557a5bc3SPaul Kirth   }
107557a5bc3SPaul Kirth 
108557a5bc3SPaul Kirth   void getAnalysisUsage(AnalysisUsage &AU) const override {
109557a5bc3SPaul Kirth     AU.setPreservesAll();
110557a5bc3SPaul Kirth     MachineFunctionPass::getAnalysisUsage(AU);
111557a5bc3SPaul Kirth     AU.addRequired<MachineOptimizationRemarkEmitterPass>();
112557a5bc3SPaul Kirth   }
113557a5bc3SPaul Kirth 
114557a5bc3SPaul Kirth   bool runOnMachineFunction(MachineFunction &MF) override {
115557a5bc3SPaul Kirth     // TODO: We should implement a similar filter for remarks:
116557a5bc3SPaul Kirth     //   -Rpass-func-filter=<regex>
117557a5bc3SPaul Kirth     if (!isFunctionInPrintList(MF.getName()))
118557a5bc3SPaul Kirth       return false;
119557a5bc3SPaul Kirth 
120557a5bc3SPaul Kirth     LLVMContext &Ctx = MF.getFunction().getContext();
121557a5bc3SPaul Kirth     if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE))
122557a5bc3SPaul Kirth       return false;
123557a5bc3SPaul Kirth 
124557a5bc3SPaul Kirth     MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout",
125557a5bc3SPaul Kirth                                           MF.getFunction().getSubprogram(),
126557a5bc3SPaul Kirth                                           &MF.front());
127557a5bc3SPaul Kirth     Rem << ("\nFunction: " + MF.getName()).str();
128557a5bc3SPaul Kirth     emitStackFrameLayoutRemarks(MF, Rem);
129557a5bc3SPaul Kirth     getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE().emit(Rem);
130557a5bc3SPaul Kirth     return false;
131557a5bc3SPaul Kirth   }
132557a5bc3SPaul Kirth 
133557a5bc3SPaul Kirth   std::string getTypeString(SlotType Ty) {
134557a5bc3SPaul Kirth     switch (Ty) {
135557a5bc3SPaul Kirth     case SlotType::Spill:
136557a5bc3SPaul Kirth       return "Spill";
137*e31794f9SHari Limaye     case SlotType::Fixed:
138*e31794f9SHari Limaye       return "Fixed";
139*e31794f9SHari Limaye     case SlotType::VariableSized:
140*e31794f9SHari Limaye       return "VariableSized";
141557a5bc3SPaul Kirth     case SlotType::StackProtector:
142557a5bc3SPaul Kirth       return "Protector";
143557a5bc3SPaul Kirth     case SlotType::Variable:
144557a5bc3SPaul Kirth       return "Variable";
145557a5bc3SPaul Kirth     default:
146557a5bc3SPaul Kirth       llvm_unreachable("bad slot type for stack layout");
147557a5bc3SPaul Kirth     }
148557a5bc3SPaul Kirth   }
149557a5bc3SPaul Kirth 
150557a5bc3SPaul Kirth   void emitStackSlotRemark(const MachineFunction &MF, const SlotData &D,
151557a5bc3SPaul Kirth                            MachineOptimizationRemarkAnalysis &Rem) {
152557a5bc3SPaul Kirth     // To make it easy to understand the stack layout from the CLI, we want to
153557a5bc3SPaul Kirth     // print each slot like the following:
154557a5bc3SPaul Kirth     //
155557a5bc3SPaul Kirth     //   Offset: [SP+8], Type: Spill, Align: 8, Size: 16
156557a5bc3SPaul Kirth     //       foo @ /path/to/file.c:25
157557a5bc3SPaul Kirth     //       bar @ /path/to/file.c:35
158557a5bc3SPaul Kirth     //
159557a5bc3SPaul Kirth     // Which prints the size, alignment, and offset from the SP at function
160557a5bc3SPaul Kirth     // entry.
161557a5bc3SPaul Kirth     //
162557a5bc3SPaul Kirth     // But we also want the machine readable remarks data to be nicely
163557a5bc3SPaul Kirth     // organized. So we print some additional data as strings for the CLI
164557a5bc3SPaul Kirth     // output, but maintain more structured data for the YAML.
165557a5bc3SPaul Kirth     //
166557a5bc3SPaul Kirth     // For example we store the Offset in YAML as:
167557a5bc3SPaul Kirth     //    ...
168557a5bc3SPaul Kirth     //    - Offset: -8
169dc1c00f6SHari Limaye     //    - ScalableOffset: -16
170dc1c00f6SHari Limaye     // Note: the ScalableOffset entries are added only for slots with non-zero
171dc1c00f6SHari Limaye     // scalable offsets.
172557a5bc3SPaul Kirth     //
173dc1c00f6SHari Limaye     // But we print it to the CLI as:
174557a5bc3SPaul Kirth     //   Offset: [SP-8]
175dc1c00f6SHari Limaye     //
176dc1c00f6SHari Limaye     // Or with non-zero scalable offset:
177dc1c00f6SHari Limaye     //   Offset: [SP-8-16 x vscale]
178557a5bc3SPaul Kirth 
179557a5bc3SPaul Kirth     // Negative offsets will print a leading `-`, so only add `+`
180557a5bc3SPaul Kirth     std::string Prefix =
181dc1c00f6SHari Limaye         formatv("\nOffset: [SP{0}", (D.Offset.getFixed() < 0) ? "" : "+").str();
182dc1c00f6SHari Limaye     Rem << Prefix << ore::NV("Offset", D.Offset.getFixed());
183dc1c00f6SHari Limaye 
184dc1c00f6SHari Limaye     if (D.Offset.getScalable()) {
185dc1c00f6SHari Limaye       Rem << ((D.Offset.getScalable() < 0) ? "" : "+")
186dc1c00f6SHari Limaye           << ore::NV("ScalableOffset", D.Offset.getScalable()) << " x vscale";
187dc1c00f6SHari Limaye     }
188dc1c00f6SHari Limaye 
189dc1c00f6SHari Limaye     Rem << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy))
190557a5bc3SPaul Kirth         << ", Align: " << ore::NV("Align", D.Align)
191e09032f7SDavid Green         << ", Size: " << ore::NV("Size", ElementCount::get(D.Size, D.Scalable));
192557a5bc3SPaul Kirth   }
193557a5bc3SPaul Kirth 
194557a5bc3SPaul Kirth   void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N,
195557a5bc3SPaul Kirth                            MachineOptimizationRemarkAnalysis &Rem) {
196557a5bc3SPaul Kirth     std::string Loc =
197557a5bc3SPaul Kirth         formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine())
198557a5bc3SPaul Kirth             .str();
199557a5bc3SPaul Kirth     Rem << "\n    " << ore::NV("DataLoc", Loc);
200557a5bc3SPaul Kirth   }
201557a5bc3SPaul Kirth 
202dc1c00f6SHari Limaye   StackOffset getStackOffset(const MachineFunction &MF,
203dc1c00f6SHari Limaye                              const MachineFrameInfo &MFI,
204dc1c00f6SHari Limaye                              const TargetFrameLowering *FI, int FrameIdx) {
205dc1c00f6SHari Limaye     if (!FI)
206dc1c00f6SHari Limaye       return StackOffset::getFixed(MFI.getObjectOffset(FrameIdx));
207dc1c00f6SHari Limaye 
208dc1c00f6SHari Limaye     return FI->getFrameIndexReferenceFromSP(MF, FrameIdx);
209dc1c00f6SHari Limaye   }
210dc1c00f6SHari Limaye 
211557a5bc3SPaul Kirth   void emitStackFrameLayoutRemarks(MachineFunction &MF,
212557a5bc3SPaul Kirth                                    MachineOptimizationRemarkAnalysis &Rem) {
213557a5bc3SPaul Kirth     const MachineFrameInfo &MFI = MF.getFrameInfo();
214557a5bc3SPaul Kirth     if (!MFI.hasStackObjects())
215557a5bc3SPaul Kirth       return;
216557a5bc3SPaul Kirth 
217557a5bc3SPaul Kirth     const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering();
218557a5bc3SPaul Kirth 
219557a5bc3SPaul Kirth     LLVM_DEBUG(dbgs() << "getStackProtectorIndex =="
220557a5bc3SPaul Kirth                       << MFI.getStackProtectorIndex() << "\n");
221557a5bc3SPaul Kirth 
222557a5bc3SPaul Kirth     std::vector<SlotData> SlotInfo;
223557a5bc3SPaul Kirth 
224557a5bc3SPaul Kirth     const unsigned int NumObj = MFI.getNumObjects();
225557a5bc3SPaul Kirth     SlotInfo.reserve(NumObj);
226557a5bc3SPaul Kirth     // initialize slot info
227557a5bc3SPaul Kirth     for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd();
228557a5bc3SPaul Kirth          Idx != EndIdx; ++Idx) {
229557a5bc3SPaul Kirth       if (MFI.isDeadObjectIndex(Idx))
230557a5bc3SPaul Kirth         continue;
231dc1c00f6SHari Limaye       SlotInfo.emplace_back(MFI, getStackOffset(MF, MFI, FI, Idx), Idx);
232557a5bc3SPaul Kirth     }
233557a5bc3SPaul Kirth 
234557a5bc3SPaul Kirth     // sort the ordering, to match the actual layout in memory
235557a5bc3SPaul Kirth     llvm::sort(SlotInfo);
236557a5bc3SPaul Kirth 
237557a5bc3SPaul Kirth     SlotDbgMap SlotMap = genSlotDbgMapping(MF);
238557a5bc3SPaul Kirth 
239557a5bc3SPaul Kirth     for (const SlotData &Info : SlotInfo) {
240557a5bc3SPaul Kirth       emitStackSlotRemark(MF, Info, Rem);
241557a5bc3SPaul Kirth       for (const DILocalVariable *N : SlotMap[Info.Slot])
242557a5bc3SPaul Kirth         emitSourceLocRemark(MF, N, Rem);
243557a5bc3SPaul Kirth     }
244557a5bc3SPaul Kirth   }
245557a5bc3SPaul Kirth 
246557a5bc3SPaul Kirth   // We need to generate a mapping of slots to the values that are stored to
247557a5bc3SPaul Kirth   // them. This information is lost by the time we need to print out the frame,
248557a5bc3SPaul Kirth   // so we reconstruct it here by walking the CFG, and generating the mapping.
249557a5bc3SPaul Kirth   SlotDbgMap genSlotDbgMapping(MachineFunction &MF) {
250557a5bc3SPaul Kirth     SlotDbgMap SlotDebugMap;
251557a5bc3SPaul Kirth 
252557a5bc3SPaul Kirth     // add variables to the map
2533db7d0dfSFelipe de Azevedo Piovezan     for (MachineFunction::VariableDbgInfo &DI :
2543db7d0dfSFelipe de Azevedo Piovezan          MF.getInStackSlotVariableDbgInfo())
2553db7d0dfSFelipe de Azevedo Piovezan       SlotDebugMap[DI.getStackSlot()].insert(DI.Var);
256557a5bc3SPaul Kirth 
257557a5bc3SPaul Kirth     // Then add all the spills that have debug data
258557a5bc3SPaul Kirth     for (MachineBasicBlock &MBB : MF) {
259557a5bc3SPaul Kirth       for (MachineInstr &MI : MBB) {
260557a5bc3SPaul Kirth         for (MachineMemOperand *MO : MI.memoperands()) {
261557a5bc3SPaul Kirth           if (!MO->isStore())
262557a5bc3SPaul Kirth             continue;
263557a5bc3SPaul Kirth           auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>(
264557a5bc3SPaul Kirth               MO->getPseudoValue());
265557a5bc3SPaul Kirth           if (!FI)
266557a5bc3SPaul Kirth             continue;
267557a5bc3SPaul Kirth           int FrameIdx = FI->getFrameIndex();
268557a5bc3SPaul Kirth           SmallVector<MachineInstr *> Dbg;
269557a5bc3SPaul Kirth           MI.collectDebugValues(Dbg);
270557a5bc3SPaul Kirth 
271557a5bc3SPaul Kirth           for (MachineInstr *MI : Dbg)
272557a5bc3SPaul Kirth             SlotDebugMap[FrameIdx].insert(MI->getDebugVariable());
273557a5bc3SPaul Kirth         }
274557a5bc3SPaul Kirth       }
275557a5bc3SPaul Kirth     }
276557a5bc3SPaul Kirth 
277557a5bc3SPaul Kirth     return SlotDebugMap;
278557a5bc3SPaul Kirth   }
279557a5bc3SPaul Kirth };
280557a5bc3SPaul Kirth 
281557a5bc3SPaul Kirth char StackFrameLayoutAnalysisPass::ID = 0;
282557a5bc3SPaul Kirth } // namespace
283557a5bc3SPaul Kirth 
284557a5bc3SPaul Kirth char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID;
285557a5bc3SPaul Kirth INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout",
286557a5bc3SPaul Kirth                 "Stack Frame Layout", false, false)
287557a5bc3SPaul Kirth 
288557a5bc3SPaul Kirth namespace llvm {
289557a5bc3SPaul Kirth /// Returns a newly-created StackFrameLayout pass.
290557a5bc3SPaul Kirth MachineFunctionPass *createStackFrameLayoutAnalysisPass() {
291557a5bc3SPaul Kirth   return new StackFrameLayoutAnalysisPass();
292557a5bc3SPaul Kirth }
293557a5bc3SPaul Kirth 
294557a5bc3SPaul Kirth } // namespace llvm
295