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 | |
51 | (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 (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 (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 (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 | |
272 | class StackFrameLayoutAnalysisLegacy : public MachineFunctionPass { |
273 | public: |
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 | |
294 | char StackFrameLayoutAnalysisLegacy::ID = 0; |
295 | } // namespace |
296 | |
297 | PreservedAnalyses |
298 | llvm::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 | |
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. |
311 | MachineFunctionPass *createStackFrameLayoutAnalysisPass() { |
312 | return new StackFrameLayoutAnalysisLegacy(); |
313 | } |
314 | |
315 | } // namespace llvm |
316 | |