Skip to content

[PATCH] [Xtensa] Implement FrameLowering methods and stack operation lowering. #92960

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 6 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
259 changes: 253 additions & 6 deletions llvm/lib/Target/Xtensa/XtensaFrameLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,240 @@

using namespace llvm;

XtensaFrameLowering::XtensaFrameLowering()
XtensaFrameLowering::XtensaFrameLowering(const XtensaSubtarget &STI)
: TargetFrameLowering(TargetFrameLowering::StackGrowsDown, Align(4), 0,
Align(4)) {}
Align(4)),
STI(STI), TII(*STI.getInstrInfo()), TRI(STI.getRegisterInfo()) {}

bool XtensaFrameLowering::hasFP(const MachineFunction &MF) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
return MF.getTarget().Options.DisableFramePointerElim(MF) ||
MFI.hasVarSizedObjects();
}

#ifndef NDEBUG
/* Check whether instruction I stores some callee-saved register from CSI
*/
static bool checkStoreInstruction(MachineBasicBlock::iterator I,
const std::vector<CalleeSavedInfo> &CSI) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name is too general. Also, this is way more code than I expected for this assert. Don't you expect these instructions to appear in order?

Can you use TII->isStoreToStackSlot and isCopyInstr to avoid special casing these specific opcodes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I will implement then isStoreToStackSlot /isLoadFromStackSlot functions. Current PrologueEpilogueInsterter implementation places stores in direct order:
.. for (const CalleeSavedInfo &CS : CSI)..
and uses reverse order for placement loads at the end of basic block. So, we can assume that we always have such order and create redundant version which checks instructions. Is it acceptable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're relying on seeing these exact instructions, it's OK to rely on the order. The point of the assert was to verify the instructions exactly match what you expected, which includes the order

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented isStoreToStackSlot /isLoadFromStackSlot functions and simplified callee-saved instructions checking.

bool IsStoreInstruction = false;

if (I->getOpcode() == TargetOpcode::COPY) {
Register DstReg = I->getOperand(0).getReg();
Register Reg = I->getOperand(1).getReg();
for (const auto &Info : CSI) {
if (Info.isSpilledToReg()) {
IsStoreInstruction =
(Info.getDstReg() == DstReg) && (Info.getReg() == Reg);
}
if (IsStoreInstruction)
break;
}
} else if (I->getOpcode() == Xtensa::S32I) {
Register Reg = I->getOperand(0).getReg();
for (const auto &Info : CSI) {
if (!Info.isSpilledToReg()) {
IsStoreInstruction = (Info.getReg() == Reg);
}
if (IsStoreInstruction)
break;
}
}

return IsStoreInstruction;
}

/* Check whether instruction I restores some callee-saved register from CSI
*/
static bool checkRestoreInstruction(MachineBasicBlock::iterator I,
const std::vector<CalleeSavedInfo> &CSI) {
bool IsRestoreInstruction = false;

if (I->getOpcode() == TargetOpcode::COPY) {
Register Reg = I->getOperand(0).getReg();
Register DstReg = I->getOperand(1).getReg();
for (const auto &Info : CSI) {
if (Info.isSpilledToReg()) {
IsRestoreInstruction =
(Info.getDstReg() == DstReg) && (Info.getReg() == Reg);
}
if (IsRestoreInstruction)
break;
}
} else if (I->getOpcode() == Xtensa::L32I) {
Register Reg = I->getOperand(0).getReg();
for (const auto &Info : CSI) {
if (!Info.isSpilledToReg()) {
IsRestoreInstruction = (Info.getReg() == Reg);
}
if (IsRestoreInstruction)
break;
}
}

return IsRestoreInstruction;
}
#endif

void XtensaFrameLowering::emitPrologue(MachineFunction &MF,
MachineBasicBlock &MBB) const {}
MachineBasicBlock &MBB) const {
assert(&MBB == &MF.front() && "Shrink-wrapping not yet implemented");
MachineFrameInfo &MFI = MF.getFrameInfo();
MachineBasicBlock::iterator MBBI = MBB.begin();
DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc();
MCRegister SP = Xtensa::SP;
MCRegister FP = TRI->getFrameRegister(MF);
MachineModuleInfo &MMI = MF.getMMI();
const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo();

// First, compute final stack size.
uint64_t StackSize = MFI.getStackSize();
uint64_t PrevStackSize = StackSize;

// Round up StackSize to 16*N
StackSize += (16 - StackSize) & 0xf;

// No need to allocate space on the stack.
if (StackSize == 0 && !MFI.adjustsStack())
return;

// Adjust stack.
TII.adjustStackPtr(SP, -StackSize, MBB, MBBI);

// emit ".cfi_def_cfa_offset StackSize"
unsigned CFIIndex =
MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, StackSize));
BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);

const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();

if (!CSI.empty()) {
// Find the instruction past the last instruction that saves a
// callee-saved register to the stack. The callee-saved store
// instructions are placed at the begin of basic block, so
// iterate over instruction sequence and check that
// save instructions are placed correctly.
for (unsigned i = 0; i < CSI.size(); ++i) {
assert(checkStoreInstruction(MBBI, CSI) &&
"Unexpected callee-saved register store instruction");
++MBBI;
}

// Iterate over list of callee-saved registers and emit .cfi_offset
// directives.
for (const auto &I : CSI) {
int64_t Offset = MFI.getObjectOffset(I.getFrameIdx());
Register Reg = I.getReg();

unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset(
nullptr, MRI->getDwarfRegNum(Reg, 1), Offset));
BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
}
}

// if framepointer enabled, set it to point to the stack pointer.
if (hasFP(MF)) {
// Insert instruction "move $fp, $sp" at this location.
BuildMI(MBB, MBBI, DL, TII.get(Xtensa::OR), FP)
.addReg(SP)
.addReg(SP)
.setMIFlag(MachineInstr::FrameSetup);

// emit ".cfi_def_cfa_register $fp"
unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createDefCfaRegister(
nullptr, MRI->getDwarfRegNum(FP, true)));
BuildMI(MBB, MBBI, DL, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
}

if (StackSize != PrevStackSize) {
MFI.setStackSize(StackSize);

for (int i = MFI.getObjectIndexBegin(); i < MFI.getObjectIndexEnd(); i++) {
if (!MFI.isDeadObjectIndex(i)) {
int64_t SPOffset = MFI.getObjectOffset(i);

if (SPOffset < 0)
MFI.setObjectOffset(i, SPOffset - StackSize + PrevStackSize);
}
}
}
}

void XtensaFrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock &MBB) const {}
MachineBasicBlock &MBB) const {
MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr();
MachineFrameInfo &MFI = MF.getFrameInfo();
DebugLoc DL = MBBI->getDebugLoc();
MCRegister SP = Xtensa::SP;
MCRegister FP = TRI->getFrameRegister(MF);

// if framepointer enabled, restore the stack pointer.
if (hasFP(MF)) {
// We should place restore stack pointer instruction just before
// sequence of instructions which restores callee-saved registers.
// This sequence is placed at the end of the basic block,
// so we should find first instruction of the sequence.
MachineBasicBlock::iterator I = MBBI;

const std::vector<CalleeSavedInfo> &CSI = MFI.getCalleeSavedInfo();

// Find the first instruction at the end that restores a callee-saved
// register.
for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
--I;
assert(checkRestoreInstruction(I, CSI) &&
"Unexpected callee-saved register restore instruction");
}

BuildMI(MBB, I, DL, TII.get(Xtensa::OR), SP).addReg(FP).addReg(FP);
}

// Get the number of bytes from FrameInfo
uint64_t StackSize = MFI.getStackSize();

if (!StackSize)
return;

// Adjust stack.
TII.adjustStackPtr(SP, StackSize, MBB, MBBI);
}

bool XtensaFrameLowering::spillCalleeSavedRegisters(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
ArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const {
MachineFunction *MF = MBB.getParent();
MachineBasicBlock &EntryBlock = *(MF->begin());

for (unsigned i = 0, e = CSI.size(); i != e; ++i) {
// Add the callee-saved register as live-in. Do not add if the register is
// A0 and return address is taken, because it will be implemented in
// method XtensaTargetLowering::LowerRETURNADDR.
// It's killed at the spill, unless the register is RA and return address
// is taken.
Register Reg = CSI[i].getReg();
bool IsA0AndRetAddrIsTaken =
(Reg == Xtensa::A0) && MF->getFrameInfo().isReturnAddressTaken();
if (!IsA0AndRetAddrIsTaken)
EntryBlock.addLiveIn(Reg);

// Insert the spill to the stack frame.
bool IsKill = !IsA0AndRetAddrIsTaken;
const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg);
TII.storeRegToStackSlot(EntryBlock, MI, Reg, IsKill, CSI[i].getFrameIdx(),
RC, TRI, Register());
}

return true;
}

bool XtensaFrameLowering::restoreCalleeSavedRegisters(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
MutableArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const {
return TargetFrameLowering::restoreCalleeSavedRegisters(MBB, MI, CSI, TRI);
}

// Eliminate ADJCALLSTACKDOWN, ADJCALLSTACKUP pseudo instructions
MachineBasicBlock::iterator XtensaFrameLowering::eliminateCallFramePseudoInstr(
Expand All @@ -51,9 +270,37 @@ MachineBasicBlock::iterator XtensaFrameLowering::eliminateCallFramePseudoInstr(
if (I->getOpcode() == Xtensa::ADJCALLSTACKDOWN)
Amount = -Amount;

unsigned SP = Xtensa::SP;
TII.adjustStackPtr(SP, Amount, MBB, I);
TII.adjustStackPtr(Xtensa::SP, Amount, MBB, I);
}

return MBB.erase(I);
}

void XtensaFrameLowering::determineCalleeSaves(MachineFunction &MF,
BitVector &SavedRegs,
RegScavenger *RS) const {
unsigned FP = TRI->getFrameRegister(MF);

TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS);

// Mark $fp as used if function has dedicated frame pointer.
if (hasFP(MF))
SavedRegs.set(FP);
}

void XtensaFrameLowering::processFunctionBeforeFrameFinalized(
MachineFunction &MF, RegScavenger *RS) const {
// Set scavenging frame index if necessary.
MachineFrameInfo &MFI = MF.getFrameInfo();
uint64_t MaxSPOffset = MFI.estimateStackSize(MF);

if (isInt<12>(MaxSPOffset))
return;

const TargetRegisterClass &RC = Xtensa::ARRegClass;
unsigned Size = TRI->getSpillSize(RC);
Align Alignment = TRI->getSpillAlign(RC);
int FI = MF.getFrameInfo().CreateStackObject(Size, Alignment, false);

RS->addScavengingFrameIndex(FI);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's safe to do this in determineCalleeSaves. Can you create the emergency scavenging index in processFunctionBeforeFrameFinalized instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved frame index scavenging to processFunctionBeforeFrameFinalized

}
24 changes: 23 additions & 1 deletion llvm/lib/Target/Xtensa/XtensaFrameLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@
namespace llvm {
class XtensaTargetMachine;
class XtensaSubtarget;
class XtensaInstrInfo;
class XtensaRegisterInfo;

class XtensaFrameLowering : public TargetFrameLowering {
const XtensaSubtarget &STI;
const XtensaInstrInfo &TII;
const XtensaRegisterInfo *TRI;

public:
XtensaFrameLowering();
XtensaFrameLowering(const XtensaSubtarget &STI);

bool hasFP(const MachineFunction &MF) const override;

Expand All @@ -29,6 +35,22 @@ class XtensaFrameLowering : public TargetFrameLowering {
MachineBasicBlock::iterator
eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
MachineBasicBlock::iterator I) const override;

bool spillCalleeSavedRegisters(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
ArrayRef<CalleeSavedInfo> CSI,
const TargetRegisterInfo *TRI) const override;
bool
restoreCalleeSavedRegisters(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
MutableArrayRef<CalleeSavedInfo> CSI,
const TargetRegisterInfo *TRI) const override;

void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs,
RegScavenger *RS) const override;

void processFunctionBeforeFrameFinalized(MachineFunction &MF,
RegScavenger *RS) const override;
};

} // namespace llvm
Expand Down
49 changes: 49 additions & 0 deletions llvm/lib/Target/Xtensa/XtensaISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ XtensaTargetLowering::XtensaTargetLowering(const TargetMachine &TM,

setOperationAction(ISD::ConstantPool, PtrVT, Custom);

// Implement custom stack allocations
setOperationAction(ISD::DYNAMIC_STACKALLOC, PtrVT, Custom);
// Implement custom stack save and restore
setOperationAction(ISD::STACKSAVE, MVT::Other, Custom);
setOperationAction(ISD::STACKRESTORE, MVT::Other, Custom);

// Compute derived properties from the register classes
computeRegisterProperties(STI.getRegisterInfo());
}
Expand Down Expand Up @@ -534,13 +540,56 @@ SDValue XtensaTargetLowering::LowerConstantPool(ConstantPoolSDNode *CP,
return getAddrPCRel(Result, DAG);
}

SDValue XtensaTargetLowering::LowerSTACKSAVE(SDValue Op,
SelectionDAG &DAG) const {
return DAG.getCopyFromReg(Op.getOperand(0), SDLoc(Op), Xtensa::SP,
Op.getValueType());
}

SDValue XtensaTargetLowering::LowerSTACKRESTORE(SDValue Op,
SelectionDAG &DAG) const {
return DAG.getCopyToReg(Op.getOperand(0), SDLoc(Op), Xtensa::SP,
Op.getOperand(1));
}

SDValue XtensaTargetLowering::LowerDYNAMIC_STACKALLOC(SDValue Op,
SelectionDAG &DAG) const {
SDValue Chain = Op.getOperand(0); // Legalize the chain.
SDValue Size = Op.getOperand(1); // Legalize the size.
EVT VT = Size->getValueType(0);
SDLoc DL(Op);

// Round up Size to 32
SDValue SizeTmp =
DAG.getNode(ISD::ADD, DL, VT, Size, DAG.getConstant(31, DL, MVT::i32));
SDValue SizeRoundUp = DAG.getNode(ISD::AND, DL, VT, SizeTmp,
DAG.getConstant(~31, DL, MVT::i32));

unsigned SPReg = Xtensa::SP;
SDValue SP = DAG.getCopyFromReg(Chain, DL, SPReg, VT);
SDValue NewSP = DAG.getNode(ISD::SUB, DL, VT, SP, SizeRoundUp); // Value
Chain = DAG.getCopyToReg(SP.getValue(1), DL, SPReg, NewSP); // Output chain

SDValue NewVal = DAG.getCopyFromReg(Chain, DL, SPReg, MVT::i32);
Chain = NewVal.getValue(1);

SDValue Ops[2] = {NewVal, Chain};
return DAG.getMergeValues(Ops, DL);
}

SDValue XtensaTargetLowering::LowerOperation(SDValue Op,
SelectionDAG &DAG) const {
switch (Op.getOpcode()) {
case ISD::Constant:
return LowerImmediate(Op, DAG);
case ISD::ConstantPool:
return LowerConstantPool(cast<ConstantPoolSDNode>(Op), DAG);
case ISD::STACKSAVE:
return LowerSTACKSAVE(Op, DAG);
case ISD::STACKRESTORE:
return LowerSTACKRESTORE(Op, DAG);
case ISD::DYNAMIC_STACKALLOC:
return LowerDYNAMIC_STACKALLOC(Op, DAG);
default:
report_fatal_error("Unexpected node to lower");
}
Expand Down
Loading
Loading