1 //===-- StackFrameLayoutAnalysisPass.cpp
2 //------------------------------------===//
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
8 //===----------------------------------------------------------------------===//
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.
17 //===----------------------------------------------------------------------===//
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"
39 #define DEBUG_TYPE "stack-frame-layout"
43 /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a
46 struct StackFrameLayoutAnalysisPass
: public MachineFunctionPass
{
47 using SlotDbgMap
= SmallDenseMap
<int, SetVector
<const DILocalVariable
*>>;
51 Spill
, // a Spill slot
52 Fixed
, // a Fixed slot (e.g. arguments passed on the stack)
53 VariableSized
, // a variable sized object
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
67 SlotData(const MachineFrameInfo
&MFI
, const StackOffset Offset
,
69 : Slot(Idx
), Size(MFI
.getObjectSize(Idx
)),
70 Align(MFI
.getObjectAlign(Idx
).value()), Offset(Offset
),
71 SlotTy(Invalid
), Scalable(false) {
72 Scalable
= MFI
.getStackID(Idx
) == TargetStackID::ScalableVector
;
73 if (MFI
.isSpillSlotObjectIndex(Idx
))
74 SlotTy
= SlotType::Spill
;
75 else if (MFI
.isFixedObjectIndex(Idx
))
76 SlotTy
= SlotType::Fixed
;
77 else if (MFI
.isVariableSizedObjectIndex(Idx
))
78 SlotTy
= SlotType::VariableSized
;
79 else if (MFI
.hasStackProtectorIndex() &&
80 Idx
== MFI
.getStackProtectorIndex())
81 SlotTy
= SlotType::StackProtector
;
83 SlotTy
= SlotType::Variable
;
86 bool isVarSize() const { return SlotTy
== SlotType::VariableSized
; }
88 // We use this to sort in reverse order, so that the layout is displayed
89 // correctly. Variable sized slots are sorted to the end of the list, as
90 // offsets are currently incorrect for these but they reside at the end of
91 // the stack frame. The Slot index is used to ensure deterministic order
92 // when offsets are equal.
93 bool operator<(const SlotData
&Rhs
) const {
94 return std::make_tuple(!isVarSize(),
95 Offset
.getFixed() + Offset
.getScalable(), Slot
) >
96 std::make_tuple(!Rhs
.isVarSize(),
97 Rhs
.Offset
.getFixed() + Rhs
.Offset
.getScalable(),
102 StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID
) {}
104 StringRef
getPassName() const override
{
105 return "Stack Frame Layout Analysis";
108 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
109 AU
.setPreservesAll();
110 MachineFunctionPass::getAnalysisUsage(AU
);
111 AU
.addRequired
<MachineOptimizationRemarkEmitterPass
>();
114 bool runOnMachineFunction(MachineFunction
&MF
) override
{
115 // TODO: We should implement a similar filter for remarks:
116 // -Rpass-func-filter=<regex>
117 if (!isFunctionInPrintList(MF
.getName()))
120 LLVMContext
&Ctx
= MF
.getFunction().getContext();
121 if (!Ctx
.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE
))
124 MachineOptimizationRemarkAnalysis
Rem(DEBUG_TYPE
, "StackLayout",
125 MF
.getFunction().getSubprogram(),
127 Rem
<< ("\nFunction: " + MF
.getName()).str();
128 emitStackFrameLayoutRemarks(MF
, Rem
);
129 getAnalysis
<MachineOptimizationRemarkEmitterPass
>().getORE().emit(Rem
);
133 std::string
getTypeString(SlotType Ty
) {
135 case SlotType::Spill
:
137 case SlotType::Fixed
:
139 case SlotType::VariableSized
:
140 return "VariableSized";
141 case SlotType::StackProtector
:
143 case SlotType::Variable
:
146 llvm_unreachable("bad slot type for stack layout");
150 void emitStackSlotRemark(const MachineFunction
&MF
, const SlotData
&D
,
151 MachineOptimizationRemarkAnalysis
&Rem
) {
152 // To make it easy to understand the stack layout from the CLI, we want to
153 // print each slot like the following:
155 // Offset: [SP+8], Type: Spill, Align: 8, Size: 16
156 // foo @ /path/to/file.c:25
157 // bar @ /path/to/file.c:35
159 // Which prints the size, alignment, and offset from the SP at function
162 // But we also want the machine readable remarks data to be nicely
163 // organized. So we print some additional data as strings for the CLI
164 // output, but maintain more structured data for the YAML.
166 // For example we store the Offset in YAML as:
169 // - ScalableOffset: -16
170 // Note: the ScalableOffset entries are added only for slots with non-zero
173 // But we print it to the CLI as:
176 // Or with non-zero scalable offset:
177 // Offset: [SP-8-16 x vscale]
179 // Negative offsets will print a leading `-`, so only add `+`
181 formatv("\nOffset: [SP{0}", (D
.Offset
.getFixed() < 0) ? "" : "+").str();
182 Rem
<< Prefix
<< ore::NV("Offset", D
.Offset
.getFixed());
184 if (D
.Offset
.getScalable()) {
185 Rem
<< ((D
.Offset
.getScalable() < 0) ? "" : "+")
186 << ore::NV("ScalableOffset", D
.Offset
.getScalable()) << " x vscale";
189 Rem
<< "], Type: " << ore::NV("Type", getTypeString(D
.SlotTy
))
190 << ", Align: " << ore::NV("Align", D
.Align
)
191 << ", Size: " << ore::NV("Size", ElementCount::get(D
.Size
, D
.Scalable
));
194 void emitSourceLocRemark(const MachineFunction
&MF
, const DILocalVariable
*N
,
195 MachineOptimizationRemarkAnalysis
&Rem
) {
197 formatv("{0} @ {1}:{2}", N
->getName(), N
->getFilename(), N
->getLine())
199 Rem
<< "\n " << ore::NV("DataLoc", Loc
);
202 StackOffset
getStackOffset(const MachineFunction
&MF
,
203 const MachineFrameInfo
&MFI
,
204 const TargetFrameLowering
*FI
, int FrameIdx
) {
206 return StackOffset::getFixed(MFI
.getObjectOffset(FrameIdx
));
208 return FI
->getFrameIndexReferenceFromSP(MF
, FrameIdx
);
211 void emitStackFrameLayoutRemarks(MachineFunction
&MF
,
212 MachineOptimizationRemarkAnalysis
&Rem
) {
213 const MachineFrameInfo
&MFI
= MF
.getFrameInfo();
214 if (!MFI
.hasStackObjects())
217 const TargetFrameLowering
*FI
= MF
.getSubtarget().getFrameLowering();
219 LLVM_DEBUG(dbgs() << "getStackProtectorIndex =="
220 << MFI
.getStackProtectorIndex() << "\n");
222 std::vector
<SlotData
> SlotInfo
;
224 const unsigned int NumObj
= MFI
.getNumObjects();
225 SlotInfo
.reserve(NumObj
);
226 // initialize slot info
227 for (int Idx
= MFI
.getObjectIndexBegin(), EndIdx
= MFI
.getObjectIndexEnd();
228 Idx
!= EndIdx
; ++Idx
) {
229 if (MFI
.isDeadObjectIndex(Idx
))
231 SlotInfo
.emplace_back(MFI
, getStackOffset(MF
, MFI
, FI
, Idx
), Idx
);
234 // sort the ordering, to match the actual layout in memory
235 llvm::sort(SlotInfo
);
237 SlotDbgMap SlotMap
= genSlotDbgMapping(MF
);
239 for (const SlotData
&Info
: SlotInfo
) {
240 emitStackSlotRemark(MF
, Info
, Rem
);
241 for (const DILocalVariable
*N
: SlotMap
[Info
.Slot
])
242 emitSourceLocRemark(MF
, N
, Rem
);
246 // We need to generate a mapping of slots to the values that are stored to
247 // them. This information is lost by the time we need to print out the frame,
248 // so we reconstruct it here by walking the CFG, and generating the mapping.
249 SlotDbgMap
genSlotDbgMapping(MachineFunction
&MF
) {
250 SlotDbgMap SlotDebugMap
;
252 // add variables to the map
253 for (MachineFunction::VariableDbgInfo
&DI
:
254 MF
.getInStackSlotVariableDbgInfo())
255 SlotDebugMap
[DI
.getStackSlot()].insert(DI
.Var
);
257 // Then add all the spills that have debug data
258 for (MachineBasicBlock
&MBB
: MF
) {
259 for (MachineInstr
&MI
: MBB
) {
260 for (MachineMemOperand
*MO
: MI
.memoperands()) {
263 auto *FI
= dyn_cast_or_null
<FixedStackPseudoSourceValue
>(
264 MO
->getPseudoValue());
267 int FrameIdx
= FI
->getFrameIndex();
268 SmallVector
<MachineInstr
*> Dbg
;
269 MI
.collectDebugValues(Dbg
);
271 for (MachineInstr
*MI
: Dbg
)
272 SlotDebugMap
[FrameIdx
].insert(MI
->getDebugVariable());
281 char StackFrameLayoutAnalysisPass::ID
= 0;
284 char &llvm::StackFrameLayoutAnalysisPassID
= StackFrameLayoutAnalysisPass::ID
;
285 INITIALIZE_PASS(StackFrameLayoutAnalysisPass
, "stack-frame-layout",
286 "Stack Frame Layout", false, false)
289 /// Returns a newly-created StackFrameLayout pass.
290 MachineFunctionPass
*createStackFrameLayoutAnalysisPass() {
291 return new StackFrameLayoutAnalysisPass();