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 | StackProtector, // Stack Protector slot |
55 | Variable, // a slot used to store a local data (could be a tmp) |
56 | Invalid // It's an error for a slot to have this type |
57 | }; |
58 | |
59 | struct SlotData { |
60 | int Slot; |
61 | int Size; |
62 | int Align; |
63 | int Offset; |
64 | SlotType SlotTy; |
65 | |
66 | SlotData(const MachineFrameInfo &MFI, const int ValOffset, const int Idx) |
67 | : Slot(Idx), Size(MFI.getObjectSize(ObjectIdx: Idx)), |
68 | Align(MFI.getObjectAlign(ObjectIdx: Idx).value()), |
69 | Offset(MFI.getObjectOffset(ObjectIdx: Idx) - ValOffset), SlotTy(Invalid) { |
70 | if (MFI.isSpillSlotObjectIndex(ObjectIdx: Idx)) |
71 | SlotTy = SlotType::Spill; |
72 | else if (Idx == MFI.getStackProtectorIndex()) |
73 | SlotTy = SlotType::StackProtector; |
74 | else |
75 | SlotTy = SlotType::Variable; |
76 | } |
77 | |
78 | // we use this to sort in reverse order, so that the layout is displayed |
79 | // correctly |
80 | bool operator<(const SlotData &Rhs) const { return Offset > Rhs.Offset; } |
81 | }; |
82 | |
83 | StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {} |
84 | |
85 | StringRef getPassName() const override { |
86 | return "Stack Frame Layout Analysis" ; |
87 | } |
88 | |
89 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
90 | AU.setPreservesAll(); |
91 | MachineFunctionPass::getAnalysisUsage(AU); |
92 | AU.addRequired<MachineOptimizationRemarkEmitterPass>(); |
93 | } |
94 | |
95 | bool runOnMachineFunction(MachineFunction &MF) override { |
96 | // TODO: We should implement a similar filter for remarks: |
97 | // -Rpass-func-filter=<regex> |
98 | if (!isFunctionInPrintList(FunctionName: MF.getName())) |
99 | return false; |
100 | |
101 | LLVMContext &Ctx = MF.getFunction().getContext(); |
102 | if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE)) |
103 | return false; |
104 | |
105 | MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout" , |
106 | MF.getFunction().getSubprogram(), |
107 | &MF.front()); |
108 | Rem << ("\nFunction: " + MF.getName()).str(); |
109 | emitStackFrameLayoutRemarks(MF, Rem); |
110 | getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE().emit(OptDiag&: Rem); |
111 | return false; |
112 | } |
113 | |
114 | std::string getTypeString(SlotType Ty) { |
115 | switch (Ty) { |
116 | case SlotType::Spill: |
117 | return "Spill" ; |
118 | case SlotType::StackProtector: |
119 | return "Protector" ; |
120 | case SlotType::Variable: |
121 | return "Variable" ; |
122 | default: |
123 | llvm_unreachable("bad slot type for stack layout" ); |
124 | } |
125 | } |
126 | |
127 | void (const MachineFunction &MF, const SlotData &D, |
128 | MachineOptimizationRemarkAnalysis &Rem) { |
129 | // To make it easy to understand the stack layout from the CLI, we want to |
130 | // print each slot like the following: |
131 | // |
132 | // Offset: [SP+8], Type: Spill, Align: 8, Size: 16 |
133 | // foo @ /path/to/file.c:25 |
134 | // bar @ /path/to/file.c:35 |
135 | // |
136 | // Which prints the size, alignment, and offset from the SP at function |
137 | // entry. |
138 | // |
139 | // But we also want the machine readable remarks data to be nicely |
140 | // organized. So we print some additional data as strings for the CLI |
141 | // output, but maintain more structured data for the YAML. |
142 | // |
143 | // For example we store the Offset in YAML as: |
144 | // ... |
145 | // - Offset: -8 |
146 | // |
147 | // But we print it to the CLI as |
148 | // Offset: [SP-8] |
149 | |
150 | // Negative offsets will print a leading `-`, so only add `+` |
151 | std::string Prefix = |
152 | formatv(Fmt: "\nOffset: [SP{0}" , Vals: (D.Offset < 0) ? "" : "+" ).str(); |
153 | Rem << Prefix << ore::NV("Offset" , D.Offset) |
154 | << "], Type: " << ore::NV("Type" , getTypeString(Ty: D.SlotTy)) |
155 | << ", Align: " << ore::NV("Align" , D.Align) |
156 | << ", Size: " << ore::NV("Size" , D.Size); |
157 | } |
158 | |
159 | void (const MachineFunction &MF, const DILocalVariable *N, |
160 | MachineOptimizationRemarkAnalysis &Rem) { |
161 | std::string Loc = |
162 | formatv(Fmt: "{0} @ {1}:{2}" , Vals: N->getName(), Vals: N->getFilename(), Vals: N->getLine()) |
163 | .str(); |
164 | Rem << "\n " << ore::NV("DataLoc" , Loc); |
165 | } |
166 | |
167 | void (MachineFunction &MF, |
168 | MachineOptimizationRemarkAnalysis &Rem) { |
169 | const MachineFrameInfo &MFI = MF.getFrameInfo(); |
170 | if (!MFI.hasStackObjects()) |
171 | return; |
172 | |
173 | // ValOffset is the offset to the local area from the SP at function entry. |
174 | // To display the true offset from SP, we need to subtract ValOffset from |
175 | // MFI's ObjectOffset. |
176 | const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering(); |
177 | const int ValOffset = (FI ? FI->getOffsetOfLocalArea() : 0); |
178 | |
179 | LLVM_DEBUG(dbgs() << "getStackProtectorIndex ==" |
180 | << MFI.getStackProtectorIndex() << "\n" ); |
181 | |
182 | std::vector<SlotData> SlotInfo; |
183 | |
184 | const unsigned int NumObj = MFI.getNumObjects(); |
185 | SlotInfo.reserve(n: NumObj); |
186 | // initialize slot info |
187 | for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd(); |
188 | Idx != EndIdx; ++Idx) { |
189 | if (MFI.isDeadObjectIndex(ObjectIdx: Idx)) |
190 | continue; |
191 | SlotInfo.emplace_back(args: MFI, args: ValOffset, args&: Idx); |
192 | } |
193 | |
194 | // sort the ordering, to match the actual layout in memory |
195 | llvm::sort(C&: SlotInfo); |
196 | |
197 | SlotDbgMap SlotMap = genSlotDbgMapping(MF); |
198 | |
199 | for (const SlotData &Info : SlotInfo) { |
200 | emitStackSlotRemark(MF, D: Info, Rem); |
201 | for (const DILocalVariable *N : SlotMap[Info.Slot]) |
202 | emitSourceLocRemark(MF, N, Rem); |
203 | } |
204 | } |
205 | |
206 | // We need to generate a mapping of slots to the values that are stored to |
207 | // them. This information is lost by the time we need to print out the frame, |
208 | // so we reconstruct it here by walking the CFG, and generating the mapping. |
209 | SlotDbgMap genSlotDbgMapping(MachineFunction &MF) { |
210 | SlotDbgMap SlotDebugMap; |
211 | |
212 | // add variables to the map |
213 | for (MachineFunction::VariableDbgInfo &DI : |
214 | MF.getInStackSlotVariableDbgInfo()) |
215 | SlotDebugMap[DI.getStackSlot()].insert(X: DI.Var); |
216 | |
217 | // Then add all the spills that have debug data |
218 | for (MachineBasicBlock &MBB : MF) { |
219 | for (MachineInstr &MI : MBB) { |
220 | for (MachineMemOperand *MO : MI.memoperands()) { |
221 | if (!MO->isStore()) |
222 | continue; |
223 | auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>( |
224 | Val: MO->getPseudoValue()); |
225 | if (!FI) |
226 | continue; |
227 | int FrameIdx = FI->getFrameIndex(); |
228 | SmallVector<MachineInstr *> Dbg; |
229 | MI.collectDebugValues(DbgValues&: Dbg); |
230 | |
231 | for (MachineInstr *MI : Dbg) |
232 | SlotDebugMap[FrameIdx].insert(X: MI->getDebugVariable()); |
233 | } |
234 | } |
235 | } |
236 | |
237 | return SlotDebugMap; |
238 | } |
239 | }; |
240 | |
241 | char StackFrameLayoutAnalysisPass::ID = 0; |
242 | } // namespace |
243 | |
244 | char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID; |
245 | INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout" , |
246 | "Stack Frame Layout" , false, false) |
247 | |
248 | namespace llvm { |
249 | /// Returns a newly-created StackFrameLayout pass. |
250 | MachineFunctionPass *createStackFrameLayoutAnalysisPass() { |
251 | return new StackFrameLayoutAnalysisPass(); |
252 | } |
253 | |
254 | } // namespace llvm |
255 | |