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