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/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" 36 37 #include <sstream> 38 39 using namespace llvm; 40 41 #define DEBUG_TYPE "stack-frame-layout" 42 43 namespace { 44 45 /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a 46 /// MachineFunction. 47 /// 48 struct StackFrameLayoutAnalysisPass : public MachineFunctionPass { 49 using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>; 50 static char ID; 51 52 enum SlotType { 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 59 }; 60 61 struct SlotData { 62 int Slot; 63 int Size; 64 int Align; 65 StackOffset Offset; 66 SlotType SlotTy; 67 bool Scalable; 68 69 SlotData(const MachineFrameInfo &MFI, const StackOffset Offset, 70 const int Idx) 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; 84 else 85 SlotTy = SlotType::Variable; 86 } 87 88 bool isVarSize() const { return SlotTy == SlotType::VariableSized; } 89 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(), 100 Rhs.Slot); 101 } 102 }; 103 104 StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {} 105 106 StringRef getPassName() const override { 107 return "Stack Frame Layout Analysis"; 108 } 109 110 void getAnalysisUsage(AnalysisUsage &AU) const override { 111 AU.setPreservesAll(); 112 MachineFunctionPass::getAnalysisUsage(AU); 113 AU.addRequired<MachineOptimizationRemarkEmitterPass>(); 114 } 115 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())) 120 return false; 121 122 LLVMContext &Ctx = MF.getFunction().getContext(); 123 if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE)) 124 return false; 125 126 MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout", 127 MF.getFunction().getSubprogram(), 128 &MF.front()); 129 Rem << ("\nFunction: " + MF.getName()).str(); 130 emitStackFrameLayoutRemarks(MF, Rem); 131 getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE().emit(Rem); 132 return false; 133 } 134 135 std::string getTypeString(SlotType Ty) { 136 switch (Ty) { 137 case SlotType::Spill: 138 return "Spill"; 139 case SlotType::Fixed: 140 return "Fixed"; 141 case SlotType::VariableSized: 142 return "VariableSized"; 143 case SlotType::StackProtector: 144 return "Protector"; 145 case SlotType::Variable: 146 return "Variable"; 147 default: 148 llvm_unreachable("bad slot type for stack layout"); 149 } 150 } 151 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: 156 // 157 // Offset: [SP+8], Type: Spill, Align: 8, Size: 16 158 // foo @ /path/to/file.c:25 159 // bar @ /path/to/file.c:35 160 // 161 // Which prints the size, alignment, and offset from the SP at function 162 // entry. 163 // 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. 167 // 168 // For example we store the Offset in YAML as: 169 // ... 170 // - Offset: -8 171 // - ScalableOffset: -16 172 // Note: the ScalableOffset entries are added only for slots with non-zero 173 // scalable offsets. 174 // 175 // But we print it to the CLI as: 176 // Offset: [SP-8] 177 // 178 // Or with non-zero scalable offset: 179 // Offset: [SP-8-16 x vscale] 180 181 // Negative offsets will print a leading `-`, so only add `+` 182 std::string Prefix = 183 formatv("\nOffset: [SP{0}", (D.Offset.getFixed() < 0) ? "" : "+").str(); 184 Rem << Prefix << ore::NV("Offset", D.Offset.getFixed()); 185 186 if (D.Offset.getScalable()) { 187 Rem << ((D.Offset.getScalable() < 0) ? "" : "+") 188 << ore::NV("ScalableOffset", D.Offset.getScalable()) << " x vscale"; 189 } 190 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)); 194 } 195 196 void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N, 197 MachineOptimizationRemarkAnalysis &Rem) { 198 std::string Loc = 199 formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine()) 200 .str(); 201 Rem << "\n " << ore::NV("DataLoc", Loc); 202 } 203 204 StackOffset getStackOffset(const MachineFunction &MF, 205 const MachineFrameInfo &MFI, 206 const TargetFrameLowering *FI, int FrameIdx) { 207 if (!FI) 208 return StackOffset::getFixed(MFI.getObjectOffset(FrameIdx)); 209 210 return FI->getFrameIndexReferenceFromSP(MF, FrameIdx); 211 } 212 213 void emitStackFrameLayoutRemarks(MachineFunction &MF, 214 MachineOptimizationRemarkAnalysis &Rem) { 215 const MachineFrameInfo &MFI = MF.getFrameInfo(); 216 if (!MFI.hasStackObjects()) 217 return; 218 219 const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering(); 220 221 LLVM_DEBUG(dbgs() << "getStackProtectorIndex ==" 222 << MFI.getStackProtectorIndex() << "\n"); 223 224 std::vector<SlotData> SlotInfo; 225 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)) 232 continue; 233 SlotInfo.emplace_back(MFI, getStackOffset(MF, MFI, FI, Idx), Idx); 234 } 235 236 // sort the ordering, to match the actual layout in memory 237 llvm::sort(SlotInfo); 238 239 SlotDbgMap SlotMap = genSlotDbgMapping(MF); 240 241 for (const SlotData &Info : SlotInfo) { 242 emitStackSlotRemark(MF, Info, Rem); 243 for (const DILocalVariable *N : SlotMap[Info.Slot]) 244 emitSourceLocRemark(MF, N, Rem); 245 } 246 } 247 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; 253 254 // add variables to the map 255 for (MachineFunction::VariableDbgInfo &DI : 256 MF.getInStackSlotVariableDbgInfo()) 257 SlotDebugMap[DI.getStackSlot()].insert(DI.Var); 258 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()) { 263 if (!MO->isStore()) 264 continue; 265 auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>( 266 MO->getPseudoValue()); 267 if (!FI) 268 continue; 269 int FrameIdx = FI->getFrameIndex(); 270 SmallVector<MachineInstr *> Dbg; 271 MI.collectDebugValues(Dbg); 272 273 for (MachineInstr *MI : Dbg) 274 SlotDebugMap[FrameIdx].insert(MI->getDebugVariable()); 275 } 276 } 277 } 278 279 return SlotDebugMap; 280 } 281 }; 282 283 char StackFrameLayoutAnalysisPass::ID = 0; 284 } // namespace 285 286 char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID; 287 INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout", 288 "Stack Frame Layout", false, false) 289 290 namespace llvm { 291 /// Returns a newly-created StackFrameLayout pass. 292 MachineFunctionPass *createStackFrameLayoutAnalysisPass() { 293 return new StackFrameLayoutAnalysisPass(); 294 } 295 296 } // namespace llvm 297