Skip to content

release/19.x: [StackFrameLayoutAnalysis] Support more SlotTypes (#100562) #101042

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions llvm/include/llvm/CodeGen/TargetFrameLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,13 @@ class TargetFrameLowering {
return getFrameIndexReference(MF, FI, FrameReg);
}

/// getFrameIndexReferenceFromSP - This method returns the offset from the
/// stack pointer to the slot of the specified index. This function serves to
/// provide a comparable offset from a single reference point (the value of
/// the stack-pointer at function entry) that can be used for analysis.
virtual StackOffset getFrameIndexReferenceFromSP(const MachineFunction &MF,
int FI) const;

/// Returns the callee-saved registers as computed by determineCalleeSaves
/// in the BitVector \p SavedRegs.
virtual void getCalleeSaves(const MachineFunction &MF,
Expand Down
72 changes: 54 additions & 18 deletions llvm/lib/CodeGen/StackFrameLayoutAnalysisPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {

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
Expand All @@ -60,29 +62,42 @@ struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {
int Slot;
int Size;
int Align;
int Offset;
StackOffset Offset;
SlotType SlotTy;
bool Scalable;

SlotData(const MachineFrameInfo &MFI, const int ValOffset, const int Idx)
SlotData(const MachineFrameInfo &MFI, const StackOffset Offset,
const int Idx)
: Slot(Idx), Size(MFI.getObjectSize(Idx)),
Align(MFI.getObjectAlign(Idx).value()),
Offset(MFI.getObjectOffset(Idx) - ValOffset), SlotTy(Invalid),
Scalable(false) {
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 (Idx == MFI.getStackProtectorIndex())
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. Scalable slots are sorted to the end of the list.
// 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(!Scalable, Offset) >
std::make_tuple(!Rhs.Scalable, Rhs.Offset);
return std::make_tuple(!isVarSize(),
Offset.getFixed() + Offset.getScalable(), Slot) >
std::make_tuple(!Rhs.isVarSize(),
Rhs.Offset.getFixed() + Rhs.Offset.getScalable(),
Rhs.Slot);
}
};

Expand Down Expand Up @@ -121,6 +136,10 @@ struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {
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:
Expand Down Expand Up @@ -149,15 +168,27 @@ struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {
// 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
// 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 < 0) ? "" : "+").str();
Rem << Prefix << ore::NV("Offset", D.Offset)
<< "], Type: " << ore::NV("Type", getTypeString(D.SlotTy))
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));
}
Expand All @@ -170,17 +201,22 @@ struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {
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;

// 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");
Expand All @@ -194,7 +230,7 @@ struct StackFrameLayoutAnalysisPass : public MachineFunctionPass {
Idx != EndIdx; ++Idx) {
if (MFI.isDeadObjectIndex(Idx))
continue;
SlotInfo.emplace_back(MFI, ValOffset, Idx);
SlotInfo.emplace_back(MFI, getStackOffset(MF, MFI, FI, Idx), Idx);
}

// sort the ordering, to match the actual layout in memory
Expand Down
14 changes: 14 additions & 0 deletions llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@ TargetFrameLowering::getFrameIndexReference(const MachineFunction &MF, int FI,
MFI.getOffsetAdjustment());
}

/// Returns the offset from the stack pointer to the slot of the specified
/// index. This function serves to provide a comparable offset from a single
/// reference point (the value of the stack-pointer at function entry) that can
/// be used for analysis. This is the default implementation using
/// MachineFrameInfo offsets.
StackOffset
TargetFrameLowering::getFrameIndexReferenceFromSP(const MachineFunction &MF,
int FI) const {
// To display the true offset from SP, we need to subtract the offset to the
// local area from MFI's ObjectOffset.
return StackOffset::getFixed(MF.getFrameInfo().getObjectOffset(FI) -
getOffsetOfLocalArea());
}

bool TargetFrameLowering::needsFrameIndexResolution(
const MachineFunction &MF) const {
return MF.getFrameInfo().hasStackObjects();
Expand Down
35 changes: 35 additions & 0 deletions llvm/lib/Target/AArch64/AArch64FrameLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2603,6 +2603,41 @@ AArch64FrameLowering::getFrameIndexReference(const MachineFunction &MF, int FI,
/*ForSimm=*/false);
}

StackOffset
AArch64FrameLowering::getFrameIndexReferenceFromSP(const MachineFunction &MF,
int FI) const {
// This function serves to provide a comparable offset from a single reference
// point (the value of SP at function entry) that can be used for analysis,
// e.g. the stack-frame-layout analysis pass. It is not guaranteed to be
// correct for all objects in the presence of VLA-area objects or dynamic
// stack re-alignment.

const auto &MFI = MF.getFrameInfo();

int64_t ObjectOffset = MFI.getObjectOffset(FI);

// This is correct in the absence of any SVE stack objects.
StackOffset SVEStackSize = getSVEStackSize(MF);
if (!SVEStackSize)
return StackOffset::getFixed(ObjectOffset - getOffsetOfLocalArea());

const auto *AFI = MF.getInfo<AArch64FunctionInfo>();
if (MFI.getStackID(FI) == TargetStackID::ScalableVector) {
return StackOffset::get(-((int64_t)AFI->getCalleeSavedStackSize()),
ObjectOffset);
}

bool IsFixed = MFI.isFixedObjectIndex(FI);
bool IsCSR =
!IsFixed && ObjectOffset >= -((int)AFI->getCalleeSavedStackSize(MFI));

StackOffset ScalableOffset = {};
if (!IsFixed && !IsCSR)
ScalableOffset = -SVEStackSize;

return StackOffset::getFixed(ObjectOffset) + ScalableOffset;
}

StackOffset
AArch64FrameLowering::getNonLocalFrameIndexReference(const MachineFunction &MF,
int FI) const {
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/AArch64/AArch64FrameLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class AArch64FrameLowering : public TargetFrameLowering {

StackOffset getFrameIndexReference(const MachineFunction &MF, int FI,
Register &FrameReg) const override;
StackOffset getFrameIndexReferenceFromSP(const MachineFunction &MF,
int FI) const override;
StackOffset resolveFrameIndexReference(const MachineFunction &MF, int FI,
Register &FrameReg, bool PreferFP,
bool ForSimm) const;
Expand Down
Loading
Loading