1bdd1243dSDimitry Andric //===-- StackFrameLayoutAnalysisPass.cpp
2bdd1243dSDimitry Andric //------------------------------------===//
3bdd1243dSDimitry Andric //
4bdd1243dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5bdd1243dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
6bdd1243dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7bdd1243dSDimitry Andric //
8bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
9bdd1243dSDimitry Andric //
10bdd1243dSDimitry Andric // StackFrameLayoutAnalysisPass implementation. Outputs information about the
11bdd1243dSDimitry Andric // layout of the stack frame, using the remarks interface. On the CLI it prints
12bdd1243dSDimitry Andric // a textual representation of the stack frame. When possible it prints the
13bdd1243dSDimitry Andric // values that occupy a stack slot using any available debug information. Since
14bdd1243dSDimitry Andric // output is remarks based, it is also available in a machine readable file
15bdd1243dSDimitry Andric // format, such as YAML.
16bdd1243dSDimitry Andric //
17bdd1243dSDimitry Andric //===----------------------------------------------------------------------===//
18bdd1243dSDimitry Andric
19bdd1243dSDimitry Andric #include "llvm/ADT/SetVector.h"
20bdd1243dSDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h"
21bdd1243dSDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h"
22bdd1243dSDimitry Andric #include "llvm/CodeGen/MachineFunction.h"
23bdd1243dSDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
24bdd1243dSDimitry Andric #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
25bdd1243dSDimitry Andric #include "llvm/CodeGen/Passes.h"
26bdd1243dSDimitry Andric #include "llvm/CodeGen/SlotIndexes.h"
27bdd1243dSDimitry Andric #include "llvm/CodeGen/StackProtector.h"
28bdd1243dSDimitry Andric #include "llvm/CodeGen/TargetFrameLowering.h"
29bdd1243dSDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
30bdd1243dSDimitry Andric #include "llvm/IR/DebugInfoMetadata.h"
31bdd1243dSDimitry Andric #include "llvm/IR/PrintPasses.h"
32bdd1243dSDimitry Andric #include "llvm/InitializePasses.h"
33bdd1243dSDimitry Andric #include "llvm/Support/Debug.h"
34bdd1243dSDimitry Andric #include "llvm/Support/FormatVariadic.h"
35bdd1243dSDimitry Andric #include "llvm/Support/raw_ostream.h"
36bdd1243dSDimitry Andric
37bdd1243dSDimitry Andric #include <sstream>
38bdd1243dSDimitry Andric
39bdd1243dSDimitry Andric using namespace llvm;
40bdd1243dSDimitry Andric
41bdd1243dSDimitry Andric #define DEBUG_TYPE "stack-frame-layout"
42bdd1243dSDimitry Andric
43bdd1243dSDimitry Andric namespace {
44bdd1243dSDimitry Andric
45bdd1243dSDimitry Andric /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a
46bdd1243dSDimitry Andric /// MachineFunction.
47bdd1243dSDimitry Andric ///
48bdd1243dSDimitry Andric struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {
49bdd1243dSDimitry Andric using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>;
50bdd1243dSDimitry Andric static char ID;
51bdd1243dSDimitry Andric
52bdd1243dSDimitry Andric enum SlotType {
53bdd1243dSDimitry Andric Spill, // a Spill slot
54bdd1243dSDimitry Andric StackProtector, // Stack Protector slot
55bdd1243dSDimitry Andric Variable, // a slot used to store a local data (could be a tmp)
56bdd1243dSDimitry Andric Invalid // It's an error for a slot to have this type
57bdd1243dSDimitry Andric };
58bdd1243dSDimitry Andric
59bdd1243dSDimitry Andric struct SlotData {
60bdd1243dSDimitry Andric int Slot;
61bdd1243dSDimitry Andric int Size;
62bdd1243dSDimitry Andric int Align;
63bdd1243dSDimitry Andric int Offset;
64bdd1243dSDimitry Andric SlotType SlotTy;
65bdd1243dSDimitry Andric
SlotData__anond1383e840111::StackFrameLayoutAnalysisPass::SlotData66bdd1243dSDimitry Andric SlotData(const MachineFrameInfo &MFI, const int ValOffset, const int Idx)
67bdd1243dSDimitry Andric : Slot(Idx), Size(MFI.getObjectSize(Idx)),
68bdd1243dSDimitry Andric Align(MFI.getObjectAlign(Idx).value()),
69bdd1243dSDimitry Andric Offset(MFI.getObjectOffset(Idx) - ValOffset), SlotTy(Invalid) {
70bdd1243dSDimitry Andric if (MFI.isSpillSlotObjectIndex(Idx))
71bdd1243dSDimitry Andric SlotTy = SlotType::Spill;
72bdd1243dSDimitry Andric else if (Idx == MFI.getStackProtectorIndex())
73bdd1243dSDimitry Andric SlotTy = SlotType::StackProtector;
74bdd1243dSDimitry Andric else
75bdd1243dSDimitry Andric SlotTy = SlotType::Variable;
76bdd1243dSDimitry Andric }
77bdd1243dSDimitry Andric
78bdd1243dSDimitry Andric // we use this to sort in reverse order, so that the layout is displayed
79bdd1243dSDimitry Andric // correctly
operator <__anond1383e840111::StackFrameLayoutAnalysisPass::SlotData80bdd1243dSDimitry Andric bool operator<(const SlotData &Rhs) const { return Offset > Rhs.Offset; }
81bdd1243dSDimitry Andric };
82bdd1243dSDimitry Andric
StackFrameLayoutAnalysisPass__anond1383e840111::StackFrameLayoutAnalysisPass83bdd1243dSDimitry Andric StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {}
84bdd1243dSDimitry Andric
getPassName__anond1383e840111::StackFrameLayoutAnalysisPass85bdd1243dSDimitry Andric StringRef getPassName() const override {
86bdd1243dSDimitry Andric return "Stack Frame Layout Analysis";
87bdd1243dSDimitry Andric }
88bdd1243dSDimitry Andric
getAnalysisUsage__anond1383e840111::StackFrameLayoutAnalysisPass89bdd1243dSDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override {
90bdd1243dSDimitry Andric AU.setPreservesAll();
91bdd1243dSDimitry Andric MachineFunctionPass::getAnalysisUsage(AU);
92bdd1243dSDimitry Andric AU.addRequired<MachineOptimizationRemarkEmitterPass>();
93bdd1243dSDimitry Andric }
94bdd1243dSDimitry Andric
runOnMachineFunction__anond1383e840111::StackFrameLayoutAnalysisPass95bdd1243dSDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override {
96bdd1243dSDimitry Andric // TODO: We should implement a similar filter for remarks:
97bdd1243dSDimitry Andric // -Rpass-func-filter=<regex>
98bdd1243dSDimitry Andric if (!isFunctionInPrintList(MF.getName()))
99bdd1243dSDimitry Andric return false;
100bdd1243dSDimitry Andric
101bdd1243dSDimitry Andric LLVMContext &Ctx = MF.getFunction().getContext();
102bdd1243dSDimitry Andric if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE))
103bdd1243dSDimitry Andric return false;
104bdd1243dSDimitry Andric
105bdd1243dSDimitry Andric MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout",
106bdd1243dSDimitry Andric MF.getFunction().getSubprogram(),
107bdd1243dSDimitry Andric &MF.front());
108bdd1243dSDimitry Andric Rem << ("\nFunction: " + MF.getName()).str();
109bdd1243dSDimitry Andric emitStackFrameLayoutRemarks(MF, Rem);
110bdd1243dSDimitry Andric getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE().emit(Rem);
111bdd1243dSDimitry Andric return false;
112bdd1243dSDimitry Andric }
113bdd1243dSDimitry Andric
getTypeString__anond1383e840111::StackFrameLayoutAnalysisPass114bdd1243dSDimitry Andric std::string getTypeString(SlotType Ty) {
115bdd1243dSDimitry Andric switch (Ty) {
116bdd1243dSDimitry Andric case SlotType::Spill:
117bdd1243dSDimitry Andric return "Spill";
118bdd1243dSDimitry Andric case SlotType::StackProtector:
119bdd1243dSDimitry Andric return "Protector";
120bdd1243dSDimitry Andric case SlotType::Variable:
121bdd1243dSDimitry Andric return "Variable";
122bdd1243dSDimitry Andric default:
123bdd1243dSDimitry Andric llvm_unreachable("bad slot type for stack layout");
124bdd1243dSDimitry Andric }
125bdd1243dSDimitry Andric }
126bdd1243dSDimitry Andric
emitStackSlotRemark__anond1383e840111::StackFrameLayoutAnalysisPass127bdd1243dSDimitry Andric void emitStackSlotRemark(const MachineFunction &MF, const SlotData &D,
128bdd1243dSDimitry Andric MachineOptimizationRemarkAnalysis &Rem) {
129bdd1243dSDimitry Andric // To make it easy to understand the stack layout from the CLI, we want to
130bdd1243dSDimitry Andric // print each slot like the following:
131bdd1243dSDimitry Andric //
132bdd1243dSDimitry Andric // Offset: [SP+8], Type: Spill, Align: 8, Size: 16
133bdd1243dSDimitry Andric // foo @ /path/to/file.c:25
134bdd1243dSDimitry Andric // bar @ /path/to/file.c:35
135bdd1243dSDimitry Andric //
136bdd1243dSDimitry Andric // Which prints the size, alignment, and offset from the SP at function
137bdd1243dSDimitry Andric // entry.
138bdd1243dSDimitry Andric //
139bdd1243dSDimitry Andric // But we also want the machine readable remarks data to be nicely
140bdd1243dSDimitry Andric // organized. So we print some additional data as strings for the CLI
141bdd1243dSDimitry Andric // output, but maintain more structured data for the YAML.
142bdd1243dSDimitry Andric //
143bdd1243dSDimitry Andric // For example we store the Offset in YAML as:
144bdd1243dSDimitry Andric // ...
145bdd1243dSDimitry Andric // - Offset: -8
146bdd1243dSDimitry Andric //
147bdd1243dSDimitry Andric // But we print it to the CLI as
148bdd1243dSDimitry Andric // Offset: [SP-8]
149bdd1243dSDimitry Andric
150bdd1243dSDimitry Andric // Negative offsets will print a leading `-`, so only add `+`
151bdd1243dSDimitry Andric std::string Prefix =
152bdd1243dSDimitry Andric formatv("\nOffset: [SP{0}", (D.Offset < 0) ? "" : "+").str();
153bdd1243dSDimitry Andric Rem << Prefix << ore::NV("Offset", D.Offset)
154bdd1243dSDimitry Andric << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy))
155bdd1243dSDimitry Andric << ", Align: " << ore::NV("Align", D.Align)
156bdd1243dSDimitry Andric << ", Size: " << ore::NV("Size", D.Size);
157bdd1243dSDimitry Andric }
158bdd1243dSDimitry Andric
emitSourceLocRemark__anond1383e840111::StackFrameLayoutAnalysisPass159bdd1243dSDimitry Andric void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N,
160bdd1243dSDimitry Andric MachineOptimizationRemarkAnalysis &Rem) {
161bdd1243dSDimitry Andric std::string Loc =
162bdd1243dSDimitry Andric formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine())
163bdd1243dSDimitry Andric .str();
164bdd1243dSDimitry Andric Rem << "\n " << ore::NV("DataLoc", Loc);
165bdd1243dSDimitry Andric }
166bdd1243dSDimitry Andric
emitStackFrameLayoutRemarks__anond1383e840111::StackFrameLayoutAnalysisPass167bdd1243dSDimitry Andric void emitStackFrameLayoutRemarks(MachineFunction &MF,
168bdd1243dSDimitry Andric MachineOptimizationRemarkAnalysis &Rem) {
169bdd1243dSDimitry Andric const MachineFrameInfo &MFI = MF.getFrameInfo();
170bdd1243dSDimitry Andric if (!MFI.hasStackObjects())
171bdd1243dSDimitry Andric return;
172bdd1243dSDimitry Andric
173bdd1243dSDimitry Andric // ValOffset is the offset to the local area from the SP at function entry.
174bdd1243dSDimitry Andric // To display the true offset from SP, we need to subtract ValOffset from
175bdd1243dSDimitry Andric // MFI's ObjectOffset.
176bdd1243dSDimitry Andric const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering();
177bdd1243dSDimitry Andric const int ValOffset = (FI ? FI->getOffsetOfLocalArea() : 0);
178bdd1243dSDimitry Andric
179bdd1243dSDimitry Andric LLVM_DEBUG(dbgs() << "getStackProtectorIndex =="
180bdd1243dSDimitry Andric << MFI.getStackProtectorIndex() << "\n");
181bdd1243dSDimitry Andric
182bdd1243dSDimitry Andric std::vector<SlotData> SlotInfo;
183bdd1243dSDimitry Andric
184bdd1243dSDimitry Andric const unsigned int NumObj = MFI.getNumObjects();
185bdd1243dSDimitry Andric SlotInfo.reserve(NumObj);
186bdd1243dSDimitry Andric // initialize slot info
187bdd1243dSDimitry Andric for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd();
188bdd1243dSDimitry Andric Idx != EndIdx; ++Idx) {
189bdd1243dSDimitry Andric if (MFI.isDeadObjectIndex(Idx))
190bdd1243dSDimitry Andric continue;
191bdd1243dSDimitry Andric SlotInfo.emplace_back(MFI, ValOffset, Idx);
192bdd1243dSDimitry Andric }
193bdd1243dSDimitry Andric
194bdd1243dSDimitry Andric // sort the ordering, to match the actual layout in memory
195bdd1243dSDimitry Andric llvm::sort(SlotInfo);
196bdd1243dSDimitry Andric
197bdd1243dSDimitry Andric SlotDbgMap SlotMap = genSlotDbgMapping(MF);
198bdd1243dSDimitry Andric
199bdd1243dSDimitry Andric for (const SlotData &Info : SlotInfo) {
200bdd1243dSDimitry Andric emitStackSlotRemark(MF, Info, Rem);
201bdd1243dSDimitry Andric for (const DILocalVariable *N : SlotMap[Info.Slot])
202bdd1243dSDimitry Andric emitSourceLocRemark(MF, N, Rem);
203bdd1243dSDimitry Andric }
204bdd1243dSDimitry Andric }
205bdd1243dSDimitry Andric
206bdd1243dSDimitry Andric // We need to generate a mapping of slots to the values that are stored to
207bdd1243dSDimitry Andric // them. This information is lost by the time we need to print out the frame,
208bdd1243dSDimitry Andric // so we reconstruct it here by walking the CFG, and generating the mapping.
genSlotDbgMapping__anond1383e840111::StackFrameLayoutAnalysisPass209bdd1243dSDimitry Andric SlotDbgMap genSlotDbgMapping(MachineFunction &MF) {
210bdd1243dSDimitry Andric SlotDbgMap SlotDebugMap;
211bdd1243dSDimitry Andric
212bdd1243dSDimitry Andric // add variables to the map
213*fe013be4SDimitry Andric for (MachineFunction::VariableDbgInfo &DI :
214*fe013be4SDimitry Andric MF.getInStackSlotVariableDbgInfo())
215*fe013be4SDimitry Andric SlotDebugMap[DI.getStackSlot()].insert(DI.Var);
216bdd1243dSDimitry Andric
217bdd1243dSDimitry Andric // Then add all the spills that have debug data
218bdd1243dSDimitry Andric for (MachineBasicBlock &MBB : MF) {
219bdd1243dSDimitry Andric for (MachineInstr &MI : MBB) {
220bdd1243dSDimitry Andric for (MachineMemOperand *MO : MI.memoperands()) {
221bdd1243dSDimitry Andric if (!MO->isStore())
222bdd1243dSDimitry Andric continue;
223bdd1243dSDimitry Andric auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>(
224bdd1243dSDimitry Andric MO->getPseudoValue());
225bdd1243dSDimitry Andric if (!FI)
226bdd1243dSDimitry Andric continue;
227bdd1243dSDimitry Andric int FrameIdx = FI->getFrameIndex();
228bdd1243dSDimitry Andric SmallVector<MachineInstr *> Dbg;
229bdd1243dSDimitry Andric MI.collectDebugValues(Dbg);
230bdd1243dSDimitry Andric
231bdd1243dSDimitry Andric for (MachineInstr *MI : Dbg)
232bdd1243dSDimitry Andric SlotDebugMap[FrameIdx].insert(MI->getDebugVariable());
233bdd1243dSDimitry Andric }
234bdd1243dSDimitry Andric }
235bdd1243dSDimitry Andric }
236bdd1243dSDimitry Andric
237bdd1243dSDimitry Andric return SlotDebugMap;
238bdd1243dSDimitry Andric }
239bdd1243dSDimitry Andric };
240bdd1243dSDimitry Andric
241bdd1243dSDimitry Andric char StackFrameLayoutAnalysisPass::ID = 0;
242bdd1243dSDimitry Andric } // namespace
243bdd1243dSDimitry Andric
244bdd1243dSDimitry Andric char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID;
245bdd1243dSDimitry Andric INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout",
246bdd1243dSDimitry Andric "Stack Frame Layout", false, false)
247bdd1243dSDimitry Andric
248bdd1243dSDimitry Andric namespace llvm {
249bdd1243dSDimitry Andric /// Returns a newly-created StackFrameLayout pass.
createStackFrameLayoutAnalysisPass()250bdd1243dSDimitry Andric MachineFunctionPass *createStackFrameLayoutAnalysisPass() {
251bdd1243dSDimitry Andric return new StackFrameLayoutAnalysisPass();
252bdd1243dSDimitry Andric }
253bdd1243dSDimitry Andric
254bdd1243dSDimitry Andric } // namespace llvm
255