| //===-- StackFrameLayoutAnalysisPass.cpp | |
| //------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // StackFrameLayoutAnalysisPass implementation. Outputs information about the | |
| // layout of the stack frame, using the remarks interface. On the CLI it prints | |
| // a textual representation of the stack frame. When possible it prints the | |
| // values that occupy a stack slot using any available debug information. Since | |
| // output is remarks based, it is also available in a machine readable file | |
| // format, such as YAML. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ADT/SetVector.h" | |
| #include "llvm/Analysis/OptimizationRemarkEmitter.h" | |
| #include "llvm/CodeGen/MachineFrameInfo.h" | |
| #include "llvm/CodeGen/MachineFunction.h" | |
| #include "llvm/CodeGen/MachineFunctionPass.h" | |
| #include "llvm/CodeGen/MachineOptimizationRemarkEmitter.h" | |
| #include "llvm/CodeGen/Passes.h" | |
| #include "llvm/CodeGen/SlotIndexes.h" | |
| #include "llvm/CodeGen/StackProtector.h" | |
| #include "llvm/CodeGen/TargetFrameLowering.h" | |
| #include "llvm/CodeGen/TargetSubtargetInfo.h" | |
| #include "llvm/IR/DebugInfoMetadata.h" | |
| #include "llvm/IR/PrintPasses.h" | |
| #include "llvm/InitializePasses.h" | |
| #include "llvm/Support/Debug.h" | |
| #include "llvm/Support/FormatVariadic.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <sstream> | |
| using namespace llvm; | |
| #define DEBUG_TYPE "stack-frame-layout" | |
| namespace { | |
| /// StackFrameLayoutAnalysisPass - This is a pass to dump the stack frame of a | |
| /// MachineFunction. | |
| /// | |
| struct StackFrameLayoutAnalysisPass : public MachineFunctionPass { | |
| using SlotDbgMap = SmallDenseMap<int, SetVector<const DILocalVariable *>>; | |
| static char ID; | |
| enum SlotType { | |
| Spill, // a Spill slot | |
| StackProtector, // Stack Protector slot | |
| Variable, // a slot used to store a local data (could be a tmp) | |
| Invalid // It's an error for a slot to have this type | |
| }; | |
| struct SlotData { | |
| int Slot; | |
| int Size; | |
| int Align; | |
| int Offset; | |
| SlotType SlotTy; | |
| SlotData(const MachineFrameInfo &MFI, const int ValOffset, const int Idx) | |
| : Slot(Idx), Size(MFI.getObjectSize(Idx)), | |
| Align(MFI.getObjectAlign(Idx).value()), | |
| Offset(MFI.getObjectOffset(Idx) - ValOffset), SlotTy(Invalid) { | |
| if (MFI.isSpillSlotObjectIndex(Idx)) | |
| SlotTy = SlotType::Spill; | |
| else if (Idx == MFI.getStackProtectorIndex()) | |
| SlotTy = SlotType::StackProtector; | |
| else | |
| SlotTy = SlotType::Variable; | |
| } | |
| // we use this to sort in reverse order, so that the layout is displayed | |
| // correctly | |
| bool operator<(const SlotData &Rhs) const { return Offset > Rhs.Offset; } | |
| }; | |
| StackFrameLayoutAnalysisPass() : MachineFunctionPass(ID) {} | |
| StringRef getPassName() const override { | |
| return "Stack Frame Layout Analysis"; | |
| } | |
| void getAnalysisUsage(AnalysisUsage &AU) const override { | |
| AU.setPreservesAll(); | |
| MachineFunctionPass::getAnalysisUsage(AU); | |
| AU.addRequired<MachineOptimizationRemarkEmitterPass>(); | |
| } | |
| bool runOnMachineFunction(MachineFunction &MF) override { | |
| // TODO: We should implement a similar filter for remarks: | |
| // -Rpass-func-filter=<regex> | |
| if (!isFunctionInPrintList(MF.getName())) | |
| return false; | |
| LLVMContext &Ctx = MF.getFunction().getContext(); | |
| if (!Ctx.getDiagHandlerPtr()->isAnalysisRemarkEnabled(DEBUG_TYPE)) | |
| return false; | |
| MachineOptimizationRemarkAnalysis Rem(DEBUG_TYPE, "StackLayout", | |
| MF.getFunction().getSubprogram(), | |
| &MF.front()); | |
| Rem << ("\nFunction: " + MF.getName()).str(); | |
| emitStackFrameLayoutRemarks(MF, Rem); | |
| getAnalysis<MachineOptimizationRemarkEmitterPass>().getORE().emit(Rem); | |
| return false; | |
| } | |
| std::string getTypeString(SlotType Ty) { | |
| switch (Ty) { | |
| case SlotType::Spill: | |
| return "Spill"; | |
| case SlotType::StackProtector: | |
| return "Protector"; | |
| case SlotType::Variable: | |
| return "Variable"; | |
| default: | |
| llvm_unreachable("bad slot type for stack layout"); | |
| } | |
| } | |
| void emitStackSlotRemark(const MachineFunction &MF, const SlotData &D, | |
| MachineOptimizationRemarkAnalysis &Rem) { | |
| // To make it easy to understand the stack layout from the CLI, we want to | |
| // print each slot like the following: | |
| // | |
| // Offset: [SP+8], Type: Spill, Align: 8, Size: 16 | |
| // foo @ /path/to/file.c:25 | |
| // bar @ /path/to/file.c:35 | |
| // | |
| // Which prints the size, alignment, and offset from the SP at function | |
| // entry. | |
| // | |
| // But we also want the machine readable remarks data to be nicely | |
| // organized. So we print some additional data as strings for the CLI | |
| // output, but maintain more structured data for the YAML. | |
| // | |
| // For example we store the Offset in YAML as: | |
| // ... | |
| // - Offset: -8 | |
| // | |
| // But we print it to the CLI as | |
| // Offset: [SP-8] | |
| // Negative offsets will print a leading `-`, so only add `+` | |
| std::string Prefix = | |
| formatv("\nOffset: [SP{0}", (D.Offset < 0) ? "" : "+").str(); | |
| Rem << Prefix << ore::NV("Offset", D.Offset) | |
| << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy)) | |
| << ", Align: " << ore::NV("Align", D.Align) | |
| << ", Size: " << ore::NV("Size", D.Size); | |
| } | |
| void emitSourceLocRemark(const MachineFunction &MF, const DILocalVariable *N, | |
| MachineOptimizationRemarkAnalysis &Rem) { | |
| std::string Loc = | |
| formatv("{0} @ {1}:{2}", N->getName(), N->getFilename(), N->getLine()) | |
| .str(); | |
| Rem << "\n " << ore::NV("DataLoc", Loc); | |
| } | |
| void emitStackFrameLayoutRemarks(MachineFunction &MF, | |
| MachineOptimizationRemarkAnalysis &Rem) { | |
| const MachineFrameInfo &MFI = MF.getFrameInfo(); | |
| if (!MFI.hasStackObjects()) | |
| return; | |
| // ValOffset is the offset to the local area from the SP at function entry. | |
| // To display the true offset from SP, we need to subtract ValOffset from | |
| // MFI's ObjectOffset. | |
| const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering(); | |
| const int ValOffset = (FI ? FI->getOffsetOfLocalArea() : 0); | |
| LLVM_DEBUG(dbgs() << "getStackProtectorIndex ==" | |
| << MFI.getStackProtectorIndex() << "\n"); | |
| std::vector<SlotData> SlotInfo; | |
| const unsigned int NumObj = MFI.getNumObjects(); | |
| SlotInfo.reserve(NumObj); | |
| // initialize slot info | |
| for (int Idx = MFI.getObjectIndexBegin(), EndIdx = MFI.getObjectIndexEnd(); | |
| Idx != EndIdx; ++Idx) { | |
| if (MFI.isDeadObjectIndex(Idx)) | |
| continue; | |
| SlotInfo.emplace_back(MFI, ValOffset, Idx); | |
| } | |
| // sort the ordering, to match the actual layout in memory | |
| llvm::sort(SlotInfo); | |
| SlotDbgMap SlotMap = genSlotDbgMapping(MF); | |
| for (const SlotData &Info : SlotInfo) { | |
| emitStackSlotRemark(MF, Info, Rem); | |
| for (const DILocalVariable *N : SlotMap[Info.Slot]) | |
| emitSourceLocRemark(MF, N, Rem); | |
| } | |
| } | |
| // We need to generate a mapping of slots to the values that are stored to | |
| // them. This information is lost by the time we need to print out the frame, | |
| // so we reconstruct it here by walking the CFG, and generating the mapping. | |
| SlotDbgMap genSlotDbgMapping(MachineFunction &MF) { | |
| SlotDbgMap SlotDebugMap; | |
| // add variables to the map | |
| for (MachineFunction::VariableDbgInfo &DI : MF.getVariableDbgInfo()) | |
| SlotDebugMap[DI.Slot].insert(DI.Var); | |
| // Then add all the spills that have debug data | |
| for (MachineBasicBlock &MBB : MF) { | |
| for (MachineInstr &MI : MBB) { | |
| for (MachineMemOperand *MO : MI.memoperands()) { | |
| if (!MO->isStore()) | |
| continue; | |
| auto *FI = dyn_cast_or_null<FixedStackPseudoSourceValue>( | |
| MO->getPseudoValue()); | |
| if (!FI) | |
| continue; | |
| int FrameIdx = FI->getFrameIndex(); | |
| SmallVector<MachineInstr *> Dbg; | |
| MI.collectDebugValues(Dbg); | |
| for (MachineInstr *MI : Dbg) | |
| SlotDebugMap[FrameIdx].insert(MI->getDebugVariable()); | |
| } | |
| } | |
| } | |
| return SlotDebugMap; | |
| } | |
| }; | |
| char StackFrameLayoutAnalysisPass::ID = 0; | |
| } // namespace | |
| char &llvm::StackFrameLayoutAnalysisPassID = StackFrameLayoutAnalysisPass::ID; | |
| INITIALIZE_PASS(StackFrameLayoutAnalysisPass, "stack-frame-layout", | |
| "Stack Frame Layout", false, false) | |
| namespace llvm { | |
| /// Returns a newly-created StackFrameLayout pass. | |
| MachineFunctionPass *createStackFrameLayoutAnalysisPass() { | |
| return new StackFrameLayoutAnalysisPass(); | |
| } | |
| } // namespace llvm |