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