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
38 using namespace llvm;
39
40 #define DEBUG_TYPE "stack-frame-layout"
41
42 namespace {
43
44 /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a
45 /// MachineFunction.
46 ///
47 struct StackFrameLayoutAnalysis {
48 using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>;
49 MachineOptimizationRemarkEmitter &ORE;
50
StackFrameLayoutAnalysis__anon00dd9cce0111::StackFrameLayoutAnalysis51 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
SlotData__anon00dd9cce0111::StackFrameLayoutAnalysis::SlotData70 SlotData(const MachineFrameInfo &MFI, const StackOffset Offset,
71 const int Idx)
72 : Slot(Idx), Size(MFI.getObjectSize(Idx)),
73 Align(MFI.getObjectAlign(Idx).value()), Offset(Offset),
74 SlotTy(Invalid), Scalable(false) {
75 Scalable = MFI.getStackID(Idx) == TargetStackID::ScalableVector;
76 if (MFI.isSpillSlotObjectIndex(Idx))
77 SlotTy = SlotType::Spill;
78 else if (MFI.isFixedObjectIndex(Idx))
79 SlotTy = SlotType::Fixed;
80 else if (MFI.isVariableSizedObjectIndex(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
isVarSize__anon00dd9cce0111::StackFrameLayoutAnalysis::SlotData89 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.
operator <__anon00dd9cce0111::StackFrameLayoutAnalysis::SlotData96 bool operator<(const SlotData &Rhs) const {
97 return std::make_tuple(!isVarSize(),
98 Offset.getFixed() + Offset.getScalable(), Slot) >
99 std::make_tuple(!Rhs.isVarSize(),
100 Rhs.Offset.getFixed() + Rhs.Offset.getScalable(),
101 Rhs.Slot);
102 }
103 };
104
run__anon00dd9cce0111::StackFrameLayoutAnalysis105 bool run(MachineFunction &MF) {
106 // TODO: We should implement a similar filter for remarks:
107 // -Rpass-func-filter=<regex>
108 if (!isFunctionInPrintList(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(Rem);
121 return false;
122 }
123
getTypeString__anon00dd9cce0111::StackFrameLayoutAnalysis124 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
emitStackSlotRemark__anon00dd9cce0111::StackFrameLayoutAnalysis141 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("\nOffset: [SP{0}", (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(D.SlotTy))
181 << ", Align: " << ore::NV("Align", D.Align)
182 << ", Size: " << ore::NV("Size", ElementCount::get(D.Size, D.Scalable));
183 }
184
emitSourceLocRemark__anon00dd9cce0111::StackFrameLayoutAnalysis185 void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N,
186 MachineOptimizationRemarkAnalysis &Rem) {
187 std::string Loc =
188 formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine())
189 .str();
190 Rem << "\n " << ore::NV("DataLoc", Loc);
191 }
192
getStackOffset__anon00dd9cce0111::StackFrameLayoutAnalysis193 StackOffset getStackOffset(const MachineFunction &MF,
194 const MachineFrameInfo &MFI,
195 const TargetFrameLowering *FI, int FrameIdx) {
196 if (!FI)
197 return StackOffset::getFixed(MFI.getObjectOffset(FrameIdx));
198
199 return FI->getFrameIndexReferenceFromSP(MF, FrameIdx);
200 }
201
emitStackFrameLayoutRemarks__anon00dd9cce0111::StackFrameLayoutAnalysis202 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(NumObj);
217 // initialize slot info
218 for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd();
219 Idx != EndIdx; ++Idx) {
220 if (MFI.isDeadObjectIndex(Idx))
221 continue;
222 SlotInfo.emplace_back(MFI, getStackOffset(MF, MFI, FI, Idx), Idx);
223 }
224
225 // sort the ordering, to match the actual layout in memory
226 llvm::sort(SlotInfo);
227
228 SlotDbgMap SlotMap = genSlotDbgMapping(MF);
229
230 for (const SlotData &Info : SlotInfo) {
231 emitStackSlotRemark(MF, 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.
genSlotDbgMapping__anon00dd9cce0111::StackFrameLayoutAnalysis240 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(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 MO->getPseudoValue());
256 if (!FI)
257 continue;
258 int FrameIdx = FI->getFrameIndex();
259 SmallVector<MachineInstr *> Dbg;
260 MI.collectDebugValues(Dbg);
261
262 for (MachineInstr *MI : Dbg)
263 SlotDebugMap[FrameIdx].insert(MI->getDebugVariable());
264 }
265 }
266 }
267
268 return SlotDebugMap;
269 }
270 };
271
272 class StackFrameLayoutAnalysisLegacy : public MachineFunctionPass {
273 public:
274 static char ID;
275
StackFrameLayoutAnalysisLegacy()276 StackFrameLayoutAnalysisLegacy() : MachineFunctionPass(ID) {}
277
getPassName() const278 StringRef getPassName() const override {
279 return "Stack Frame Layout Analysis";
280 }
281
getAnalysisUsage(AnalysisUsage & AU) const282 void getAnalysisUsage(AnalysisUsage &AU) const override {
283 AU.setPreservesAll();
284 MachineFunctionPass::getAnalysisUsage(AU);
285 AU.addRequired<MachineOptimizationRemarkEmitterPass>();
286 }
287
runOnMachineFunction(MachineFunction & MF)288 bool runOnMachineFunction(MachineFunction &MF) override {
289 auto &ORE = getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE();
290 return StackFrameLayoutAnalysis(ORE).run(MF);
291 }
292 };
293
294 char StackFrameLayoutAnalysisLegacy::ID = 0;
295 } // namespace
296
297 PreservedAnalyses
run(MachineFunction & MF,MachineFunctionAnalysisManager & MFAM)298 llvm::StackFrameLayoutAnalysisPass::run(MachineFunction &MF,
299 MachineFunctionAnalysisManager &MFAM) {
300 auto &ORE = MFAM.getResult<MachineOptimizationRemarkEmitterAnalysis>(MF);
301 StackFrameLayoutAnalysis(ORE).run(MF);
302 return PreservedAnalyses::all();
303 }
304
305 char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisLegacy::ID;
306 INITIALIZE_PASS(StackFrameLayoutAnalysisLegacy, "stack-frame-layout",
307 "Stack Frame Layout", false, false)
308
309 namespace llvm {
310 /// Returns a newly-created StackFrameLayout pass.
createStackFrameLayoutAnalysisPass()311 MachineFunctionPass *createStackFrameLayoutAnalysisPass() {
312 return new StackFrameLayoutAnalysisLegacy();
313 }
314
315 } // namespace llvm
316