| //===-- 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 |