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"
41 #define DEBUG_TYPE "stack-frame-layout"
45 /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a
48 struct StackFrameLayoutAnalysisPass
: public MachineFunctionPass
{
49 using SlotDbgMap
= SmallDenseMap
<int, SetVector
<const DILocalVariable
*>>;
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
69 SlotData(const MachineFrameInfo
&MFI
, const StackOffset Offset
,
71 : Slot(Idx
), Size(MFI
.getObjectSize(Idx
)),
72 Align(MFI
.getObjectAlign(Idx
).value()), Offset(Offset
),
73 SlotTy(Invalid
), Scalable(false) {
74 Scalable
= MFI
.getStackID(Idx
) == TargetStackID::ScalableVector
;
75 if (MFI
.isSpillSlotObjectIndex(Idx
))
76 SlotTy
= SlotType::Spill
;
77 else if (MFI
.isFixedObjectIndex(Idx
))
78 SlotTy
= SlotType::Fixed
;
79 else if (MFI
.isVariableSizedObjectIndex(Idx
))
80 SlotTy
= SlotType::VariableSized
;
81 else if (MFI
.hasStackProtectorIndex() &&
82 Idx
== MFI
.getStackProtectorIndex())
83 SlotTy
= SlotType::StackProtector
;
85 SlotTy
= SlotType::Variable
;
88 bool isVarSize() const { return SlotTy
== SlotType::VariableSized
; }
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(!isVarSize(),
97 Offset
.getFixed() + Offset
.getScalable(), Slot
) >
98 std::make_tuple(!Rhs
.isVarSize(),
99 Rhs
.Offset
.getFixed() + Rhs
.Offset
.getScalable(),
104 StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID
) {}
106 StringRef
getPassName() const override
{
107 return "Stack Frame Layout Analysis";
110 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
111 AU
.setPreservesAll();
112 MachineFunctionPass::getAnalysisUsage(AU
);
113 AU
.addRequired
<MachineOptimizationRemarkEmitterPass
>();
116 bool runOnMachineFunction(MachineFunction
&MF
) override
{
117 // TODO: We should implement a similar filter for remarks:
118 // -Rpass-func-filter=<regex>
119 if (!isFunctionInPrintList(MF
.getName()))
122 LLVMContext
&Ctx
= MF
.getFunction().getContext();
123 if (!Ctx
.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE
))
126 MachineOptimizationRemarkAnalysis
Rem(DEBUG_TYPE
, "StackLayout",
127 MF
.getFunction().getSubprogram(),
129 Rem
<< ("\nFunction: " + MF
.getName()).str();
130 emitStackFrameLayoutRemarks(MF
, Rem
);
131 getAnalysis
<MachineOptimizationRemarkEmitterPass
>().getORE().emit(Rem
);
135 std::string
getTypeString(SlotType Ty
) {
137 case SlotType::Spill
:
139 case SlotType::Fixed
:
141 case SlotType::VariableSized
:
142 return "VariableSized";
143 case SlotType::StackProtector
:
145 case SlotType::Variable
:
148 llvm_unreachable("bad slot type for stack layout");
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:
157 // Offset: [SP+8], Type: Spill, Align: 8, Size: 16
158 // foo @ /path/to/file.c:25
159 // bar @ /path/to/file.c:35
161 // Which prints the size, alignment, and offset from the SP at function
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.
168 // For example we store the Offset in YAML as:
171 // - ScalableOffset: -16
172 // Note: the ScalableOffset entries are added only for slots with non-zero
175 // But we print it to the CLI as:
178 // Or with non-zero scalable offset:
179 // Offset: [SP-8-16 x vscale]
181 // Negative offsets will print a leading `-`, so only add `+`
183 formatv("\nOffset: [SP{0}", (D
.Offset
.getFixed() < 0) ? "" : "+").str();
184 Rem
<< Prefix
<< ore::NV("Offset", D
.Offset
.getFixed());
186 if (D
.Offset
.getScalable()) {
187 Rem
<< ((D
.Offset
.getScalable() < 0) ? "" : "+")
188 << ore::NV("ScalableOffset", D
.Offset
.getScalable()) << " x vscale";
191 Rem
<< "], Type: " << ore::NV("Type", getTypeString(D
.SlotTy
))
192 << ", Align: " << ore::NV("Align", D
.Align
)
193 << ", Size: " << ore::NV("Size", ElementCount::get(D
.Size
, D
.Scalable
));
196 void emitSourceLocRemark(const MachineFunction
&MF
, const DILocalVariable
*N
,
197 MachineOptimizationRemarkAnalysis
&Rem
) {
199 formatv("{0} @ {1}:{2}", N
->getName(), N
->getFilename(), N
->getLine())
201 Rem
<< "\n " << ore::NV("DataLoc", Loc
);
204 StackOffset
getStackOffset(const MachineFunction
&MF
,
205 const MachineFrameInfo
&MFI
,
206 const TargetFrameLowering
*FI
, int FrameIdx
) {
208 return StackOffset::getFixed(MFI
.getObjectOffset(FrameIdx
));
210 return FI
->getFrameIndexReferenceFromSP(MF
, FrameIdx
);
213 void emitStackFrameLayoutRemarks(MachineFunction
&MF
,
214 MachineOptimizationRemarkAnalysis
&Rem
) {
215 const MachineFrameInfo
&MFI
= MF
.getFrameInfo();
216 if (!MFI
.hasStackObjects())
219 const TargetFrameLowering
*FI
= MF
.getSubtarget().getFrameLowering();
221 LLVM_DEBUG(dbgs() << "getStackProtectorIndex =="
222 << MFI
.getStackProtectorIndex() << "\n");
224 std::vector
<SlotData
> SlotInfo
;
226 const unsigned int NumObj
= MFI
.getNumObjects();
227 SlotInfo
.reserve(NumObj
);
228 // initialize slot info
229 for (int Idx
= MFI
.getObjectIndexBegin(), EndIdx
= MFI
.getObjectIndexEnd();
230 Idx
!= EndIdx
; ++Idx
) {
231 if (MFI
.isDeadObjectIndex(Idx
))
233 SlotInfo
.emplace_back(MFI
, getStackOffset(MF
, MFI
, FI
, Idx
), Idx
);
236 // sort the ordering, to match the actual layout in memory
237 llvm::sort(SlotInfo
);
239 SlotDbgMap SlotMap
= genSlotDbgMapping(MF
);
241 for (const SlotData
&Info
: SlotInfo
) {
242 emitStackSlotRemark(MF
, Info
, Rem
);
243 for (const DILocalVariable
*N
: SlotMap
[Info
.Slot
])
244 emitSourceLocRemark(MF
, N
, Rem
);
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
;
254 // add variables to the map
255 for (MachineFunction::VariableDbgInfo
&DI
:
256 MF
.getInStackSlotVariableDbgInfo())
257 SlotDebugMap
[DI
.getStackSlot()].insert(DI
.Var
);
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()) {
265 auto *FI
= dyn_cast_or_null
<FixedStackPseudoSourceValue
>(
266 MO
->getPseudoValue());
269 int FrameIdx
= FI
->getFrameIndex();
270 SmallVector
<MachineInstr
*> Dbg
;
271 MI
.collectDebugValues(Dbg
);
273 for (MachineInstr
*MI
: Dbg
)
274 SlotDebugMap
[FrameIdx
].insert(MI
->getDebugVariable());
283 char StackFrameLayoutAnalysisPass::ID
= 0;
286 char &llvm::StackFrameLayoutAnalysisPassID
= StackFrameLayoutAnalysisPass::ID
;
287 INITIALIZE_PASS(StackFrameLayoutAnalysisPass
, "stack-frame-layout",
288 "Stack Frame Layout", false, false)
291 /// Returns a newly-created StackFrameLayout pass.
292 MachineFunctionPass
*createStackFrameLayoutAnalysisPass() {
293 return new StackFrameLayoutAnalysisPass();