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
SlotData__anon00dd9cce0111::StackFrameLayoutAnalysisPass::SlotData69 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
isVarSize__anon00dd9cce0111::StackFrameLayoutAnalysisPass::SlotData88 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.
operator <__anon00dd9cce0111::StackFrameLayoutAnalysisPass::SlotData95 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
StackFrameLayoutAnalysisPass__anon00dd9cce0111::StackFrameLayoutAnalysisPass104 StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {}
105
getPassName__anon00dd9cce0111::StackFrameLayoutAnalysisPass106 StringRef getPassName() const override {
107 return "Stack Frame Layout Analysis";
108 }
109
getAnalysisUsage__anon00dd9cce0111::StackFrameLayoutAnalysisPass110 void getAnalysisUsage(AnalysisUsage &AU) const override {
111 AU.setPreservesAll();
112 MachineFunctionPass::getAnalysisUsage(AU);
113 AU.addRequired<MachineOptimizationRemarkEmitterPass>();
114 }
115
runOnMachineFunction__anon00dd9cce0111::StackFrameLayoutAnalysisPass116 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
getTypeString__anon00dd9cce0111::StackFrameLayoutAnalysisPass135 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
emitStackSlotRemark__anon00dd9cce0111::StackFrameLayoutAnalysisPass152 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
emitSourceLocRemark__anon00dd9cce0111::StackFrameLayoutAnalysisPass196 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
getStackOffset__anon00dd9cce0111::StackFrameLayoutAnalysisPass204 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
emitStackFrameLayoutRemarks__anon00dd9cce0111::StackFrameLayoutAnalysisPass213 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.
genSlotDbgMapping__anon00dd9cce0111::StackFrameLayoutAnalysisPass251 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.
createStackFrameLayoutAnalysisPass()292 MachineFunctionPass *createStackFrameLayoutAnalysisPass() {
293 return new StackFrameLayoutAnalysisPass();
294 }
295
296 } // namespace llvm
297