313 lines · cpp
1//===-- StackFrameLayoutAnalysisPass.cpp2//------------------------------------===//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-exception7//8//===----------------------------------------------------------------------===//9//10// StackFrameLayoutAnalysisPass implementation. Outputs information about the11// layout of the stack frame, using the remarks interface. On the CLI it prints12// a textual representation of the stack frame. When possible it prints the13// values that occupy a stack slot using any available debug information. Since14// output is remarks based, it is also available in a machine readable file15// 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 38using namespace llvm;39 40#define DEBUG_TYPE "stack-frame-layout"41 42namespace {43 44/// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a45/// MachineFunction.46///47struct 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 slot55 Fixed, // a Fixed slot (e.g. arguments passed on the stack)56 VariableSized, // a variable sized object57 StackProtector, // Stack Protector slot58 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 type60 };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.hasScalableStackID(Idx);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 else86 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 displayed92 // correctly. Variable sized slots are sorted to the end of the list, as93 // offsets are currently incorrect for these but they reside at the end of94 // the stack frame. The Slot index is used to ensure deterministic order95 // 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 to144 // print each slot like the following:145 //146 // Offset: [SP+8], Type: Spill, Align: 8, Size: 16147 // foo @ /path/to/file.c:25148 // bar @ /path/to/file.c:35149 //150 // Which prints the size, alignment, and offset from the SP at function151 // entry.152 //153 // But we also want the machine readable remarks data to be nicely154 // organized. So we print some additional data as strings for the CLI155 // output, but maintain more structured data for the YAML.156 //157 // For example we store the Offset in YAML as:158 // ...159 // - Offset: -8160 // - ScalableOffset: -16161 // Note: the ScalableOffset entries are added only for slots with non-zero162 // 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 info218 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 memory226 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 to238 // 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 map244 for (MachineFunction::VariableDbgInfo &DI :245 MF.getInStackSlotVariableDbgInfo())246 SlotDebugMap[DI.getStackSlot()].insert(DI.Var);247 248 // Then add all the spills that have debug data249 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 272class StackFrameLayoutAnalysisLegacy : public MachineFunctionPass {273public: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 294char StackFrameLayoutAnalysisLegacy::ID = 0;295} // namespace296 297PreservedAnalyses298llvm::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 305char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisLegacy::ID;306INITIALIZE_PASS(StackFrameLayoutAnalysisLegacy, "stack-frame-layout",307 "Stack Frame Layout", false, false)308 309/// Returns a newly-created StackFrameLayout pass.310MachineFunctionPass *llvm::createStackFrameLayoutAnalysisPass() {311 return new StackFrameLayoutAnalysisLegacy();312}313