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/CodeGen/StackFrameLayoutAnalysisPass.h"
20#include "llvm/ADT/SetVector.h"
21#include "llvm/Analysis/OptimizationRemarkEmitter.h"
22#include "llvm/CodeGen/MachineFrameInfo.h"
23#include "llvm/CodeGen/MachineFunction.h"
24#include "llvm/CodeGen/MachineFunctionPass.h"
25#include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
26#include "llvm/CodeGen/Passes.h"
27#include "llvm/CodeGen/SlotIndexes.h"
28#include "llvm/CodeGen/StackProtector.h"
29#include "llvm/CodeGen/TargetFrameLowering.h"
30#include "llvm/CodeGen/TargetSubtargetInfo.h"
31#include "llvm/IR/DebugInfoMetadata.h"
32#include "llvm/IR/PrintPasses.h"
33#include "llvm/InitializePasses.h"
34#include "llvm/Support/Debug.h"
35#include "llvm/Support/FormatVariadic.h"
36#include "llvm/Support/raw_ostream.h"
37
38using namespace llvm;
39
40#define DEBUG_TYPE "stack-frame-layout"
41
42namespace {
43
44/// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a
45/// MachineFunction.
46///
47struct StackFrameLayoutAnalysis {
48 using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>;
49 MachineOptimizationRemarkEmitter &ORE;
50
51 StackFrameLayoutAnalysis(MachineOptimizationRemarkEmitter &ORE) : ORE(ORE) {}
52
53 enum SlotType {
54 Spill, // a Spill slot
55 Fixed, // a Fixed slot (e.g. arguments passed on the stack)
56 VariableSized, // a variable sized object
57 StackProtector, // Stack Protector slot
58 Variable, // a slot used to store a local data (could be a tmp)
59 Invalid // It's an error for a slot to have this type
60 };
61
62 struct SlotData {
63 int Slot;
64 int Size;
65 int Align;
66 StackOffset Offset;
67 SlotType SlotTy;
68 bool Scalable;
69
70 SlotData(const MachineFrameInfo &MFI, const StackOffset Offset,
71 const int Idx)
72 : Slot(Idx), Size(MFI.getObjectSize(ObjectIdx: Idx)),
73 Align(MFI.getObjectAlign(ObjectIdx: Idx).value()), Offset(Offset),
74 SlotTy(Invalid), Scalable(false) {
75 Scalable = MFI.getStackID(ObjectIdx: Idx) == TargetStackID::ScalableVector;
76 if (MFI.isSpillSlotObjectIndex(ObjectIdx: Idx))
77 SlotTy = SlotType::Spill;
78 else if (MFI.isFixedObjectIndex(ObjectIdx: Idx))
79 SlotTy = SlotType::Fixed;
80 else if (MFI.isVariableSizedObjectIndex(ObjectIdx: Idx))
81 SlotTy = SlotType::VariableSized;
82 else if (MFI.hasStackProtectorIndex() &&
83 Idx == MFI.getStackProtectorIndex())
84 SlotTy = SlotType::StackProtector;
85 else
86 SlotTy = SlotType::Variable;
87 }
88
89 bool isVarSize() const { return SlotTy == SlotType::VariableSized; }
90
91 // We use this to sort in reverse order, so that the layout is displayed
92 // correctly. Variable sized slots are sorted to the end of the list, as
93 // offsets are currently incorrect for these but they reside at the end of
94 // the stack frame. The Slot index is used to ensure deterministic order
95 // when offsets are equal.
96 bool operator<(const SlotData &Rhs) const {
97 return std::make_tuple(args: !isVarSize(),
98 args: Offset.getFixed() + Offset.getScalable(), args: Slot) >
99 std::make_tuple(args: !Rhs.isVarSize(),
100 args: Rhs.Offset.getFixed() + Rhs.Offset.getScalable(),
101 args: Rhs.Slot);
102 }
103 };
104
105 bool run(MachineFunction &MF) {
106 // TODO: We should implement a similar filter for remarks:
107 // -Rpass-func-filter=<regex>
108 if (!isFunctionInPrintList(FunctionName: MF.getName()))
109 return false;
110
111 LLVMContext &Ctx = MF.getFunction().getContext();
112 if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE))
113 return false;
114
115 MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout",
116 MF.getFunction().getSubprogram(),
117 &MF.front());
118 Rem << ("\nFunction: " + MF.getName()).str();
119 emitStackFrameLayoutRemarks(MF, Rem);
120 ORE.emit(OptDiag&: Rem);
121 return false;
122 }
123
124 std::string getTypeString(SlotType Ty) {
125 switch (Ty) {
126 case SlotType::Spill:
127 return "Spill";
128 case SlotType::Fixed:
129 return "Fixed";
130 case SlotType::VariableSized:
131 return "VariableSized";
132 case SlotType::StackProtector:
133 return "Protector";
134 case SlotType::Variable:
135 return "Variable";
136 default:
137 llvm_unreachable("bad slot type for stack layout");
138 }
139 }
140
141 void emitStackSlotRemark(const MachineFunction &MF, const SlotData &D,
142 MachineOptimizationRemarkAnalysis &Rem) {
143 // To make it easy to understand the stack layout from the CLI, we want to
144 // print each slot like the following:
145 //
146 // Offset: [SP+8], Type: Spill, Align: 8, Size: 16
147 // foo @ /path/to/file.c:25
148 // bar @ /path/to/file.c:35
149 //
150 // Which prints the size, alignment, and offset from the SP at function
151 // entry.
152 //
153 // But we also want the machine readable remarks data to be nicely
154 // organized. So we print some additional data as strings for the CLI
155 // output, but maintain more structured data for the YAML.
156 //
157 // For example we store the Offset in YAML as:
158 // ...
159 // - Offset: -8
160 // - ScalableOffset: -16
161 // Note: the ScalableOffset entries are added only for slots with non-zero
162 // scalable offsets.
163 //
164 // But we print it to the CLI as:
165 // Offset: [SP-8]
166 //
167 // Or with non-zero scalable offset:
168 // Offset: [SP-8-16 x vscale]
169
170 // Negative offsets will print a leading `-`, so only add `+`
171 std::string Prefix =
172 formatv(Fmt: "\nOffset: [SP{0}", Vals: (D.Offset.getFixed() < 0) ? "" : "+").str();
173 Rem << Prefix << ore::NV("Offset", D.Offset.getFixed());
174
175 if (D.Offset.getScalable()) {
176 Rem << ((D.Offset.getScalable() < 0) ? "" : "+")
177 << ore::NV("ScalableOffset", D.Offset.getScalable()) << " x vscale";
178 }
179
180 Rem << "], Type: " << ore::NV("Type", getTypeString(Ty: D.SlotTy))
181 << ", Align: " << ore::NV("Align", D.Align)
182 << ", Size: " << ore::NV("Size", ElementCount::get(MinVal: D.Size, Scalable: D.Scalable));
183 }
184
185 void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N,
186 MachineOptimizationRemarkAnalysis &Rem) {
187 std::string Loc =
188 formatv(Fmt: "{0} @ {1}:{2}", Vals: N->getName(), Vals: N->getFilename(), Vals: N->getLine())
189 .str();
190 Rem << "\n " << ore::NV("DataLoc", Loc);
191 }
192
193 StackOffset getStackOffset(const MachineFunction &MF,
194 const MachineFrameInfo &MFI,
195 const TargetFrameLowering *FI, int FrameIdx) {
196 if (!FI)
197 return StackOffset::getFixed(Fixed: MFI.getObjectOffset(ObjectIdx: FrameIdx));
198
199 return FI->getFrameIndexReferenceFromSP(MF, FI: FrameIdx);
200 }
201
202 void emitStackFrameLayoutRemarks(MachineFunction &MF,
203 MachineOptimizationRemarkAnalysis &Rem) {
204 const MachineFrameInfo &MFI = MF.getFrameInfo();
205 if (!MFI.hasStackObjects())
206 return;
207
208 const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering();
209
210 LLVM_DEBUG(dbgs() << "getStackProtectorIndex =="
211 << MFI.getStackProtectorIndex() << "\n");
212
213 std::vector<SlotData> SlotInfo;
214
215 const unsigned int NumObj = MFI.getNumObjects();
216 SlotInfo.reserve(n: NumObj);
217 // initialize slot info
218 for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd();
219 Idx != EndIdx; ++Idx) {
220 if (MFI.isDeadObjectIndex(ObjectIdx: Idx))
221 continue;
222 SlotInfo.emplace_back(args: MFI, args: getStackOffset(MF, MFI, FI, FrameIdx: Idx), args&: Idx);
223 }
224
225 // sort the ordering, to match the actual layout in memory
226 llvm::sort(C&: SlotInfo);
227
228 SlotDbgMap SlotMap = genSlotDbgMapping(MF);
229
230 for (const SlotData &Info : SlotInfo) {
231 emitStackSlotRemark(MF, D: Info, Rem);
232 for (const DILocalVariable *N : SlotMap[Info.Slot])
233 emitSourceLocRemark(MF, N, Rem);
234 }
235 }
236
237 // We need to generate a mapping of slots to the values that are stored to
238 // them. This information is lost by the time we need to print out the frame,
239 // so we reconstruct it here by walking the CFG, and generating the mapping.
240 SlotDbgMap genSlotDbgMapping(MachineFunction &MF) {
241 SlotDbgMap SlotDebugMap;
242
243 // add variables to the map
244 for (MachineFunction::VariableDbgInfo &DI :
245 MF.getInStackSlotVariableDbgInfo())
246 SlotDebugMap[DI.getStackSlot()].insert(X: DI.Var);
247
248 // Then add all the spills that have debug data
249 for (MachineBasicBlock &MBB : MF) {
250 for (MachineInstr &MI : MBB) {
251 for (MachineMemOperand *MO : MI.memoperands()) {
252 if (!MO->isStore())
253 continue;
254 auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>(
255 Val: MO->getPseudoValue());
256 if (!FI)
257 continue;
258 int FrameIdx = FI->getFrameIndex();
259 SmallVector<MachineInstr *> Dbg;
260 MI.collectDebugValues(DbgValues&: Dbg);
261
262 for (MachineInstr *MI : Dbg)
263 SlotDebugMap[FrameIdx].insert(X: MI->getDebugVariable());
264 }
265 }
266 }
267
268 return SlotDebugMap;
269 }
270};
271
272class StackFrameLayoutAnalysisLegacy : public MachineFunctionPass {
273public:
274 static char ID;
275
276 StackFrameLayoutAnalysisLegacy() : MachineFunctionPass(ID) {}
277
278 StringRef getPassName() const override {
279 return "Stack Frame Layout Analysis";
280 }
281
282 void getAnalysisUsage(AnalysisUsage &AU) const override {
283 AU.setPreservesAll();
284 MachineFunctionPass::getAnalysisUsage(AU);
285 AU.addRequired<MachineOptimizationRemarkEmitterPass>();
286 }
287
288 bool runOnMachineFunction(MachineFunction &MF) override {
289 auto &ORE = getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE();
290 return StackFrameLayoutAnalysis(ORE).run(MF);
291 }
292};
293
294char StackFrameLayoutAnalysisLegacy::ID = 0;
295} // namespace
296
297PreservedAnalyses
298llvm::StackFrameLayoutAnalysisPass::run(MachineFunction &MF,
299 MachineFunctionAnalysisManager &MFAM) {
300 auto &ORE = MFAM.getResult<MachineOptimizationRemarkEmitterAnalysis>(IR&: MF);
301 StackFrameLayoutAnalysis(ORE).run(MF);
302 return PreservedAnalyses::all();
303}
304
305char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisLegacy::ID;
306INITIALIZE_PASS(StackFrameLayoutAnalysisLegacy, "stack-frame-layout",
307 "Stack Frame Layout", false, false)
308
309namespace llvm {
310/// Returns a newly-created StackFrameLayout pass.
311MachineFunctionPass *createStackFrameLayoutAnalysisPass() {
312 return new StackFrameLayoutAnalysisLegacy();
313}
314
315} // namespace llvm
316