diff options
Diffstat (limited to 'llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp')
-rw-r--r-- | llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp | 1431 |
1 files changed, 1431 insertions, 0 deletions
diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp new file mode 100644 index 00000000000..653b3abaccf --- /dev/null +++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -0,0 +1,1431 @@ +//===-- AVRExpandPseudoInsts.cpp - Expand pseudo instructions -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a pass that expands pseudo instructions into target +// instructions. This pass should be run after register allocation but before +// the post-regalloc scheduling pass. +// +//===----------------------------------------------------------------------===// + +#include "AVR.h" +#include "AVRInstrInfo.h" +#include "AVRTargetMachine.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Target/TargetRegisterInfo.h" + +using namespace llvm; + +namespace { + +/// Expands "placeholder" instructions marked as pseudo into +/// actual AVR instructions. +class AVRExpandPseudo : public MachineFunctionPass { +public: + static char ID; + + AVRExpandPseudo() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) override; + + StringRef getPassName() const override { + return "AVR pseudo instruction expansion pass"; + } + +private: + typedef MachineBasicBlock Block; + typedef Block::iterator BlockIt; + + const AVRRegisterInfo *TRI; + const TargetInstrInfo *TII; + + /// The register to be used for temporary storage. + const unsigned SCRATCH_REGISTER = AVR::R0; + /// The IO address of the status register. + const unsigned SREG_ADDR = 0x3f; + + bool expandMBB(Block &MBB); + bool expandMI(Block &MBB, BlockIt MBBI); + template <unsigned OP> bool expand(Block &MBB, BlockIt MBBI); + + MachineInstrBuilder buildMI(Block &MBB, BlockIt MBBI, unsigned Opcode) { + return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(Opcode)); + } + + MachineInstrBuilder buildMI(Block &MBB, BlockIt MBBI, unsigned Opcode, + unsigned DstReg) { + return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(Opcode), DstReg); + } + + MachineRegisterInfo &getRegInfo(Block &MBB) { return MBB.getParent()->getRegInfo(); } + + bool expandArith(unsigned OpLo, unsigned OpHi, Block &MBB, BlockIt MBBI); + bool expandLogic(unsigned Op, Block &MBB, BlockIt MBBI); + bool expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI); + + template<typename Func> + bool expandAtomic(Block &MBB, BlockIt MBBI, Func f); + + template<typename Func> + bool expandAtomicBinaryOp(unsigned Opcode, Block &MBB, BlockIt MBBI, Func f); + + bool expandAtomicBinaryOp(unsigned Opcode, Block &MBB, BlockIt MBBI); + + bool expandAtomicArithmeticOp(unsigned MemOpcode, + unsigned ArithOpcode, + Block &MBB, + BlockIt MBBI); +}; + +char AVRExpandPseudo::ID = 0; + +} // end of anonymous namespace + +bool AVRExpandPseudo::expandMBB(MachineBasicBlock &MBB) { + bool Modified = false; + + BlockIt MBBI = MBB.begin(), E = MBB.end(); + while (MBBI != E) { + BlockIt NMBBI = std::next(MBBI); + Modified |= expandMI(MBB, MBBI); + MBBI = NMBBI; + } + + return Modified; +} + +bool AVRExpandPseudo::runOnMachineFunction(MachineFunction &MF) { + bool Modified = false; + + const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); + TRI = STI.getRegisterInfo(); + TII = STI.getInstrInfo(); + + for (Block &MBB : MF) { + bool ContinueExpanding = true; + unsigned ExpandCount = 0; + + // Continue expanding the block until all pseudos are expanded. + do { + assert(ExpandCount < 10 && "pseudo expand limit reached"); + + bool BlockModified = expandMBB(MBB); + Modified |= BlockModified; + ExpandCount++; + + ContinueExpanding = BlockModified; + } while (ContinueExpanding); + } + + return Modified; +} + +bool AVRExpandPseudo:: +expandArith(unsigned OpLo, unsigned OpHi, Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned SrcLoReg, SrcHiReg, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool SrcIsKill = MI.getOperand(2).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(4).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +bool AVRExpandPseudo:: +expandLogic(unsigned Op, Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned SrcLoReg, SrcHiReg, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool SrcIsKill = MI.getOperand(2).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, Op) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + // SREG is always implicitly dead + MIBLO->getOperand(3).setIsDead(); + + auto MIBHI = buildMI(MBB, MBBI, Op) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +bool AVRExpandPseudo:: +expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + unsigned Imm = MI.getOperand(2).getImm(); + unsigned Lo8 = Imm & 0xff; + unsigned Hi8 = (Imm >> 8) & 0xff; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, Op) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(SrcIsKill)) + .addImm(Lo8); + + // SREG is always implicitly dead + MIBLO->getOperand(3).setIsDead(); + + auto MIBHI = buildMI(MBB, MBBI, Op) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(SrcIsKill)) + .addImm(Hi8); + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::ADDWRdRr>(Block &MBB, BlockIt MBBI) { + return expandArith(AVR::ADDRdRr, AVR::ADCRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::ADCWRdRr>(Block &MBB, BlockIt MBBI) { + return expandArith(AVR::ADCRdRr, AVR::ADCRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::SUBWRdRr>(Block &MBB, BlockIt MBBI) { + return expandArith(AVR::SUBRdRr, AVR::SBCRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::SUBIWRdK>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, AVR::SUBIRdK) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, AVR::SBCIRdK) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(SrcIsKill)); + + switch (MI.getOperand(2).getType()) { + case MachineOperand::MO_GlobalAddress: { + const GlobalValue *GV = MI.getOperand(2).getGlobal(); + int64_t Offs = MI.getOperand(2).getOffset(); + unsigned TF = MI.getOperand(2).getTargetFlags(); + MIBLO.addGlobalAddress(GV, Offs, TF | AVRII::MO_NEG | AVRII::MO_LO); + MIBHI.addGlobalAddress(GV, Offs, TF | AVRII::MO_NEG | AVRII::MO_HI); + break; + } + case MachineOperand::MO_Immediate: { + unsigned Imm = MI.getOperand(2).getImm(); + MIBLO.addImm(Imm & 0xff); + MIBHI.addImm((Imm >> 8) & 0xff); + break; + } + default: + llvm_unreachable("Unknown operand type!"); + } + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(4).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::SBCWRdRr>(Block &MBB, BlockIt MBBI) { + return expandArith(AVR::SBCRdRr, AVR::SBCRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::SBCIWRdK>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + unsigned Imm = MI.getOperand(2).getImm(); + unsigned Lo8 = Imm & 0xff; + unsigned Hi8 = (Imm >> 8) & 0xff; + OpLo = AVR::SBCIRdK; + OpHi = AVR::SBCIRdK; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(SrcIsKill)) + .addImm(Lo8); + + // SREG is always implicitly killed + MIBLO->getOperand(4).setIsKill(); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(SrcIsKill)) + .addImm(Hi8); + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(4).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::ANDWRdRr>(Block &MBB, BlockIt MBBI) { + return expandLogic(AVR::ANDRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::ANDIWRdK>(Block &MBB, BlockIt MBBI) { + return expandLogicImm(AVR::ANDIRdK, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::ORWRdRr>(Block &MBB, BlockIt MBBI) { + return expandLogic(AVR::ORRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::ORIWRdK>(Block &MBB, BlockIt MBBI) { + return expandLogicImm(AVR::ORIRdK, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::EORWRdRr>(Block &MBB, BlockIt MBBI) { + return expandLogic(AVR::EORRdRr, MBB, MBBI); +} + +template <> +bool AVRExpandPseudo::expand<AVR::COMWRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::COMRd; + OpHi = AVR::COMRd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + // SREG is always implicitly dead + MIBLO->getOperand(2).setIsDead(); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(2).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::CPWRdRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsKill = MI.getOperand(0).isKill(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::CPRdRr; + OpHi = AVR::CPCRdRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // Low part + buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(2).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(3).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::CPCWRdRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsKill = MI.getOperand(0).isKill(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::CPCRdRr; + OpHi = AVR::CPCRdRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + // SREG is always implicitly killed + MIBLO->getOperand(3).setIsKill(); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(2).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(3).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDIWRdK>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + OpLo = AVR::LDIRdK; + OpHi = AVR::LDIRdK; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)); + + switch (MI.getOperand(1).getType()) { + case MachineOperand::MO_GlobalAddress: { + const GlobalValue *GV = MI.getOperand(1).getGlobal(); + int64_t Offs = MI.getOperand(1).getOffset(); + unsigned TF = MI.getOperand(1).getTargetFlags(); + + MIBLO.addGlobalAddress(GV, Offs, TF | AVRII::MO_LO); + MIBHI.addGlobalAddress(GV, Offs, TF | AVRII::MO_HI); + break; + } + case MachineOperand::MO_BlockAddress: { + const BlockAddress *BA = MI.getOperand(1).getBlockAddress(); + unsigned TF = MI.getOperand(1).getTargetFlags(); + + MIBLO.addOperand(MachineOperand::CreateBA(BA, TF | AVRII::MO_LO)); + MIBHI.addOperand(MachineOperand::CreateBA(BA, TF | AVRII::MO_HI)); + break; + } + case MachineOperand::MO_Immediate: { + unsigned Imm = MI.getOperand(1).getImm(); + + MIBLO.addImm(Imm & 0xff); + MIBHI.addImm((Imm >> 8) & 0xff); + break; + } + default: + llvm_unreachable("Unknown operand type!"); + } + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDSWRdK>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + OpLo = AVR::LDSRdK; + OpHi = AVR::LDSRdK; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)); + + switch (MI.getOperand(1).getType()) { + case MachineOperand::MO_GlobalAddress: { + const GlobalValue *GV = MI.getOperand(1).getGlobal(); + int64_t Offs = MI.getOperand(1).getOffset(); + unsigned TF = MI.getOperand(1).getTargetFlags(); + + MIBLO.addGlobalAddress(GV, Offs, TF); + MIBHI.addGlobalAddress(GV, Offs + 1, TF); + break; + } + case MachineOperand::MO_Immediate: { + unsigned Imm = MI.getOperand(1).getImm(); + + MIBLO.addImm(Imm); + MIBHI.addImm(Imm + 1); + break; + } + default: + llvm_unreachable("Unknown operand type!"); + } + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDWRdPtr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::LDRdPtr; + OpHi = AVR::LDDRdPtrQ; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, getKillRegState(SrcIsKill)) + .addImm(1); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDWRdPtrPi>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsDead = MI.getOperand(1).isKill(); + OpLo = AVR::LDRdPtrPi; + OpHi = AVR::LDRdPtrPi; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, RegState::Define) + .addReg(SrcReg, RegState::Kill); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, RegState::Define | getDeadRegState(SrcIsDead)) + .addReg(SrcReg, RegState::Kill); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDWRdPtrPd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsDead = MI.getOperand(1).isKill(); + OpLo = AVR::LDRdPtrPd; + OpHi = AVR::LDRdPtrPd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, RegState::Define) + .addReg(SrcReg, RegState::Kill); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, RegState::Define | getDeadRegState(SrcIsDead)) + .addReg(SrcReg, RegState::Kill); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LDDWRdPtrQ>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + unsigned Imm = MI.getOperand(2).getImm(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::LDDRdPtrQ; + OpHi = AVR::LDDRdPtrQ; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(Imm < 63 && "Offset is out of range"); + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg) + .addImm(Imm); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, getKillRegState(SrcIsKill)) + .addImm(Imm + 1); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template<typename Func> +bool AVRExpandPseudo::expandAtomic(Block &MBB, BlockIt MBBI, Func f) { + // Remove the pseudo instruction. + MachineInstr &MI = *MBBI; + + // Store the SREG. + buildMI(MBB, MBBI, AVR::INRdA) + .addReg(SCRATCH_REGISTER, RegState::Define) + .addImm(SREG_ADDR); + + // Disable exceptions. + buildMI(MBB, MBBI, AVR::BCLRs).addImm(7); // CLI + + f(MI); + + // Restore the status reg. + buildMI(MBB, MBBI, AVR::OUTARr) + .addImm(SREG_ADDR) + .addReg(SCRATCH_REGISTER); + + MI.eraseFromParent(); + return true; +} + +template<typename Func> +bool AVRExpandPseudo::expandAtomicBinaryOp(unsigned Opcode, + Block &MBB, + BlockIt MBBI, + Func f) { + return expandAtomic(MBB, MBBI, [&](MachineInstr &MI) { + auto Op1 = MI.getOperand(0); + auto Op2 = MI.getOperand(1); + + MachineInstr &NewInst = *buildMI(MBB, MBBI, Opcode) + .addOperand(Op1).addOperand(Op2) + .getInstr(); + f(NewInst); + }); +} + +bool AVRExpandPseudo::expandAtomicBinaryOp(unsigned Opcode, + Block &MBB, + BlockIt MBBI) { + return expandAtomicBinaryOp(Opcode, MBB, MBBI, [](MachineInstr &MI) {}); +} + +bool AVRExpandPseudo::expandAtomicArithmeticOp(unsigned Width, + unsigned ArithOpcode, + Block &MBB, + BlockIt MBBI) { + return expandAtomic(MBB, MBBI, [&](MachineInstr &MI) { + auto Op1 = MI.getOperand(0); + auto Op2 = MI.getOperand(1); + + unsigned LoadOpcode = (Width == 8) ? AVR::LDRdPtr : AVR::LDWRdPtr; + unsigned StoreOpcode = (Width == 8) ? AVR::STPtrRr : AVR::STWPtrRr; + + // Create the load + buildMI(MBB, MBBI, LoadOpcode).addOperand(Op1).addOperand(Op2); + + // Create the arithmetic op + buildMI(MBB, MBBI, ArithOpcode) + .addOperand(Op1).addOperand(Op1) + .addOperand(Op2); + + // Create the store + buildMI(MBB, MBBI, StoreOpcode).addOperand(Op2).addOperand(Op1); + }); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoad8>(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::LDRdPtr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoad16>(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::LDWRdPtr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicStore8>(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::STPtrRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicStore16>(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::STWPtrRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadAdd8>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::ADDRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadAdd16>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::ADDWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadSub8>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::SUBRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadSub16>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::SUBWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadAnd8>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::ANDRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadAnd16>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::ANDWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadOr8>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::ORRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadOr16>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::ORWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadXor8>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::EORRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicLoadXor16>(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::EORWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand<AVR::AtomicFence>(Block &MBB, BlockIt MBBI) { + // On AVR, there is only one core and so atomic fences do nothing. + MBBI->eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::STSWKRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned SrcReg = MI.getOperand(1).getReg(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::STSKRr; + OpHi = AVR::STSKRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + // Write the high byte first in case this address belongs to a special + // I/O address with a special temporary register. + auto MIBHI = buildMI(MBB, MBBI, OpHi); + auto MIBLO = buildMI(MBB, MBBI, OpLo); + + switch (MI.getOperand(0).getType()) { + case MachineOperand::MO_GlobalAddress: { + const GlobalValue *GV = MI.getOperand(0).getGlobal(); + int64_t Offs = MI.getOperand(0).getOffset(); + unsigned TF = MI.getOperand(0).getTargetFlags(); + + MIBLO.addGlobalAddress(GV, Offs, TF); + MIBHI.addGlobalAddress(GV, Offs + 1, TF); + break; + } + case MachineOperand::MO_Immediate: { + unsigned Imm = MI.getOperand(0).getImm(); + + MIBLO.addImm(Imm); + MIBHI.addImm(Imm + 1); + break; + } + default: + llvm_unreachable("Unknown operand type!"); + } + + MIBLO.addReg(SrcLoReg, getKillRegState(SrcIsKill)); + MIBHI.addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::STWPtrRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsKill = MI.getOperand(0).isKill(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::STPtrRr; + OpHi = AVR::STDPtrQRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + //:TODO: need to reverse this order like inw and stsw? + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstReg) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addImm(1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::STWPtrPiRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + unsigned Imm = MI.getOperand(3).getImm(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(2).isKill(); + OpLo = AVR::STPtrPiRr; + OpHi = AVR::STPtrPiRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstReg, RegState::Define) + .addReg(DstReg, RegState::Kill) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .addImm(Imm); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, RegState::Kill) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .addImm(Imm); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::STWPtrPdRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + unsigned Imm = MI.getOperand(3).getImm(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(2).isKill(); + OpLo = AVR::STPtrPdRr; + OpHi = AVR::STPtrPdRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstReg, RegState::Define) + .addReg(DstReg, RegState::Kill) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .addImm(Imm); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, RegState::Kill) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .addImm(Imm); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::STDWPtrQRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + unsigned Imm = MI.getOperand(1).getImm(); + bool DstIsKill = MI.getOperand(0).isKill(); + bool SrcIsKill = MI.getOperand(2).isKill(); + OpLo = AVR::STDPtrQRr; + OpHi = AVR::STDPtrQRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + assert(Imm < 63 && "Offset is out of range"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstReg) + .addImm(Imm) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addImm(Imm + 1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::INWRdA>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned Imm = MI.getOperand(1).getImm(); + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + OpLo = AVR::INRdA; + OpHi = AVR::INRdA; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(Imm < 63 && "Address is out of range"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addImm(Imm); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addImm(Imm + 1); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::OUTWARr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned Imm = MI.getOperand(0).getImm(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::OUTARr; + OpHi = AVR::OUTARr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + assert(Imm < 63 && "Address is out of range"); + + // 16 bit I/O writes need the high byte first + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addImm(Imm + 1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addImm(Imm) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::PUSHWRr>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned SrcReg = MI.getOperand(0).getReg(); + bool SrcIsKill = MI.getOperand(0).isKill(); + unsigned Flags = MI.getFlags(); + OpLo = AVR::PUSHRr; + OpHi = AVR::PUSHRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + // Low part + buildMI(MBB, MBBI, OpLo) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .setMIFlags(Flags); + + // High part + buildMI(MBB, MBBI, OpHi) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .setMIFlags(Flags); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::POPWRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned Flags = MI.getFlags(); + OpLo = AVR::POPRd; + OpHi = AVR::POPRd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + buildMI(MBB, MBBI, OpHi, DstHiReg).setMIFlags(Flags); // High + buildMI(MBB, MBBI, OpLo, DstLoReg).setMIFlags(Flags); // Low + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LSLWRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::LSLRd; + OpHi = AVR::ROLRd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // Low part + buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(2).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(3).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::LSRWRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::RORRd; + OpHi = AVR::LSRRd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // High part + buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + if (ImpIsDead) + MIBLO->getOperand(2).setIsDead(); + + // SREG is always implicitly killed + MIBLO->getOperand(3).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::RORWRd>(Block &MBB, BlockIt MBBI) { + llvm_unreachable("RORW unimplemented"); + return false; +} + +template <> +bool AVRExpandPseudo::expand<AVR::ROLWRd>(Block &MBB, BlockIt MBBI) { + llvm_unreachable("ROLW unimplemented"); + return false; +} + +template <> +bool AVRExpandPseudo::expand<AVR::ASRWRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + OpLo = AVR::RORRd; + OpHi = AVR::ASRRd; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // High part + buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + if (ImpIsDead) + MIBLO->getOperand(2).setIsDead(); + + // SREG is always implicitly killed + MIBLO->getOperand(3).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> bool AVRExpandPseudo::expand<AVR::SEXT>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned DstLoReg, DstHiReg; + // sext R17:R16, R17 + // mov r16, r17 + // lsl r17 + // sbc r17, r17 + // sext R17:R16, R13 + // mov r16, r13 + // mov r17, r13 + // lsl r17 + // sbc r17, r17 + // sext R17:R16, R16 + // mov r17, r16 + // lsl r17 + // sbc r17, r17 + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + if (SrcReg != DstLoReg) { + auto MOV = buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg); + + if (SrcReg == DstHiReg) { + MOV->getOperand(1).setIsKill(); + } + } + + if (SrcReg != DstHiReg) { + buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstHiReg, RegState::Define) + .addReg(SrcReg, getKillRegState(SrcIsKill)); + } + + buildMI(MBB, MBBI, AVR::LSLRd) + .addReg(DstHiReg, RegState::Define) + .addReg(DstHiReg, RegState::Kill); + + auto SBC = buildMI(MBB, MBBI, AVR::SBCRdRr) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, RegState::Kill) + .addReg(DstHiReg, RegState::Kill); + + if (ImpIsDead) + SBC->getOperand(3).setIsDead(); + + // SREG is always implicitly killed + SBC->getOperand(4).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> bool AVRExpandPseudo::expand<AVR::ZEXT>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned DstLoReg, DstHiReg; + // zext R25:R24, R20 + // mov R24, R20 + // eor R25, R25 + // zext R25:R24, R24 + // eor R25, R25 + // zext R25:R24, R25 + // mov R24, R25 + // eor R25, R25 + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + if (SrcReg != DstLoReg) { + buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, getKillRegState(SrcIsKill)); + } + + auto EOR = buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, RegState::Kill) + .addReg(DstHiReg, RegState::Kill); + + if (ImpIsDead) + EOR->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::SPREAD>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + unsigned Flags = MI.getFlags(); + OpLo = AVR::INRdA; + OpHi = AVR::INRdA; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // Low part + buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addImm(0x3d) + .setMIFlags(Flags); + + // High part + buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addImm(0x3e) + .setMIFlags(Flags); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand<AVR::SPWRITE>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned SrcLoReg, SrcHiReg; + unsigned SrcReg = MI.getOperand(1).getReg(); + bool SrcIsKill = MI.getOperand(1).isKill(); + unsigned Flags = MI.getFlags(); + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + buildMI(MBB, MBBI, AVR::INRdA) + .addReg(AVR::R0, RegState::Define) + .addImm(SREG_ADDR) + .setMIFlags(Flags); + + buildMI(MBB, MBBI, AVR::BCLRs).addImm(0x07).setMIFlags(Flags); + + buildMI(MBB, MBBI, AVR::OUTARr) + .addImm(0x3e) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)) + .setMIFlags(Flags); + + buildMI(MBB, MBBI, AVR::OUTARr) + .addImm(SREG_ADDR) + .addReg(AVR::R0, RegState::Kill) + .setMIFlags(Flags); + + buildMI(MBB, MBBI, AVR::OUTARr) + .addImm(0x3d) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)) + .setMIFlags(Flags); + + MI.eraseFromParent(); + return true; +} + +bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + int Opcode = MBBI->getOpcode(); + +#define EXPAND(Op) \ + case Op: \ + return expand<Op>(MBB, MI) + + switch (Opcode) { + EXPAND(AVR::ADDWRdRr); + EXPAND(AVR::ADCWRdRr); + EXPAND(AVR::SUBWRdRr); + EXPAND(AVR::SUBIWRdK); + EXPAND(AVR::SBCWRdRr); + EXPAND(AVR::SBCIWRdK); + EXPAND(AVR::ANDWRdRr); + EXPAND(AVR::ANDIWRdK); + EXPAND(AVR::ORWRdRr); + EXPAND(AVR::ORIWRdK); + EXPAND(AVR::EORWRdRr); + EXPAND(AVR::COMWRd); + EXPAND(AVR::CPWRdRr); + EXPAND(AVR::CPCWRdRr); + EXPAND(AVR::LDIWRdK); + EXPAND(AVR::LDSWRdK); + EXPAND(AVR::LDWRdPtr); + EXPAND(AVR::LDWRdPtrPi); + EXPAND(AVR::LDWRdPtrPd); + case AVR::LDDWRdYQ: //:FIXME: remove this once PR13375 gets fixed + EXPAND(AVR::LDDWRdPtrQ); + EXPAND(AVR::AtomicLoad8); + EXPAND(AVR::AtomicLoad16); + EXPAND(AVR::AtomicStore8); + EXPAND(AVR::AtomicStore16); + EXPAND(AVR::AtomicLoadAdd8); + EXPAND(AVR::AtomicLoadAdd16); + EXPAND(AVR::AtomicLoadSub8); + EXPAND(AVR::AtomicLoadSub16); + EXPAND(AVR::AtomicLoadAnd8); + EXPAND(AVR::AtomicLoadAnd16); + EXPAND(AVR::AtomicLoadOr8); + EXPAND(AVR::AtomicLoadOr16); + EXPAND(AVR::AtomicLoadXor8); + EXPAND(AVR::AtomicLoadXor16); + EXPAND(AVR::AtomicFence); + EXPAND(AVR::STSWKRr); + EXPAND(AVR::STWPtrRr); + EXPAND(AVR::STWPtrPiRr); + EXPAND(AVR::STWPtrPdRr); + EXPAND(AVR::STDWPtrQRr); + EXPAND(AVR::INWRdA); + EXPAND(AVR::OUTWARr); + EXPAND(AVR::PUSHWRr); + EXPAND(AVR::POPWRd); + EXPAND(AVR::LSLWRd); + EXPAND(AVR::LSRWRd); + EXPAND(AVR::RORWRd); + EXPAND(AVR::ROLWRd); + EXPAND(AVR::ASRWRd); + EXPAND(AVR::SEXT); + EXPAND(AVR::ZEXT); + EXPAND(AVR::SPREAD); + EXPAND(AVR::SPWRITE); + } +#undef EXPAND + return false; +} + +namespace llvm { + +FunctionPass *createAVRExpandPseudoPass() { return new AVRExpandPseudo(); } + +} // end of namespace llvm |