diff options
Diffstat (limited to 'llvm/lib/CodeGen/StackFrameLayoutAnalysisPass.cpp')
-rw-r--r-- | llvm/lib/CodeGen/StackFrameLayoutAnalysisPass.cpp | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/llvm/lib/CodeGen/StackFrameLayoutAnalysisPass.cpp b/llvm/lib/CodeGen/StackFrameLayoutAnalysisPass.cpp new file mode 100644 index 000000000000..3a48dd5b0a03 --- /dev/null +++ b/llvm/lib/CodeGen/StackFrameLayoutAnalysisPass.cpp @@ -0,0 +1,253 @@ +//===-- 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 |