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
39using namespace llvm;
40
41#define DEBUG_TYPE "stack-frame-layout"
42
43namespace {
44
45/// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a
46/// MachineFunction.
47///
48struct 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
69 SlotData(const MachineFrameInfo &MFI, const StackOffset Offset,
70 const int Idx)
71 : Slot(Idx), Size(MFI.getObjectSize(ObjectIdx: Idx)),
72 Align(MFI.getObjectAlign(ObjectIdx: Idx).value()), Offset(Offset),
73 SlotTy(Invalid), Scalable(false) {
74 Scalable = MFI.getStackID(ObjectIdx: Idx) == TargetStackID::ScalableVector;
75 if (MFI.isSpillSlotObjectIndex(ObjectIdx: Idx))
76 SlotTy = SlotType::Spill;
77 else if (MFI.isFixedObjectIndex(ObjectIdx: Idx))
78 SlotTy = SlotType::Fixed;
79 else if (MFI.isVariableSizedObjectIndex(ObjectIdx: 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
88 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.
95 bool operator<(const SlotData &Rhs) const {
96 return std::make_tuple(args: !isVarSize(),
97 args: Offset.getFixed() + Offset.getScalable(), args: Slot) >
98 std::make_tuple(args: !Rhs.isVarSize(),
99 args: Rhs.Offset.getFixed() + Rhs.Offset.getScalable(),
100 args: Rhs.Slot);
101 }
102 };
103
104 StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {}
105
106 StringRef getPassName() const override {
107 return "Stack Frame Layout Analysis";
108 }
109
110 void getAnalysisUsage(AnalysisUsage &AU) const override {
111 AU.setPreservesAll();
112 MachineFunctionPass::getAnalysisUsage(AU);
113 AU.addRequired<MachineOptimizationRemarkEmitterPass>();
114 }
115
116 bool runOnMachineFunction(MachineFunction &MF) override {
117 // TODO: We should implement a similar filter for remarks:
118 // -Rpass-func-filter=<regex>
119 if (!isFunctionInPrintList(FunctionName: 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(OptDiag&: Rem);
132 return false;
133 }
134
135 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
152 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(Fmt: "\nOffset: [SP{0}", Vals: (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(Ty: D.SlotTy))
192 << ", Align: " << ore::NV("Align", D.Align)
193 << ", Size: " << ore::NV("Size", ElementCount::get(MinVal: D.Size, Scalable: D.Scalable));
194 }
195
196 void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N,
197 MachineOptimizationRemarkAnalysis &Rem) {
198 std::string Loc =
199 formatv(Fmt: "{0} @ {1}:{2}", Vals: N->getName(), Vals: N->getFilename(), Vals: N->getLine())
200 .str();
201 Rem << "\n " << ore::NV("DataLoc", Loc);
202 }
203
204 StackOffset getStackOffset(const MachineFunction &MF,
205 const MachineFrameInfo &MFI,
206 const TargetFrameLowering *FI, int FrameIdx) {
207 if (!FI)
208 return StackOffset::getFixed(Fixed: MFI.getObjectOffset(ObjectIdx: FrameIdx));
209
210 return FI->getFrameIndexReferenceFromSP(MF, FI: FrameIdx);
211 }
212
213 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(n: NumObj);
228 // initialize slot info
229 for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd();
230 Idx != EndIdx; ++Idx) {
231 if (MFI.isDeadObjectIndex(ObjectIdx: Idx))
232 continue;
233 SlotInfo.emplace_back(args: MFI, args: getStackOffset(MF, MFI, FI, FrameIdx: Idx), args&: Idx);
234 }
235
236 // sort the ordering, to match the actual layout in memory
237 llvm::sort(C&: SlotInfo);
238
239 SlotDbgMap SlotMap = genSlotDbgMapping(MF);
240
241 for (const SlotData &Info : SlotInfo) {
242 emitStackSlotRemark(MF, D: 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.
251 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(X: 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 Val: MO->getPseudoValue());
267 if (!FI)
268 continue;
269 int FrameIdx = FI->getFrameIndex();
270 SmallVector<MachineInstr *> Dbg;
271 MI.collectDebugValues(DbgValues&: Dbg);
272
273 for (MachineInstr *MI : Dbg)
274 SlotDebugMap[FrameIdx].insert(X: MI->getDebugVariable());
275 }
276 }
277 }
278
279 return SlotDebugMap;
280 }
281};
282
283char StackFrameLayoutAnalysisPass::ID = 0;
284} // namespace
285
286char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID;
287INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout",
288 "Stack Frame Layout", false, false)
289
290namespace llvm {
291/// Returns a newly-created StackFrameLayout pass.
292MachineFunctionPass *createStackFrameLayoutAnalysisPass() {
293 return new StackFrameLayoutAnalysisPass();
294}
295
296} // namespace llvm
297