mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-03 00:26:05 +00:00
295 lines
10 KiB
C++
295 lines
10 KiB
C++
//===-- 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"
|
|
|
|
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
|
|
Fixed, // a Fixed slot (e.g. arguments passed on the stack)
|
|
VariableSized, // a variable sized object
|
|
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;
|
|
StackOffset Offset;
|
|
SlotType SlotTy;
|
|
bool Scalable;
|
|
|
|
SlotData(const MachineFrameInfo &MFI, const StackOffset Offset,
|
|
const int Idx)
|
|
: Slot(Idx), Size(MFI.getObjectSize(Idx)),
|
|
Align(MFI.getObjectAlign(Idx).value()), Offset(Offset),
|
|
SlotTy(Invalid), Scalable(false) {
|
|
Scalable = MFI.getStackID(Idx) == TargetStackID::ScalableVector;
|
|
if (MFI.isSpillSlotObjectIndex(Idx))
|
|
SlotTy = SlotType::Spill;
|
|
else if (MFI.isFixedObjectIndex(Idx))
|
|
SlotTy = SlotType::Fixed;
|
|
else if (MFI.isVariableSizedObjectIndex(Idx))
|
|
SlotTy = SlotType::VariableSized;
|
|
else if (MFI.hasStackProtectorIndex() &&
|
|
Idx == MFI.getStackProtectorIndex())
|
|
SlotTy = SlotType::StackProtector;
|
|
else
|
|
SlotTy = SlotType::Variable;
|
|
}
|
|
|
|
bool isVarSize() const { return SlotTy == SlotType::VariableSized; }
|
|
|
|
// We use this to sort in reverse order, so that the layout is displayed
|
|
// correctly. Variable sized slots are sorted to the end of the list, as
|
|
// offsets are currently incorrect for these but they reside at the end of
|
|
// the stack frame. The Slot index is used to ensure deterministic order
|
|
// when offsets are equal.
|
|
bool operator<(const SlotData &Rhs) const {
|
|
return std::make_tuple(!isVarSize(),
|
|
Offset.getFixed() + Offset.getScalable(), Slot) >
|
|
std::make_tuple(!Rhs.isVarSize(),
|
|
Rhs.Offset.getFixed() + Rhs.Offset.getScalable(),
|
|
Rhs.Slot);
|
|
}
|
|
};
|
|
|
|
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::Fixed:
|
|
return "Fixed";
|
|
case SlotType::VariableSized:
|
|
return "VariableSized";
|
|
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
|
|
// - ScalableOffset: -16
|
|
// Note: the ScalableOffset entries are added only for slots with non-zero
|
|
// scalable offsets.
|
|
//
|
|
// But we print it to the CLI as:
|
|
// Offset: [SP-8]
|
|
//
|
|
// Or with non-zero scalable offset:
|
|
// Offset: [SP-8-16 x vscale]
|
|
|
|
// Negative offsets will print a leading `-`, so only add `+`
|
|
std::string Prefix =
|
|
formatv("\nOffset: [SP{0}", (D.Offset.getFixed() < 0) ? "" : "+").str();
|
|
Rem << Prefix << ore::NV("Offset", D.Offset.getFixed());
|
|
|
|
if (D.Offset.getScalable()) {
|
|
Rem << ((D.Offset.getScalable() < 0) ? "" : "+")
|
|
<< ore::NV("ScalableOffset", D.Offset.getScalable()) << " x vscale";
|
|
}
|
|
|
|
Rem << "], Type: " << ore::NV("Type", getTypeString(D.SlotTy))
|
|
<< ", Align: " << ore::NV("Align", D.Align)
|
|
<< ", Size: " << ore::NV("Size", ElementCount::get(D.Size, D.Scalable));
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
StackOffset getStackOffset(const MachineFunction &MF,
|
|
const MachineFrameInfo &MFI,
|
|
const TargetFrameLowering *FI, int FrameIdx) {
|
|
if (!FI)
|
|
return StackOffset::getFixed(MFI.getObjectOffset(FrameIdx));
|
|
|
|
return FI->getFrameIndexReferenceFromSP(MF, FrameIdx);
|
|
}
|
|
|
|
void emitStackFrameLayoutRemarks(MachineFunction &MF,
|
|
MachineOptimizationRemarkAnalysis &Rem) {
|
|
const MachineFrameInfo &MFI = MF.getFrameInfo();
|
|
if (!MFI.hasStackObjects())
|
|
return;
|
|
|
|
const TargetFrameLowering *FI = MF.getSubtarget().getFrameLowering();
|
|
|
|
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, getStackOffset(MF, MFI, FI, Idx), 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.getInStackSlotVariableDbgInfo())
|
|
SlotDebugMap[DI.getStackSlot()].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
|