xref: /freebsd/contrib/llvm-project/llvm/lib/CodeGen/StackFrameLayoutAnalysisPass.cpp (revision 3d9fd9fcb432750f3716b28f6ccb0104cd9d351a)
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