-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[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
Changes from 3 commits
78c9f32
c84f7f1
cfe1305
7df895e
039f075
7a15a99
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) { | ||
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( | ||
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I moved frame index scavenging to processFunctionBeforeFrameFinalized |
||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.