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
SlotData__anon0c4f220e0111::StackFrameLayoutAnalysisPass::SlotData66 SlotData(const MachineFrameInfo &MFI, const int ValOffset, const int Idx)
67 : Slot(Idx), Size(MFI.getObjectSize(Idx)),
68 Align(MFI.getObjectAlign(Idx).value()),
69 Offset(MFI.getObjectOffset(Idx) - ValOffset), SlotTy(Invalid) {
70 if (MFI.isSpillSlotObjectIndex(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
operator <__anon0c4f220e0111::StackFrameLayoutAnalysisPass::SlotData80 bool operator<(const SlotData &Rhs) const { return Offset > Rhs.Offset; }
81 };
82
StackFrameLayoutAnalysisPass__anon0c4f220e0111::StackFrameLayoutAnalysisPass83 StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {}
84
getPassName__anon0c4f220e0111::StackFrameLayoutAnalysisPass85 StringRef getPassName() const override {
86 return "Stack Frame Layout Analysis";
87 }
88
getAnalysisUsage__anon0c4f220e0111::StackFrameLayoutAnalysisPass89 void getAnalysisUsage(AnalysisUsage &AU) const override {
90 AU.setPreservesAll();
91 MachineFunctionPass::getAnalysisUsage(AU);
92 AU.addRequired<MachineOptimizationRemarkEmitterPass>();
93 }
94
runOnMachineFunction__anon0c4f220e0111::StackFrameLayoutAnalysisPass95 bool runOnMachineFunction(MachineFunction &MF) override {
96 // TODO: We should implement a similar filter for remarks:
97 // -Rpass-func-filter=<regex>
98 if (!isFunctionInPrintList(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(Rem);
111 return false;
112 }
113
getTypeString__anon0c4f220e0111::StackFrameLayoutAnalysisPass114 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
emitStackSlotRemark__anon0c4f220e0111::StackFrameLayoutAnalysisPass127 void emitStackSlotRemark(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("\nOffset: [SP{0}", (D.Offset < 0) ? "" : "+").str();
153 Rem << Prefix << ore::NV("Offset", D.Offset)
154 << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy))
155 << ", Align: " << ore::NV("Align", D.Align)
156 << ", Size: " << ore::NV("Size", D.Size);
157 }
158
emitSourceLocRemark__anon0c4f220e0111::StackFrameLayoutAnalysisPass159 void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N,
160 MachineOptimizationRemarkAnalysis &Rem) {
161 std::string Loc =
162 formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine())
163 .str();
164 Rem << "\n " << ore::NV("DataLoc", Loc);
165 }
166
emitStackFrameLayoutRemarks__anon0c4f220e0111::StackFrameLayoutAnalysisPass167 void emitStackFrameLayoutRemarks(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(NumObj);
186 // initialize slot info
187 for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd();
188 Idx != EndIdx; ++Idx) {
189 if (MFI.isDeadObjectIndex(Idx))
190 continue;
191 SlotInfo.emplace_back(MFI, ValOffset, Idx);
192 }
193
194 // sort the ordering, to match the actual layout in memory
195 llvm::sort(SlotInfo);
196
197 SlotDbgMap SlotMap = genSlotDbgMapping(MF);
198
199 for (const SlotData &Info : SlotInfo) {
200 emitStackSlotRemark(MF, 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.
genSlotDbgMapping__anon0c4f220e0111::StackFrameLayoutAnalysisPass209 SlotDbgMap genSlotDbgMapping(MachineFunction &MF) {
210 SlotDbgMap SlotDebugMap;
211
212 // add variables to the map
213 for (MachineFunction::VariableDbgInfo &DI : MF.getVariableDbgInfo())
214 SlotDebugMap[DI.Slot].insert(DI.Var);
215
216 // Then add all the spills that have debug data
217 for (MachineBasicBlock &MBB : MF) {
218 for (MachineInstr &MI : MBB) {
219 for (MachineMemOperand *MO : MI.memoperands()) {
220 if (!MO->isStore())
221 continue;
222 auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>(
223 MO->getPseudoValue());
224 if (!FI)
225 continue;
226 int FrameIdx = FI->getFrameIndex();
227 SmallVector<MachineInstr *> Dbg;
228 MI.collectDebugValues(Dbg);
229
230 for (MachineInstr *MI : Dbg)
231 SlotDebugMap[FrameIdx].insert(MI->getDebugVariable());
232 }
233 }
234 }
235
236 return SlotDebugMap;
237 }
238 };
239
240 char StackFrameLayoutAnalysisPass::ID = 0;
241 } // namespace
242
243 char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID;
244 INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout",
245 "Stack Frame Layout", false, false)
246
247 namespace llvm {
248 /// Returns a newly-created StackFrameLayout pass.
createStackFrameLayoutAnalysisPass()249 MachineFunctionPass *createStackFrameLayoutAnalysisPass() {
250 return new StackFrameLayoutAnalysisPass();
251 }
252
253 } // namespace llvm
254