From 192405a31a8125ec4454ec5471b2506aaef5b81e Mon Sep 17 00:00:00 2001 From: Dylan McKay Date: Wed, 5 Oct 2016 11:48:56 +0000 Subject: [PATCH] [AVR] Add the AVR frame lowering code Summary: This allows AVR to lower frames into assembly code. Reviewers: arsenm, kparzysz Subscribers: japaric, wdng, beanz, mgorny Differential Revision: https://reviews.llvm.org/D25032 llvm-svn: 283311 --- llvm/lib/Target/AVR/AVRFrameLowering.cpp | 537 +++++++++++++++++++++++++++++++ llvm/lib/Target/AVR/CMakeLists.txt | 1 + 2 files changed, 538 insertions(+) create mode 100644 llvm/lib/Target/AVR/AVRFrameLowering.cpp diff --git a/llvm/lib/Target/AVR/AVRFrameLowering.cpp b/llvm/lib/Target/AVR/AVRFrameLowering.cpp new file mode 100644 index 0000000..97e33b8 --- /dev/null +++ b/llvm/lib/Target/AVR/AVRFrameLowering.cpp @@ -0,0 +1,537 @@ +//===-- AVRFrameLowering.cpp - AVR Frame Information ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the AVR implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#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" + +namespace llvm { + +AVRFrameLowering::AVRFrameLowering() + : TargetFrameLowering(TargetFrameLowering::StackGrowsDown, 1, -2) {} + +bool AVRFrameLowering::canSimplifyCallFramePseudos( + const MachineFunction &MF) const { + // Always simplify call frame pseudo instructions, even when + // hasReservedCallFrame is false. + return true; +} + +bool AVRFrameLowering::hasReservedCallFrame(const MachineFunction &MF) const { + // Reserve call frame memory in function prologue under the following + // conditions: + // - Y pointer is reserved to be the frame pointer. + // - The function does not contain variable sized objects. + // - MaxCallFrameSize doesn't fit into 6-bits (when it's greater than 63). + const MachineFrameInfo &MFI = MF.getFrameInfo(); + return (hasFP(MF) && !MFI.hasVarSizedObjects() && + !isUInt<6>(MFI.getMaxCallFrameSize())); +} + +void AVRFrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator MBBI = MBB.begin(); + CallingConv::ID CallConv = MF.getFunction()->getCallingConv(); + DebugLoc DL = (MBBI != MBB.end()) ? MBBI->getDebugLoc() : DebugLoc(); + const AVRSubtarget &STI = MF.getSubtarget(); + const AVRInstrInfo &TII = *STI.getInstrInfo(); + + // Interrupt handlers re-enable interrupts in function entry. + if (CallConv == CallingConv::AVR_INTR) { + BuildMI(MBB, MBBI, DL, TII.get(AVR::BSETs)) + .addImm(0x07) + .setMIFlag(MachineInstr::FrameSetup); + } + + // Emit special prologue code to save R1, R0 and SREG in interrupt/signal + // handlers before saving any other registers. + if (CallConv == CallingConv::AVR_INTR || + CallConv == CallingConv::AVR_SIGNAL) { + 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(0x3f) + .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::R0, RegState::Define) + .addReg(AVR::R0, RegState::Kill) + .addReg(AVR::R0, RegState::Kill) + .setMIFlag(MachineInstr::FrameSetup); + } + + // Early exit if the frame pointer is not needed in this function. + if (!hasFP(MF)) { + return; + } + + const MachineFrameInfo &MFI = MF.getFrameInfo(); + const AVRMachineFunctionInfo *AFI = MF.getInfo(); + unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize(); + + // Skip the callee-saved push instructions. + while ( + (MBBI != MBB.end()) && MBBI->getFlag(MachineInstr::FrameSetup) && + (MBBI->getOpcode() == AVR::PUSHRr || MBBI->getOpcode() == AVR::PUSHWRr)) { + ++MBBI; + } + + // Update Y with the new base value. + BuildMI(MBB, MBBI, DL, TII.get(AVR::SPREAD), AVR::R29R28) + .addReg(AVR::SP) + .setMIFlag(MachineInstr::FrameSetup); + + // Mark the FramePtr as live-in in every block except the entry. + for (MachineFunction::iterator I = std::next(MF.begin()), E = MF.end(); + I != E; ++I) { + I->addLiveIn(AVR::R29R28); + } + + if (!FrameSize) { + return; + } + + // Reserve the necessary frame memory by doing FP -= . + 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); + // The SREG implicit def is dead. + MI->getOperand(3).setIsDead(); + + // Write back R29R28 to SP and temporarily disable interrupts. + BuildMI(MBB, MBBI, DL, TII.get(AVR::SPWRITE), AVR::SP) + .addReg(AVR::R29R28) + .setMIFlag(MachineInstr::FrameSetup); +} + +void AVRFrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + CallingConv::ID CallConv = MF.getFunction()->getCallingConv(); + bool isHandler = (CallConv == CallingConv::AVR_INTR || + CallConv == CallingConv::AVR_SIGNAL); + + // Early exit if the frame pointer is not needed in this function except for + // signal/interrupt handlers where special code generation is required. + if (!hasFP(MF) && !isHandler) { + return; + } + + MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + assert(MBBI == MBB.end() && + "Can only insert epilog into returning blocks"); + DebugLoc DL = MBBI->getDebugLoc(); + const MachineFrameInfo &MFI = MF.getFrameInfo(); + const AVRMachineFunctionInfo *AFI = MF.getInfo(); + unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize(); + const AVRSubtarget &STI = MF.getSubtarget(); + const AVRInstrInfo &TII = *STI.getInstrInfo(); + + // Emit special epilogue code to restore R1, R0 and SREG in interrupt/signal + // handlers at the very end of the function, just before reti. + if (isHandler) { + BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0); + BuildMI(MBB, MBBI, DL, TII.get(AVR::OUTARr)) + .addImm(0x3f) + .addReg(AVR::R0, RegState::Kill); + BuildMI(MBB, MBBI, DL, TII.get(AVR::POPWRd), AVR::R1R0); + } + + // Early exit if there is no need to restore the frame pointer. + if (!FrameSize) { + return; + } + + // Skip the callee-saved pop instructions. + 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; + } + + unsigned Opcode; + + // Select the optimal opcode depending on how big it is. + if (isUInt<6>(FrameSize)) { + Opcode = AVR::ADIWRdK; + } else { + Opcode = AVR::SUBIWRdK; + FrameSize = -FrameSize; + } + + // Restore the frame pointer by doing FP += . + MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(Opcode), AVR::R29R28) + .addReg(AVR::R29R28, RegState::Kill) + .addImm(FrameSize); + // The SREG implicit def is dead. + MI->getOperand(3).setIsDead(); + + // Write back R29R28 to SP and temporarily disable interrupts. + BuildMI(MBB, MBBI, DL, TII.get(AVR::SPWRITE), AVR::SP) + .addReg(AVR::R29R28, RegState::Kill); +} + +// Return true if the specified function should have a dedicated frame +// pointer register. This is true if the function meets any of the following +// conditions: +// - a register has been spilled +// - has allocas +// - input arguments are passed using the stack +// +// Notice that strictly this is not a frame pointer because it contains SP after +// frame allocation instead of having the original SP in function entry. +bool AVRFrameLowering::hasFP(const MachineFunction &MF) const { + const AVRMachineFunctionInfo *FuncInfo = MF.getInfo(); + + return (FuncInfo->getHasSpills() || FuncInfo->getHasAllocas() || + FuncInfo->getHasStackArgs()); +} + +bool AVRFrameLowering::spillCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const std::vector &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(); + const TargetInstrInfo &TII = *STI.getInstrInfo(); + AVRMachineFunctionInfo *AVRFI = MF.getInfo(); + + for (unsigned i = CSI.size(); i != 0; --i) { + unsigned Reg = CSI[i - 1].getReg(); + bool IsNotLiveIn = !MBB.isLiveIn(Reg); + + assert(TRI->getMinimalPhysRegClass(Reg)->getSize() == 1 && + "Invalid register size"); + + // Add the callee-saved register as live-in only if it is not already a + // live-in register, this usually happens with arguments that are passed + // through callee-saved registers. + if (IsNotLiveIn) { + MBB.addLiveIn(Reg); + } + + // Do not kill the register when it is an input argument. + 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, + const std::vector &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(); + const TargetInstrInfo &TII = *STI.getInstrInfo(); + + for (const CalleeSavedInfo &CSI : CSI) { + unsigned Reg = CSI.getReg(); + + assert(TRI->getMinimalPhysRegClass(Reg)->getSize() == 1 && + "Invalid register size"); + + BuildMI(MBB, MI, DL, TII.get(AVR::POPRd), Reg); + } + + return true; +} + +/// Replace pseudo store instructions that pass arguments through the stack with +/// real instructions. If insertPushes is true then all instructions are +/// replaced with push instructions, otherwise regular std instructions are +/// inserted. +static void fixStackStores(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const TargetInstrInfo &TII, bool insertPushes) { + const AVRSubtarget &STI = MBB.getParent()->getSubtarget(); + const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); + + // Iterate through the BB until we hit a call instruction or we reach the end. + for (auto I = MI, E = MBB.end(); I != E && !I->isCall();) { + MachineBasicBlock::iterator NextMI = std::next(I); + MachineInstr &MI = *I; + unsigned Opcode = I->getOpcode(); + + // Only care of pseudo store instructions where SP is the base pointer. + if (Opcode != AVR::STDSPQRr && Opcode != AVR::STDWSPQRr) { + I = NextMI; + continue; + } + + assert(MI.getOperand(0).getReg() == AVR::SP && + "Invalid register, should be SP!"); + if (insertPushes) { + // Replace this instruction with a push. + unsigned SrcReg = MI.getOperand(2).getReg(); + bool SrcIsKill = MI.getOperand(2).isKill(); + + // We can't use PUSHWRr here because when expanded the order of the new + // instructions are reversed from what we need. Perform the expansion now. + if (Opcode == AVR::STDWSPQRr) { + BuildMI(MBB, I, MI.getDebugLoc(), TII.get(AVR::PUSHRr)) + .addReg(TRI.getSubReg(SrcReg, AVR::sub_hi), + getKillRegState(SrcIsKill)); + BuildMI(MBB, I, MI.getDebugLoc(), TII.get(AVR::PUSHRr)) + .addReg(TRI.getSubReg(SrcReg, AVR::sub_lo), + getKillRegState(SrcIsKill)); + } else { + BuildMI(MBB, I, MI.getDebugLoc(), TII.get(AVR::PUSHRr)) + .addReg(SrcReg, getKillRegState(SrcIsKill)); + } + + MI.eraseFromParent(); + I = NextMI; + continue; + } + + // Replace this instruction with a regular store. Use Y as the base + // pointer since it is guaranteed to contain a copy of SP. + unsigned STOpc = + (Opcode == AVR::STDWSPQRr) ? AVR::STDWPtrQRr : AVR::STDPtrQRr; + assert(isUInt<6>(MI.getOperand(1).getImm()) && "Offset is out of range"); + + MI.setDesc(TII.get(STOpc)); + MI.getOperand(0).setReg(AVR::R29R28); + + I = NextMI; + } +} + +MachineBasicBlock::iterator AVRFrameLowering::eliminateCallFramePseudoInstr( + MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const { + const AVRSubtarget &STI = MF.getSubtarget(); + const TargetFrameLowering &TFI = *STI.getFrameLowering(); + const AVRInstrInfo &TII = *STI.getInstrInfo(); + + // There is nothing to insert when the call frame memory is allocated during + // function entry. Delete the call frame pseudo and replace all pseudo stores + // with real store instructions. + if (TFI.hasReservedCallFrame(MF)) { + fixStackStores(MBB, MI, TII, false); + return MBB.erase(MI); + } + + DebugLoc DL = MI->getDebugLoc(); + unsigned int Opcode = MI->getOpcode(); + int Amount = MI->getOperand(0).getImm(); + + // Adjcallstackup does not need to allocate stack space for the call, instead + // we insert push instructions that will allocate the necessary stack. + // For adjcallstackdown we convert it into an 'adiw reg, ' handling + // the read and write of SP in I/O space. + if (Amount != 0) { + assert(TFI.getStackAlignment() == 1 && "Unsupported stack alignment"); + + if (Opcode == TII.getCallFrameSetupOpcode()) { + fixStackStores(MBB, MI, TII, true); + } else { + assert(Opcode == TII.getCallFrameDestroyOpcode()); + + // Select the best opcode to adjust SP based on the offset size. + unsigned addOpcode; + if (isUInt<6>(Amount)) { + addOpcode = AVR::ADIWRdK; + } else { + addOpcode = AVR::SUBIWRdK; + Amount = -Amount; + } + + // Build the instruction sequence. + 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); + + // Spill register Y when it is used as the frame pointer. + if (hasFP(MF)) { + SavedRegs.set(AVR::R29R28); + SavedRegs.set(AVR::R29); + SavedRegs.set(AVR::R28); + } +} +/// The frame analyzer pass. +/// +/// Scans the function for allocas and used arguments +/// that are passed through the stack. +struct AVRFrameAnalyzer : public MachineFunctionPass { + static char ID; + AVRFrameAnalyzer() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + AVRMachineFunctionInfo *FuncInfo = MF.getInfo(); + + // If there are no fixed frame indexes during this stage it means there + // are allocas present in the function. + if (MFI.getNumObjects() != MFI.getNumFixedObjects()) { + // Check for the type of allocas present in the function. We only care + // about fixed size allocas so do not give false positives if only + // variable sized allocas are present. + for (unsigned i = 0, e = MFI.getObjectIndexEnd(); i != e; ++i) { + // Variable sized objects have size 0. + if (MFI.getObjectSize(i)) { + FuncInfo->setHasAllocas(true); + break; + } + } + } + + // If there are fixed frame indexes present, scan the function to see if + // they are really being used. + if (MFI.getNumFixedObjects() == 0) { + return false; + } + + // Ok fixed frame indexes present, now scan the function to see if they + // are really being used, otherwise we can ignore them. + 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())) { + FuncInfo->setHasStackArgs(true); + return false; + } + } + } + } + + return false; + } + + const char *getPassName() const { return "AVR Frame Analyzer"; } +}; + +char AVRFrameAnalyzer::ID = 0; + +/// Creates instance of the frame analyzer pass. +FunctionPass *createAVRFrameAnalyzerPass() { return new AVRFrameAnalyzer(); } + +/// Create the Dynalloca Stack Pointer Save/Restore pass. +/// Insert a copy of SP before allocating the dynamic stack memory and restore +/// it in function exit to restore the original SP state. This avoids the need +/// of reserving a register pair for a frame pointer. +struct AVRDynAllocaSR : public MachineFunctionPass { + static char ID; + AVRDynAllocaSR() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) { + // Early exit when there are no variable sized objects in the function. + if (!MF.getFrameInfo().hasVarSizedObjects()) { + return false; + } + + const AVRSubtarget &STI = MF.getSubtarget(); + const TargetInstrInfo &TII = *STI.getInstrInfo(); + MachineBasicBlock &EntryMBB = MF.front(); + MachineBasicBlock::iterator MBBI = EntryMBB.begin(); + DebugLoc DL = EntryMBB.findDebugLoc(MBBI); + + unsigned SPCopy = + MF.getRegInfo().createVirtualRegister(&AVR::DREGSRegClass); + + // Create a copy of SP in function entry before any dynallocas are + // inserted. + BuildMI(EntryMBB, MBBI, DL, TII.get(AVR::COPY), SPCopy).addReg(AVR::SP); + + // Restore SP in all exit basic blocks. + for (MachineBasicBlock &MBB : MF) { + // If last instruction is a return instruction, add a restore copy. + if (!MBB.empty() && MBB.back().isReturn()) { + MBBI = MBB.getLastNonDebugInstr(); + DL = MBBI->getDebugLoc(); + BuildMI(MBB, MBBI, DL, TII.get(AVR::COPY), AVR::SP) + .addReg(SPCopy, RegState::Kill); + } + } + + return true; + } + + const char *getPassName() const { + return "AVR dynalloca stack pointer save/restore"; + } +}; + +char AVRDynAllocaSR::ID = 0; + +/// createAVRDynAllocaSRPass - returns an instance of the dynalloca stack +/// pointer save/restore pass. +FunctionPass *createAVRDynAllocaSRPass() { return new AVRDynAllocaSR(); } + +} // end of namespace llvm + diff --git a/llvm/lib/Target/AVR/CMakeLists.txt b/llvm/lib/Target/AVR/CMakeLists.txt index 42626d5..1ed516c 100644 --- a/llvm/lib/Target/AVR/CMakeLists.txt +++ b/llvm/lib/Target/AVR/CMakeLists.txt @@ -8,6 +8,7 @@ tablegen(LLVM AVRGenSubtargetInfo.inc -gen-subtarget) add_public_tablegen_target(AVRCommonTableGen) add_llvm_target(AVRCodeGen + AVRFrameLowering.cpp AVRInstrInfo.cpp AVRRegisterInfo.cpp AVRSubtarget.cpp -- 2.7.4