| //===-- 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/CodeGen/RegisterScavenging.h" | |
| #include "llvm/CodeGen/TargetRegisterInfo.h" | |
| using namespace llvm; | |
| #define AVR_EXPAND_PSEUDO_NAME "AVR pseudo instruction expansion pass" | |
| namespace { | |
| /// Expands "placeholder" instructions marked as pseudo into | |
| /// actual AVR instructions. | |
| class AVRExpandPseudo : public MachineFunctionPass { | |
| public: | |
| static char ID; | |
| AVRExpandPseudo() : MachineFunctionPass(ID) { | |
| initializeAVRExpandPseudoPass(*PassRegistry::getPassRegistry()); | |
| } | |
| bool runOnMachineFunction(MachineFunction &MF) override; | |
| StringRef getPassName() const override { return AVR_EXPAND_PSEUDO_NAME; } | |
| 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); | |
| bool isLogicImmOpRedundant(unsigned Op, unsigned ImmVal) const; | |
| 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); | |
| /// Scavenges a free GPR8 register for use. | |
| unsigned scavengeGPR8(MachineInstr &MI); | |
| }; | |
| char AVRExpandPseudo::ID = 0; | |
| 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(); | |
| // We need to track liveness in order to use register scavenging. | |
| MF.getProperties().set(MachineFunctionProperties::Property::TracksLiveness); | |
| 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:: | |
| isLogicImmOpRedundant(unsigned Op, unsigned ImmVal) const { | |
| // ANDI Rd, 0xff is redundant. | |
| if (Op == AVR::ANDIRdK && ImmVal == 0xff) | |
| return true; | |
| // ORI Rd, 0x0 is redundant. | |
| if (Op == AVR::ORIRdK && ImmVal == 0x0) | |
| return true; | |
| return false; | |
| } | |
| 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); | |
| if (!isLogicImmOpRedundant(Op, Lo8)) { | |
| 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(); | |
| } | |
| if (!isLogicImmOpRedundant(Op, Hi8)) { | |
| 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.add(MachineOperand::CreateBA(BA, TF | AVRII::MO_LO)); | |
| MIBHI.add(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 TmpReg = 0; // 0 for no temporary register | |
| unsigned SrcReg = MI.getOperand(1).getReg(); | |
| bool SrcIsKill = MI.getOperand(1).isKill(); | |
| OpLo = AVR::LDRdPtrPi; | |
| OpHi = AVR::LDRdPtr; | |
| TRI->splitReg(DstReg, DstLoReg, DstHiReg); | |
| // Use a temporary register if src and dst registers are the same. | |
| if (DstReg == SrcReg) | |
| TmpReg = scavengeGPR8(MI); | |
| unsigned CurDstLoReg = (DstReg == SrcReg) ? TmpReg : DstLoReg; | |
| unsigned CurDstHiReg = (DstReg == SrcReg) ? TmpReg : DstHiReg; | |
| // Load low byte. | |
| auto MIBLO = buildMI(MBB, MBBI, OpLo) | |
| .addReg(CurDstLoReg, RegState::Define) | |
| .addReg(SrcReg, RegState::Define) | |
| .addReg(SrcReg); | |
| // Push low byte onto stack if necessary. | |
| if (TmpReg) | |
| buildMI(MBB, MBBI, AVR::PUSHRr).addReg(TmpReg); | |
| // Load high byte. | |
| auto MIBHI = buildMI(MBB, MBBI, OpHi) | |
| .addReg(CurDstHiReg, RegState::Define) | |
| .addReg(SrcReg, getKillRegState(SrcIsKill)); | |
| if (TmpReg) { | |
| // Move the high byte into the final destination. | |
| buildMI(MBB, MBBI, AVR::MOVRdRr).addReg(DstHiReg).addReg(TmpReg); | |
| // Move the low byte from the scratch space into the final destination. | |
| buildMI(MBB, MBBI, AVR::POPRd).addReg(DstLoReg); | |
| } | |
| 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 TmpReg = 0; // 0 for no temporary register | |
| unsigned SrcReg = MI.getOperand(1).getReg(); | |
| unsigned Imm = MI.getOperand(2).getImm(); | |
| bool SrcIsKill = MI.getOperand(1).isKill(); | |
| OpLo = AVR::LDDRdPtrQ; | |
| OpHi = AVR::LDDRdPtrQ; | |
| TRI->splitReg(DstReg, DstLoReg, DstHiReg); | |
| // Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value | |
| // allowed for the instruction, 62 is the limit here. | |
| assert(Imm <= 62 && "Offset is out of range"); | |
| // Use a temporary register if src and dst registers are the same. | |
| if (DstReg == SrcReg) | |
| TmpReg = scavengeGPR8(MI); | |
| unsigned CurDstLoReg = (DstReg == SrcReg) ? TmpReg : DstLoReg; | |
| unsigned CurDstHiReg = (DstReg == SrcReg) ? TmpReg : DstHiReg; | |
| // Load low byte. | |
| auto MIBLO = buildMI(MBB, MBBI, OpLo) | |
| .addReg(CurDstLoReg, RegState::Define) | |
| .addReg(SrcReg) | |
| .addImm(Imm); | |
| // Push low byte onto stack if necessary. | |
| if (TmpReg) | |
| buildMI(MBB, MBBI, AVR::PUSHRr).addReg(TmpReg); | |
| // Load high byte. | |
| auto MIBHI = buildMI(MBB, MBBI, OpHi) | |
| .addReg(CurDstHiReg, RegState::Define) | |
| .addReg(SrcReg, getKillRegState(SrcIsKill)) | |
| .addImm(Imm + 1); | |
| if (TmpReg) { | |
| // Move the high byte into the final destination. | |
| buildMI(MBB, MBBI, AVR::MOVRdRr).addReg(DstHiReg).addReg(TmpReg); | |
| // Move the low byte from the scratch space into the final destination. | |
| buildMI(MBB, MBBI, AVR::POPRd).addReg(DstLoReg); | |
| } | |
| 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::LPMWRdZ>(Block &MBB, BlockIt MBBI) { | |
| MachineInstr &MI = *MBBI; | |
| unsigned OpLo, OpHi, DstLoReg, DstHiReg; | |
| unsigned DstReg = MI.getOperand(0).getReg(); | |
| unsigned TmpReg = 0; // 0 for no temporary register | |
| unsigned SrcReg = MI.getOperand(1).getReg(); | |
| bool SrcIsKill = MI.getOperand(1).isKill(); | |
| OpLo = AVR::LPMRdZPi; | |
| OpHi = AVR::LPMRdZ; | |
| TRI->splitReg(DstReg, DstLoReg, DstHiReg); | |
| // Use a temporary register if src and dst registers are the same. | |
| if (DstReg == SrcReg) | |
| TmpReg = scavengeGPR8(MI); | |
| unsigned CurDstLoReg = (DstReg == SrcReg) ? TmpReg : DstLoReg; | |
| unsigned CurDstHiReg = (DstReg == SrcReg) ? TmpReg : DstHiReg; | |
| // Load low byte. | |
| auto MIBLO = buildMI(MBB, MBBI, OpLo) | |
| .addReg(CurDstLoReg, RegState::Define) | |
| .addReg(SrcReg); | |
| // Push low byte onto stack if necessary. | |
| if (TmpReg) | |
| buildMI(MBB, MBBI, AVR::PUSHRr).addReg(TmpReg); | |
| // Load high byte. | |
| auto MIBHI = buildMI(MBB, MBBI, OpHi) | |
| .addReg(CurDstHiReg, RegState::Define) | |
| .addReg(SrcReg, getKillRegState(SrcIsKill)); | |
| if (TmpReg) { | |
| // Move the high byte into the final destination. | |
| buildMI(MBB, MBBI, AVR::MOVRdRr).addReg(DstHiReg).addReg(TmpReg); | |
| // Move the low byte from the scratch space into the final destination. | |
| buildMI(MBB, MBBI, AVR::POPRd).addReg(DstLoReg); | |
| } | |
| 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::LPMWRdZPi>(Block &MBB, BlockIt MBBI) { | |
| llvm_unreachable("wide LPMPi is unimplemented"); | |
| } | |
| 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).add(Op1).add(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).add(Op1).add(Op2); | |
| // Create the arithmetic op | |
| buildMI(MBB, MBBI, ArithOpcode).add(Op1).add(Op1).add(Op2); | |
| // Create the store | |
| buildMI(MBB, MBBI, StoreOpcode).add(Op2).add(Op1); | |
| }); | |
| } | |
| unsigned AVRExpandPseudo::scavengeGPR8(MachineInstr &MI) { | |
| MachineBasicBlock &MBB = *MI.getParent(); | |
| RegScavenger RS; | |
| RS.enterBasicBlock(MBB); | |
| RS.forward(MI); | |
| BitVector Candidates = | |
| TRI->getAllocatableSet | |
| (*MBB.getParent(), &AVR::GPR8RegClass); | |
| // Exclude all the registers being used by the instruction. | |
| for (MachineOperand &MO : MI.operands()) { | |
| if (MO.isReg() && MO.getReg() != 0 && !MO.isDef() && | |
| !TargetRegisterInfo::isVirtualRegister(MO.getReg())) | |
| Candidates.reset(MO.getReg()); | |
| } | |
| BitVector Available = RS.getRegsAvailable(&AVR::GPR8RegClass); | |
| Available &= Candidates; | |
| signed Reg = Available.find_first(); | |
| assert(Reg != -1 && "ran out of registers"); | |
| return Reg; | |
| } | |
| 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 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) | |
| .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); | |
| // Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value | |
| // allowed for the instruction, 62 is the limit here. | |
| assert(Imm <= 62 && "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); | |
| // Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value | |
| // allowed for the instruction, 62 is the limit here. | |
| assert(Imm <= 62 && "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); | |
| // Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value | |
| // allowed for the instruction, 62 is the limit here. | |
| assert(Imm <= 62 && "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::LPMWRdZ); | |
| EXPAND(AVR::LPMWRdZPi); | |
| 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; | |
| } | |
| } // end of anonymous namespace | |
| INITIALIZE_PASS(AVRExpandPseudo, "avr-expand-pseudo", | |
| AVR_EXPAND_PSEUDO_NAME, false, false) | |
| namespace llvm { | |
| FunctionPass *createAVRExpandPseudoPass() { return new AVRExpandPseudo(); } | |
| } // end of namespace llvm |