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