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/CodeGen/StackFrameLayoutAnalysisPass.h" 20 #include "llvm/ADT/SetVector.h" 21 #include "llvm/Analysis/OptimizationRemarkEmitter.h" 22 #include "llvm/CodeGen/MachineFrameInfo.h" 23 #include "llvm/CodeGen/MachineFunction.h" 24 #include "llvm/CodeGen/MachineFunctionPass.h" 25 #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h" 26 #include "llvm/CodeGen/Passes.h" 27 #include "llvm/CodeGen/SlotIndexes.h" 28 #include "llvm/CodeGen/StackProtector.h" 29 #include "llvm/CodeGen/TargetFrameLowering.h" 30 #include "llvm/CodeGen/TargetSubtargetInfo.h" 31 #include "llvm/IR/DebugInfoMetadata.h" 32 #include "llvm/IR/PrintPasses.h" 33 #include "llvm/InitializePasses.h" 34 #include "llvm/Support/Debug.h" 35 #include "llvm/Support/FormatVariadic.h" 36 #include "llvm/Support/raw_ostream.h" 37 38 using namespace llvm; 39 40 #define DEBUG_TYPE "stack-frame-layout" 41 42 namespace { 43 44 /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a 45 /// MachineFunction. 46 /// 47 struct StackFrameLayoutAnalysis { 48 using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>; 49 MachineOptimizationRemarkEmitter &ORE; 50 51 StackFrameLayoutAnalysis(MachineOptimizationRemarkEmitter &ORE) : ORE(ORE) {} 52 53 enum SlotType { 54 Spill, // a Spill slot 55 Fixed, // a Fixed slot (e.g. arguments passed on the stack) 56 VariableSized, // a variable sized object 57 StackProtector, // Stack Protector slot 58 Variable, // a slot used to store a local data (could be a tmp) 59 Invalid // It's an error for a slot to have this type 60 }; 61 62 struct SlotData { 63 int Slot; 64 int Size; 65 int Align; 66 StackOffset Offset; 67 SlotType SlotTy; 68 bool Scalable; 69 70 SlotData(const MachineFrameInfo &MFI, const StackOffset Offset, 71 const int Idx) 72 : Slot(Idx), Size(MFI.getObjectSize(Idx)), 73 Align(MFI.getObjectAlign(Idx).value()), Offset(Offset), 74 SlotTy(Invalid), Scalable(false) { 75 Scalable = MFI.getStackID(Idx) == TargetStackID::ScalableVector; 76 if (MFI.isSpillSlotObjectIndex(Idx)) 77 SlotTy = SlotType::Spill; 78 else if (MFI.isFixedObjectIndex(Idx)) 79 SlotTy = SlotType::Fixed; 80 else if (MFI.isVariableSizedObjectIndex(Idx)) 81 SlotTy = SlotType::VariableSized; 82 else if (MFI.hasStackProtectorIndex() && 83 Idx == MFI.getStackProtectorIndex()) 84 SlotTy = SlotType::StackProtector; 85 else 86 SlotTy = SlotType::Variable; 87 } 88 89 bool isVarSize() const { return SlotTy == SlotType::VariableSized; } 90 91 // We use this to sort in reverse order, so that the layout is displayed 92 // correctly. Variable sized slots are sorted to the end of the list, as 93 // offsets are currently incorrect for these but they reside at the end of 94 // the stack frame. The Slot index is used to ensure deterministic order 95 // when offsets are equal. 96 bool operator<(const SlotData &Rhs) const { 97 return std::make_tuple(!isVarSize(), 98 Offset.getFixed() + Offset.getScalable(), Slot) > 99 std::make_tuple(!Rhs.isVarSize(), 100 Rhs.Offset.getFixed() + Rhs.Offset.getScalable(), 101 Rhs.Slot); 102 } 103 }; 104 105 bool run(MachineFunction &MF) { 106 // TODO: We should implement a similar filter for remarks: 107 // -Rpass-func-filter=<regex> 108 if (!isFunctionInPrintList(MF.getName())) 109 return false; 110 111 LLVMContext &Ctx = MF.getFunction().getContext(); 112 if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE)) 113 return false; 114 115 MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout", 116 MF.getFunction().getSubprogram(), 117 &MF.front()); 118 Rem << ("\nFunction: " + MF.getName()).str(); 119 emitStackFrameLayoutRemarks(MF, Rem); 120 ORE.emit(Rem); 121 return false; 122 } 123 124 std::string getTypeString(SlotType Ty) { 125 switch (Ty) { 126 case SlotType::Spill: 127 return "Spill"; 128 case SlotType::Fixed: 129 return "Fixed"; 130 case SlotType::VariableSized: 131 return "VariableSized"; 132 case SlotType::StackProtector: 133 return "Protector"; 134 case SlotType::Variable: 135 return "Variable"; 136 default: 137 llvm_unreachable("bad slot type for stack layout"); 138 } 139 } 140 141 void emitStackSlotRemark(const MachineFunction &MF, const SlotData &D, 142 MachineOptimizationRemarkAnalysis &Rem) { 143 // To make it easy to understand the stack layout from the CLI, we want to 144 // print each slot like the following: 145 // 146 // Offset: [SP+8], Type: Spill, Align: 8, Size: 16 147 // foo @ /path/to/file.c:25 148 // bar @ /path/to/file.c:35 149 // 150 // Which prints the size, alignment, and offset from the SP at function 151 // entry. 152 // 153 // But we also want the machine readable remarks data to be nicely 154 // organized. So we print some additional data as strings for the CLI 155 // output, but maintain more structured data for the YAML. 156 // 157 // For example we store the Offset in YAML as: 158 // ... 159 // - Offset: -8 160 // - ScalableOffset: -16 161 // Note: the ScalableOffset entries are added only for slots with non-zero 162 // scalable offsets. 163 // 164 // But we print it to the CLI as: 165 // Offset: [SP-8] 166 // 167 // Or with non-zero scalable offset: 168 // Offset: [SP-8-16 x vscale] 169 170 // Negative offsets will print a leading `-`, so only add `+` 171 std::string Prefix = 172 formatv("\nOffset: [SP{0}", (D.Offset.getFixed() < 0) ? "" : "+").str(); 173 Rem << Prefix << ore::NV("Offset", D.Offset.getFixed()); 174 175 if (D.Offset.getScalable()) { 176 Rem << ((D.Offset.getScalable() < 0) ? "" : "+") 177 << ore::NV("ScalableOffset", D.Offset.getScalable()) << " x vscale"; 178 } 179 180 Rem << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy)) 181 << ", Align: " << ore::NV("Align", D.Align) 182 << ", Size: " << ore::NV("Size", ElementCount::get(D.Size, D.Scalable)); 183 } 184 185 void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N, 186 MachineOptimizationRemarkAnalysis &Rem) { 187 std::string Loc = 188 formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine()) 189 .str(); 190 Rem << "\n " << ore::NV("DataLoc", Loc); 191 } 192 193 StackOffset getStackOffset(const MachineFunction &MF, 194 const MachineFrameInfo &MFI, 195 const TargetFrameLowering *FI, int FrameIdx) { 196 if (!FI) 197 return StackOffset::getFixed(MFI.getObjectOffset(FrameIdx)); 198 199 return FI->getFrameIndexReferenceFromSP(MF, FrameIdx); 200 } 201 202 void emitStackFrameLayoutRemarks(MachineFunction &MF, 203 MachineOptimizationRemarkAnalysis &Rem) { 204 const MachineFrameInfo &MFI = MF.getFrameInfo(); 205 if (!MFI.hasStackObjects()) 206 return; 207 208 const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering(); 209 210 LLVM_DEBUG(dbgs() << "getStackProtectorIndex ==" 211 << MFI.getStackProtectorIndex() << "\n"); 212 213 std::vector<SlotData> SlotInfo; 214 215 const unsigned int NumObj = MFI.getNumObjects(); 216 SlotInfo.reserve(NumObj); 217 // initialize slot info 218 for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd(); 219 Idx != EndIdx; ++Idx) { 220 if (MFI.isDeadObjectIndex(Idx)) 221 continue; 222 SlotInfo.emplace_back(MFI, getStackOffset(MF, MFI, FI, Idx), Idx); 223 } 224 225 // sort the ordering, to match the actual layout in memory 226 llvm::sort(SlotInfo); 227 228 SlotDbgMap SlotMap = genSlotDbgMapping(MF); 229 230 for (const SlotData &Info : SlotInfo) { 231 emitStackSlotRemark(MF, Info, Rem); 232 for (const DILocalVariable *N : SlotMap[Info.Slot]) 233 emitSourceLocRemark(MF, N, Rem); 234 } 235 } 236 237 // We need to generate a mapping of slots to the values that are stored to 238 // them. This information is lost by the time we need to print out the frame, 239 // so we reconstruct it here by walking the CFG, and generating the mapping. 240 SlotDbgMap genSlotDbgMapping(MachineFunction &MF) { 241 SlotDbgMap SlotDebugMap; 242 243 // add variables to the map 244 for (MachineFunction::VariableDbgInfo &DI : 245 MF.getInStackSlotVariableDbgInfo()) 246 SlotDebugMap[DI.getStackSlot()].insert(DI.Var); 247 248 // Then add all the spills that have debug data 249 for (MachineBasicBlock &MBB : MF) { 250 for (MachineInstr &MI : MBB) { 251 for (MachineMemOperand *MO : MI.memoperands()) { 252 if (!MO->isStore()) 253 continue; 254 auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>( 255 MO->getPseudoValue()); 256 if (!FI) 257 continue; 258 int FrameIdx = FI->getFrameIndex(); 259 SmallVector<MachineInstr *> Dbg; 260 MI.collectDebugValues(Dbg); 261 262 for (MachineInstr *MI : Dbg) 263 SlotDebugMap[FrameIdx].insert(MI->getDebugVariable()); 264 } 265 } 266 } 267 268 return SlotDebugMap; 269 } 270 }; 271 272 class StackFrameLayoutAnalysisLegacy : public MachineFunctionPass { 273 public: 274 static char ID; 275 276 StackFrameLayoutAnalysisLegacy() : MachineFunctionPass(ID) {} 277 278 StringRef getPassName() const override { 279 return "Stack Frame Layout Analysis"; 280 } 281 282 void getAnalysisUsage(AnalysisUsage &AU) const override { 283 AU.setPreservesAll(); 284 MachineFunctionPass::getAnalysisUsage(AU); 285 AU.addRequired<MachineOptimizationRemarkEmitterPass>(); 286 } 287 288 bool runOnMachineFunction(MachineFunction &MF) override { 289 auto &ORE = getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE(); 290 return StackFrameLayoutAnalysis(ORE).run(MF); 291 } 292 }; 293 294 char StackFrameLayoutAnalysisLegacy::ID = 0; 295 } // namespace 296 297 PreservedAnalyses 298 llvm::StackFrameLayoutAnalysisPass::run(MachineFunction &MF, 299 MachineFunctionAnalysisManager &MFAM) { 300 auto &ORE = MFAM.getResult<MachineOptimizationRemarkEmitterAnalysis>(MF); 301 StackFrameLayoutAnalysis(ORE).run(MF); 302 return PreservedAnalyses::all(); 303 } 304 305 char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisLegacy::ID; 306 INITIALIZE_PASS(StackFrameLayoutAnalysisLegacy, "stack-frame-layout", 307 "Stack Frame Layout", false, false) 308 309 namespace llvm { 310 /// Returns a newly-created StackFrameLayout pass. 311 MachineFunctionPass *createStackFrameLayoutAnalysisPass() { 312 return new StackFrameLayoutAnalysisLegacy(); 313 } 314 315 } // namespace llvm 316