#include "AVRFrameLowering.h"
#include "AVR.h"
#include "AVRInstrInfo.h"
#include "AVRMachineFunctionInfo.h"
#include "AVRTargetMachine.h"
#include "MCTargetDesc/AVRMCTargetDesc.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/IR/Function.h"
#include <vector>
namespace llvm {
AVRFrameLowering::AVRFrameLowering()
: TargetFrameLowering(TargetFrameLowering::StackGrowsDown, Align(1), -2) {}
bool AVRFrameLowering::canSimplifyCallFramePseudos(
const MachineFunction &MF) const {
return true;
}
bool AVRFrameLowering::hasReservedCallFrame(const MachineFunction &MF) const {
const MachineFrameInfo &MFI = MF.getFrameInfo();
return hasFP(MF) && !MFI.hasVarSizedObjects();
}
void AVRFrameLowering::emitPrologue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
MachineBasicBlock::iterator MBBI = MBB.begin();
DebugLoc DL = (MBBI != MBB.end()) ? MBBI->getDebugLoc() : DebugLoc();
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
const AVRInstrInfo &TII = *STI.getInstrInfo();
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
bool HasFP = hasFP(MF);
if (AFI->isInterruptHandler()) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::BSETs))
.addImm(0x07)
.setMIFlag(MachineInstr::FrameSetup);
}
if (AFI->isInterruptOrSignalHandler()) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::PUSHWRr))
.addReg(AVR::R1R0, RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup);
BuildMI(MBB, MBBI, DL, TII.get(AVR::INRdA), AVR::R0)
.addImm(STI.getIORegSREG())
.setMIFlag(MachineInstr::FrameSetup);
BuildMI(MBB, MBBI, DL, TII.get(AVR::PUSHRr))
.addReg(AVR::R0, RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup);
BuildMI(MBB, MBBI, DL, TII.get(AVR::EORRdRr))
.addReg(AVR::R1, RegState::Define)
.addReg(AVR::R1, RegState::Kill)
.addReg(AVR::R1, RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup);
}
if (!HasFP) {
return;
}
const MachineFrameInfo &MFI = MF.getFrameInfo();
unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize();
while (
(MBBI != MBB.end()) && MBBI->getFlag(MachineInstr::FrameSetup) &&
(MBBI->getOpcode() == AVR::PUSHRr || MBBI->getOpcode() == AVR::PUSHWRr)) {
++MBBI;
}
BuildMI(MBB, MBBI, DL, TII.get(AVR::SPREAD), AVR::R29R28)
.addReg(AVR::SP)
.setMIFlag(MachineInstr::FrameSetup);
for (MachineBasicBlock &MBBJ : llvm::drop_begin(MF)) {
MBBJ.addLiveIn(AVR::R29R28);
}
if (!FrameSize) {
return;
}
unsigned Opcode = (isUInt<6>(FrameSize)) ? AVR::SBIWRdK : AVR::SUBIWRdK;
MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(Opcode), AVR::R29R28)
.addReg(AVR::R29R28, RegState::Kill)
.addImm(FrameSize)
.setMIFlag(MachineInstr::FrameSetup);
MI->getOperand(3).setIsDead();
BuildMI(MBB, MBBI, DL, TII.get(AVR::SPWRITE), AVR::SP)
.addReg(AVR::R29R28)
.setMIFlag(MachineInstr::FrameSetup);
}
static void restoreStatusRegister(MachineFunction &MF, MachineBasicBlock &MBB) {
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr();
DebugLoc DL = MBBI->getDebugLoc();
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
const AVRInstrInfo &TII = *STI.getInstrInfo();
if (AFI->isInterruptOrSignalHandler()) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0);
BuildMI(MBB, MBBI, DL, TII.get(AVR::OUTARr))
.addImm(STI.getIORegSREG())
.addReg(AVR::R0, RegState::Kill);
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPWRd), AVR::R1R0);
}
}
void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
if (!hasFP(MF) && !AFI->isInterruptOrSignalHandler()) {
return;
}
MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr();
assert(MBBI->getDesc().isReturn() &&
"Can only insert epilog into returning blocks");
DebugLoc DL = MBBI->getDebugLoc();
const MachineFrameInfo &MFI = MF.getFrameInfo();
unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize();
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
const AVRInstrInfo &TII = *STI.getInstrInfo();
if (!FrameSize && !MF.getFrameInfo().hasVarSizedObjects()) {
restoreStatusRegister(MF, MBB);
return;
}
while (MBBI != MBB.begin()) {
MachineBasicBlock::iterator PI = std::prev(MBBI);
int Opc = PI->getOpcode();
if (Opc != AVR::POPRd && Opc != AVR::POPWRd && !PI->isTerminator()) {
break;
}
--MBBI;
}
if (FrameSize) {
unsigned Opcode;
if (isUInt<6>(FrameSize)) {
Opcode = AVR::ADIWRdK;
} else {
Opcode = AVR::SUBIWRdK;
FrameSize = -FrameSize;
}
MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(Opcode), AVR::R29R28)
.addReg(AVR::R29R28, RegState::Kill)
.addImm(FrameSize);
MI->getOperand(3).setIsDead();
}
BuildMI(MBB, MBBI, DL, TII.get(AVR::SPWRITE), AVR::SP)
.addReg(AVR::R29R28, RegState::Kill);
restoreStatusRegister(MF, MBB);
}
bool AVRFrameLowering::hasFP(const MachineFunction &MF) const {
const AVRMachineFunctionInfo *FuncInfo = MF.getInfo<AVRMachineFunctionInfo>();
return (FuncInfo->getHasSpills() || FuncInfo->getHasAllocas() ||
FuncInfo->getHasStackArgs() ||
MF.getFrameInfo().hasVarSizedObjects());
}
bool AVRFrameLowering::spillCalleeSavedRegisters(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
ArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const {
if (CSI.empty()) {
return false;
}
unsigned CalleeFrameSize = 0;
DebugLoc DL = MBB.findDebugLoc(MI);
MachineFunction &MF = *MBB.getParent();
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
const TargetInstrInfo &TII = *STI.getInstrInfo();
AVRMachineFunctionInfo *AVRFI = MF.getInfo<AVRMachineFunctionInfo>();
for (const CalleeSavedInfo &I : llvm::reverse(CSI)) {
Register Reg = I.getReg();
bool IsNotLiveIn = !MBB.isLiveIn(Reg);
assert(TRI->getRegSizeInBits(*TRI->getMinimalPhysRegClass(Reg)) == 8 &&
"Invalid register size");
if (IsNotLiveIn) {
MBB.addLiveIn(Reg);
}
BuildMI(MBB, MI, DL, TII.get(AVR::PUSHRr))
.addReg(Reg, getKillRegState(IsNotLiveIn))
.setMIFlag(MachineInstr::FrameSetup);
++CalleeFrameSize;
}
AVRFI->setCalleeSavedFrameSize(CalleeFrameSize);
return true;
}
bool AVRFrameLowering::restoreCalleeSavedRegisters(
MachineBasicBlock &MBB, MachineBasicBlock::iterator MI,
MutableArrayRef<CalleeSavedInfo> CSI, const TargetRegisterInfo *TRI) const {
if (CSI.empty()) {
return false;
}
DebugLoc DL = MBB.findDebugLoc(MI);
const MachineFunction &MF = *MBB.getParent();
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
const TargetInstrInfo &TII = *STI.getInstrInfo();
for (const CalleeSavedInfo &CCSI : CSI) {
Register Reg = CCSI.getReg();
assert(TRI->getRegSizeInBits(*TRI->getMinimalPhysRegClass(Reg)) == 8 &&
"Invalid register size");
BuildMI(MBB, MI, DL, TII.get(AVR::POPRd), Reg);
}
return true;
}
static void fixStackStores(MachineBasicBlock &MBB,
MachineBasicBlock::iterator StartMI,
const TargetInstrInfo &TII) {
for (MachineInstr &MI :
llvm::make_early_inc_range(llvm::make_range(StartMI, MBB.end()))) {
if (MI.isCall())
break;
unsigned Opcode = MI.getOpcode();
if (Opcode != AVR::STDSPQRr && Opcode != AVR::STDWSPQRr)
continue;
assert(MI.getOperand(0).getReg() == AVR::SP &&
"SP is expected as base pointer");
unsigned STOpc =
(Opcode == AVR::STDWSPQRr) ? AVR::STDWPtrQRr : AVR::STDPtrQRr;
MI.setDesc(TII.get(STOpc));
MI.getOperand(0).setReg(AVR::R31R30);
}
}
MachineBasicBlock::iterator AVRFrameLowering::eliminateCallFramePseudoInstr(
MachineFunction &MF, MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI) const {
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
const AVRInstrInfo &TII = *STI.getInstrInfo();
if (hasReservedCallFrame(MF)) {
return MBB.erase(MI);
}
DebugLoc DL = MI->getDebugLoc();
unsigned int Opcode = MI->getOpcode();
int Amount = TII.getFrameSize(*MI);
if (Amount == 0) {
return MBB.erase(MI);
}
assert(getStackAlign() == Align(1) && "Unsupported stack alignment");
if (Opcode == TII.getCallFrameSetupOpcode()) {
BuildMI(MBB, MI, DL, TII.get(AVR::SPREAD), AVR::R31R30).addReg(AVR::SP);
MachineInstr *New =
BuildMI(MBB, MI, DL, TII.get(AVR::SUBIWRdK), AVR::R31R30)
.addReg(AVR::R31R30, RegState::Kill)
.addImm(Amount);
New->getOperand(3).setIsDead();
BuildMI(MBB, MI, DL, TII.get(AVR::SPWRITE), AVR::SP).addReg(AVR::R31R30);
fixStackStores(MBB, MI, TII);
} else {
assert(Opcode == TII.getCallFrameDestroyOpcode());
unsigned AddOpcode;
if (isUInt<6>(Amount)) {
AddOpcode = AVR::ADIWRdK;
} else {
AddOpcode = AVR::SUBIWRdK;
Amount = -Amount;
}
BuildMI(MBB, MI, DL, TII.get(AVR::SPREAD), AVR::R31R30).addReg(AVR::SP);
MachineInstr *New = BuildMI(MBB, MI, DL, TII.get(AddOpcode), AVR::R31R30)
.addReg(AVR::R31R30, RegState::Kill)
.addImm(Amount);
New->getOperand(3).setIsDead();
BuildMI(MBB, MI, DL, TII.get(AVR::SPWRITE), AVR::SP)
.addReg(AVR::R31R30, RegState::Kill);
}
return MBB.erase(MI);
}
void AVRFrameLowering::determineCalleeSaves(MachineFunction &MF,
BitVector &SavedRegs,
RegScavenger *RS) const {
TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS);
if (hasFP(MF)) {
SavedRegs.set(AVR::R29);
SavedRegs.set(AVR::R28);
}
}
struct AVRFrameAnalyzer : public MachineFunctionPass {
static char ID;
AVRFrameAnalyzer() : MachineFunctionPass(ID) {}
bool runOnMachineFunction(MachineFunction &MF) override {
const MachineFrameInfo &MFI = MF.getFrameInfo();
AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
if (MFI.getNumObjects() != MFI.getNumFixedObjects()) {
for (unsigned i = 0, e = MFI.getObjectIndexEnd(); i != e; ++i) {
if (MFI.getObjectSize(i)) {
AFI->setHasAllocas(true);
break;
}
}
}
if (MFI.getNumFixedObjects() == 0) {
return false;
}
for (const MachineBasicBlock &BB : MF) {
for (const MachineInstr &MI : BB) {
int Opcode = MI.getOpcode();
if ((Opcode != AVR::LDDRdPtrQ) && (Opcode != AVR::LDDWRdPtrQ) &&
(Opcode != AVR::STDPtrQRr) && (Opcode != AVR::STDWPtrQRr)) {
continue;
}
for (const MachineOperand &MO : MI.operands()) {
if (!MO.isFI()) {
continue;
}
if (MFI.isFixedObjectIndex(MO.getIndex())) {
AFI->setHasStackArgs(true);
return false;
}
}
}
}
return false;
}
StringRef getPassName() const override { return "AVR Frame Analyzer"; }
};
char AVRFrameAnalyzer::ID = 0;
FunctionPass *createAVRFrameAnalyzerPass() { return new AVRFrameAnalyzer(); }
}