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 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 57 }; 58 59 struct SlotData { 60 int Slot; 61 int Size; 62 int Align; 63 int Offset; 64 SlotType SlotTy; 65 bool Scalable; 66 67 SlotData(const MachineFrameInfo &MFI, const int ValOffset, const int Idx) 68 : Slot(Idx), Size(MFI.getObjectSize(Idx)), 69 Align(MFI.getObjectAlign(Idx).value()), 70 Offset(MFI.getObjectOffset(Idx) - ValOffset), SlotTy(Invalid), 71 Scalable(false) { 72 Scalable = MFI.getStackID(Idx) == TargetStackID::ScalableVector; 73 if (MFI.isSpillSlotObjectIndex(Idx)) 74 SlotTy = SlotType::Spill; 75 else if (Idx == MFI.getStackProtectorIndex()) 76 SlotTy = SlotType::StackProtector; 77 else 78 SlotTy = SlotType::Variable; 79 } 80 81 // We use this to sort in reverse order, so that the layout is displayed 82 // correctly. Scalable slots are sorted to the end of the list. 83 bool operator<(const SlotData &Rhs) const { 84 return std::make_tuple(!Scalable, Offset) > 85 std::make_tuple(!Rhs.Scalable, Rhs.Offset); 86 } 87 }; 88 89 StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {} 90 91 StringRef getPassName() const override { 92 return "Stack Frame Layout Analysis"; 93 } 94 95 void getAnalysisUsage(AnalysisUsage &AU) const override { 96 AU.setPreservesAll(); 97 MachineFunctionPass::getAnalysisUsage(AU); 98 AU.addRequired<MachineOptimizationRemarkEmitterPass>(); 99 } 100 101 bool runOnMachineFunction(MachineFunction &MF) override { 102 // TODO: We should implement a similar filter for remarks: 103 // -Rpass-func-filter=<regex> 104 if (!isFunctionInPrintList(MF.getName())) 105 return false; 106 107 LLVMContext &Ctx = MF.getFunction().getContext(); 108 if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE)) 109 return false; 110 111 MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout", 112 MF.getFunction().getSubprogram(), 113 &MF.front()); 114 Rem << ("\nFunction: " + MF.getName()).str(); 115 emitStackFrameLayoutRemarks(MF, Rem); 116 getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE().emit(Rem); 117 return false; 118 } 119 120 std::string getTypeString(SlotType Ty) { 121 switch (Ty) { 122 case SlotType::Spill: 123 return "Spill"; 124 case SlotType::StackProtector: 125 return "Protector"; 126 case SlotType::Variable: 127 return "Variable"; 128 default: 129 llvm_unreachable("bad slot type for stack layout"); 130 } 131 } 132 133 void emitStackSlotRemark(const MachineFunction &MF, const SlotData &D, 134 MachineOptimizationRemarkAnalysis &Rem) { 135 // To make it easy to understand the stack layout from the CLI, we want to 136 // print each slot like the following: 137 // 138 // Offset: [SP+8], Type: Spill, Align: 8, Size: 16 139 // foo @ /path/to/file.c:25 140 // bar @ /path/to/file.c:35 141 // 142 // Which prints the size, alignment, and offset from the SP at function 143 // entry. 144 // 145 // But we also want the machine readable remarks data to be nicely 146 // organized. So we print some additional data as strings for the CLI 147 // output, but maintain more structured data for the YAML. 148 // 149 // For example we store the Offset in YAML as: 150 // ... 151 // - Offset: -8 152 // 153 // But we print it to the CLI as 154 // Offset: [SP-8] 155 156 // Negative offsets will print a leading `-`, so only add `+` 157 std::string Prefix = 158 formatv("\nOffset: [SP{0}", (D.Offset < 0) ? "" : "+").str(); 159 Rem << Prefix << ore::NV("Offset", D.Offset) 160 << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy)) 161 << ", Align: " << ore::NV("Align", D.Align) 162 << ", Size: " << ore::NV("Size", ElementCount::get(D.Size, D.Scalable)); 163 } 164 165 void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N, 166 MachineOptimizationRemarkAnalysis &Rem) { 167 std::string Loc = 168 formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine()) 169 .str(); 170 Rem << "\n " << ore::NV("DataLoc", Loc); 171 } 172 173 void emitStackFrameLayoutRemarks(MachineFunction &MF, 174 MachineOptimizationRemarkAnalysis &Rem) { 175 const MachineFrameInfo &MFI = MF.getFrameInfo(); 176 if (!MFI.hasStackObjects()) 177 return; 178 179 // ValOffset is the offset to the local area from the SP at function entry. 180 // To display the true offset from SP, we need to subtract ValOffset from 181 // MFI's ObjectOffset. 182 const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering(); 183 const int ValOffset = (FI ? FI->getOffsetOfLocalArea() : 0); 184 185 LLVM_DEBUG(dbgs() << "getStackProtectorIndex ==" 186 << MFI.getStackProtectorIndex() << "\n"); 187 188 std::vector<SlotData> SlotInfo; 189 190 const unsigned int NumObj = MFI.getNumObjects(); 191 SlotInfo.reserve(NumObj); 192 // initialize slot info 193 for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd(); 194 Idx != EndIdx; ++Idx) { 195 if (MFI.isDeadObjectIndex(Idx)) 196 continue; 197 SlotInfo.emplace_back(MFI, ValOffset, Idx); 198 } 199 200 // sort the ordering, to match the actual layout in memory 201 llvm::sort(SlotInfo); 202 203 SlotDbgMap SlotMap = genSlotDbgMapping(MF); 204 205 for (const SlotData &Info : SlotInfo) { 206 emitStackSlotRemark(MF, Info, Rem); 207 for (const DILocalVariable *N : SlotMap[Info.Slot]) 208 emitSourceLocRemark(MF, N, Rem); 209 } 210 } 211 212 // We need to generate a mapping of slots to the values that are stored to 213 // them. This information is lost by the time we need to print out the frame, 214 // so we reconstruct it here by walking the CFG, and generating the mapping. 215 SlotDbgMap genSlotDbgMapping(MachineFunction &MF) { 216 SlotDbgMap SlotDebugMap; 217 218 // add variables to the map 219 for (MachineFunction::VariableDbgInfo &DI : 220 MF.getInStackSlotVariableDbgInfo()) 221 SlotDebugMap[DI.getStackSlot()].insert(DI.Var); 222 223 // Then add all the spills that have debug data 224 for (MachineBasicBlock &MBB : MF) { 225 for (MachineInstr &MI : MBB) { 226 for (MachineMemOperand *MO : MI.memoperands()) { 227 if (!MO->isStore()) 228 continue; 229 auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>( 230 MO->getPseudoValue()); 231 if (!FI) 232 continue; 233 int FrameIdx = FI->getFrameIndex(); 234 SmallVector<MachineInstr *> Dbg; 235 MI.collectDebugValues(Dbg); 236 237 for (MachineInstr *MI : Dbg) 238 SlotDebugMap[FrameIdx].insert(MI->getDebugVariable()); 239 } 240 } 241 } 242 243 return SlotDebugMap; 244 } 245 }; 246 247 char StackFrameLayoutAnalysisPass::ID = 0; 248 } // namespace 249 250 char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID; 251 INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout", 252 "Stack Frame Layout", false, false) 253 254 namespace llvm { 255 /// Returns a newly-created StackFrameLayout pass. 256 MachineFunctionPass *createStackFrameLayoutAnalysisPass() { 257 return new StackFrameLayoutAnalysisPass(); 258 } 259 260 } // namespace llvm 261