//===-- 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 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 bool expandAtomic(Block &MBB, BlockIt MBBI, Func f); template 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(); 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(Block &MBB, BlockIt MBBI) { return expandArith(AVR::ADDRdRr, AVR::ADCRdRr, MBB, MBBI); } template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandArith(AVR::ADCRdRr, AVR::ADCRdRr, MBB, MBBI); } template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandArith(AVR::SUBRdRr, AVR::SBCRdRr, MBB, MBBI); } template <> bool AVRExpandPseudo::expand(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(Block &MBB, BlockIt MBBI) { return expandArith(AVR::SBCRdRr, AVR::SBCRdRr, MBB, MBBI); } template <> bool AVRExpandPseudo::expand(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(Block &MBB, BlockIt MBBI) { return expandLogic(AVR::ANDRdRr, MBB, MBBI); } template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandLogicImm(AVR::ANDIRdK, MBB, MBBI); } template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandLogic(AVR::ORRdRr, MBB, MBBI); } template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandLogicImm(AVR::ORIRdK, MBB, MBBI); } template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandLogic(AVR::EORRdRr, MBB, MBBI); } template <> bool AVRExpandPseudo::expand(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(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(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(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(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(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(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(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(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(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(Block &MBB, BlockIt MBBI) { llvm_unreachable("wide LPMPi is unimplemented"); } template 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 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(Block &MBB, BlockIt MBBI) { return expandAtomicBinaryOp(AVR::LDRdPtr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicBinaryOp(AVR::LDWRdPtr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicBinaryOp(AVR::STPtrRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicBinaryOp(AVR::STWPtrRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicArithmeticOp(8, AVR::ADDRdRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicArithmeticOp(16, AVR::ADDWRdRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicArithmeticOp(8, AVR::SUBRdRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicArithmeticOp(16, AVR::SUBWRdRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicArithmeticOp(8, AVR::ANDRdRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicArithmeticOp(16, AVR::ANDWRdRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicArithmeticOp(8, AVR::ORRdRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicArithmeticOp(16, AVR::ORWRdRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicArithmeticOp(8, AVR::EORRdRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return expandAtomicArithmeticOp(16, AVR::EORWRdRr, MBB, MBBI); } template<> bool AVRExpandPseudo::expand(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(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(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(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(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(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(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(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(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(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(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(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(Block &MBB, BlockIt MBBI) { llvm_unreachable("RORW unimplemented"); return false; } template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { llvm_unreachable("ROLW unimplemented"); return false; } template <> bool AVRExpandPseudo::expand(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(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(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(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(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(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