1557a5bc3SPaul Kirth //===-- StackFrameLayoutAnalysisPass.cpp 2557a5bc3SPaul Kirth //------------------------------------===// 3557a5bc3SPaul Kirth // 4557a5bc3SPaul Kirth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5557a5bc3SPaul Kirth // See https://llvm.org/LICENSE.txt for license information. 6557a5bc3SPaul Kirth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7557a5bc3SPaul Kirth // 8557a5bc3SPaul Kirth //===----------------------------------------------------------------------===// 9557a5bc3SPaul Kirth // 10557a5bc3SPaul Kirth // StackFrameLayoutAnalysisPass implementation. Outputs information about the 11557a5bc3SPaul Kirth // layout of the stack frame, using the remarks interface. On the CLI it prints 12557a5bc3SPaul Kirth // a textual representation of the stack frame. When possible it prints the 13557a5bc3SPaul Kirth // values that occupy a stack slot using any available debug information. Since 14557a5bc3SPaul Kirth // output is remarks based, it is also available in a machine readable file 15557a5bc3SPaul Kirth // format, such as YAML. 16557a5bc3SPaul Kirth // 17557a5bc3SPaul Kirth //===----------------------------------------------------------------------===// 18557a5bc3SPaul Kirth 19af9a452eSPaul Kirth #include "llvm/ADT/SetVector.h" 20557a5bc3SPaul Kirth #include "llvm/Analysis/OptimizationRemarkEmitter.h" 21557a5bc3SPaul Kirth #include "llvm/CodeGen/MachineFrameInfo.h" 22557a5bc3SPaul Kirth #include "llvm/CodeGen/MachineFunction.h" 23557a5bc3SPaul Kirth #include "llvm/CodeGen/MachineFunctionPass.h" 24557a5bc3SPaul Kirth #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h" 25557a5bc3SPaul Kirth #include "llvm/CodeGen/Passes.h" 26557a5bc3SPaul Kirth #include "llvm/CodeGen/SlotIndexes.h" 27557a5bc3SPaul Kirth #include "llvm/CodeGen/StackProtector.h" 28557a5bc3SPaul Kirth #include "llvm/CodeGen/TargetFrameLowering.h" 29557a5bc3SPaul Kirth #include "llvm/CodeGen/TargetSubtargetInfo.h" 30557a5bc3SPaul Kirth #include "llvm/IR/DebugInfoMetadata.h" 31557a5bc3SPaul Kirth #include "llvm/IR/PrintPasses.h" 32557a5bc3SPaul Kirth #include "llvm/InitializePasses.h" 33557a5bc3SPaul Kirth #include "llvm/Support/Debug.h" 34557a5bc3SPaul Kirth #include "llvm/Support/FormatVariadic.h" 35557a5bc3SPaul Kirth #include "llvm/Support/raw_ostream.h" 36557a5bc3SPaul Kirth 37557a5bc3SPaul Kirth using namespace llvm; 38557a5bc3SPaul Kirth 39557a5bc3SPaul Kirth #define DEBUG_TYPE "stack-frame-layout" 40557a5bc3SPaul Kirth 41557a5bc3SPaul Kirth namespace { 42557a5bc3SPaul Kirth 43557a5bc3SPaul Kirth /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a 44557a5bc3SPaul Kirth /// MachineFunction. 45557a5bc3SPaul Kirth /// 46557a5bc3SPaul Kirth struct StackFrameLayoutAnalysisPass : public MachineFunctionPass { 47af9a452eSPaul Kirth using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>; 48557a5bc3SPaul Kirth static char ID; 49557a5bc3SPaul Kirth 50557a5bc3SPaul Kirth enum SlotType { 51557a5bc3SPaul Kirth Spill, // a Spill slot 52*e31794f9SHari Limaye Fixed, // a Fixed slot (e.g. arguments passed on the stack) 53*e31794f9SHari Limaye VariableSized, // a variable sized object 54557a5bc3SPaul Kirth StackProtector, // Stack Protector slot 55557a5bc3SPaul Kirth Variable, // a slot used to store a local data (could be a tmp) 56557a5bc3SPaul Kirth Invalid // It's an error for a slot to have this type 57557a5bc3SPaul Kirth }; 58557a5bc3SPaul Kirth 59557a5bc3SPaul Kirth struct SlotData { 60557a5bc3SPaul Kirth int Slot; 61557a5bc3SPaul Kirth int Size; 62557a5bc3SPaul Kirth int Align; 63dc1c00f6SHari Limaye StackOffset Offset; 64557a5bc3SPaul Kirth SlotType SlotTy; 65e09032f7SDavid Green bool Scalable; 66557a5bc3SPaul Kirth 67dc1c00f6SHari Limaye SlotData(const MachineFrameInfo &MFI, const StackOffset Offset, 68dc1c00f6SHari Limaye const int Idx) 69557a5bc3SPaul Kirth : Slot(Idx), Size(MFI.getObjectSize(Idx)), 70dc1c00f6SHari Limaye Align(MFI.getObjectAlign(Idx).value()), Offset(Offset), 71dc1c00f6SHari Limaye SlotTy(Invalid), Scalable(false) { 72e09032f7SDavid Green Scalable = MFI.getStackID(Idx) == TargetStackID::ScalableVector; 73557a5bc3SPaul Kirth if (MFI.isSpillSlotObjectIndex(Idx)) 74557a5bc3SPaul Kirth SlotTy = SlotType::Spill; 75*e31794f9SHari Limaye else if (MFI.isFixedObjectIndex(Idx)) 76*e31794f9SHari Limaye SlotTy = SlotType::Fixed; 77*e31794f9SHari Limaye else if (MFI.isVariableSizedObjectIndex(Idx)) 78*e31794f9SHari Limaye SlotTy = SlotType::VariableSized; 79*e31794f9SHari Limaye else if (MFI.hasStackProtectorIndex() && 80*e31794f9SHari Limaye Idx == MFI.getStackProtectorIndex()) 81557a5bc3SPaul Kirth SlotTy = SlotType::StackProtector; 82557a5bc3SPaul Kirth else 83557a5bc3SPaul Kirth SlotTy = SlotType::Variable; 84557a5bc3SPaul Kirth } 85557a5bc3SPaul Kirth 86*e31794f9SHari Limaye bool isVarSize() const { return SlotTy == SlotType::VariableSized; } 87*e31794f9SHari Limaye 88e09032f7SDavid Green // We use this to sort in reverse order, so that the layout is displayed 89*e31794f9SHari Limaye // correctly. Variable sized slots are sorted to the end of the list, as 90*e31794f9SHari Limaye // offsets are currently incorrect for these but they reside at the end of 91*e31794f9SHari Limaye // the stack frame. The Slot index is used to ensure deterministic order 92*e31794f9SHari Limaye // when offsets are equal. 93e09032f7SDavid Green bool operator<(const SlotData &Rhs) const { 94*e31794f9SHari Limaye return std::make_tuple(!isVarSize(), 95*e31794f9SHari Limaye Offset.getFixed() + Offset.getScalable(), Slot) > 96*e31794f9SHari Limaye std::make_tuple(!Rhs.isVarSize(), 97*e31794f9SHari Limaye Rhs.Offset.getFixed() + Rhs.Offset.getScalable(), 98*e31794f9SHari Limaye Rhs.Slot); 99e09032f7SDavid Green } 100557a5bc3SPaul Kirth }; 101557a5bc3SPaul Kirth 102557a5bc3SPaul Kirth StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {} 103557a5bc3SPaul Kirth 104557a5bc3SPaul Kirth StringRef getPassName() const override { 105557a5bc3SPaul Kirth return "Stack Frame Layout Analysis"; 106557a5bc3SPaul Kirth } 107557a5bc3SPaul Kirth 108557a5bc3SPaul Kirth void getAnalysisUsage(AnalysisUsage &AU) const override { 109557a5bc3SPaul Kirth AU.setPreservesAll(); 110557a5bc3SPaul Kirth MachineFunctionPass::getAnalysisUsage(AU); 111557a5bc3SPaul Kirth AU.addRequired<MachineOptimizationRemarkEmitterPass>(); 112557a5bc3SPaul Kirth } 113557a5bc3SPaul Kirth 114557a5bc3SPaul Kirth bool runOnMachineFunction(MachineFunction &MF) override { 115557a5bc3SPaul Kirth // TODO: We should implement a similar filter for remarks: 116557a5bc3SPaul Kirth // -Rpass-func-filter=<regex> 117557a5bc3SPaul Kirth if (!isFunctionInPrintList(MF.getName())) 118557a5bc3SPaul Kirth return false; 119557a5bc3SPaul Kirth 120557a5bc3SPaul Kirth LLVMContext &Ctx = MF.getFunction().getContext(); 121557a5bc3SPaul Kirth if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE)) 122557a5bc3SPaul Kirth return false; 123557a5bc3SPaul Kirth 124557a5bc3SPaul Kirth MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout", 125557a5bc3SPaul Kirth MF.getFunction().getSubprogram(), 126557a5bc3SPaul Kirth &MF.front()); 127557a5bc3SPaul Kirth Rem << ("\nFunction: " + MF.getName()).str(); 128557a5bc3SPaul Kirth emitStackFrameLayoutRemarks(MF, Rem); 129557a5bc3SPaul Kirth getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE().emit(Rem); 130557a5bc3SPaul Kirth return false; 131557a5bc3SPaul Kirth } 132557a5bc3SPaul Kirth 133557a5bc3SPaul Kirth std::string getTypeString(SlotType Ty) { 134557a5bc3SPaul Kirth switch (Ty) { 135557a5bc3SPaul Kirth case SlotType::Spill: 136557a5bc3SPaul Kirth return "Spill"; 137*e31794f9SHari Limaye case SlotType::Fixed: 138*e31794f9SHari Limaye return "Fixed"; 139*e31794f9SHari Limaye case SlotType::VariableSized: 140*e31794f9SHari Limaye return "VariableSized"; 141557a5bc3SPaul Kirth case SlotType::StackProtector: 142557a5bc3SPaul Kirth return "Protector"; 143557a5bc3SPaul Kirth case SlotType::Variable: 144557a5bc3SPaul Kirth return "Variable"; 145557a5bc3SPaul Kirth default: 146557a5bc3SPaul Kirth llvm_unreachable("bad slot type for stack layout"); 147557a5bc3SPaul Kirth } 148557a5bc3SPaul Kirth } 149557a5bc3SPaul Kirth 150557a5bc3SPaul Kirth void emitStackSlotRemark(const MachineFunction &MF, const SlotData &D, 151557a5bc3SPaul Kirth MachineOptimizationRemarkAnalysis &Rem) { 152557a5bc3SPaul Kirth // To make it easy to understand the stack layout from the CLI, we want to 153557a5bc3SPaul Kirth // print each slot like the following: 154557a5bc3SPaul Kirth // 155557a5bc3SPaul Kirth // Offset: [SP+8], Type: Spill, Align: 8, Size: 16 156557a5bc3SPaul Kirth // foo @ /path/to/file.c:25 157557a5bc3SPaul Kirth // bar @ /path/to/file.c:35 158557a5bc3SPaul Kirth // 159557a5bc3SPaul Kirth // Which prints the size, alignment, and offset from the SP at function 160557a5bc3SPaul Kirth // entry. 161557a5bc3SPaul Kirth // 162557a5bc3SPaul Kirth // But we also want the machine readable remarks data to be nicely 163557a5bc3SPaul Kirth // organized. So we print some additional data as strings for the CLI 164557a5bc3SPaul Kirth // output, but maintain more structured data for the YAML. 165557a5bc3SPaul Kirth // 166557a5bc3SPaul Kirth // For example we store the Offset in YAML as: 167557a5bc3SPaul Kirth // ... 168557a5bc3SPaul Kirth // - Offset: -8 169dc1c00f6SHari Limaye // - ScalableOffset: -16 170dc1c00f6SHari Limaye // Note: the ScalableOffset entries are added only for slots with non-zero 171dc1c00f6SHari Limaye // scalable offsets. 172557a5bc3SPaul Kirth // 173dc1c00f6SHari Limaye // But we print it to the CLI as: 174557a5bc3SPaul Kirth // Offset: [SP-8] 175dc1c00f6SHari Limaye // 176dc1c00f6SHari Limaye // Or with non-zero scalable offset: 177dc1c00f6SHari Limaye // Offset: [SP-8-16 x vscale] 178557a5bc3SPaul Kirth 179557a5bc3SPaul Kirth // Negative offsets will print a leading `-`, so only add `+` 180557a5bc3SPaul Kirth std::string Prefix = 181dc1c00f6SHari Limaye formatv("\nOffset: [SP{0}", (D.Offset.getFixed() < 0) ? "" : "+").str(); 182dc1c00f6SHari Limaye Rem << Prefix << ore::NV("Offset", D.Offset.getFixed()); 183dc1c00f6SHari Limaye 184dc1c00f6SHari Limaye if (D.Offset.getScalable()) { 185dc1c00f6SHari Limaye Rem << ((D.Offset.getScalable() < 0) ? "" : "+") 186dc1c00f6SHari Limaye << ore::NV("ScalableOffset", D.Offset.getScalable()) << " x vscale"; 187dc1c00f6SHari Limaye } 188dc1c00f6SHari Limaye 189dc1c00f6SHari Limaye Rem << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy)) 190557a5bc3SPaul Kirth << ", Align: " << ore::NV("Align", D.Align) 191e09032f7SDavid Green << ", Size: " << ore::NV("Size", ElementCount::get(D.Size, D.Scalable)); 192557a5bc3SPaul Kirth } 193557a5bc3SPaul Kirth 194557a5bc3SPaul Kirth void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N, 195557a5bc3SPaul Kirth MachineOptimizationRemarkAnalysis &Rem) { 196557a5bc3SPaul Kirth std::string Loc = 197557a5bc3SPaul Kirth formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine()) 198557a5bc3SPaul Kirth .str(); 199557a5bc3SPaul Kirth Rem << "\n " << ore::NV("DataLoc", Loc); 200557a5bc3SPaul Kirth } 201557a5bc3SPaul Kirth 202dc1c00f6SHari Limaye StackOffset getStackOffset(const MachineFunction &MF, 203dc1c00f6SHari Limaye const MachineFrameInfo &MFI, 204dc1c00f6SHari Limaye const TargetFrameLowering *FI, int FrameIdx) { 205dc1c00f6SHari Limaye if (!FI) 206dc1c00f6SHari Limaye return StackOffset::getFixed(MFI.getObjectOffset(FrameIdx)); 207dc1c00f6SHari Limaye 208dc1c00f6SHari Limaye return FI->getFrameIndexReferenceFromSP(MF, FrameIdx); 209dc1c00f6SHari Limaye } 210dc1c00f6SHari Limaye 211557a5bc3SPaul Kirth void emitStackFrameLayoutRemarks(MachineFunction &MF, 212557a5bc3SPaul Kirth MachineOptimizationRemarkAnalysis &Rem) { 213557a5bc3SPaul Kirth const MachineFrameInfo &MFI = MF.getFrameInfo(); 214557a5bc3SPaul Kirth if (!MFI.hasStackObjects()) 215557a5bc3SPaul Kirth return; 216557a5bc3SPaul Kirth 217557a5bc3SPaul Kirth const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering(); 218557a5bc3SPaul Kirth 219557a5bc3SPaul Kirth LLVM_DEBUG(dbgs() << "getStackProtectorIndex ==" 220557a5bc3SPaul Kirth << MFI.getStackProtectorIndex() << "\n"); 221557a5bc3SPaul Kirth 222557a5bc3SPaul Kirth std::vector<SlotData> SlotInfo; 223557a5bc3SPaul Kirth 224557a5bc3SPaul Kirth const unsigned int NumObj = MFI.getNumObjects(); 225557a5bc3SPaul Kirth SlotInfo.reserve(NumObj); 226557a5bc3SPaul Kirth // initialize slot info 227557a5bc3SPaul Kirth for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd(); 228557a5bc3SPaul Kirth Idx != EndIdx; ++Idx) { 229557a5bc3SPaul Kirth if (MFI.isDeadObjectIndex(Idx)) 230557a5bc3SPaul Kirth continue; 231dc1c00f6SHari Limaye SlotInfo.emplace_back(MFI, getStackOffset(MF, MFI, FI, Idx), Idx); 232557a5bc3SPaul Kirth } 233557a5bc3SPaul Kirth 234557a5bc3SPaul Kirth // sort the ordering, to match the actual layout in memory 235557a5bc3SPaul Kirth llvm::sort(SlotInfo); 236557a5bc3SPaul Kirth 237557a5bc3SPaul Kirth SlotDbgMap SlotMap = genSlotDbgMapping(MF); 238557a5bc3SPaul Kirth 239557a5bc3SPaul Kirth for (const SlotData &Info : SlotInfo) { 240557a5bc3SPaul Kirth emitStackSlotRemark(MF, Info, Rem); 241557a5bc3SPaul Kirth for (const DILocalVariable *N : SlotMap[Info.Slot]) 242557a5bc3SPaul Kirth emitSourceLocRemark(MF, N, Rem); 243557a5bc3SPaul Kirth } 244557a5bc3SPaul Kirth } 245557a5bc3SPaul Kirth 246557a5bc3SPaul Kirth // We need to generate a mapping of slots to the values that are stored to 247557a5bc3SPaul Kirth // them. This information is lost by the time we need to print out the frame, 248557a5bc3SPaul Kirth // so we reconstruct it here by walking the CFG, and generating the mapping. 249557a5bc3SPaul Kirth SlotDbgMap genSlotDbgMapping(MachineFunction &MF) { 250557a5bc3SPaul Kirth SlotDbgMap SlotDebugMap; 251557a5bc3SPaul Kirth 252557a5bc3SPaul Kirth // add variables to the map 2533db7d0dfSFelipe de Azevedo Piovezan for (MachineFunction::VariableDbgInfo &DI : 2543db7d0dfSFelipe de Azevedo Piovezan MF.getInStackSlotVariableDbgInfo()) 2553db7d0dfSFelipe de Azevedo Piovezan SlotDebugMap[DI.getStackSlot()].insert(DI.Var); 256557a5bc3SPaul Kirth 257557a5bc3SPaul Kirth // Then add all the spills that have debug data 258557a5bc3SPaul Kirth for (MachineBasicBlock &MBB : MF) { 259557a5bc3SPaul Kirth for (MachineInstr &MI : MBB) { 260557a5bc3SPaul Kirth for (MachineMemOperand *MO : MI.memoperands()) { 261557a5bc3SPaul Kirth if (!MO->isStore()) 262557a5bc3SPaul Kirth continue; 263557a5bc3SPaul Kirth auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>( 264557a5bc3SPaul Kirth MO->getPseudoValue()); 265557a5bc3SPaul Kirth if (!FI) 266557a5bc3SPaul Kirth continue; 267557a5bc3SPaul Kirth int FrameIdx = FI->getFrameIndex(); 268557a5bc3SPaul Kirth SmallVector<MachineInstr *> Dbg; 269557a5bc3SPaul Kirth MI.collectDebugValues(Dbg); 270557a5bc3SPaul Kirth 271557a5bc3SPaul Kirth for (MachineInstr *MI : Dbg) 272557a5bc3SPaul Kirth SlotDebugMap[FrameIdx].insert(MI->getDebugVariable()); 273557a5bc3SPaul Kirth } 274557a5bc3SPaul Kirth } 275557a5bc3SPaul Kirth } 276557a5bc3SPaul Kirth 277557a5bc3SPaul Kirth return SlotDebugMap; 278557a5bc3SPaul Kirth } 279557a5bc3SPaul Kirth }; 280557a5bc3SPaul Kirth 281557a5bc3SPaul Kirth char StackFrameLayoutAnalysisPass::ID = 0; 282557a5bc3SPaul Kirth } // namespace 283557a5bc3SPaul Kirth 284557a5bc3SPaul Kirth char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID; 285557a5bc3SPaul Kirth INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout", 286557a5bc3SPaul Kirth "Stack Frame Layout", false, false) 287557a5bc3SPaul Kirth 288557a5bc3SPaul Kirth namespace llvm { 289557a5bc3SPaul Kirth /// Returns a newly-created StackFrameLayout pass. 290557a5bc3SPaul Kirth MachineFunctionPass *createStackFrameLayoutAnalysisPass() { 291557a5bc3SPaul Kirth return new StackFrameLayoutAnalysisPass(); 292557a5bc3SPaul Kirth } 293557a5bc3SPaul Kirth 294557a5bc3SPaul Kirth } // namespace llvm 295