mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-04-17 05:40:48 +00:00

We were iterating over a SmallPtrSet when outputting slot variables. This is still correct but made the test fail under reverse iteration. This patch replaces the SmallPtrSet with a SmallVector. Also remove the "Stack Frame Layout" lines from arm64-opt-remarks-lazy-bfi test, since those also break under reverse iteration. Reviewed By: nickdesaulniers Differential Revision: https://reviews.llvm.org/D142127
254 lines
8.7 KiB
C++
254 lines
8.7 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"
|
|
|
|
#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
|