xref: /src/contrib/llvm-project/llvm/lib/CodeGen/StackFrameLayoutAnalysisPass.cpp (revision 52418fc2be8efa5172b90a3a9e617017173612c4)
1e3b55780SDimitry Andric //===-- StackFrameLayoutAnalysisPass.cpp
2e3b55780SDimitry Andric //------------------------------------===//
3e3b55780SDimitry Andric //
4e3b55780SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5e3b55780SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
6e3b55780SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7e3b55780SDimitry Andric //
8e3b55780SDimitry Andric //===----------------------------------------------------------------------===//
9e3b55780SDimitry Andric //
10e3b55780SDimitry Andric // StackFrameLayoutAnalysisPass implementation. Outputs information about the
11e3b55780SDimitry Andric // layout of the stack frame, using the remarks interface. On the CLI it prints
12e3b55780SDimitry Andric // a textual representation of the stack frame. When possible it prints the
13e3b55780SDimitry Andric // values that occupy a stack slot using any available debug information. Since
14e3b55780SDimitry Andric // output is remarks based, it is also available in a machine readable file
15e3b55780SDimitry Andric // format, such as YAML.
16e3b55780SDimitry Andric //
17e3b55780SDimitry Andric //===----------------------------------------------------------------------===//
18e3b55780SDimitry Andric 
19e3b55780SDimitry Andric #include "llvm/ADT/SetVector.h"
20e3b55780SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h"
21e3b55780SDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h"
22e3b55780SDimitry Andric #include "llvm/CodeGen/MachineFunction.h"
23e3b55780SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
24e3b55780SDimitry Andric #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
25e3b55780SDimitry Andric #include "llvm/CodeGen/Passes.h"
26e3b55780SDimitry Andric #include "llvm/CodeGen/SlotIndexes.h"
27e3b55780SDimitry Andric #include "llvm/CodeGen/StackProtector.h"
28e3b55780SDimitry Andric #include "llvm/CodeGen/TargetFrameLowering.h"
29e3b55780SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
30e3b55780SDimitry Andric #include "llvm/IR/DebugInfoMetadata.h"
31e3b55780SDimitry Andric #include "llvm/IR/PrintPasses.h"
32e3b55780SDimitry Andric #include "llvm/InitializePasses.h"
33e3b55780SDimitry Andric #include "llvm/Support/Debug.h"
34e3b55780SDimitry Andric #include "llvm/Support/FormatVariadic.h"
35e3b55780SDimitry Andric #include "llvm/Support/raw_ostream.h"
36e3b55780SDimitry Andric 
37e3b55780SDimitry Andric #include <sstream>
38e3b55780SDimitry Andric 
39e3b55780SDimitry Andric using namespace llvm;
40e3b55780SDimitry Andric 
41e3b55780SDimitry Andric #define DEBUG_TYPE "stack-frame-layout"
42e3b55780SDimitry Andric 
43e3b55780SDimitry Andric namespace {
44e3b55780SDimitry Andric 
45e3b55780SDimitry Andric /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a
46e3b55780SDimitry Andric /// MachineFunction.
47e3b55780SDimitry Andric ///
48e3b55780SDimitry Andric struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {
49e3b55780SDimitry Andric   using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>;
50e3b55780SDimitry Andric   static char ID;
51e3b55780SDimitry Andric 
52e3b55780SDimitry Andric   enum SlotType {
53e3b55780SDimitry Andric     Spill,          // a Spill slot
549b950333SDimitry Andric     Fixed,          // a Fixed slot (e.g. arguments passed on the stack)
559b950333SDimitry Andric     VariableSized,  // a variable sized object
56e3b55780SDimitry Andric     StackProtector, // Stack Protector slot
57e3b55780SDimitry Andric     Variable,       // a slot used to store a local data (could be a tmp)
58e3b55780SDimitry Andric     Invalid         // It's an error for a slot to have this type
59e3b55780SDimitry Andric   };
60e3b55780SDimitry Andric 
61e3b55780SDimitry Andric   struct SlotData {
62e3b55780SDimitry Andric     int Slot;
63e3b55780SDimitry Andric     int Size;
64e3b55780SDimitry Andric     int Align;
659b950333SDimitry Andric     StackOffset Offset;
66e3b55780SDimitry Andric     SlotType SlotTy;
67ac9a064cSDimitry Andric     bool Scalable;
68e3b55780SDimitry Andric 
SlotData__anon35d767e30111::StackFrameLayoutAnalysisPass::SlotData699b950333SDimitry Andric     SlotData(const MachineFrameInfo &MFI, const StackOffset Offset,
709b950333SDimitry Andric              const int Idx)
71e3b55780SDimitry Andric         : Slot(Idx), Size(MFI.getObjectSize(Idx)),
729b950333SDimitry Andric           Align(MFI.getObjectAlign(Idx).value()), Offset(Offset),
739b950333SDimitry Andric           SlotTy(Invalid), Scalable(false) {
74ac9a064cSDimitry Andric       Scalable = MFI.getStackID(Idx) == TargetStackID::ScalableVector;
75e3b55780SDimitry Andric       if (MFI.isSpillSlotObjectIndex(Idx))
76e3b55780SDimitry Andric         SlotTy = SlotType::Spill;
779b950333SDimitry Andric       else if (MFI.isFixedObjectIndex(Idx))
789b950333SDimitry Andric         SlotTy = SlotType::Fixed;
799b950333SDimitry Andric       else if (MFI.isVariableSizedObjectIndex(Idx))
809b950333SDimitry Andric         SlotTy = SlotType::VariableSized;
819b950333SDimitry Andric       else if (MFI.hasStackProtectorIndex() &&
829b950333SDimitry Andric                Idx == MFI.getStackProtectorIndex())
83e3b55780SDimitry Andric         SlotTy = SlotType::StackProtector;
84e3b55780SDimitry Andric       else
85e3b55780SDimitry Andric         SlotTy = SlotType::Variable;
86e3b55780SDimitry Andric     }
87e3b55780SDimitry Andric 
isVarSize__anon35d767e30111::StackFrameLayoutAnalysisPass::SlotData889b950333SDimitry Andric     bool isVarSize() const { return SlotTy == SlotType::VariableSized; }
899b950333SDimitry Andric 
90ac9a064cSDimitry Andric     // We use this to sort in reverse order, so that the layout is displayed
919b950333SDimitry Andric     // correctly. Variable sized slots are sorted to the end of the list, as
929b950333SDimitry Andric     // offsets are currently incorrect for these but they reside at the end of
939b950333SDimitry Andric     // the stack frame. The Slot index is used to ensure deterministic order
949b950333SDimitry Andric     // when offsets are equal.
operator <__anon35d767e30111::StackFrameLayoutAnalysisPass::SlotData95ac9a064cSDimitry Andric     bool operator<(const SlotData &Rhs) const {
969b950333SDimitry Andric       return std::make_tuple(!isVarSize(),
979b950333SDimitry Andric                              Offset.getFixed() + Offset.getScalable(), Slot) >
989b950333SDimitry Andric              std::make_tuple(!Rhs.isVarSize(),
999b950333SDimitry Andric                              Rhs.Offset.getFixed() + Rhs.Offset.getScalable(),
1009b950333SDimitry Andric                              Rhs.Slot);
101ac9a064cSDimitry Andric     }
102e3b55780SDimitry Andric   };
103e3b55780SDimitry Andric 
StackFrameLayoutAnalysisPass__anon35d767e30111::StackFrameLayoutAnalysisPass104e3b55780SDimitry Andric   StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {}
105e3b55780SDimitry Andric 
getPassName__anon35d767e30111::StackFrameLayoutAnalysisPass106e3b55780SDimitry Andric   StringRef getPassName() const override {
107e3b55780SDimitry Andric     return "Stack Frame Layout Analysis";
108e3b55780SDimitry Andric   }
109e3b55780SDimitry Andric 
getAnalysisUsage__anon35d767e30111::StackFrameLayoutAnalysisPass110e3b55780SDimitry Andric   void getAnalysisUsage(AnalysisUsage &AU) const override {
111e3b55780SDimitry Andric     AU.setPreservesAll();
112e3b55780SDimitry Andric     MachineFunctionPass::getAnalysisUsage(AU);
113e3b55780SDimitry Andric     AU.addRequired<MachineOptimizationRemarkEmitterPass>();
114e3b55780SDimitry Andric   }
115e3b55780SDimitry Andric 
runOnMachineFunction__anon35d767e30111::StackFrameLayoutAnalysisPass116e3b55780SDimitry Andric   bool runOnMachineFunction(MachineFunction &MF) override {
117e3b55780SDimitry Andric     // TODO: We should implement a similar filter for remarks:
118e3b55780SDimitry Andric     //   -Rpass-func-filter=<regex>
119e3b55780SDimitry Andric     if (!isFunctionInPrintList(MF.getName()))
120e3b55780SDimitry Andric       return false;
121e3b55780SDimitry Andric 
122e3b55780SDimitry Andric     LLVMContext &Ctx = MF.getFunction().getContext();
123e3b55780SDimitry Andric     if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE))
124e3b55780SDimitry Andric       return false;
125e3b55780SDimitry Andric 
126e3b55780SDimitry Andric     MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout",
127e3b55780SDimitry Andric                                           MF.getFunction().getSubprogram(),
128e3b55780SDimitry Andric                                           &MF.front());
129e3b55780SDimitry Andric     Rem << ("\nFunction: " + MF.getName()).str();
130e3b55780SDimitry Andric     emitStackFrameLayoutRemarks(MF, Rem);
131e3b55780SDimitry Andric     getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE().emit(Rem);
132e3b55780SDimitry Andric     return false;
133e3b55780SDimitry Andric   }
134e3b55780SDimitry Andric 
getTypeString__anon35d767e30111::StackFrameLayoutAnalysisPass135e3b55780SDimitry Andric   std::string getTypeString(SlotType Ty) {
136e3b55780SDimitry Andric     switch (Ty) {
137e3b55780SDimitry Andric     case SlotType::Spill:
138e3b55780SDimitry Andric       return "Spill";
1399b950333SDimitry Andric     case SlotType::Fixed:
1409b950333SDimitry Andric       return "Fixed";
1419b950333SDimitry Andric     case SlotType::VariableSized:
1429b950333SDimitry Andric       return "VariableSized";
143e3b55780SDimitry Andric     case SlotType::StackProtector:
144e3b55780SDimitry Andric       return "Protector";
145e3b55780SDimitry Andric     case SlotType::Variable:
146e3b55780SDimitry Andric       return "Variable";
147e3b55780SDimitry Andric     default:
148e3b55780SDimitry Andric       llvm_unreachable("bad slot type for stack layout");
149e3b55780SDimitry Andric     }
150e3b55780SDimitry Andric   }
151e3b55780SDimitry Andric 
emitStackSlotRemark__anon35d767e30111::StackFrameLayoutAnalysisPass152e3b55780SDimitry Andric   void emitStackSlotRemark(const MachineFunction &MF, const SlotData &D,
153e3b55780SDimitry Andric                            MachineOptimizationRemarkAnalysis &Rem) {
154e3b55780SDimitry Andric     // To make it easy to understand the stack layout from the CLI, we want to
155e3b55780SDimitry Andric     // print each slot like the following:
156e3b55780SDimitry Andric     //
157e3b55780SDimitry Andric     //   Offset: [SP+8], Type: Spill, Align: 8, Size: 16
158e3b55780SDimitry Andric     //       foo @ /path/to/file.c:25
159e3b55780SDimitry Andric     //       bar @ /path/to/file.c:35
160e3b55780SDimitry Andric     //
161e3b55780SDimitry Andric     // Which prints the size, alignment, and offset from the SP at function
162e3b55780SDimitry Andric     // entry.
163e3b55780SDimitry Andric     //
164e3b55780SDimitry Andric     // But we also want the machine readable remarks data to be nicely
165e3b55780SDimitry Andric     // organized. So we print some additional data as strings for the CLI
166e3b55780SDimitry Andric     // output, but maintain more structured data for the YAML.
167e3b55780SDimitry Andric     //
168e3b55780SDimitry Andric     // For example we store the Offset in YAML as:
169e3b55780SDimitry Andric     //    ...
170e3b55780SDimitry Andric     //    - Offset: -8
1719b950333SDimitry Andric     //    - ScalableOffset: -16
1729b950333SDimitry Andric     // Note: the ScalableOffset entries are added only for slots with non-zero
1739b950333SDimitry Andric     // scalable offsets.
174e3b55780SDimitry Andric     //
1759b950333SDimitry Andric     // But we print it to the CLI as:
176e3b55780SDimitry Andric     //   Offset: [SP-8]
1779b950333SDimitry Andric     //
1789b950333SDimitry Andric     // Or with non-zero scalable offset:
1799b950333SDimitry Andric     //   Offset: [SP-8-16 x vscale]
180e3b55780SDimitry Andric 
181e3b55780SDimitry Andric     // Negative offsets will print a leading `-`, so only add `+`
182e3b55780SDimitry Andric     std::string Prefix =
1839b950333SDimitry Andric         formatv("\nOffset: [SP{0}", (D.Offset.getFixed() < 0) ? "" : "+").str();
1849b950333SDimitry Andric     Rem << Prefix << ore::NV("Offset", D.Offset.getFixed());
1859b950333SDimitry Andric 
1869b950333SDimitry Andric     if (D.Offset.getScalable()) {
1879b950333SDimitry Andric       Rem << ((D.Offset.getScalable() < 0) ? "" : "+")
1889b950333SDimitry Andric           << ore::NV("ScalableOffset", D.Offset.getScalable()) << " x vscale";
1899b950333SDimitry Andric     }
1909b950333SDimitry Andric 
1919b950333SDimitry Andric     Rem << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy))
192e3b55780SDimitry Andric         << ", Align: " << ore::NV("Align", D.Align)
193ac9a064cSDimitry Andric         << ", Size: " << ore::NV("Size", ElementCount::get(D.Size, D.Scalable));
194e3b55780SDimitry Andric   }
195e3b55780SDimitry Andric 
emitSourceLocRemark__anon35d767e30111::StackFrameLayoutAnalysisPass196e3b55780SDimitry Andric   void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N,
197e3b55780SDimitry Andric                            MachineOptimizationRemarkAnalysis &Rem) {
198e3b55780SDimitry Andric     std::string Loc =
199e3b55780SDimitry Andric         formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine())
200e3b55780SDimitry Andric             .str();
201e3b55780SDimitry Andric     Rem << "\n    " << ore::NV("DataLoc", Loc);
202e3b55780SDimitry Andric   }
203e3b55780SDimitry Andric 
getStackOffset__anon35d767e30111::StackFrameLayoutAnalysisPass2049b950333SDimitry Andric   StackOffset getStackOffset(const MachineFunction &MF,
2059b950333SDimitry Andric                              const MachineFrameInfo &MFI,
2069b950333SDimitry Andric                              const TargetFrameLowering *FI, int FrameIdx) {
2079b950333SDimitry Andric     if (!FI)
2089b950333SDimitry Andric       return StackOffset::getFixed(MFI.getObjectOffset(FrameIdx));
2099b950333SDimitry Andric 
2109b950333SDimitry Andric     return FI->getFrameIndexReferenceFromSP(MF, FrameIdx);
2119b950333SDimitry Andric   }
2129b950333SDimitry Andric 
emitStackFrameLayoutRemarks__anon35d767e30111::StackFrameLayoutAnalysisPass213e3b55780SDimitry Andric   void emitStackFrameLayoutRemarks(MachineFunction &MF,
214e3b55780SDimitry Andric                                    MachineOptimizationRemarkAnalysis &Rem) {
215e3b55780SDimitry Andric     const MachineFrameInfo &MFI = MF.getFrameInfo();
216e3b55780SDimitry Andric     if (!MFI.hasStackObjects())
217e3b55780SDimitry Andric       return;
218e3b55780SDimitry Andric 
219e3b55780SDimitry Andric     const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering();
220e3b55780SDimitry Andric 
221e3b55780SDimitry Andric     LLVM_DEBUG(dbgs() << "getStackProtectorIndex =="
222e3b55780SDimitry Andric                       << MFI.getStackProtectorIndex() << "\n");
223e3b55780SDimitry Andric 
224e3b55780SDimitry Andric     std::vector<SlotData> SlotInfo;
225e3b55780SDimitry Andric 
226e3b55780SDimitry Andric     const unsigned int NumObj = MFI.getNumObjects();
227e3b55780SDimitry Andric     SlotInfo.reserve(NumObj);
228e3b55780SDimitry Andric     // initialize slot info
229e3b55780SDimitry Andric     for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd();
230e3b55780SDimitry Andric          Idx != EndIdx; ++Idx) {
231e3b55780SDimitry Andric       if (MFI.isDeadObjectIndex(Idx))
232e3b55780SDimitry Andric         continue;
2339b950333SDimitry Andric       SlotInfo.emplace_back(MFI, getStackOffset(MF, MFI, FI, Idx), Idx);
234e3b55780SDimitry Andric     }
235e3b55780SDimitry Andric 
236e3b55780SDimitry Andric     // sort the ordering, to match the actual layout in memory
237e3b55780SDimitry Andric     llvm::sort(SlotInfo);
238e3b55780SDimitry Andric 
239e3b55780SDimitry Andric     SlotDbgMap SlotMap = genSlotDbgMapping(MF);
240e3b55780SDimitry Andric 
241e3b55780SDimitry Andric     for (const SlotData &Info : SlotInfo) {
242e3b55780SDimitry Andric       emitStackSlotRemark(MF, Info, Rem);
243e3b55780SDimitry Andric       for (const DILocalVariable *N : SlotMap[Info.Slot])
244e3b55780SDimitry Andric         emitSourceLocRemark(MF, N, Rem);
245e3b55780SDimitry Andric     }
246e3b55780SDimitry Andric   }
247e3b55780SDimitry Andric 
248e3b55780SDimitry Andric   // We need to generate a mapping of slots to the values that are stored to
249e3b55780SDimitry Andric   // them. This information is lost by the time we need to print out the frame,
250e3b55780SDimitry Andric   // so we reconstruct it here by walking the CFG, and generating the mapping.
genSlotDbgMapping__anon35d767e30111::StackFrameLayoutAnalysisPass251e3b55780SDimitry Andric   SlotDbgMap genSlotDbgMapping(MachineFunction &MF) {
252e3b55780SDimitry Andric     SlotDbgMap SlotDebugMap;
253e3b55780SDimitry Andric 
254e3b55780SDimitry Andric     // add variables to the map
2557fa27ce4SDimitry Andric     for (MachineFunction::VariableDbgInfo &DI :
2567fa27ce4SDimitry Andric          MF.getInStackSlotVariableDbgInfo())
2577fa27ce4SDimitry Andric       SlotDebugMap[DI.getStackSlot()].insert(DI.Var);
258e3b55780SDimitry Andric 
259e3b55780SDimitry Andric     // Then add all the spills that have debug data
260e3b55780SDimitry Andric     for (MachineBasicBlock &MBB : MF) {
261e3b55780SDimitry Andric       for (MachineInstr &MI : MBB) {
262e3b55780SDimitry Andric         for (MachineMemOperand *MO : MI.memoperands()) {
263e3b55780SDimitry Andric           if (!MO->isStore())
264e3b55780SDimitry Andric             continue;
265e3b55780SDimitry Andric           auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>(
266e3b55780SDimitry Andric               MO->getPseudoValue());
267e3b55780SDimitry Andric           if (!FI)
268e3b55780SDimitry Andric             continue;
269e3b55780SDimitry Andric           int FrameIdx = FI->getFrameIndex();
270e3b55780SDimitry Andric           SmallVector<MachineInstr *> Dbg;
271e3b55780SDimitry Andric           MI.collectDebugValues(Dbg);
272e3b55780SDimitry Andric 
273e3b55780SDimitry Andric           for (MachineInstr *MI : Dbg)
274e3b55780SDimitry Andric             SlotDebugMap[FrameIdx].insert(MI->getDebugVariable());
275e3b55780SDimitry Andric         }
276e3b55780SDimitry Andric       }
277e3b55780SDimitry Andric     }
278e3b55780SDimitry Andric 
279e3b55780SDimitry Andric     return SlotDebugMap;
280e3b55780SDimitry Andric   }
281e3b55780SDimitry Andric };
282e3b55780SDimitry Andric 
283e3b55780SDimitry Andric char StackFrameLayoutAnalysisPass::ID = 0;
284e3b55780SDimitry Andric } // namespace
285e3b55780SDimitry Andric 
286e3b55780SDimitry Andric char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID;
287e3b55780SDimitry Andric INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout",
288e3b55780SDimitry Andric                 "Stack Frame Layout", false, false)
289e3b55780SDimitry Andric 
290e3b55780SDimitry Andric namespace llvm {
291e3b55780SDimitry Andric /// Returns a newly-created StackFrameLayout pass.
createStackFrameLayoutAnalysisPass()292e3b55780SDimitry Andric MachineFunctionPass *createStackFrameLayoutAnalysisPass() {
293e3b55780SDimitry Andric   return new StackFrameLayoutAnalysisPass();
294e3b55780SDimitry Andric }
295e3b55780SDimitry Andric 
296e3b55780SDimitry Andric } // namespace llvm
297