| //===-- AVRExpandPseudoInsts.cpp - Expand pseudo instructions -------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // 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 register that will always contain zero. |
| const unsigned ZERO_REGISTER = AVR::R1; |
| /// 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; |
| Register DstReg = MI.getOperand(0).getReg(); |
| Register 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; |
| Register DstReg = MI.getOperand(0).getReg(); |
| Register 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; |
| Register 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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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::LDRdPtr; |
| OpHi = AVR::LDDRdPtrQ; |
| 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); |
| |
| // 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(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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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() && |
| !Register::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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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()); |
| MIBHI.setMemRefs(MI.memoperands()); |
| |
| 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::ROLBRd>(Block &MBB, BlockIt MBBI) { |
| // In AVR, the rotate instructions behave quite unintuitively. They rotate |
| // bits through the carry bit in SREG, effectively rotating over 9 bits, |
| // instead of 8. This is useful when we are dealing with numbers over |
| // multiple registers, but when we actually need to rotate stuff, we have |
| // to explicitly add the carry bit. |
| |
| MachineInstr &MI = *MBBI; |
| unsigned OpShift, OpCarry; |
| unsigned DstReg = MI.getOperand(0).getReg(); |
| bool DstIsDead = MI.getOperand(0).isDead(); |
| OpShift = AVR::ADDRdRr; |
| OpCarry = AVR::ADCRdRr; |
| |
| // add r16, r16 |
| // adc r16, r1 |
| |
| // Shift part |
| buildMI(MBB, MBBI, OpShift) |
| .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) |
| .addReg(DstReg) |
| .addReg(DstReg); |
| |
| // Add the carry bit |
| auto MIB = buildMI(MBB, MBBI, OpCarry) |
| .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) |
| .addReg(DstReg) |
| .addReg(ZERO_REGISTER); |
| |
| // SREG is always implicitly killed |
| MIB->getOperand(2).setIsKill(); |
| |
| MI.eraseFromParent(); |
| return true; |
| } |
| |
| template <> |
| bool AVRExpandPseudo::expand<AVR::RORBRd>(Block &MBB, BlockIt MBBI) { |
| // In AVR, the rotate instructions behave quite unintuitively. They rotate |
| // bits through the carry bit in SREG, effectively rotating over 9 bits, |
| // instead of 8. This is useful when we are dealing with numbers over |
| // multiple registers, but when we actually need to rotate stuff, we have |
| // to explicitly add the carry bit. |
| |
| MachineInstr &MI = *MBBI; |
| unsigned OpShiftOut, OpLoad, OpShiftIn, OpAdd; |
| unsigned DstReg = MI.getOperand(0).getReg(); |
| bool DstIsDead = MI.getOperand(0).isDead(); |
| OpShiftOut = AVR::LSRRd; |
| OpLoad = AVR::LDIRdK; |
| OpShiftIn = AVR::RORRd; |
| OpAdd = AVR::ORRdRr; |
| |
| // lsr r16 |
| // ldi r0, 0 |
| // ror r0 |
| // or r16, r17 |
| |
| // Shift out |
| buildMI(MBB, MBBI, OpShiftOut) |
| .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) |
| .addReg(DstReg); |
| |
| // Put 0 in temporary register |
| buildMI(MBB, MBBI, OpLoad) |
| .addReg(SCRATCH_REGISTER, RegState::Define | getDeadRegState(true)) |
| .addImm(0x00); |
| |
| // Shift in |
| buildMI(MBB, MBBI, OpShiftIn) |
| .addReg(SCRATCH_REGISTER, RegState::Define | getDeadRegState(true)) |
| .addReg(SCRATCH_REGISTER); |
| |
| // Add the results together using an or-instruction |
| auto MIB = buildMI(MBB, MBBI, OpAdd) |
| .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) |
| .addReg(DstReg) |
| .addReg(SCRATCH_REGISTER); |
| |
| // SREG is always implicitly killed |
| MIB->getOperand(2).setIsKill(); |
| |
| 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::ADDRdRr; // ADD Rd, Rd <==> LSL Rd |
| OpHi = AVR::ADCRdRr; // ADC Rd, Rd <==> ROL Rd |
| TRI->splitReg(DstReg, DstLoReg, DstHiReg); |
| |
| // Low part |
| buildMI(MBB, MBBI, OpLo) |
| .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) |
| .addReg(DstLoReg) |
| .addReg(DstLoReg, getKillRegState(DstIsKill)); |
| |
| auto MIBHI = buildMI(MBB, MBBI, OpHi) |
| .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) |
| .addReg(DstHiReg) |
| .addReg(DstHiReg, getKillRegState(DstIsKill)); |
| |
| if (ImpIsDead) |
| MIBHI->getOperand(3).setIsDead(); |
| |
| // SREG is always implicitly killed |
| MIBHI->getOperand(4).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::ADDRdRr) // LSL Rd <==> ADD Rd, Rr |
| .addReg(DstHiReg, RegState::Define) |
| .addReg(DstHiReg) |
| .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::ROLBRd); |
| EXPAND(AVR::RORBRd); |
| 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 |