1e3b55780SDimitry Andric //===-- StackFrameLayoutAnalysisPass.cpp
2e3b55780SDimitry Andric //------------------------------------===//
3e3b55780SDimitry Andric //
4e3b55780SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5e3b55780SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
6e3b55780SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7e3b55780SDimitry Andric //
8e3b55780SDimitry Andric //===----------------------------------------------------------------------===//
9e3b55780SDimitry Andric //
10e3b55780SDimitry Andric // StackFrameLayoutAnalysisPass implementation. Outputs information about the
11e3b55780SDimitry Andric // layout of the stack frame, using the remarks interface. On the CLI it prints
12e3b55780SDimitry Andric // a textual representation of the stack frame. When possible it prints the
13e3b55780SDimitry Andric // values that occupy a stack slot using any available debug information. Since
14e3b55780SDimitry Andric // output is remarks based, it is also available in a machine readable file
15e3b55780SDimitry Andric // format, such as YAML.
16e3b55780SDimitry Andric //
17e3b55780SDimitry Andric //===----------------------------------------------------------------------===//
18e3b55780SDimitry Andric
19e3b55780SDimitry Andric #include "llvm/ADT/SetVector.h"
20e3b55780SDimitry Andric #include "llvm/Analysis/OptimizationRemarkEmitter.h"
21e3b55780SDimitry Andric #include "llvm/CodeGen/MachineFrameInfo.h"
22e3b55780SDimitry Andric #include "llvm/CodeGen/MachineFunction.h"
23e3b55780SDimitry Andric #include "llvm/CodeGen/MachineFunctionPass.h"
24e3b55780SDimitry Andric #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h"
25e3b55780SDimitry Andric #include "llvm/CodeGen/Passes.h"
26e3b55780SDimitry Andric #include "llvm/CodeGen/SlotIndexes.h"
27e3b55780SDimitry Andric #include "llvm/CodeGen/StackProtector.h"
28e3b55780SDimitry Andric #include "llvm/CodeGen/TargetFrameLowering.h"
29e3b55780SDimitry Andric #include "llvm/CodeGen/TargetSubtargetInfo.h"
30e3b55780SDimitry Andric #include "llvm/IR/DebugInfoMetadata.h"
31e3b55780SDimitry Andric #include "llvm/IR/PrintPasses.h"
32e3b55780SDimitry Andric #include "llvm/InitializePasses.h"
33e3b55780SDimitry Andric #include "llvm/Support/Debug.h"
34e3b55780SDimitry Andric #include "llvm/Support/FormatVariadic.h"
35e3b55780SDimitry Andric #include "llvm/Support/raw_ostream.h"
36e3b55780SDimitry Andric
37e3b55780SDimitry Andric #include <sstream>
38e3b55780SDimitry Andric
39e3b55780SDimitry Andric using namespace llvm;
40e3b55780SDimitry Andric
41e3b55780SDimitry Andric #define DEBUG_TYPE "stack-frame-layout"
42e3b55780SDimitry Andric
43e3b55780SDimitry Andric namespace {
44e3b55780SDimitry Andric
45e3b55780SDimitry Andric /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a
46e3b55780SDimitry Andric /// MachineFunction.
47e3b55780SDimitry Andric ///
48e3b55780SDimitry Andric struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {
49e3b55780SDimitry Andric using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>;
50e3b55780SDimitry Andric static char ID;
51e3b55780SDimitry Andric
52e3b55780SDimitry Andric enum SlotType {
53e3b55780SDimitry Andric Spill, // a Spill slot
549b950333SDimitry Andric Fixed, // a Fixed slot (e.g. arguments passed on the stack)
559b950333SDimitry Andric VariableSized, // a variable sized object
56e3b55780SDimitry Andric StackProtector, // Stack Protector slot
57e3b55780SDimitry Andric Variable, // a slot used to store a local data (could be a tmp)
58e3b55780SDimitry Andric Invalid // It's an error for a slot to have this type
59e3b55780SDimitry Andric };
60e3b55780SDimitry Andric
61e3b55780SDimitry Andric struct SlotData {
62e3b55780SDimitry Andric int Slot;
63e3b55780SDimitry Andric int Size;
64e3b55780SDimitry Andric int Align;
659b950333SDimitry Andric StackOffset Offset;
66e3b55780SDimitry Andric SlotType SlotTy;
67ac9a064cSDimitry Andric bool Scalable;
68e3b55780SDimitry Andric
SlotData__anon35d767e30111::StackFrameLayoutAnalysisPass::SlotData699b950333SDimitry Andric SlotData(const MachineFrameInfo &MFI, const StackOffset Offset,
709b950333SDimitry Andric const int Idx)
71e3b55780SDimitry Andric : Slot(Idx), Size(MFI.getObjectSize(Idx)),
729b950333SDimitry Andric Align(MFI.getObjectAlign(Idx).value()), Offset(Offset),
739b950333SDimitry Andric SlotTy(Invalid), Scalable(false) {
74ac9a064cSDimitry Andric Scalable = MFI.getStackID(Idx) == TargetStackID::ScalableVector;
75e3b55780SDimitry Andric if (MFI.isSpillSlotObjectIndex(Idx))
76e3b55780SDimitry Andric SlotTy = SlotType::Spill;
779b950333SDimitry Andric else if (MFI.isFixedObjectIndex(Idx))
789b950333SDimitry Andric SlotTy = SlotType::Fixed;
799b950333SDimitry Andric else if (MFI.isVariableSizedObjectIndex(Idx))
809b950333SDimitry Andric SlotTy = SlotType::VariableSized;
819b950333SDimitry Andric else if (MFI.hasStackProtectorIndex() &&
829b950333SDimitry Andric Idx == MFI.getStackProtectorIndex())
83e3b55780SDimitry Andric SlotTy = SlotType::StackProtector;
84e3b55780SDimitry Andric else
85e3b55780SDimitry Andric SlotTy = SlotType::Variable;
86e3b55780SDimitry Andric }
87e3b55780SDimitry Andric
isVarSize__anon35d767e30111::StackFrameLayoutAnalysisPass::SlotData889b950333SDimitry Andric bool isVarSize() const { return SlotTy == SlotType::VariableSized; }
899b950333SDimitry Andric
90ac9a064cSDimitry Andric // We use this to sort in reverse order, so that the layout is displayed
919b950333SDimitry Andric // correctly. Variable sized slots are sorted to the end of the list, as
929b950333SDimitry Andric // offsets are currently incorrect for these but they reside at the end of
939b950333SDimitry Andric // the stack frame. The Slot index is used to ensure deterministic order
949b950333SDimitry Andric // when offsets are equal.
operator <__anon35d767e30111::StackFrameLayoutAnalysisPass::SlotData95ac9a064cSDimitry Andric bool operator<(const SlotData &Rhs) const {
969b950333SDimitry Andric return std::make_tuple(!isVarSize(),
979b950333SDimitry Andric Offset.getFixed() + Offset.getScalable(), Slot) >
989b950333SDimitry Andric std::make_tuple(!Rhs.isVarSize(),
999b950333SDimitry Andric Rhs.Offset.getFixed() + Rhs.Offset.getScalable(),
1009b950333SDimitry Andric Rhs.Slot);
101ac9a064cSDimitry Andric }
102e3b55780SDimitry Andric };
103e3b55780SDimitry Andric
StackFrameLayoutAnalysisPass__anon35d767e30111::StackFrameLayoutAnalysisPass104e3b55780SDimitry Andric StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {}
105e3b55780SDimitry Andric
getPassName__anon35d767e30111::StackFrameLayoutAnalysisPass106e3b55780SDimitry Andric StringRef getPassName() const override {
107e3b55780SDimitry Andric return "Stack Frame Layout Analysis";
108e3b55780SDimitry Andric }
109e3b55780SDimitry Andric
getAnalysisUsage__anon35d767e30111::StackFrameLayoutAnalysisPass110e3b55780SDimitry Andric void getAnalysisUsage(AnalysisUsage &AU) const override {
111e3b55780SDimitry Andric AU.setPreservesAll();
112e3b55780SDimitry Andric MachineFunctionPass::getAnalysisUsage(AU);
113e3b55780SDimitry Andric AU.addRequired<MachineOptimizationRemarkEmitterPass>();
114e3b55780SDimitry Andric }
115e3b55780SDimitry Andric
runOnMachineFunction__anon35d767e30111::StackFrameLayoutAnalysisPass116e3b55780SDimitry Andric bool runOnMachineFunction(MachineFunction &MF) override {
117e3b55780SDimitry Andric // TODO: We should implement a similar filter for remarks:
118e3b55780SDimitry Andric // -Rpass-func-filter=<regex>
119e3b55780SDimitry Andric if (!isFunctionInPrintList(MF.getName()))
120e3b55780SDimitry Andric return false;
121e3b55780SDimitry Andric
122e3b55780SDimitry Andric LLVMContext &Ctx = MF.getFunction().getContext();
123e3b55780SDimitry Andric if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE))
124e3b55780SDimitry Andric return false;
125e3b55780SDimitry Andric
126e3b55780SDimitry Andric MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout",
127e3b55780SDimitry Andric MF.getFunction().getSubprogram(),
128e3b55780SDimitry Andric &MF.front());
129e3b55780SDimitry Andric Rem << ("\nFunction: " + MF.getName()).str();
130e3b55780SDimitry Andric emitStackFrameLayoutRemarks(MF, Rem);
131e3b55780SDimitry Andric getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE().emit(Rem);
132e3b55780SDimitry Andric return false;
133e3b55780SDimitry Andric }
134e3b55780SDimitry Andric
getTypeString__anon35d767e30111::StackFrameLayoutAnalysisPass135e3b55780SDimitry Andric std::string getTypeString(SlotType Ty) {
136e3b55780SDimitry Andric switch (Ty) {
137e3b55780SDimitry Andric case SlotType::Spill:
138e3b55780SDimitry Andric return "Spill";
1399b950333SDimitry Andric case SlotType::Fixed:
1409b950333SDimitry Andric return "Fixed";
1419b950333SDimitry Andric case SlotType::VariableSized:
1429b950333SDimitry Andric return "VariableSized";
143e3b55780SDimitry Andric case SlotType::StackProtector:
144e3b55780SDimitry Andric return "Protector";
145e3b55780SDimitry Andric case SlotType::Variable:
146e3b55780SDimitry Andric return "Variable";
147e3b55780SDimitry Andric default:
148e3b55780SDimitry Andric llvm_unreachable("bad slot type for stack layout");
149e3b55780SDimitry Andric }
150e3b55780SDimitry Andric }
151e3b55780SDimitry Andric
emitStackSlotRemark__anon35d767e30111::StackFrameLayoutAnalysisPass152e3b55780SDimitry Andric void emitStackSlotRemark(const MachineFunction &MF, const SlotData &D,
153e3b55780SDimitry Andric MachineOptimizationRemarkAnalysis &Rem) {
154e3b55780SDimitry Andric // To make it easy to understand the stack layout from the CLI, we want to
155e3b55780SDimitry Andric // print each slot like the following:
156e3b55780SDimitry Andric //
157e3b55780SDimitry Andric // Offset: [SP+8], Type: Spill, Align: 8, Size: 16
158e3b55780SDimitry Andric // foo @ /path/to/file.c:25
159e3b55780SDimitry Andric // bar @ /path/to/file.c:35
160e3b55780SDimitry Andric //
161e3b55780SDimitry Andric // Which prints the size, alignment, and offset from the SP at function
162e3b55780SDimitry Andric // entry.
163e3b55780SDimitry Andric //
164e3b55780SDimitry Andric // But we also want the machine readable remarks data to be nicely
165e3b55780SDimitry Andric // organized. So we print some additional data as strings for the CLI
166e3b55780SDimitry Andric // output, but maintain more structured data for the YAML.
167e3b55780SDimitry Andric //
168e3b55780SDimitry Andric // For example we store the Offset in YAML as:
169e3b55780SDimitry Andric // ...
170e3b55780SDimitry Andric // - Offset: -8
1719b950333SDimitry Andric // - ScalableOffset: -16
1729b950333SDimitry Andric // Note: the ScalableOffset entries are added only for slots with non-zero
1739b950333SDimitry Andric // scalable offsets.
174e3b55780SDimitry Andric //
1759b950333SDimitry Andric // But we print it to the CLI as:
176e3b55780SDimitry Andric // Offset: [SP-8]
1779b950333SDimitry Andric //
1789b950333SDimitry Andric // Or with non-zero scalable offset:
1799b950333SDimitry Andric // Offset: [SP-8-16 x vscale]
180e3b55780SDimitry Andric
181e3b55780SDimitry Andric // Negative offsets will print a leading `-`, so only add `+`
182e3b55780SDimitry Andric std::string Prefix =
1839b950333SDimitry Andric formatv("\nOffset: [SP{0}", (D.Offset.getFixed() < 0) ? "" : "+").str();
1849b950333SDimitry Andric Rem << Prefix << ore::NV("Offset", D.Offset.getFixed());
1859b950333SDimitry Andric
1869b950333SDimitry Andric if (D.Offset.getScalable()) {
1879b950333SDimitry Andric Rem << ((D.Offset.getScalable() < 0) ? "" : "+")
1889b950333SDimitry Andric << ore::NV("ScalableOffset", D.Offset.getScalable()) << " x vscale";
1899b950333SDimitry Andric }
1909b950333SDimitry Andric
1919b950333SDimitry Andric Rem << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy))
192e3b55780SDimitry Andric << ", Align: " << ore::NV("Align", D.Align)
193ac9a064cSDimitry Andric << ", Size: " << ore::NV("Size", ElementCount::get(D.Size, D.Scalable));
194e3b55780SDimitry Andric }
195e3b55780SDimitry Andric
emitSourceLocRemark__anon35d767e30111::StackFrameLayoutAnalysisPass196e3b55780SDimitry Andric void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N,
197e3b55780SDimitry Andric MachineOptimizationRemarkAnalysis &Rem) {
198e3b55780SDimitry Andric std::string Loc =
199e3b55780SDimitry Andric formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine())
200e3b55780SDimitry Andric .str();
201e3b55780SDimitry Andric Rem << "\n " << ore::NV("DataLoc", Loc);
202e3b55780SDimitry Andric }
203e3b55780SDimitry Andric
getStackOffset__anon35d767e30111::StackFrameLayoutAnalysisPass2049b950333SDimitry Andric StackOffset getStackOffset(const MachineFunction &MF,
2059b950333SDimitry Andric const MachineFrameInfo &MFI,
2069b950333SDimitry Andric const TargetFrameLowering *FI, int FrameIdx) {
2079b950333SDimitry Andric if (!FI)
2089b950333SDimitry Andric return StackOffset::getFixed(MFI.getObjectOffset(FrameIdx));
2099b950333SDimitry Andric
2109b950333SDimitry Andric return FI->getFrameIndexReferenceFromSP(MF, FrameIdx);
2119b950333SDimitry Andric }
2129b950333SDimitry Andric
emitStackFrameLayoutRemarks__anon35d767e30111::StackFrameLayoutAnalysisPass213e3b55780SDimitry Andric void emitStackFrameLayoutRemarks(MachineFunction &MF,
214e3b55780SDimitry Andric MachineOptimizationRemarkAnalysis &Rem) {
215e3b55780SDimitry Andric const MachineFrameInfo &MFI = MF.getFrameInfo();
216e3b55780SDimitry Andric if (!MFI.hasStackObjects())
217e3b55780SDimitry Andric return;
218e3b55780SDimitry Andric
219e3b55780SDimitry Andric const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering();
220e3b55780SDimitry Andric
221e3b55780SDimitry Andric LLVM_DEBUG(dbgs() << "getStackProtectorIndex =="
222e3b55780SDimitry Andric << MFI.getStackProtectorIndex() << "\n");
223e3b55780SDimitry Andric
224e3b55780SDimitry Andric std::vector<SlotData> SlotInfo;
225e3b55780SDimitry Andric
226e3b55780SDimitry Andric const unsigned int NumObj = MFI.getNumObjects();
227e3b55780SDimitry Andric SlotInfo.reserve(NumObj);
228e3b55780SDimitry Andric // initialize slot info
229e3b55780SDimitry Andric for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd();
230e3b55780SDimitry Andric Idx != EndIdx; ++Idx) {
231e3b55780SDimitry Andric if (MFI.isDeadObjectIndex(Idx))
232e3b55780SDimitry Andric continue;
2339b950333SDimitry Andric SlotInfo.emplace_back(MFI, getStackOffset(MF, MFI, FI, Idx), Idx);
234e3b55780SDimitry Andric }
235e3b55780SDimitry Andric
236e3b55780SDimitry Andric // sort the ordering, to match the actual layout in memory
237e3b55780SDimitry Andric llvm::sort(SlotInfo);
238e3b55780SDimitry Andric
239e3b55780SDimitry Andric SlotDbgMap SlotMap = genSlotDbgMapping(MF);
240e3b55780SDimitry Andric
241e3b55780SDimitry Andric for (const SlotData &Info : SlotInfo) {
242e3b55780SDimitry Andric emitStackSlotRemark(MF, Info, Rem);
243e3b55780SDimitry Andric for (const DILocalVariable *N : SlotMap[Info.Slot])
244e3b55780SDimitry Andric emitSourceLocRemark(MF, N, Rem);
245e3b55780SDimitry Andric }
246e3b55780SDimitry Andric }
247e3b55780SDimitry Andric
248e3b55780SDimitry Andric // We need to generate a mapping of slots to the values that are stored to
249e3b55780SDimitry Andric // them. This information is lost by the time we need to print out the frame,
250e3b55780SDimitry Andric // so we reconstruct it here by walking the CFG, and generating the mapping.
genSlotDbgMapping__anon35d767e30111::StackFrameLayoutAnalysisPass251e3b55780SDimitry Andric SlotDbgMap genSlotDbgMapping(MachineFunction &MF) {
252e3b55780SDimitry Andric SlotDbgMap SlotDebugMap;
253e3b55780SDimitry Andric
254e3b55780SDimitry Andric // add variables to the map
2557fa27ce4SDimitry Andric for (MachineFunction::VariableDbgInfo &DI :
2567fa27ce4SDimitry Andric MF.getInStackSlotVariableDbgInfo())
2577fa27ce4SDimitry Andric SlotDebugMap[DI.getStackSlot()].insert(DI.Var);
258e3b55780SDimitry Andric
259e3b55780SDimitry Andric // Then add all the spills that have debug data
260e3b55780SDimitry Andric for (MachineBasicBlock &MBB : MF) {
261e3b55780SDimitry Andric for (MachineInstr &MI : MBB) {
262e3b55780SDimitry Andric for (MachineMemOperand *MO : MI.memoperands()) {
263e3b55780SDimitry Andric if (!MO->isStore())
264e3b55780SDimitry Andric continue;
265e3b55780SDimitry Andric auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>(
266e3b55780SDimitry Andric MO->getPseudoValue());
267e3b55780SDimitry Andric if (!FI)
268e3b55780SDimitry Andric continue;
269e3b55780SDimitry Andric int FrameIdx = FI->getFrameIndex();
270e3b55780SDimitry Andric SmallVector<MachineInstr *> Dbg;
271e3b55780SDimitry Andric MI.collectDebugValues(Dbg);
272e3b55780SDimitry Andric
273e3b55780SDimitry Andric for (MachineInstr *MI : Dbg)
274e3b55780SDimitry Andric SlotDebugMap[FrameIdx].insert(MI->getDebugVariable());
275e3b55780SDimitry Andric }
276e3b55780SDimitry Andric }
277e3b55780SDimitry Andric }
278e3b55780SDimitry Andric
279e3b55780SDimitry Andric return SlotDebugMap;
280e3b55780SDimitry Andric }
281e3b55780SDimitry Andric };
282e3b55780SDimitry Andric
283e3b55780SDimitry Andric char StackFrameLayoutAnalysisPass::ID = 0;
284e3b55780SDimitry Andric } // namespace
285e3b55780SDimitry Andric
286e3b55780SDimitry Andric char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID;
287e3b55780SDimitry Andric INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout",
288e3b55780SDimitry Andric "Stack Frame Layout", false, false)
289e3b55780SDimitry Andric
290e3b55780SDimitry Andric namespace llvm {
291e3b55780SDimitry Andric /// Returns a newly-created StackFrameLayout pass.
createStackFrameLayoutAnalysisPass()292e3b55780SDimitry Andric MachineFunctionPass *createStackFrameLayoutAnalysisPass() {
293e3b55780SDimitry Andric return new StackFrameLayoutAnalysisPass();
294e3b55780SDimitry Andric }
295e3b55780SDimitry Andric
296e3b55780SDimitry Andric } // namespace llvm
297