diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp new file mode 100644 index 000000000000..653b3abaccfa --- /dev/null +++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -0,0 +1,1431 @@ +//===-- AVRExpandPseudoInsts.cpp - Expand pseudo instructions -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a pass that expands pseudo instructions into target +// instructions. This pass should be run after register allocation but before +// the post-regalloc scheduling pass. +// +//===----------------------------------------------------------------------===// + +#include "AVR.h" +#include "AVRInstrInfo.h" +#include "AVRTargetMachine.h" +#include "MCTargetDesc/AVRMCTargetDesc.h" + +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Target/TargetRegisterInfo.h" + +using namespace llvm; + +namespace { + +/// Expands "placeholder" instructions marked as pseudo into +/// actual AVR instructions. +class AVRExpandPseudo : public MachineFunctionPass { +public: + static char ID; + + AVRExpandPseudo() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) override; + + StringRef getPassName() const override { + return "AVR pseudo instruction expansion pass"; + } + +private: + typedef MachineBasicBlock Block; + typedef Block::iterator BlockIt; + + const AVRRegisterInfo *TRI; + const TargetInstrInfo *TII; + + /// The register to be used for temporary storage. + const unsigned SCRATCH_REGISTER = AVR::R0; + /// The IO address of the status register. + const unsigned SREG_ADDR = 0x3f; + + bool expandMBB(Block &MBB); + bool expandMI(Block &MBB, BlockIt MBBI); + template bool expand(Block &MBB, BlockIt MBBI); + + MachineInstrBuilder buildMI(Block &MBB, BlockIt MBBI, unsigned Opcode) { + return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(Opcode)); + } + + MachineInstrBuilder buildMI(Block &MBB, BlockIt MBBI, unsigned Opcode, + unsigned DstReg) { + return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(Opcode), DstReg); + } + + MachineRegisterInfo &getRegInfo(Block &MBB) { return MBB.getParent()->getRegInfo(); } + + bool expandArith(unsigned OpLo, unsigned OpHi, Block &MBB, BlockIt MBBI); + bool expandLogic(unsigned Op, Block &MBB, BlockIt MBBI); + bool expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI); + + template + 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); +}; + +char AVRExpandPseudo::ID = 0; + +} // end of anonymous namespace + +bool AVRExpandPseudo::expandMBB(MachineBasicBlock &MBB) { + bool Modified = false; + + BlockIt MBBI = MBB.begin(), E = MBB.end(); + while (MBBI != E) { + BlockIt NMBBI = std::next(MBBI); + Modified |= expandMI(MBB, MBBI); + MBBI = NMBBI; + } + + return Modified; +} + +bool AVRExpandPseudo::runOnMachineFunction(MachineFunction &MF) { + bool Modified = false; + + const AVRSubtarget &STI = MF.getSubtarget(); + TRI = STI.getRegisterInfo(); + TII = STI.getInstrInfo(); + + for (Block &MBB : MF) { + bool ContinueExpanding = true; + unsigned ExpandCount = 0; + + // Continue expanding the block until all pseudos are expanded. + do { + assert(ExpandCount < 10 && "pseudo expand limit reached"); + + bool BlockModified = expandMBB(MBB); + Modified |= BlockModified; + ExpandCount++; + + ContinueExpanding = BlockModified; + } while (ContinueExpanding); + } + + return Modified; +} + +bool AVRExpandPseudo:: +expandArith(unsigned OpLo, unsigned OpHi, Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned SrcLoReg, SrcHiReg, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool SrcIsKill = MI.getOperand(2).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + // SREG is always implicitly killed + MIBHI->getOperand(4).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +bool AVRExpandPseudo:: +expandLogic(unsigned Op, Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned SrcLoReg, SrcHiReg, DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + unsigned SrcReg = MI.getOperand(2).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool SrcIsKill = MI.getOperand(2).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, Op) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + // SREG is always implicitly dead + MIBLO->getOperand(3).setIsDead(); + + auto MIBHI = buildMI(MBB, MBBI, Op) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +bool AVRExpandPseudo:: +expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned DstLoReg, DstHiReg; + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(3).isDead(); + unsigned Imm = MI.getOperand(2).getImm(); + unsigned Lo8 = Imm & 0xff; + unsigned Hi8 = (Imm >> 8) & 0xff; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + auto MIBLO = buildMI(MBB, MBBI, Op) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(SrcIsKill)) + .addImm(Lo8); + + // SREG is always implicitly dead + MIBLO->getOperand(3).setIsDead(); + + auto MIBHI = buildMI(MBB, MBBI, Op) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(SrcIsKill)) + .addImm(Hi8); + + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(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.addOperand(MachineOperand::CreateBA(BA, TF | AVRII::MO_LO)); + MIBHI.addOperand(MachineOperand::CreateBA(BA, TF | AVRII::MO_HI)); + break; + } + case MachineOperand::MO_Immediate: { + unsigned Imm = MI.getOperand(1).getImm(); + + MIBLO.addImm(Imm & 0xff); + MIBHI.addImm((Imm >> 8) & 0xff); + break; + } + default: + llvm_unreachable("Unknown operand type!"); + } + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(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 SrcReg = MI.getOperand(1).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::LDRdPtr; + OpHi = AVR::LDDRdPtrQ; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, getKillRegState(SrcIsKill)) + .addImm(1); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(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 SrcReg = MI.getOperand(1).getReg(); + unsigned Imm = MI.getOperand(2).getImm(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::LDDRdPtrQ; + OpHi = AVR::LDDRdPtrQ; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(Imm < 63 && "Offset is out of range"); + assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg) + .addImm(Imm); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(SrcReg, getKillRegState(SrcIsKill)) + .addImm(Imm + 1); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template +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) + .addOperand(Op1).addOperand(Op2) + .getInstr(); + f(NewInst); + }); +} + +bool AVRExpandPseudo::expandAtomicBinaryOp(unsigned Opcode, + Block &MBB, + BlockIt MBBI) { + return expandAtomicBinaryOp(Opcode, MBB, MBBI, [](MachineInstr &MI) {}); +} + +bool AVRExpandPseudo::expandAtomicArithmeticOp(unsigned Width, + unsigned ArithOpcode, + Block &MBB, + BlockIt MBBI) { + return expandAtomic(MBB, MBBI, [&](MachineInstr &MI) { + auto Op1 = MI.getOperand(0); + auto Op2 = MI.getOperand(1); + + unsigned LoadOpcode = (Width == 8) ? AVR::LDRdPtr : AVR::LDWRdPtr; + unsigned StoreOpcode = (Width == 8) ? AVR::STPtrRr : AVR::STWPtrRr; + + // Create the load + buildMI(MBB, MBBI, LoadOpcode).addOperand(Op1).addOperand(Op2); + + // Create the arithmetic op + buildMI(MBB, MBBI, ArithOpcode) + .addOperand(Op1).addOperand(Op1) + .addOperand(Op2); + + // Create the store + buildMI(MBB, MBBI, StoreOpcode).addOperand(Op2).addOperand(Op1); + }); +} + +template<> +bool AVRExpandPseudo::expand(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 DstIsKill = MI.getOperand(0).isKill(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::STPtrRr; + OpHi = AVR::STDPtrQRr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + //:TODO: need to reverse this order like inw and stsw? + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstReg) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addImm(1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(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); + + assert(Imm < 63 && "Offset is out of range"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstReg) + .addImm(Imm) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addImm(Imm + 1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, DstLoReg, DstHiReg; + unsigned Imm = MI.getOperand(1).getImm(); + unsigned DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + OpLo = AVR::INRdA; + OpHi = AVR::INRdA; + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + assert(Imm < 63 && "Address is out of range"); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addImm(Imm); + + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addImm(Imm + 1); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned OpLo, OpHi, SrcLoReg, SrcHiReg; + unsigned Imm = MI.getOperand(0).getImm(); + unsigned SrcReg = MI.getOperand(1).getReg(); + bool SrcIsKill = MI.getOperand(1).isKill(); + OpLo = AVR::OUTARr; + OpHi = AVR::OUTARr; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + + assert(Imm < 63 && "Address is out of range"); + + // 16 bit I/O writes need the high byte first + auto MIBHI = buildMI(MBB, MBBI, OpHi) + .addImm(Imm + 1) + .addReg(SrcHiReg, getKillRegState(SrcIsKill)); + + auto MIBLO = buildMI(MBB, MBBI, OpLo) + .addImm(Imm) + .addReg(SrcLoReg, getKillRegState(SrcIsKill)); + + MIBLO->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + MIBHI->setMemRefs(MI.memoperands_begin(), MI.memoperands_end()); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(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::AtomicLoad8); + EXPAND(AVR::AtomicLoad16); + EXPAND(AVR::AtomicStore8); + EXPAND(AVR::AtomicStore16); + EXPAND(AVR::AtomicLoadAdd8); + EXPAND(AVR::AtomicLoadAdd16); + EXPAND(AVR::AtomicLoadSub8); + EXPAND(AVR::AtomicLoadSub16); + EXPAND(AVR::AtomicLoadAnd8); + EXPAND(AVR::AtomicLoadAnd16); + EXPAND(AVR::AtomicLoadOr8); + EXPAND(AVR::AtomicLoadOr16); + EXPAND(AVR::AtomicLoadXor8); + EXPAND(AVR::AtomicLoadXor16); + EXPAND(AVR::AtomicFence); + EXPAND(AVR::STSWKRr); + EXPAND(AVR::STWPtrRr); + EXPAND(AVR::STWPtrPiRr); + EXPAND(AVR::STWPtrPdRr); + EXPAND(AVR::STDWPtrQRr); + EXPAND(AVR::INWRdA); + EXPAND(AVR::OUTWARr); + EXPAND(AVR::PUSHWRr); + EXPAND(AVR::POPWRd); + EXPAND(AVR::LSLWRd); + EXPAND(AVR::LSRWRd); + EXPAND(AVR::RORWRd); + EXPAND(AVR::ROLWRd); + EXPAND(AVR::ASRWRd); + EXPAND(AVR::SEXT); + EXPAND(AVR::ZEXT); + EXPAND(AVR::SPREAD); + EXPAND(AVR::SPWRITE); + } +#undef EXPAND + return false; +} + +namespace llvm { + +FunctionPass *createAVRExpandPseudoPass() { return new AVRExpandPseudo(); } + +} // end of namespace llvm diff --git a/llvm/lib/Target/AVR/AVRTargetMachine.cpp b/llvm/lib/Target/AVR/AVRTargetMachine.cpp index e1a67272a623..a390b1caba8a 100644 --- a/llvm/lib/Target/AVR/AVRTargetMachine.cpp +++ b/llvm/lib/Target/AVR/AVRTargetMachine.cpp @@ -106,7 +106,7 @@ void AVRPassConfig::addPreRegAlloc() { addPass(createAVRDynAllocaSRPass()); } -void AVRPassConfig::addPreSched2() { } +void AVRPassConfig::addPreSched2() { addPass(createAVRExpandPseudoPass()); } void AVRPassConfig::addPreEmitPass() { } diff --git a/llvm/lib/Target/AVR/CMakeLists.txt b/llvm/lib/Target/AVR/CMakeLists.txt index 11ae7374cb4d..716eca891ed8 100644 --- a/llvm/lib/Target/AVR/CMakeLists.txt +++ b/llvm/lib/Target/AVR/CMakeLists.txt @@ -18,6 +18,7 @@ add_public_tablegen_target(AVRCommonTableGen) add_llvm_target(AVRCodeGen AVRAsmPrinter.cpp + AVRExpandPseudoInsts.cpp AVRFrameLowering.cpp AVRInstrInfo.cpp AVRISelDAGToDAG.cpp diff --git a/llvm/test/CodeGen/AVR/add.ll b/llvm/test/CodeGen/AVR/add.ll new file mode 100644 index 000000000000..cd3a69e6454c --- /dev/null +++ b/llvm/test/CodeGen/AVR/add.ll @@ -0,0 +1,93 @@ +; RUN: llc -mattr=addsubiw < %s -march=avr | FileCheck %s + +define i8 @add8_reg_reg(i8 %a, i8 %b) { +; CHECK-LABEL: add8_reg_reg: +; CHECK: add r24, r22 + %result = add i8 %a, %b + ret i8 %result +} + +define i8 @add8_reg_imm(i8 %a) { +; CHECK-LABEL: add8_reg_imm: +; CHECK: subi r24, -5 + %result = add i8 %a, 5 + ret i8 %result +} + +define i8 @add8_reg_increment(i8 %a) { +; CHECK-LABEL: add8_reg_increment: +; CHECK: inc r24 + %result = add i8 %a, 1 + ret i8 %result +} + + +define i16 @add16_reg_reg(i16 %a, i16 %b) { +; CHECK-LABEL: add16_reg_reg: +; CHECK: add r24, r22 +; CHECK: adc r25, r23 + %result = add i16 %a, %b + ret i16 %result +} + +define i16 @add16_reg_imm(i16 %a) { +; CHECK-LABEL: add16_reg_imm: +; CHECK: adiw r24, 63 + %result = add i16 %a, 63 + ret i16 %result +} + +define i16 @add16_reg_imm_subi(i16 %a) { +; CHECK-LABEL: add16_reg_imm_subi: +; CHECK: subi r24, 133 +; CHECK: sbci r25, 255 + %result = add i16 %a, 123 + ret i16 %result +} + +define i32 @add32_reg_reg(i32 %a, i32 %b) { +; CHECK-LABEL: add32_reg_reg: +; CHECK: add r22, r18 +; CHECK: adc r23, r19 +; CHECK: adc r24, r20 +; CHECK: adc r25, r21 + %result = add i32 %a, %b + ret i32 %result +} + +define i32 @add32_reg_imm(i32 %a) { +; CHECK-LABEL: add32_reg_imm: +; CHECK: subi r22, 251 +; CHECK: sbci r23, 255 +; CHECK: sbci r24, 255 +; CHECK: sbci r25, 255 + %result = add i32 %a, 5 + ret i32 %result +} + +define i64 @add64_reg_reg(i64 %a, i64 %b) { +; CHECK-LABEL: add64_reg_reg: +; CHECK: add r18, r10 +; CHECK: adc r20, r12 +; CHECK: adc r21, r13 +; CHECK: adc r22, r14 +; CHECK: adc r23, r15 +; CHECK: adc r24, r16 +; CHECK: adc r25, r17 + %result = add i64 %a, %b + ret i64 %result +} + +define i64 @add64_reg_imm(i64 %a) { +; CHECK-LABEL: add64_reg_imm: +; CHECK: subi r18, 251 +; CHECK: sbci r19, 255 +; CHECK: sbci r20, 255 +; CHECK: sbci r21, 255 +; CHECK: sbci r22, 255 +; CHECK: sbci r23, 255 +; CHECK: sbci r24, 255 +; CHECK: sbci r25, 255 + %result = add i64 %a, 5 + ret i64 %result +} diff --git a/llvm/test/CodeGen/AVR/alloca.ll b/llvm/test/CodeGen/AVR/alloca.ll new file mode 100644 index 000000000000..579573c0a133 --- /dev/null +++ b/llvm/test/CodeGen/AVR/alloca.ll @@ -0,0 +1,84 @@ +; RUN: llc < %s -march=avr -mattr=avr6 | FileCheck %s + +declare i16 @allocate(i16*, i16*) + +; Test taking an address of an alloca with a small offset (adiw) +define i16 @alloca_addressof_small() { +entry: +; CHECK-LABEL: alloca_addressof_small: +; Test that Y is saved +; CHECK: push r28 +; CHECK: push r29 +; CHECK: movw r24, r28 +; CHECK: adiw r24, 17 +; CHECK: movw {{.*}}, r28 +; CHECK: adiw {{.*}}, 39 +; CHECK: movw r22, {{.*}} +; CHECK: pop r29 +; CHECK: pop r28 + %p = alloca [18 x i16] + %k = alloca [14 x i16] + %arrayidx = getelementptr inbounds [14 x i16], [14 x i16]* %k, i16 0, i16 8 + %arrayidx1 = getelementptr inbounds [18 x i16], [18 x i16]* %p, i16 0, i16 5 + %call = call i16 @allocate(i16* %arrayidx, i16* %arrayidx1) + ret i16 %call +} + +; Test taking an address of an alloca with a big offset (subi/sbci pair) +define i16 @alloca_addressof_big() { +entry: +; CHECK-LABEL: alloca_addressof_big: +; CHECK: movw r24, r28 +; CHECK: adiw r24, 17 +; CHECK: movw r22, r28 +; CHECK: subi r22, 145 +; CHECK: sbci r23, 255 + %p = alloca [55 x i16] + %k = alloca [14 x i16] + %arrayidx = getelementptr inbounds [14 x i16], [14 x i16]* %k, i16 0, i16 8 + %arrayidx1 = getelementptr inbounds [55 x i16], [55 x i16]* %p, i16 0, i16 41 + %call = call i16 @allocate(i16* %arrayidx, i16* %arrayidx1) + ret i16 %call +} + +; Test writing to an allocated variable with a small and a big offset +define i16 @alloca_write(i16 %x) { +entry: +; CHECK-LABEL: alloca_write: +; Big offset here +; CHECK: adiw r28, 57 +; CHECK: std Y+62, {{.*}} +; CHECK: std Y+63, {{.*}} +; CHECK: sbiw r28, 57 +; Small offset here +; CHECK: std Y+23, {{.*}} +; CHECK: std Y+24, {{.*}} + %p = alloca [15 x i16] + %k = alloca [14 x i16] + %arrayidx = getelementptr inbounds [15 x i16], [15 x i16]* %p, i16 0, i16 45 + store i16 22, i16* %arrayidx + %arrayidx1 = getelementptr inbounds [14 x i16], [14 x i16]* %k, i16 0, i16 11 + store i16 42, i16* %arrayidx1 + %arrayidx2 = getelementptr inbounds [14 x i16], [14 x i16]* %k, i16 0, i16 0 + %arrayidx3 = getelementptr inbounds [15 x i16], [15 x i16]* %p, i16 0, i16 0 + %call = call i16 @allocate(i16* %arrayidx2, i16* %arrayidx3) + ret i16 %call +} + +; Test writing to an allocated variable with a huge offset that cant be +; materialized with adiw/sbiw but with a subi/sbci pair. +define void @alloca_write_huge() { +; CHECK-LABEL: alloca_write_huge: +; CHECK: subi r28, 41 +; CHECK: sbci r29, 255 +; CHECK: std Y+62, {{.*}} +; CHECK: std Y+63, {{.*}} +; CHECK: subi r28, 215 +; CHECK: sbci r29, 0 + %k = alloca [140 x i16] + %arrayidx = getelementptr inbounds [140 x i16], [140 x i16]* %k, i16 0, i16 138 + store i16 22, i16* %arrayidx + %arraydecay = getelementptr inbounds [140 x i16], [140 x i16]* %k, i16 0, i16 0 + call i16 @allocate(i16* %arraydecay, i16* null) + ret void +} diff --git a/llvm/test/CodeGen/AVR/and.ll b/llvm/test/CodeGen/AVR/and.ll new file mode 100644 index 000000000000..7f0e8ac2cbab --- /dev/null +++ b/llvm/test/CodeGen/AVR/and.ll @@ -0,0 +1,80 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +define i8 @and8_reg_reg(i8 %a, i8 %b) { +; CHECK-LABEL: and8_reg_reg: +; CHECK: and r24, r22 + %result = and i8 %a, %b + ret i8 %result +} + +define i8 @and8_reg_imm(i8 %a) { +; CHECK-LABEL: and8_reg_imm: +; CHECK: andi r24, 5 + %result = and i8 %a, 5 + ret i8 %result +} + +define i16 @and16_reg_reg(i16 %a, i16 %b) { +; CHECK-LABEL: and16_reg_reg: +; CHECK: and r24, r22 +; CHECK: and r25, r23 + %result = and i16 %a, %b + ret i16 %result +} + +define i16 @and16_reg_imm(i16 %a) { +; CHECK-LABEL: and16_reg_imm: +; CHECK: andi r24, 210 +; CHECK: andi r25, 4 + %result = and i16 %a, 1234 + ret i16 %result +} + +define i32 @and32_reg_reg(i32 %a, i32 %b) { +; CHECK-LABEL: and32_reg_reg: +; CHECK: and r22, r18 +; CHECK: and r23, r19 +; CHECK: and r24, r20 +; CHECK: and r25, r21 + %result = and i32 %a, %b + ret i32 %result +} + +define i32 @and32_reg_imm(i32 %a) { +; CHECK-LABEL: and32_reg_imm: +; CHECK: andi r22, 21 +; CHECK: andi r23, 205 +; CHECK: andi r24, 91 +; CHECK: andi r25, 7 + %result = and i32 %a, 123456789 + ret i32 %result +} + +define i64 @and64_reg_reg(i64 %a, i64 %b) { +; CHECK-LABEL: and64_reg_reg: +; CHECK: and r18, r10 +; CHECK: and r19, r11 +; CHECK: and r20, r12 +; CHECK: and r21, r13 +; CHECK: and r22, r14 +; CHECK: and r23, r15 +; CHECK: and r24, r16 +; CHECK: and r25, r17 + %result = and i64 %a, %b + ret i64 %result +} + +define i64 @and64_reg_imm(i64 %a) { +; CHECK-LABEL: and64_reg_imm: +; CHECK: andi r18, 253 +; CHECK: andi r19, 255 +; CHECK: andi r20, 155 +; CHECK: andi r21, 88 +; CHECK: andi r22, 76 +; CHECK: andi r23, 73 +; CHECK: andi r24, 31 +; CHECK: andi r25, 242 + %result = and i64 %a, 17446744073709551613 + ret i64 %result +} + diff --git a/llvm/test/CodeGen/AVR/atomics/fence.ll b/llvm/test/CodeGen/AVR/atomics/fence.ll new file mode 100644 index 000000000000..6ea49bc7e3fc --- /dev/null +++ b/llvm/test/CodeGen/AVR/atomics/fence.ll @@ -0,0 +1,13 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; Checks that atomic fences are simply removed from IR. +; AVR is always singlethreaded so fences do nothing. + +; CHECK_LABEL: atomic_fence8 +; CHECK: ; BB#0: +; CHECK-NEXT: ret +define void @atomic_fence8() { + fence acquire + ret void +} + diff --git a/llvm/test/CodeGen/AVR/atomics/load16.ll b/llvm/test/CodeGen/AVR/atomics/load16.ll new file mode 100644 index 000000000000..ea021c0724b9 --- /dev/null +++ b/llvm/test/CodeGen/AVR/atomics/load16.ll @@ -0,0 +1,137 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; CHECK-LABEL: atomic_load16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR:r[0-9]+]], [[RD:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR:r[0-9]+]], [[RD:(X|Y|Z)]]+ +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load16(i16* %foo) { + %val = load atomic i16, i16* %foo unordered, align 2 + ret i16 %val +} + +; CHECK-LABEL: atomic_load_swap16 +; CHECK: call __sync_lock_test_and_set_2 +define i16 @atomic_load_swap16(i16* %foo) { + %val = atomicrmw xchg i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_cmp_swap16 +; CHECK: call __sync_val_compare_and_swap_2 +define i16 @atomic_load_cmp_swap16(i16* %foo) { + %val = cmpxchg i16* %foo, i16 5, i16 10 acq_rel monotonic + %value_loaded = extractvalue { i16, i1 } %val, 0 + ret i16 %value_loaded +} + +; CHECK-LABEL: atomic_load_add16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+ +; CHECK-NEXT: add [[RR1]], [[TMP:r[0-9]+]] +; CHECK-NEXT: adc [[RR2]], [[TMP:r[0-9]+]] +; CHECK-NEXT: st [[RD1]], [[RR1]] +; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load_add16(i16* %foo) { + %val = atomicrmw add i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_sub16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+ +; CHECK-NEXT: sub [[RR1]], [[TMP:r[0-9]+]] +; CHECK-NEXT: sbc [[RR2]], [[TMP:r[0-9]+]] +; CHECK-NEXT: st [[RD1]], [[RR1]] +; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load_sub16(i16* %foo) { + %val = atomicrmw sub i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_and16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+ +; CHECK-NEXT: and [[RR1]], [[TMP:r[0-9]+]] +; CHECK-NEXT: and [[RR2]], [[TMP:r[0-9]+]] +; CHECK-NEXT: st [[RD1]], [[RR1]] +; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load_and16(i16* %foo) { + %val = atomicrmw and i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_or16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+ +; CHECK-NEXT: or [[RR1]], [[TMP:r[0-9]+]] +; CHECK-NEXT: or [[RR2]], [[TMP:r[0-9]+]] +; CHECK-NEXT: st [[RD1]], [[RR1]] +; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load_or16(i16* %foo) { + %val = atomicrmw or i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_xor16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+ +; CHECK-NEXT: eor [[RR1]], [[TMP:r[0-9]+]] +; CHECK-NEXT: eor [[RR2]], [[TMP:r[0-9]+]] +; CHECK-NEXT: st [[RD1]], [[RR1]] +; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load_xor16(i16* %foo) { + %val = atomicrmw xor i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_nand16 +; CHECK: call __sync_fetch_and_nand_2 +define i16 @atomic_load_nand16(i16* %foo) { + %val = atomicrmw nand i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_max16 +; CHECK: call __sync_fetch_and_max_2 +define i16 @atomic_load_max16(i16* %foo) { + %val = atomicrmw max i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_min16 +; CHECK: call __sync_fetch_and_min_2 +define i16 @atomic_load_min16(i16* %foo) { + %val = atomicrmw min i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_umax16 +; CHECK: call __sync_fetch_and_umax_2 +define i16 @atomic_load_umax16(i16* %foo) { + %val = atomicrmw umax i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_umin16 +; CHECK: call __sync_fetch_and_umin_2 +define i16 @atomic_load_umin16(i16* %foo) { + %val = atomicrmw umin i16* %foo, i16 13 seq_cst + ret i16 %val +} diff --git a/llvm/test/CodeGen/AVR/atomics/load8.ll b/llvm/test/CodeGen/AVR/atomics/load8.ll new file mode 100644 index 000000000000..a20e66261466 --- /dev/null +++ b/llvm/test/CodeGen/AVR/atomics/load8.ll @@ -0,0 +1,124 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; Tests atomic operations on AVR + +; CHECK-LABEL: atomic_load8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR:r[0-9]+]], [[RD:(X|Y|Z)]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load8(i8* %foo) { + %val = load atomic i8, i8* %foo unordered, align 1 + ret i8 %val +} + +; CHECK-LABEL: atomic_load_swap8 +; CHECK: call __sync_lock_test_and_set_1 +define i8 @atomic_load_swap8(i8* %foo) { + %val = atomicrmw xchg i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_cmp_swap8 +; CHECK: call __sync_val_compare_and_swap_1 +define i8 @atomic_load_cmp_swap8(i8* %foo) { + %val = cmpxchg i8* %foo, i8 5, i8 10 acq_rel monotonic + %value_loaded = extractvalue { i8, i1 } %val, 0 + ret i8 %value_loaded +} + +; CHECK-LABEL: atomic_load_add8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]] +; CHECK-NEXT: add [[RD]], [[RR1:r[0-9]+]] +; CHECK-NEXT: st [[RR]], [[RD]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load_add8(i8* %foo) { + %val = atomicrmw add i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_sub8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]] +; CHECK-NEXT: sub [[RD]], [[RR1:r[0-9]+]] +; CHECK-NEXT: st [[RR]], [[RD]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load_sub8(i8* %foo) { + %val = atomicrmw sub i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_and8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]] +; CHECK-NEXT: and [[RD]], [[RR1:r[0-9]+]] +; CHECK-NEXT: st [[RR]], [[RD]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load_and8(i8* %foo) { + %val = atomicrmw and i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_or8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]] +; CHECK-NEXT: or [[RD]], [[RR1:r[0-9]+]] +; CHECK-NEXT: st [[RR]], [[RD]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load_or8(i8* %foo) { + %val = atomicrmw or i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_xor8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]] +; CHECK-NEXT: eor [[RD]], [[RR1:r[0-9]+]] +; CHECK-NEXT: st [[RR]], [[RD]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load_xor8(i8* %foo) { + %val = atomicrmw xor i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_nand8 +; CHECK: call __sync_fetch_and_nand_1 +define i8 @atomic_load_nand8(i8* %foo) { + %val = atomicrmw nand i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_max8 +; CHECK: call __sync_fetch_and_max_1 +define i8 @atomic_load_max8(i8* %foo) { + %val = atomicrmw max i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_min8 +; CHECK: call __sync_fetch_and_min_1 +define i8 @atomic_load_min8(i8* %foo) { + %val = atomicrmw min i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_umax8 +; CHECK: call __sync_fetch_and_umax_1 +define i8 @atomic_load_umax8(i8* %foo) { + %val = atomicrmw umax i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_umin8 +; CHECK: call __sync_fetch_and_umin_1 +define i8 @atomic_load_umin8(i8* %foo) { + %val = atomicrmw umin i8* %foo, i8 13 seq_cst + ret i8 %val +} + diff --git a/llvm/test/CodeGen/AVR/atomics/store.ll b/llvm/test/CodeGen/AVR/atomics/store.ll new file mode 100644 index 000000000000..e1231c21e7d7 --- /dev/null +++ b/llvm/test/CodeGen/AVR/atomics/store.ll @@ -0,0 +1,37 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; CHECK-LABEL: atomic_store8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define void @atomic_store8(i8* %foo) { + store atomic i8 1, i8* %foo unordered, align 1 + ret void +} + +; CHECK-LABEL: atomic_store16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]] +; CHECK-NEXT: std [[RD]]+1, [[RR:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define void @atomic_store16(i16* %foo) { + store atomic i16 1, i16* %foo unordered, align 2 + ret void +} + +; CHECK-LABEL: atomic_store32 +; CHECK: call __sync_lock_test_and_set_4 +define void @atomic_store32(i32* %foo) { + store atomic i32 1, i32* %foo unordered, align 4 + ret void +} + +; CHECK-LABEL: atomic_store64 +; CHECK: call __sync_lock_test_and_set_8 +define void @atomic_store64(i64* %foo) { + store atomic i64 1, i64* %foo unordered, align 8 + ret void +} + diff --git a/llvm/test/CodeGen/AVR/atomics/store16.ll b/llvm/test/CodeGen/AVR/atomics/store16.ll new file mode 100644 index 000000000000..610a53fad736 --- /dev/null +++ b/llvm/test/CodeGen/AVR/atomics/store16.ll @@ -0,0 +1,25 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; CHECK-LABEL: atomic_store16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]] +; CHECK-NEXT: std [[RD:(X|Y|Z)]]+1, [[RR:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define void @atomic_store16(i16* %foo) { + store atomic i16 1, i16* %foo unordered, align 2 + ret void +} + +; CHECK-LABEL: monotonic +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: st Z, r24 +; CHECK-NEXT: std Z+1, r25 +; CHECK-NEXT: out 63, r0 +define void @monotonic(i16) { +entry-block: + store atomic i16 %0, i16* undef monotonic, align 2 + ret void +} + diff --git a/llvm/test/CodeGen/AVR/brind.ll b/llvm/test/CodeGen/AVR/brind.ll new file mode 100644 index 000000000000..f92038d10829 --- /dev/null +++ b/llvm/test/CodeGen/AVR/brind.ll @@ -0,0 +1,20 @@ +; RUN: llc -mattr=sram,eijmpcall < %s -march=avr | FileCheck %s + +@brind.k = private unnamed_addr constant [2 x i8*] [i8* blockaddress(@brind, %return), i8* blockaddress(@brind, %b)], align 1 + +define i8 @brind(i8 %p) { +; CHECK-LABEL: brind: +; CHECK: ld r30 +; CHECK: ldd r31 +; CHECK: ijmp +entry: + %idxprom = sext i8 %p to i16 + %arrayidx = getelementptr inbounds [2 x i8*], [2 x i8*]* @brind.k, i16 0, i16 %idxprom + %s = load i8*, i8** %arrayidx + indirectbr i8* %s, [label %return, label %b] +b: + br label %return +return: + %retval.0 = phi i8 [ 4, %b ], [ 2, %entry ] + ret i8 %retval.0 +} diff --git a/llvm/test/CodeGen/AVR/call.ll b/llvm/test/CodeGen/AVR/call.ll new file mode 100644 index 000000000000..58bffd3a6787 --- /dev/null +++ b/llvm/test/CodeGen/AVR/call.ll @@ -0,0 +1,211 @@ +; RUN: llc < %s -march=avr -mattr=avr6 | FileCheck %s + +; TODO: test returning byval structs + +declare i8 @foo8_1(i8) +declare i8 @foo8_2(i8, i8, i8) +declare i8 @foo8_3(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8) + +declare i16 @foo16_1(i16, i16) +declare i16 @foo16_2(i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16) + +declare i32 @foo32_1(i32, i32) +declare i32 @foo32_2(i32, i32, i32, i32, i32) + +declare i64 @foo64_1(i64) +declare i64 @foo64_2(i64, i64, i64) + +define i8 @calli8_reg() { +; CHECK-LABEL: calli8_reg: +; CHECK: ldi r24, 12 +; CHECK: call foo8_1 +; CHECK: ldi r24, 12 +; CHECK: ldi r22, 13 +; CHECK: ldi r20, 14 +; CHECK: call foo8_2 + %result1 = call i8 @foo8_1(i8 12) + %result2 = call i8 @foo8_2(i8 12, i8 13, i8 14) + ret i8 %result2 +} + +define i8 @calli8_stack() { +; CHECK-LABEL: calli8_stack: +; CHECK: ldi [[REG1:r[0-9]+]], 11 +; CHECK: push [[REG1]] +; CHECK: ldi [[REG1]], 10 +; CHECK: push [[REG1]] +; CHECK: call foo8_3 + %result1 = call i8 @foo8_3(i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11) + ret i8 %result1 +} + +define i16 @calli16_reg() { +; CHECK-LABEL: calli16_reg: +; CHECK: ldi r24, 1 +; CHECK: ldi r25, 2 +; CHECK: ldi r22, 2 +; CHECK: ldi r23, 2 +; CHECK: call foo16_1 + %result1 = call i16 @foo16_1(i16 513, i16 514) + ret i16 %result1 +} + +define i16 @calli16_stack() { +; CHECK-LABEL: calli16_stack: +; CHECK: ldi [[REG1:r[0-9]+]], 10 +; CHECK: ldi [[REG2:r[0-9]+]], 2 +; CHECK: push [[REG2]] +; CHECK: push [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 9 +; CHECK: ldi [[REG2:r[0-9]+]], 2 +; CHECK: push [[REG2]] +; CHECK: push [[REG1]] +; CHECK: call foo16_2 + %result1 = call i16 @foo16_2(i16 512, i16 513, i16 514, i16 515, i16 516, i16 517, i16 518, i16 519, i16 520, i16 521, i16 522) + ret i16 %result1 +} + +define i32 @calli32_reg() { +; CHECK-LABEL: calli32_reg: +; CHECK: ldi r22, 64 +; CHECK: ldi r23, 66 +; CHECK: ldi r24, 15 +; CHECK: ldi r25, 2 +; CHECK: ldi r18, 128 +; CHECK: ldi r19, 132 +; CHECK: ldi r20, 30 +; CHECK: ldi r21, 2 +; CHECK: call foo32_1 + %result1 = call i32 @foo32_1(i32 34554432, i32 35554432) + ret i32 %result1 +} + +define i32 @calli32_stack() { +; CHECK-LABEL: calli32_stack: +; CHECK: ldi [[REG1:r[0-9]+]], 15 +; CHECK: ldi [[REG2:r[0-9]+]], 2 +; CHECK: push [[REG2]] +; CHECK: push [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 64 +; CHECK: ldi [[REG2:r[0-9]+]], 66 +; CHECK: push [[REG2]] +; CHECK: push [[REG1]] +; CHECK: call foo32_2 + %result1 = call i32 @foo32_2(i32 1, i32 2, i32 3, i32 4, i32 34554432) + ret i32 %result1 +} + +define i64 @calli64_reg() { +; CHECK-LABEL: calli64_reg: +; CHECK: ldi r18, 255 +; CHECK: ldi r19, 255 +; CHECK: ldi r20, 155 +; CHECK: ldi r21, 88 +; CHECK: ldi r22, 76 +; CHECK: ldi r23, 73 +; CHECK: ldi r24, 31 +; CHECK: ldi r25, 242 +; CHECK: call foo64_1 + %result1 = call i64 @foo64_1(i64 17446744073709551615) + ret i64 %result1 +} + +define i64 @calli64_stack() { +; CHECK-LABEL: calli64_stack: +; CHECK: ldi [[REG1:r[0-9]+]], 31 +; CHECK: ldi [[REG2:r[0-9]+]], 242 +; CHECK: push [[REG2]] +; CHECK: push [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 76 +; CHECK: ldi [[REG2:r[0-9]+]], 73 +; CHECK: push [[REG2]] +; CHECK: push [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 155 +; CHECK: ldi [[REG2:r[0-9]+]], 88 +; CHECK: push [[REG2]] +; CHECK: push [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 255 +; CHECK: ldi [[REG2:r[0-9]+]], 255 +; CHECK: push [[REG2]] +; CHECK: push [[REG1]] +; CHECK: call foo64_2 + %result1 = call i64 @foo64_2(i64 1, i64 2, i64 17446744073709551615) + ret i64 %result1 +} + +; Test passing arguments through the stack when the call frame is allocated +; in the prologue. +declare void @foo64_3(i64, i64, i64, i8, i16*) + +define void @testcallprologue() { +; CHECK-LABEL: testcallprologue: +; CHECK: push r28 +; CHECK: push r29 +; CHECK: sbiw r28, 28 +; CHECK: ldi [[REG1:r[0-9]+]], 88 +; CHECK: std Y+9, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 11 +; CHECK: ldi [[REG2:r[0-9]+]], 10 +; CHECK: std Y+7, [[REG1]] +; CHECK: std Y+8, [[REG2]] +; CHECK: ldi [[REG1:r[0-9]+]], 13 +; CHECK: ldi [[REG2:r[0-9]+]], 12 +; CHECK: std Y+5, [[REG1]] +; CHECK: std Y+6, [[REG2]] +; CHECK: ldi [[REG1:r[0-9]+]], 15 +; CHECK: ldi [[REG2:r[0-9]+]], 14 +; CHECK: std Y+3, [[REG1]] +; CHECK: std Y+4, [[REG2]] +; CHECK: ldi [[REG1:r[0-9]+]], 8 +; CHECK: ldi [[REG2:r[0-9]+]], 9 +; CHECK: std Y+1, [[REG1]] +; CHECK: std Y+2, [[REG2]] +; CHECK: pop r29 +; CHECK: pop r28 + %p = alloca [8 x i16] + %arraydecay = getelementptr inbounds [8 x i16], [8 x i16]* %p, i16 0, i16 0 + call void @foo64_3(i64 723685415333071112, i64 723685415333071112, i64 723685415333071112, i8 88, i16* %arraydecay) + ret void +} + +define i32 @icall(i32 (i32)* %foo) { +; CHECK-LABEL: icall: +; CHECK: movw [[REG:r[0-9]+]], r24 +; CHECK: ldi r22, 147 +; CHECK: ldi r23, 248 +; CHECK: ldi r24, 214 +; CHECK: ldi r25, 198 +; CHECK: movw r30, [[REG]] +; CHECK: icall +; CHECK: subi r22, 251 +; CHECK: sbci r23, 255 +; CHECK: sbci r24, 255 +; CHECK: sbci r25, 255 + %1 = call i32 %foo(i32 3335977107) + %2 = add nsw i32 %1, 5 + ret i32 %2 +} + +; Calling external functions (like __divsf3) require extra processing for +; arguments and return values in the LowerCall function. +declare i32 @foofloat(float) + +define i32 @externcall(float %a, float %b) { +; CHECK-LABEL: externcall: +; CHECK: movw [[REG1:(r[0-9]+|[XYZ])]], r24 +; CHECK: movw [[REG2:(r[0-9]+|[XYZ])]], r22 +; CHECK: movw r22, r18 +; CHECK: movw r24, r20 +; CHECK: movw r18, [[REG2]] +; CHECK: movw r20, [[REG1]] +; CHECK: call __divsf3 +; CHECK: call foofloat +; CHECK: subi r22, 251 +; CHECK: sbci r23, 255 +; CHECK: sbci r24, 255 +; CHECK: sbci r25, 255 + %1 = fdiv float %b, %a + %2 = call i32 @foofloat(float %1) + %3 = add nsw i32 %2, 5 + ret i32 %3 +} diff --git a/llvm/test/CodeGen/AVR/cmp.ll b/llvm/test/CodeGen/AVR/cmp.ll new file mode 100644 index 000000000000..b3224087a689 --- /dev/null +++ b/llvm/test/CodeGen/AVR/cmp.ll @@ -0,0 +1,148 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +declare void @f1(i8) +declare void @f2(i8) +define void @cmp8(i8 %a, i8 %b) { +; CHECK-LABEL: cmp8: +; CHECK: cp +; CHECK-NOT: cpc + %cmp = icmp eq i8 %a, %b + br i1 %cmp, label %if.then, label %if.else +if.then: + tail call void @f1(i8 %a) + br label %if.end +if.else: + tail call void @f2(i8 %b) + br label %if.end +if.end: + ret void +} + +declare void @f3(i16) +declare void @f4(i16) +define void @cmp16(i16 %a, i16 %b) { +; CHECK-LABEL: cmp16: +; CHECK: cp +; CHECK-NEXT: cpc + %cmp = icmp eq i16 %a, %b + br i1 %cmp, label %if.then, label %if.else +if.then: + tail call void @f3(i16 %a) + br label %if.end +if.else: + tail call void @f4(i16 %b) + br label %if.end +if.end: + ret void +} + +declare void @f5(i32) +declare void @f6(i32) +define void @cmp32(i32 %a, i32 %b) { +; CHECK-LABEL: cmp32: +; CHECK: cp +; CHECK-NEXT: cpc +; CHECK-NEXT: cpc +; CHECK-NEXT: cpc + %cmp = icmp eq i32 %a, %b + br i1 %cmp, label %if.then, label %if.else +if.then: + tail call void @f5(i32 %a) + br label %if.end +if.else: + tail call void @f6(i32 %b) + br label %if.end +if.end: + ret void +} + +declare void @f7(i64) +declare void @f8(i64) +define void @cmp64(i64 %a, i64 %b) { +; CHECK-LABEL: cmp64: +; CHECK: cp +; CHECK-NEXT: cpc +; CHECK-NEXT: cpc +; CHECK-NEXT: cpc +; CHECK-NEXT: cpc +; CHECK-NEXT: cpc +; CHECK-NEXT: cpc +; CHECK-NEXT: cpc + %cmp = icmp eq i64 %a, %b + br i1 %cmp, label %if.then, label %if.else +if.then: + tail call void @f7(i64 %a) + br label %if.end +if.else: + tail call void @f8(i64 %b) + br label %if.end +if.end: + ret void +} + +declare void @f9() +declare void @f10() + +define void @tst8(i8 %a) { +; CHECK-LABEL: tst8: +; CHECK: tst r24 +; CHECK-NEXT: brmi + %cmp = icmp sgt i8 %a, -1 + br i1 %cmp, label %if.then, label %if.else +if.then: + tail call void @f9() + br label %if.end +if.else: + tail call void @f10() + br label %if.end +if.end: + ret void +} + +define void @tst16(i16 %a) { +; CHECK-LABEL: tst16: +; CHECK: tst r25 +; CHECK-NEXT: brmi + %cmp = icmp sgt i16 %a, -1 + br i1 %cmp, label %if.then, label %if.else +if.then: + tail call void @f9() + br label %if.end +if.else: + tail call void @f10() + br label %if.end +if.end: + ret void +} + +define void @tst32(i32 %a) { +; CHECK-LABEL: tst32: +; CHECK: tst r25 +; CHECK-NEXT: brmi + %cmp = icmp sgt i32 %a, -1 + br i1 %cmp, label %if.then, label %if.else +if.then: + tail call void @f9() + br label %if.end +if.else: + tail call void @f10() + br label %if.end +if.end: + ret void +} + +define void @tst64(i64 %a) { +; CHECK-LABEL: tst64: +; CHECK: tst r25 +; CHECK-NEXT: brmi + %cmp = icmp sgt i64 %a, -1 + br i1 %cmp, label %if.then, label %if.else +if.then: + tail call void @f9() + br label %if.end +if.else: + tail call void @f10() + br label %if.end +if.end: + ret void +} diff --git a/llvm/test/CodeGen/AVR/com.ll b/llvm/test/CodeGen/AVR/com.ll new file mode 100644 index 000000000000..e5bff6c0d113 --- /dev/null +++ b/llvm/test/CodeGen/AVR/com.ll @@ -0,0 +1,40 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +define i8 @com8(i8 %x) { +; CHECK-LABEL: com8: +; CHECK: com r24 + %neg = xor i8 %x, -1 + ret i8 %neg +} + +define i16 @com16(i16 %x) { +; CHECK-LABEL: com16: +; CHECK: com r24 +; CHECK: com r25 + %neg = xor i16 %x, -1 + ret i16 %neg +} + +define i32 @com32(i32 %x) { +; CHECK-LABEL: com32: +; CHECK: com r22 +; CHECK: com r23 +; CHECK: com r24 +; CHECK: com r25 + %neg = xor i32 %x, -1 + ret i32 %neg +} + +define i64 @com64(i64 %x) { +; CHECK-LABEL: com64: +; CHECK: com r18 +; CHECK: com r19 +; CHECK: com r20 +; CHECK: com r21 +; CHECK: com r22 +; CHECK: com r23 +; CHECK: com r24 +; CHECK: com r25 + %neg = xor i64 %x, -1 + ret i64 %neg +} diff --git a/llvm/test/CodeGen/AVR/directmem.ll b/llvm/test/CodeGen/AVR/directmem.ll new file mode 100644 index 000000000000..a97e712ed625 --- /dev/null +++ b/llvm/test/CodeGen/AVR/directmem.ll @@ -0,0 +1,345 @@ +; RUN: llc -mattr=sram,addsubiw < %s -march=avr | FileCheck %s + +@char = common global i8 0 +@char.array = common global [3 x i8] zeroinitializer +@char.static = internal global i8 0 + +@int = common global i16 0 +@int.array = common global [3 x i16] zeroinitializer +@int.static = internal global i16 0 + +@long = common global i32 0 +@long.array = common global [3 x i32] zeroinitializer +@long.static = internal global i32 0 + +@longlong = common global i64 0 +@longlong.array = common global [3 x i64] zeroinitializer +@longlong.static = internal global i64 0 + +define void @global8_store() { +; CHECK-LABEL: global8_store: +; CHECK: ldi [[REG:r[0-9]+]], 6 +; CHECK: sts char, [[REG]] + store i8 6, i8* @char + ret void +} + +define i8 @global8_load() { +; CHECK-LABEL: global8_load: +; CHECK: lds r24, char + %result = load i8, i8* @char + ret i8 %result +} + +define void @array8_store() { +; CHECK-LABEL: array8_store: +; CHECK: ldi [[REG1:r[0-9]+]], 1 +; CHECK: sts char.array, [[REG1]] +; CHECK: ldi [[REG2:r[0-9]+]], 2 +; CHECK: sts char.array+1, [[REG2]] +; CHECK: ldi [[REG:r[0-9]+]], 3 +; CHECK: sts char.array+2, [[REG]] + store i8 1, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @char.array, i32 0, i64 0) + store i8 2, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @char.array, i32 0, i64 1) + store i8 3, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @char.array, i32 0, i64 2) + ret void +} + +define i8 @array8_load() { +; CHECK-LABEL: array8_load: +; CHECK: lds r24, char.array+2 + %result = load i8, i8* getelementptr inbounds ([3 x i8], [3 x i8]* @char.array, i32 0, i64 2) + ret i8 %result +} + +define i8 @static8_inc() { +; CHECK-LABEL: static8_inc: +; CHECK: lds r24, char.static +; CHECK: inc r24 +; CHECK: sts char.static, r24 + %1 = load i8, i8* @char.static + %inc = add nsw i8 %1, 1 + store i8 %inc, i8* @char.static + ret i8 %inc +} + +define void @global16_store() { +; CHECK-LABEL: global16_store: +; CHECK: ldi [[REG1:r[0-9]+]], 187 +; CHECK: ldi [[REG2:r[0-9]+]], 170 +; CHECK: sts int+1, [[REG2]] +; CHECK: sts int, [[REG1]] + store i16 43707, i16* @int + ret void +} + +define i16 @global16_load() { +; CHECK-LABEL: global16_load: +; CHECK: lds r24, int +; CHECK: lds r25, int+1 + %result = load i16, i16* @int + ret i16 %result +} + +define void @array16_store() { +; CHECK-LABEL: array16_store: +; CHECK: ldi [[REG1:r[0-9]+]], 187 +; CHECK: ldi [[REG2:r[0-9]+]], 170 +; CHECK: sts int.array+1, [[REG2]] +; CHECK: sts int.array, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 204 +; CHECK: ldi [[REG2:r[0-9]+]], 170 +; CHECK: sts int.array+3, [[REG2]] +; CHECK: sts int.array+2, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 221 +; CHECK: ldi [[REG2:r[0-9]+]], 170 +; CHECK: sts int.array+5, [[REG2]] +; CHECK: sts int.array+4, [[REG1]] + store i16 43707, i16* getelementptr inbounds ([3 x i16], [3 x i16]* @int.array, i32 0, i64 0) + store i16 43724, i16* getelementptr inbounds ([3 x i16], [3 x i16]* @int.array, i32 0, i64 1) + store i16 43741, i16* getelementptr inbounds ([3 x i16], [3 x i16]* @int.array, i32 0, i64 2) + ret void +} + +define i16 @array16_load() { +; CHECK-LABEL: array16_load: +; CHECK: lds r24, int.array+4 +; CHECK: lds r25, int.array+5 + %result = load i16, i16* getelementptr inbounds ([3 x i16], [3 x i16]* @int.array, i32 0, i64 2) + ret i16 %result +} + +define i16 @static16_inc() { +; CHECK-LABEL: static16_inc: +; CHECK: lds r24, int.static +; CHECK: lds r25, int.static+1 +; CHECK: adiw r24, 1 +; CHECK: sts int.static+1, r25 +; CHECK: sts int.static, r24 + %1 = load i16, i16* @int.static + %inc = add nsw i16 %1, 1 + store i16 %inc, i16* @int.static + ret i16 %inc +} + +define void @global32_store() { +; CHECK-LABEL: global32_store: +; CHECK: ldi [[REG1:r[0-9]+]], 187 +; CHECK: ldi [[REG2:r[0-9]+]], 170 +; CHECK: sts long+3, [[REG2]] +; CHECK: sts long+2, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 221 +; CHECK: ldi [[REG2:r[0-9]+]], 204 +; CHECK: sts long+1, [[REG2]] +; CHECK: sts long, [[REG1]] + store i32 2864434397, i32* @long + ret void +} + +define i32 @global32_load() { +; CHECK-LABEL: global32_load: +; CHECK: lds r22, long +; CHECK: lds r23, long+1 +; CHECK: lds r24, long+2 +; CHECK: lds r25, long+3 + %result = load i32, i32* @long + ret i32 %result +} + +define void @array32_store() { +; CHECK-LABEL: array32_store: +; CHECK: ldi [[REG1:r[0-9]+]], 27 +; CHECK: ldi [[REG2:r[0-9]+]], 172 +; CHECK: sts long.array+3, [[REG2]] +; CHECK: sts long.array+2, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 68 +; CHECK: ldi [[REG2:r[0-9]+]], 13 +; CHECK: sts long.array+1, [[REG2]] +; CHECK: sts long.array, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 102 +; CHECK: ldi [[REG2:r[0-9]+]], 85 +; CHECK: sts long.array+7, [[REG2]] +; CHECK: sts long.array+6, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 136 +; CHECK: ldi [[REG2:r[0-9]+]], 119 +; CHECK: sts long.array+5, [[REG2]] +; CHECK: sts long.array+4, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 170 +; CHECK: ldi [[REG2:r[0-9]+]], 153 +; CHECK: sts long.array+11, [[REG2]] +; CHECK: sts long.array+10, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 204 +; CHECK: ldi [[REG2:r[0-9]+]], 187 +; CHECK: sts long.array+9, [[REG2]] +; CHECK: sts long.array+8, [[REG1]] + store i32 2887454020, i32* getelementptr inbounds ([3 x i32], [3 x i32]* @long.array, i32 0, i64 0) + store i32 1432778632, i32* getelementptr inbounds ([3 x i32], [3 x i32]* @long.array, i32 0, i64 1) + store i32 2578103244, i32* getelementptr inbounds ([3 x i32], [3 x i32]* @long.array, i32 0, i64 2) + ret void +} + +define i32 @array32_load() { +; CHECK-LABEL: array32_load: +; CHECK: lds r22, long.array+8 +; CHECK: lds r23, long.array+9 +; CHECK: lds r24, long.array+10 +; CHECK: lds r25, long.array+11 + %result = load i32, i32* getelementptr inbounds ([3 x i32], [3 x i32]* @long.array, i32 0, i64 2) + ret i32 %result +} + +define i32 @static32_inc() { +; CHECK-LABEL: static32_inc: +; CHECK: lds r22, long.static +; CHECK: lds r23, long.static+1 +; CHECK: lds r24, long.static+2 +; CHECK: lds r25, long.static+3 +; CHECK: subi r22, 255 +; CHECK: sbci r23, 255 +; CHECK: sbci r24, 255 +; CHECK: sbci r25, 255 +; CHECK: sts long.static+3, r25 +; CHECK: sts long.static+2, r24 +; CHECK: sts long.static+1, r23 +; CHECK: sts long.static, r22 + %1 = load i32, i32* @long.static + %inc = add nsw i32 %1, 1 + store i32 %inc, i32* @long.static + ret i32 %inc +} + +define void @global64_store() { +; CHECK-LABEL: global64_store: +; CHECK: ldi [[REG1:r[0-9]+]], 34 +; CHECK: ldi [[REG2:r[0-9]+]], 17 +; CHECK: sts longlong+7, [[REG2]] +; CHECK: sts longlong+6, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 68 +; CHECK: ldi [[REG2:r[0-9]+]], 51 +; CHECK: sts longlong+5, [[REG2]] +; CHECK: sts longlong+4, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 102 +; CHECK: ldi [[REG2:r[0-9]+]], 85 +; CHECK: sts longlong+3, [[REG2]] +; CHECK: sts longlong+2, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 136 +; CHECK: ldi [[REG2:r[0-9]+]], 119 +; CHECK: sts longlong+1, [[REG2]] +; CHECK: sts longlong, [[REG1]] + store i64 1234605616436508552, i64* @longlong + ret void +} + +define i64 @global64_load() { +; CHECK-LABEL: global64_load: +; CHECK: lds r18, longlong +; CHECK: lds r19, longlong+1 +; CHECK: lds r20, longlong+2 +; CHECK: lds r21, longlong+3 +; CHECK: lds r22, longlong+4 +; CHECK: lds r23, longlong+5 +; CHECK: lds r24, longlong+6 +; CHECK: lds r25, longlong+7 + %result = load i64, i64* @longlong + ret i64 %result +} + +define void @array64_store() { +; CHECK-LABEL: array64_store: +; CHECK: ldi [[REG1:r[0-9]+]], 34 +; CHECK: ldi [[REG2:r[0-9]+]], 17 +; CHECK: sts longlong.array+7, [[REG2]] +; CHECK: sts longlong.array+6, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 68 +; CHECK: ldi [[REG2:r[0-9]+]], 51 +; CHECK: sts longlong.array+5, [[REG2]] +; CHECK: sts longlong.array+4, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 102 +; CHECK: ldi [[REG2:r[0-9]+]], 85 +; CHECK: sts longlong.array+3, [[REG2]] +; CHECK: sts longlong.array+2, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 136 +; CHECK: ldi [[REG2:r[0-9]+]], 119 +; CHECK: sts longlong.array+1, [[REG2]] +; CHECK: sts longlong.array, [[REG1]] + store i64 1234605616436508552, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @longlong.array, i64 0, i64 0) + store i64 81985529216486895, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @longlong.array, i64 0, i64 1) + store i64 1836475854449306472, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @longlong.array, i64 0, i64 2) + ret void +} + +define i64 @array64_load() { +; CHECK-LABEL: array64_load: +; CHECK: lds r18, longlong.array+16 +; CHECK: lds r19, longlong.array+17 +; CHECK: lds r20, longlong.array+18 +; CHECK: lds r21, longlong.array+19 +; CHECK: lds r22, longlong.array+20 +; CHECK: lds r23, longlong.array+21 +; CHECK: lds r24, longlong.array+22 +; CHECK: lds r25, longlong.array+23 + %result = load i64, i64* getelementptr inbounds ([3 x i64], [3 x i64]* @longlong.array, i64 0, i64 2) + ret i64 %result +} + +define i64 @static64_inc() { +; CHECK-LABEL: static64_inc: +; CHECK: lds r18, longlong.static +; CHECK: lds r19, longlong.static+1 +; CHECK: lds r20, longlong.static+2 +; CHECK: lds r21, longlong.static+3 +; CHECK: lds r22, longlong.static+4 +; CHECK: lds r23, longlong.static+5 +; CHECK: lds r24, longlong.static+6 +; CHECK: lds r25, longlong.static+7 +; CHECK: subi r18, 255 +; CHECK: sbci r19, 255 +; CHECK: sbci r20, 255 +; CHECK: sbci r21, 255 +; CHECK: sbci r22, 255 +; CHECK: sbci r23, 255 +; CHECK: sbci r24, 255 +; CHECK: sbci r25, 255 +; CHECK: sts longlong.static+7, r25 +; CHECK: sts longlong.static+6, r24 +; CHECK: sts longlong.static+5, r23 +; CHECK: sts longlong.static+4, r22 +; CHECK: sts longlong.static+3, r21 +; CHECK: sts longlong.static+2, r20 +; CHECK: sts longlong.static+1, r19 +; CHECK: sts longlong.static, r18 + %1 = load i64, i64* @longlong.static + %inc = add nsw i64 %1, 1 + store i64 %inc, i64* @longlong.static + ret i64 %inc +} + +define i8 @constantaddr_read8() { +; CHECK-LABEL: constantaddr_read8: +; CHECK: lds r24, 1234 + %1 = load i8, i8* inttoptr (i16 1234 to i8*) + ret i8 %1 +} + +define i16 @constantaddr_read16() { +; CHECK-LABEL: constantaddr_read16: +; CHECK: lds r24, 1234 +; CHECK: lds r25, 1235 + %1 = load i16, i16* inttoptr (i16 1234 to i16*) + ret i16 %1 +} + +define void @constantaddr_write8() { +; CHECK-LABEL: constantaddr_write8: +; CHECK: sts 1234 + store i8 22, i8* inttoptr (i16 1234 to i8*) + ret void +} + +define void @constantaddr_write16() { +; CHECK-LABEL: constantaddr_write16: +; CHECK: sts 1235 +; CHECK: sts 1234 + store i16 2222, i16* inttoptr (i16 1234 to i16*) + ret void +} diff --git a/llvm/test/CodeGen/AVR/dynalloca.ll b/llvm/test/CodeGen/AVR/dynalloca.ll new file mode 100644 index 000000000000..13f503015f9f --- /dev/null +++ b/llvm/test/CodeGen/AVR/dynalloca.ll @@ -0,0 +1,78 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +declare void @foo(i16*, i16*, i8*) + +define void @test1(i16 %x) { +; CHECK-LABEL: test1: +; CHECK: out 61, r28 +; SP copy +; CHECK-NEXT: in [[SPCOPY1:r[0-9]+]], 61 +; CHECK-NEXT: in [[SPCOPY2:r[0-9]+]], 62 +; allocate first dynalloca +; CHECK: in {{.*}}, 61 +; CHECK: in {{.*}}, 62 +; CHECK: sub +; CHECK: sbc +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: out 62, {{.*}} +; CHECK-NEXT: out 63, r0 +; CHECK-NEXT: out 61, {{.*}} +; Test writes +; CHECK: std Z+12, {{.*}} +; CHECK: std Z+13, {{.*}} +; CHECK: std Z+7, {{.*}} +; CHECK-NOT: std +; Test SP restore +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: out 62, [[SPCOPY2]] +; CHECK-NEXT: out 63, r0 +; CHECK-NEXT: out 61, [[SPCOPY1]] + %a = alloca [8 x i16] + %vla = alloca i16, i16 %x + %add = shl nsw i16 %x, 1 + %vla1 = alloca i8, i16 %add + %arrayidx = getelementptr inbounds [8 x i16], [8 x i16]* %a, i16 0, i16 2 + store i16 3, i16* %arrayidx + %arrayidx2 = getelementptr inbounds i16, i16* %vla, i16 6 + store i16 4, i16* %arrayidx2 + %arrayidx3 = getelementptr inbounds i8, i8* %vla1, i16 7 + store i8 44, i8* %arrayidx3 + %arraydecay = getelementptr inbounds [8 x i16], [8 x i16]* %a, i16 0, i16 0 + call void @foo(i16* %arraydecay, i16* %vla, i8* %vla1) + ret void +} + +declare void @foo2(i16*, i64, i64, i64) + +; Test that arguments are passed through pushes into the call instead of +; allocating the call frame space in the prologue. Also test that SP is restored +; after the call frame is restored and not before. +define void @dynalloca2(i16 %x) { +; CHECK-LABEL: dynalloca2: +; CHECK: in [[SPCOPY1:r[0-9]+]], 61 +; CHECK: in [[SPCOPY2:r[0-9]+]], 62 +; CHECK: push +; CHECK-NOT: st +; CHECK-NOT: std +; CHECK: call +; Call frame restore +; CHECK-NEXT: in r30, 61 +; CHECK-NEXT: in r31, 62 +; CHECK-NEXT: adiw r30, 8 +; CHECK-NEXT: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: out 62, r31 +; CHECK-NEXT: out 63, r0 +; CHECK-NEXT: out 61, r30 +; SP restore +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: out 62, r29 +; CHECK-NEXT: out 63, r0 +; CHECK-NEXT: out 61, r28 + %vla = alloca i16, i16 %x + call void @foo2(i16* %vla, i64 0, i64 0, i64 0) + ret void +} diff --git a/llvm/test/CodeGen/AVR/eor.ll b/llvm/test/CodeGen/AVR/eor.ll new file mode 100644 index 000000000000..a32bc24a8084 --- /dev/null +++ b/llvm/test/CodeGen/AVR/eor.ll @@ -0,0 +1,92 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +; Tests for the exclusive OR operation. + +define i8 @eor8_reg_reg(i8 %a, i8 %b) { +; CHECK-LABEL: eor8_reg_reg: +; CHECK: eor r24, r22 + %result = xor i8 %a, %b + ret i8 %result +} + +define i8 @eor8_reg_imm(i8 %a) { +; CHECK-LABEL: eor8_reg_imm: +; CHECK: ldi r25, 5 +; CHECK: eor r24, r25 + %result = xor i8 %a, 5 + ret i8 %result +} + +define i16 @eor16_reg_reg(i16 %a, i16 %b) { +; CHECK-LABEL: eor16_reg_reg: +; CHECK: eor r24, r22 +; CHECK: eor r25, r23 + %result = xor i16 %a, %b + ret i16 %result +} + +define i16 @eor16_reg_imm(i16 %a) { +; CHECK-LABEL: eor16_reg_imm: +; CHECK: ldi r18, 210 +; CHECK: ldi r19, 4 +; CHECK: eor r24, r18 +; CHECK: eor r25, r19 + %result = xor i16 %a, 1234 + ret i16 %result +} + +define i32 @eor32_reg_reg(i32 %a, i32 %b) { +; CHECK-LABEL: eor32_reg_reg: +; CHECK: eor r22, r18 +; CHECK: eor r23, r19 +; CHECK: eor r24, r20 +; CHECK: eor r25, r21 + %result = xor i32 %a, %b + ret i32 %result +} + +define i32 @eor32_reg_imm(i32 %a) { +; CHECK-LABEL: eor32_reg_imm: +; CHECK: ldi r18, 210 +; CHECK: ldi r19, 4 +; CHECK: eor r22, r18 +; CHECK: eor r23, r19 + %result = xor i32 %a, 1234 + ret i32 %result +} + +define i64 @eor64_reg_reg(i64 %a, i64 %b) { +; CHECK-LABEL: eor64_reg_reg: +; CHECK: eor r18, r10 +; CHECK: eor r19, r11 +; CHECK: eor r20, r12 +; CHECK: eor r21, r13 +; CHECK: eor r22, r14 +; CHECK: eor r23, r15 +; CHECK: eor r24, r16 +; CHECK: eor r25, r17 + %result = xor i64 %a, %b + ret i64 %result +} + +define i64 @eor64_reg_imm(i64 %a) { +; CHECK-LABEL: eor64_reg_imm: +; CHECK: ldi r30, 253 +; CHECK: ldi r31, 255 +; CHECK: eor r18, r30 +; CHECK: eor r19, r31 +; CHECK: ldi r30, 155 +; CHECK: ldi r31, 88 +; CHECK: eor r20, r30 +; CHECK: eor r21, r31 +; CHECK: ldi r30, 76 +; CHECK: ldi r31, 73 +; CHECK: eor r22, r30 +; CHECK: eor r23, r31 +; CHECK: ldi r30, 31 +; CHECK: ldi r31, 242 +; CHECK: eor r24, r30 +; CHECK: eor r25, r31 + %result = xor i64 %a, 17446744073709551613 + ret i64 %result +} diff --git a/llvm/test/CodeGen/AVR/error-srcreg-destreg-same.ll b/llvm/test/CodeGen/AVR/error-srcreg-destreg-same.ll new file mode 100644 index 000000000000..8227635febe1 --- /dev/null +++ b/llvm/test/CodeGen/AVR/error-srcreg-destreg-same.ll @@ -0,0 +1,56 @@ +; RUN: llc < %s -march=avr | FileCheck %s +; XFAIL: * + +; This occurs when compiling Rust libcore. +; +; Assertion failed: +; (DstReg != SrcReg && "SrcReg and DstReg cannot be the same") +; lib/Target/AVR/AVRExpandPseudoInsts.cpp, line 817 +; +; https://github.com/avr-llvm/llvm/issues/229 + +; CHECK-LABEL: rust_eh_personality +declare void @rust_eh_personality() + +; CHECK-LABEL: __udivmoddi4 +define void @__udivmoddi4(i64 %arg, i64 %arg1) personality i32 (...)* bitcast (void ()* @rust_eh_personality to i32 (...)*) { +entry-block: + %tmp = lshr i64 %arg, 32 + %tmp2 = trunc i64 %tmp to i32 + %tmp3 = trunc i64 %arg to i32 + %tmp4 = add i64 %arg1, -1 + br label %bb135 + +bb133.loopexit: + ret void + +bb135: + %carry.0120 = phi i64 [ 0, %entry-block ], [ %phitmp, %bb135 ] + %q.sroa.12.1119 = phi i32 [ %tmp3, %entry-block ], [ %q.sroa.12.0.extract.trunc, %bb135 ] + %q.sroa.0.1118 = phi i32 [ 0, %entry-block ], [ %q.sroa.0.0.extract.trunc, %bb135 ] + %r.sroa.0.1116 = phi i32 [ %tmp2, %entry-block ], [ undef, %bb135 ] + %r.sroa.0.0.insert.ext62 = zext i32 %r.sroa.0.1116 to i64 + %r.sroa.0.0.insert.insert64 = or i64 0, %r.sroa.0.0.insert.ext62 + %tmp5 = shl nuw nsw i64 %r.sroa.0.0.insert.ext62, 1 + %q.sroa.12.0.insert.ext101 = zext i32 %q.sroa.12.1119 to i64 + %q.sroa.12.0.insert.shift102 = shl nuw i64 %q.sroa.12.0.insert.ext101, 32 + %q.sroa.0.0.insert.ext87 = zext i32 %q.sroa.0.1118 to i64 + %q.sroa.0.0.insert.insert89 = or i64 %q.sroa.12.0.insert.shift102, %q.sroa.0.0.insert.ext87 + %tmp6 = lshr i64 %q.sroa.12.0.insert.ext101, 31 + %tmp7 = lshr i64 %r.sroa.0.0.insert.insert64, 31 + %tmp8 = shl nuw nsw i64 %q.sroa.0.0.insert.ext87, 1 + %tmp9 = or i64 %tmp8, %carry.0120 + %q.sroa.0.0.extract.trunc = trunc i64 %tmp9 to i32 + %tmp10 = lshr i64 %q.sroa.0.0.insert.insert89, 31 + %q.sroa.12.0.extract.trunc = trunc i64 %tmp10 to i32 + %r.sroa.13.0.insert.shift72 = shl i64 %tmp7, 32 + %.masked114 = and i64 %tmp5, 4294967294 + %r.sroa.0.0.insert.ext57 = or i64 %tmp6, %.masked114 + %r.sroa.0.0.insert.insert59 = or i64 %r.sroa.0.0.insert.ext57, %r.sroa.13.0.insert.shift72 + %tmp11 = sub i64 %tmp4, %r.sroa.0.0.insert.insert59 + %tmp12 = ashr i64 %tmp11, 63 + %phitmp = and i64 %tmp12, 1 + %tmp13 = icmp ult i32 undef, 32 + br i1 %tmp13, label %bb135, label %bb133.loopexit +} + diff --git a/llvm/test/CodeGen/AVR/expand-integer-failure.ll b/llvm/test/CodeGen/AVR/expand-integer-failure.ll new file mode 100644 index 000000000000..cc022c5d6e86 --- /dev/null +++ b/llvm/test/CodeGen/AVR/expand-integer-failure.ll @@ -0,0 +1,23 @@ +; RUN: llc < %s -march=avr | FileCheck %s +; XFAIL: * + +; Causes an assertion error +; Assertion failed: (Lo.getValueType() == TLI.getTypeToTransformTo(*DAG.getContext(), Op.getValueType()) && +; Hi.getValueType() == Lo.getValueType() && +; "Invalid type for expanded integer"), +; function SetExpandedInteger +; file lib/CodeGen/SelectionDAG/LegalizeTypes.cpp + +; CHECK-LABEL: foo +define void @foo(i16 %a) { +ifcont: + %cmp_result = icmp eq i16 %a, 255 + %bool_result = uitofp i1 %cmp_result to double + %result = fcmp one double 0.000000e+00, %bool_result + br i1 %result, label %then, label %else +then: + ret void +else: + ret void +} + diff --git a/llvm/test/CodeGen/AVR/frame.ll b/llvm/test/CodeGen/AVR/frame.ll new file mode 100644 index 000000000000..cd997a454dbc --- /dev/null +++ b/llvm/test/CodeGen/AVR/frame.ll @@ -0,0 +1,65 @@ +; RUN: llc -mattr=mul < %s -march=avr | FileCheck %s + +declare float @dsin(float) +declare float @dcos(float) +declare float @dasin(float) + +; Test prologue and epilogue insertion +define float @f3(float %days) { +entry: +; CHECK-LABEL: f3: +; prologue code: +; CHECK: push r28 +; CHECK: push r29 +; CHECK: in r28, 61 +; CHECK-NEXT: in r29, 62 +; CHECK-NEXT: sbiw r28, [[SIZE:[0-9]+]] +; CHECK-NEXT: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: out 62, r29 +; CHECK-NEXT: out 63, r0 +; CHECK-NEXT: out 61, r28 +; epilogue code: +; CHECK: adiw r28, [[SIZE]] +; CHECK-NEXT: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: out 62, r29 +; CHECK-NEXT: out 63, r0 +; CHECK-NEXT: out 61, r28 +; CHECK: pop r29 +; CHECK: pop r28 + %mul = fmul float %days, 0x3FEF8A6C60000000 + %add = fadd float %mul, 0x40718776A0000000 + %mul1 = fmul float %days, 0x3FEF8A09A0000000 + %add2 = fadd float %mul1, 0x4076587740000000 + %mul3 = fmul float %days, 0x3E81B35CC0000000 + %sub = fsub float 0x3FFEA235C0000000, %mul3 + %call = call float @dsin(float %add2) + %mul4 = fmul float %sub, %call + %mul5 = fmul float %days, 0x3E27C04CA0000000 + %sub6 = fsub float 0x3F94790B80000000, %mul5 + %mul7 = fmul float %add2, 2.000000e+00 + %call8 = call float @dsin(float %mul7) + %mul9 = fmul float %sub6, %call8 + %add10 = fadd float %mul4, %mul9 + %add11 = fadd float %add, %add10 + %mul12 = fmul float %days, 0x3E13C5B640000000 + %sub13 = fsub float 0x3F911C1180000000, %mul12 + %mul14 = fmul float %add, 2.000000e+00 + %call15 = call float @dsin(float %mul14) + %mul16 = fmul float %call15, 0x3FF1F736C0000000 + %mul17 = fmul float %sub13, 2.000000e+00 + %mul19 = fmul float %mul17, %call + %sub20 = fsub float %mul16, %mul19 + %mul21 = fmul float %sub13, 4.000000e+00 + %mul22 = fmul float %mul21, 0x3FF1F736C0000000 + %mul24 = fmul float %mul22, %call + %call26 = call float @dcos(float %mul14) + %mul27 = fmul float %mul24, %call26 + %add28 = fadd float %sub20, %mul27 + %call29 = call float @dsin(float %add11) + %mul30 = fmul float %call29, 0x3FF0AB6960000000 + %call31 = call float @dasin(float %mul30) + %add32 = fadd float %call31, %add28 + ret float %add32 +} diff --git a/llvm/test/CodeGen/AVR/high-pressure-on-ptrregs.ll b/llvm/test/CodeGen/AVR/high-pressure-on-ptrregs.ll index 9330ce1eb2ac..dabc58b74731 100644 --- a/llvm/test/CodeGen/AVR/high-pressure-on-ptrregs.ll +++ b/llvm/test/CodeGen/AVR/high-pressure-on-ptrregs.ll @@ -1,4 +1,5 @@ ; RUN: llc < %s -march=avr | FileCheck %s +; XFAIL: * ; This tests how LLVM handles IR which puts very high ; presure on the PTRREGS class for the register allocator. diff --git a/llvm/test/CodeGen/AVR/inline-asm.ll b/llvm/test/CodeGen/AVR/inline-asm.ll new file mode 100644 index 000000000000..d322aabe32ea --- /dev/null +++ b/llvm/test/CodeGen/AVR/inline-asm.ll @@ -0,0 +1,337 @@ +; RUN: llc < %s -march=avr | FileCheck %s +; XFAIL: * + +;CHECK-LABEL: no_operands: +define void @no_operands() { + ;CHECK: add r24, r22 + call void asm sideeffect "add r24, r22", ""() nounwind + ret void +} + +;CHECK-LABEL: input_operand: +define void @input_operand(i8 %a) { + ;CHECK: add r24, r24 + call void asm sideeffect "add $0, $0", "r"(i8 %a) nounwind + ret void +} + +;CHECK-LABEL: simple_upper_regs: +define void @simple_upper_regs(i8 %p0, i8 %p1, i8 %p2, i8 %p3, + i8 %p4, i8 %p5, i8 %p6, i8 %p7) { + ;CHECK: some_instr r17, r22, r20, r18, r16, r19, r21, r23 + call void asm sideeffect "some_instr $0, $1, $2, $3, $4, $5, $6, $7", + "a,a,a,a,a,a,a,a" (i8 %p0, i8 %p1, i8 %p2, i8 %p3, + i8 %p4, i8 %p5, i8 %p6, i8 %p7) nounwind + ret void +} + +;CHECK-LABEL: upper_regs: +define void @upper_regs(i8 %p0) { + ;CHECK: some_instr r24 + call void asm sideeffect "some_instr $0", "d" (i8 %p0) nounwind + ret void +} + +;CHECK-LABEL: lower_regs: +define void @lower_regs(i8 %p0) { + ;CHECK: some_instr r15 + call void asm sideeffect "some_instr $0", "l" (i8 %p0) nounwind + ret void +} + +;CHECK-LABEL: special_upper_regs: +define void @special_upper_regs(i8 %p0, i8 %p1, i8 %p2, i8 %p3) { + ;CHECK: some_instr r24,r28,r26,r30 + call void asm sideeffect "some_instr $0,$1,$2,$3", "w,w,w,w" (i8 %p0, i8 %p1, i8 %p2, i8 %p3) nounwind + ret void +} + +;CHECK-LABEL: xyz_reg: +define void @xyz_reg(i16 %var) { + ;CHECK: some_instr r26, r28, r30 + call void asm sideeffect "some_instr $0, $1, $2", "x,y,z" (i16 %var, i16 %var, i16 %var) nounwind + ret void +} + +;TODO +; How to use SP reg properly in inline asm?? +; define void @sp_reg(i16 %var) + +;CHECK-LABEL: ptr_reg: +define void @ptr_reg(i16 %var0, i16 %var1, i16 %var2) { + ;CHECK: some_instr r28, r26, r30 + call void asm sideeffect "some_instr $0, $1, $2", "e,e,e" (i16 %var0, i16 %var1, i16 %var2) nounwind + ret void +} + +;CHECK-LABEL: base_ptr_reg: +define void @base_ptr_reg(i16 %var0, i16 %var1) { + ;CHECK: some_instr r28, r30 + call void asm sideeffect "some_instr $0, $1", "b,b" (i16 %var0, i16 %var1) nounwind + ret void +} + +;CHECK-LABEL: input_output_operand: +define i8 @input_output_operand(i8 %a, i8 %b) { + ;CHECK: add r24, r24 + %1 = call i8 asm "add $0, $1", "=r,r"(i8 %a) nounwind + ret i8 %1 +} + +;CHECK-LABEL: temp_reg: +define void @temp_reg(i8 %a) { + ;CHECK: some_instr r0 + call void asm sideeffect "some_instr $0", "t" (i8 %a) nounwind + ret void +} + +;CHECK-LABEL: int_0_63: +define void @int_0_63() { + ;CHECK: some_instr 5 + call void asm sideeffect "some_instr $0", "I" (i8 5) nounwind + ret void +} + +;CHECK-LABEL: int_minus63_0: +define void @int_minus63_0() { + ;CHECK: some_instr -5 + call void asm sideeffect "some_instr $0", "J" (i8 -5) nounwind + ret void +} + +;CHECK-LABEL: int_2_2: +define void @int_2_2() { + ;CHECK: some_instr 2 + call void asm sideeffect "some_instr $0", "K" (i8 2) nounwind + ret void +} + +;CHECK-LABEL: int_0_0: +define void @int_0_0() { + ;CHECK: some_instr 0 + call void asm sideeffect "some_instr $0", "L" (i8 0) nounwind + ret void +} + +;CHECK-LABEL: int_0_255: +define void @int_0_255() { + ;CHECK: some_instr 254 + call void asm sideeffect "some_instr $0", "M" (i8 254) nounwind + ret void +} + +;CHECK-LABEL: int_minus1_minus1: +define void @int_minus1_minus1() { + ;CHECK: some_instr -1 + call void asm sideeffect "some_instr $0", "N" (i8 -1) nounwind + ret void +} + +;CHECK-LABEL: int_8_or_16_or_24: +define void @int_8_or_16_or_24() { + ;CHECK: some_instr 8, 16, 24 + call void asm sideeffect "some_instr $0, $1, $2", "O,O,O" (i8 8, i8 16, i8 24) nounwind + ret void +} + +;CHECK-LABEL: int_1_1: +define void @int_1_1() { + ;CHECK: some_instr 1 + call void asm sideeffect "some_instr $0", "P" (i8 1) nounwind + ret void +} + +;CHECK-LABEL: int_minus6_5: +define void @int_minus6_5() { + ;CHECK: some_instr -6 + call void asm sideeffect "some_instr $0", "R" (i8 -6) nounwind + ret void +} + +;CHECK-LABEL: float_0_0: +define void @float_0_0() { + ;CHECK: some_instr 0 + call void asm sideeffect "some_instr $0", "G" (float 0.0) nounwind + ret void +} + + +; Memory constraint + +@a = internal global i16 0, align 4 +@b = internal global i16 0, align 4 + +; CHECK-LABEL: mem_global: +define void @mem_global() { + ;CHECK: some_instr Y, Z + call void asm "some_instr $0, $1", "=*Q,=*Q"(i16* @a, i16* @b) + ret void +} + +; CHECK-LABEL: mem_params: +define void @mem_params(i16* %a, i16* %b) { + ;CHECK: some_instr Y, Z + call void asm "some_instr $0, $1", "=*Q,=*Q"(i16* %a, i16* %b) + ret void +} + +; CHECK-LABEL: mem_local: +define void @mem_local() { + %a = alloca i16 + %b = alloca i16 + ;CHECK: some_instr Y+3, Y+1 + call void asm "some_instr $0, $1", "=*Q,=*Q"(i16* %a, i16* %b) + ret void +} + +; CHECK-LABEL: mem_mixed: +define void @mem_mixed() { + %a = alloca i16 + %b = alloca i16 + ;CHECK: some_instr Z, Y+3, Y+1 + call void asm "some_instr $0, $1, $2", "=*Q,=*Q,=*Q"(i16* @a, i16* %a, i16* %b) + ret void +} + +; CHECK-LABEL: mem_gep: +define i8 @mem_gep(i8* %p) { +entry: +; CHECK: movw r30, r24 + %arrayidx = getelementptr inbounds i8, i8* %p, i16 1 +; CHECK: ld r24, Z+1 + %0 = tail call i8 asm sideeffect "ld $0, $1\0A\09", "=r,*Q"(i8* %arrayidx) + ret i8 %0 +} + +; Multibyte references + +; CHECK-LABEL: multibyte_i16 +define void @multibyte_i16(i16 %a) { +entry: +; CHECK: instr r24 r25 + call void asm sideeffect "instr ${0:A} ${0:B}", "r"(i16 %a) +; CHECK: instr r25 r24 + call void asm sideeffect "instr ${0:B} ${0:A}", "r"(i16 %a) + ret void +} + +; CHECK-LABEL: multibyte_i32 +define void @multibyte_i32(i32 %a) { +entry: +; CHECK: instr r22 r23 r24 r25 + call void asm sideeffect "instr ${0:A} ${0:B} ${0:C} ${0:D}", "r"(i32 %a) +; CHECK: instr r25 r24 r23 r22 + call void asm sideeffect "instr ${0:D} ${0:C} ${0:B} ${0:A}", "r"(i32 %a) + ret void +} + +; CHECK-LABEL: multibyte_alternative_name +define void @multibyte_alternative_name(i16* %p) { +entry: +; CHECK: instr Z + call void asm sideeffect "instr ${0:a}", "e" (i16* %p) + ret void +} + +; CHECK-LABEL: multibyte_a_i32 +define void @multibyte_a_i32() { +entry: + %a = alloca i32 + %0 = load i32, i32* %a +; CHECK: instr r20 r21 r22 r23 + call void asm sideeffect "instr ${0:A} ${0:B} ${0:C} ${0:D}", "a"(i32 %0) + ret void +} + +@c = internal global i32 0 + +; CHECK-LABEL: multibyte_b_i32 +define void @multibyte_b_i32() { +entry: + %0 = load i32, i32* @c +; CHECK: instr r28 r29 r30 r31 + call void asm sideeffect "instr ${0:A} ${0:B} ${0:C} ${0:D}", "b"(i32 %0) + ret void +} + +; CHECK-LABEL: multibyte_d_i32 +define void @multibyte_d_i32() { +entry: + %a = alloca i32 + %0 = load i32, i32* %a +; CHECK: instr r18 r19 r24 r25 + call void asm sideeffect "instr ${0:A} ${0:B} ${0:C} ${0:D}", "d"(i32 %0) + ret void +} + +; CHECK-LABEL: multibyte_e_i32 +define void @multibyte_e_i32() { +entry: + %a = alloca i32 + %0 = load i32, i32* %a +; CHECK: instr r26 r27 r30 r31 + call void asm sideeffect "instr ${0:A} ${0:B} ${0:C} ${0:D}", "e"(i32 %0) + ret void +} + +; CHECK-LABEL: multibyte_l_i32 +define void @multibyte_l_i32() { +entry: + %a = alloca i32 + %0 = load i32, i32* %a +; CHECK: instr r12 r13 r14 r15 + call void asm sideeffect "instr ${0:A} ${0:B} ${0:C} ${0:D}", "l"(i32 %0) + ret void +} + +; CHECK-LABEL: multibyte_a_i16 +define void @multibyte_a_i16() { +entry: + %a = alloca i16 + %0 = load i16, i16* %a +; CHECK: instr r22 r23 + call void asm sideeffect "instr ${0:A} ${0:B}", "a"(i16 %0) + ret void +} + +; CHECK-LABEL: multibyte_b_i16 +define void @multibyte_b_i16() { +entry: + %a = alloca i16 + %0 = load i16, i16* %a +; CHECK: instr r30 r31 + call void asm sideeffect "instr ${0:A} ${0:B}", "b"(i16 %0) + ret void +} + +; CHECK-LABEL: multibyte_d_i16 +define void @multibyte_d_i16() { +entry: + %a = alloca i16 + %0 = load i16, i16* %a +; CHECK: instr r24 r25 + call void asm sideeffect "instr ${0:A} ${0:B}", "d"(i16 %0) + ret void +} + +; CHECK-LABEL: multibyte_e_i16 +define void @multibyte_e_i16() { +entry: + %a = alloca i16 + %0 = load i16, i16* %a +; CHECK: instr r30 r31 + call void asm sideeffect "instr ${0:A} ${0:B}", "e"(i16 %0) + ret void +} + +; CHECK-LABEL: multibyte_l_i16 +define void @multibyte_l_i16() { +entry: + %a = alloca i16 + %0 = load i16, i16* %a +; CHECK: instr r14 r15 + call void asm sideeffect "instr ${0:A} ${0:B}", "l"(i16 %0) + ret void +} + + diff --git a/llvm/test/CodeGen/AVR/interrupts.ll b/llvm/test/CodeGen/AVR/interrupts.ll new file mode 100644 index 000000000000..21f4eca7441a --- /dev/null +++ b/llvm/test/CodeGen/AVR/interrupts.ll @@ -0,0 +1,33 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +define avr_intrcc void @interrupt_handler() { +; CHECK-LABEL: interrupt_handler: +; CHECK: sei +; CHECK-NEXT: push r0 +; CHECK-NEXT: push r1 +; CHECK-NEXT: in r0, 63 +; CHECK-NEXT: push r0 +; CHECK: eor r0, r0 +; CHECK: pop r0 +; CHECK-NEXT: out 63, r0 +; CHECK-NEXT: pop r1 +; CHECK-NEXT: pop r0 +; CHECK-NEXT: reti + ret void +} + +define avr_signalcc void @signal_handler() { +; CHECK-LABEL: signal_handler: +; CHECK-NOT: sei +; CHECK: push r0 +; CHECK-NEXT: push r1 +; CHECK-NEXT: in r0, 63 +; CHECK-NEXT: push r0 +; CHECK: eor r0, r0 +; CHECK: pop r0 +; CHECK-NEXT: out 63, r0 +; CHECK-NEXT: pop r1 +; CHECK-NEXT: pop r0 +; CHECK-NEXT: reti + ret void +} diff --git a/llvm/test/CodeGen/AVR/io.ll b/llvm/test/CodeGen/AVR/io.ll new file mode 100644 index 000000000000..b8b04a0e0246 --- /dev/null +++ b/llvm/test/CodeGen/AVR/io.ll @@ -0,0 +1,97 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +define i8 @read8() { +; CHECK-LABEL: read8 +; CHECK: in r24, 8 + %1 = load i8, i8* inttoptr (i16 40 to i8*) + ret i8 %1 +} + +define i16 @read16() { +; CHECK-LABEL: read16 +; CHECK: in r24, 8 +; CHECK: in r25, 9 + %1 = load i16, i16* inttoptr (i16 40 to i16*) + ret i16 %1 +} + +define i32 @read32() { +; CHECK-LABEL: read32 +; CHECK: in r22, 8 +; CHECK: in r23, 9 +; CHECK: in r24, 10 +; CHECK: in r25, 11 + %1 = load i32, i32* inttoptr (i16 40 to i32*) + ret i32 %1 +} + +define i64 @read64() { +; CHECK-LABEL: read64 +; CHECK: in r18, 8 +; CHECK: in r19, 9 +; CHECK: in r20, 10 +; CHECK: in r21, 11 +; CHECK: in r22, 12 +; CHECK: in r23, 13 +; CHECK: in r24, 14 +; CHECK: in r25, 15 + %1 = load i64, i64* inttoptr (i16 40 to i64*) + ret i64 %1 +} + +define void @write8() { +; CHECK-LABEL: write8 +; CHECK: out 8 + store i8 22, i8* inttoptr (i16 40 to i8*) + ret void +} + +define void @write16() { +; CHECK-LABEL: write16 +; CHECK: out 9 +; CHECK: out 8 + store i16 1234, i16* inttoptr (i16 40 to i16*) + ret void +} + +define void @write32() { +; CHECK-LABEL: write32 +; CHECK: out 11 +; CHECK: out 10 +; CHECK: out 9 +; CHECK: out 8 + store i32 12345678, i32* inttoptr (i16 40 to i32*) + ret void +} + +define void @write64() { +; CHECK-LABEL: write64 +; CHECK: out 15 +; CHECK: out 14 +; CHECK: out 13 +; CHECK: out 12 +; CHECK: out 11 +; CHECK: out 10 +; CHECK: out 9 +; CHECK: out 8 + store i64 1234567891234567, i64* inttoptr (i16 40 to i64*) + ret void +} + +define void @sbi8() { +; CHECK-LABEL: sbi8 +; CHECK: sbi 8, 5 + %1 = load i8, i8* inttoptr (i16 40 to i8*) + %or = or i8 %1, 32 + store i8 %or, i8* inttoptr (i16 40 to i8*) + ret void +} + +define void @cbi8() { +; CHECK-LABEL: cbi8 +; CHECK: cbi 8, 5 + %1 = load volatile i8, i8* inttoptr (i16 40 to i8*) + %and = and i8 %1, -33 + store volatile i8 %and, i8* inttoptr (i16 40 to i8*) + ret void +} diff --git a/llvm/test/CodeGen/AVR/lit.local.cfg b/llvm/test/CodeGen/AVR/lit.local.cfg index efbb6c4ba44d..07865380a977 100644 --- a/llvm/test/CodeGen/AVR/lit.local.cfg +++ b/llvm/test/CodeGen/AVR/lit.local.cfg @@ -1,3 +1,27 @@ if not 'AVR' in config.root.targets: config.unsupported = True +config.suffixes = ['.ll', '.cpp'] + +import os, lit.TestRunner +from lit.formats import ShTest + +targets = set(config.root.targets_to_build.split()) +if not 'AVR' in targets: + config.unsupported = True + +if 'AVRLIT_PORT' in os.environ: + config.environment['AVRLIT_PORT'] = os.environ['AVRLIT_PORT'] + +class AVRCodeGenTest(ShTest): + def __init__(self): + ShTest.__init__(self) + + def execute(self, test, litConfig): + if test.getSourcePath().endswith('.cpp') and not 'AVRLIT_PORT' in os.environ: + return (lit.Test.UNSUPPORTED, 'AVRLIT_PORT environment variable is not set') + + return ShTest.execute(self, test, litConfig) + + +config.test_format = AVRCodeGenTest() diff --git a/llvm/test/CodeGen/AVR/load.ll b/llvm/test/CodeGen/AVR/load.ll new file mode 100644 index 000000000000..f5b15d56e614 --- /dev/null +++ b/llvm/test/CodeGen/AVR/load.ll @@ -0,0 +1,142 @@ +; RUN: llc -mattr=avr6,sram < %s -march=avr | FileCheck %s + +define i8 @load8(i8* %x) { +; CHECK-LABEL: load8: +; CHECK: ld r24, {{[XYZ]}} + %1 = load i8, i8* %x + ret i8 %1 +} + +define i16 @load16(i16* %x) { +; CHECK-LABEL: load16: +; CHECK: ld r24, {{[YZ]}} +; CHECK: ldd r25, {{[YZ]}}+1 + %1 = load i16, i16* %x + ret i16 %1 +} + +define i8 @load8disp(i8* %x) { +; CHECK-LABEL: load8disp: +; CHECK: ldd r24, {{[YZ]}}+63 + %1 = getelementptr inbounds i8, i8* %x, i64 63 + %2 = load i8, i8* %1 + ret i8 %2 +} + +define i8 @load8nodisp(i8* %x) { +; CHECK-LABEL: load8nodisp: +; CHECK: movw r26, r24 +; CHECK: subi r26, 192 +; CHECK: sbci r27, 255 +; CHECK: ld r24, {{[XYZ]}} + %1 = getelementptr inbounds i8, i8* %x, i64 64 + %2 = load i8, i8* %1 + ret i8 %2 +} + +define i16 @load16disp(i16* %x) { +; CHECK-LABEL: load16disp: +; CHECK: ldd r24, {{[YZ]}}+62 +; CHECK: ldd r25, {{[YZ]}}+63 + %1 = getelementptr inbounds i16, i16* %x, i64 31 + %2 = load i16, i16* %1 + ret i16 %2 +} + +define i16 @load16nodisp(i16* %x) { +; CHECK-LABEL: load16nodisp: +; CHECK: movw r30, r24 +; CHECK: subi r30, 192 +; CHECK: sbci r31, 255 +; CHECK: ld r24, {{[YZ]}} +; CHECK: ldd r25, {{[YZ]}}+1 + %1 = getelementptr inbounds i16, i16* %x, i64 32 + %2 = load i16, i16* %1 + ret i16 %2 +} + +define i8 @load8postinc(i8* %x, i8 %y) { +; CHECK-LABEL: load8postinc: +; CHECK: ld {{.*}}, {{[XYZ]}}+ +entry: + %tobool6 = icmp eq i8 %y, 0 + br i1 %tobool6, label %while.end, label %while.body +while.body: ; preds = %entry, %while.body + %r.09 = phi i8 [ %add, %while.body ], [ 0, %entry ] + %y.addr.08 = phi i8 [ %dec, %while.body ], [ %y, %entry ] + %x.addr.07 = phi i8* [ %incdec.ptr, %while.body ], [ %x, %entry ] + %dec = add i8 %y.addr.08, -1 + %incdec.ptr = getelementptr inbounds i8, i8* %x.addr.07, i16 1 + %0 = load i8, i8* %x.addr.07 + %add = add i8 %0, %r.09 + %tobool = icmp eq i8 %dec, 0 + br i1 %tobool, label %while.end, label %while.body +while.end: ; preds = %while.body, %entry + %r.0.lcssa = phi i8 [ 0, %entry ], [ %add, %while.body ] + ret i8 %r.0.lcssa +} + +define i16 @load16postinc(i16* %x, i16 %y) { +; CHECK-LABEL: load16postinc: +; CHECK: ld {{.*}}, {{[XYZ]}}+ +; CHECK: ld {{.*}}, {{[XYZ]}}+ +entry: + %tobool2 = icmp eq i16 %y, 0 + br i1 %tobool2, label %while.end, label %while.body +while.body: ; preds = %entry, %while.body + %r.05 = phi i16 [ %add, %while.body ], [ 0, %entry ] + %y.addr.04 = phi i16 [ %dec, %while.body ], [ %y, %entry ] + %x.addr.03 = phi i16* [ %incdec.ptr, %while.body ], [ %x, %entry ] + %dec = add nsw i16 %y.addr.04, -1 + %incdec.ptr = getelementptr inbounds i16, i16* %x.addr.03, i16 1 + %0 = load i16, i16* %x.addr.03 + %add = add nsw i16 %0, %r.05 + %tobool = icmp eq i16 %dec, 0 + br i1 %tobool, label %while.end, label %while.body +while.end: ; preds = %while.body, %entry + %r.0.lcssa = phi i16 [ 0, %entry ], [ %add, %while.body ] + ret i16 %r.0.lcssa +} + +define i8 @load8predec(i8* %x, i8 %y) { +; CHECK-LABEL: load8predec: +; CHECK: ld {{.*}}, -{{[XYZ]}} +entry: + %tobool6 = icmp eq i8 %y, 0 + br i1 %tobool6, label %while.end, label %while.body +while.body: ; preds = %entry, %while.body + %r.09 = phi i8 [ %add, %while.body ], [ 0, %entry ] + %y.addr.08 = phi i8 [ %dec, %while.body ], [ %y, %entry ] + %x.addr.07 = phi i8* [ %incdec.ptr, %while.body ], [ %x, %entry ] + %dec = add i8 %y.addr.08, -1 + %incdec.ptr = getelementptr inbounds i8, i8* %x.addr.07, i16 -1 + %0 = load i8, i8* %incdec.ptr + %add = add i8 %0, %r.09 + %tobool = icmp eq i8 %dec, 0 + br i1 %tobool, label %while.end, label %while.body +while.end: ; preds = %while.body, %entry + %r.0.lcssa = phi i8 [ 0, %entry ], [ %add, %while.body ] + ret i8 %r.0.lcssa +} + +define i16 @load16predec(i16* %x, i16 %y) { +; CHECK-LABEL: load16predec: +; CHECK: ld {{.*}}, -{{[XYZ]}} +; CHECK: ld {{.*}}, -{{[XYZ]}} +entry: + %tobool2 = icmp eq i16 %y, 0 + br i1 %tobool2, label %while.end, label %while.body +while.body: ; preds = %entry, %while.body + %r.05 = phi i16 [ %add, %while.body ], [ 0, %entry ] + %y.addr.04 = phi i16 [ %dec, %while.body ], [ %y, %entry ] + %x.addr.03 = phi i16* [ %incdec.ptr, %while.body ], [ %x, %entry ] + %dec = add nsw i16 %y.addr.04, -1 + %incdec.ptr = getelementptr inbounds i16, i16* %x.addr.03, i16 -1 + %0 = load i16, i16* %incdec.ptr + %add = add nsw i16 %0, %r.05 + %tobool = icmp eq i16 %dec, 0 + br i1 %tobool, label %while.end, label %while.body +while.end: ; preds = %while.body, %entry + %r.0.lcssa = phi i16 [ 0, %entry ], [ %add, %while.body ] + ret i16 %r.0.lcssa +} diff --git a/llvm/test/CodeGen/AVR/or.ll b/llvm/test/CodeGen/AVR/or.ll new file mode 100644 index 000000000000..7ffa531feefc --- /dev/null +++ b/llvm/test/CodeGen/AVR/or.ll @@ -0,0 +1,80 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +define i8 @or8_reg_reg(i8 %a, i8 %b) { +; CHECK-LABEL: or8_reg_reg: +; CHECK: or r24, r22 + %result = or i8 %a, %b + ret i8 %result +} + +define i8 @or8_reg_imm(i8 %a) { +; CHECK-LABEL: or8_reg_imm: +; CHECK: ori r24, 5 + %result = or i8 %a, 5 + ret i8 %result +} + +define i16 @or16_reg_reg(i16 %a, i16 %b) { +; CHECK-LABEL: or16_reg_reg: +; CHECK: or r24, r22 +; CHECK: or r25, r23 + %result = or i16 %a, %b + ret i16 %result +} + +define i16 @or16_reg_imm(i16 %a) { +; CHECK-LABEL: or16_reg_imm: +; CHECK: ori r24, 210 +; CHECK: ori r25, 4 + %result = or i16 %a, 1234 + ret i16 %result +} + +define i32 @or32_reg_reg(i32 %a, i32 %b) { +; CHECK-LABEL: or32_reg_reg: +; CHECK: or r22, r18 +; CHECK: or r23, r19 +; CHECK: or r24, r20 +; CHECK: or r25, r21 + %result = or i32 %a, %b + ret i32 %result +} + +define i32 @or32_reg_imm(i32 %a) { +; CHECK-LABEL: or32_reg_imm: +; CHECK: ori r22, 21 +; CHECK: ori r23, 205 +; CHECK: ori r24, 91 +; CHECK: ori r25, 7 + %result = or i32 %a, 123456789 + ret i32 %result +} + +define i64 @or64_reg_reg(i64 %a, i64 %b) { +; CHECK-LABEL: or64_reg_reg: +; CHECK: or r18, r10 +; CHECK: or r19, r11 +; CHECK: or r20, r12 +; CHECK: or r21, r13 +; CHECK: or r22, r14 +; CHECK: or r23, r15 +; CHECK: or r24, r16 +; CHECK: or r25, r17 + %result = or i64 %a, %b + ret i64 %result +} + +define i64 @or64_reg_imm(i64 %a) { +; CHECK-LABEL: or64_reg_imm: +; CHECK: ori r18, 204 +; CHECK: ori r19, 204 +; CHECK: ori r20, 204 +; CHECK: ori r21, 204 +; CHECK: ori r22, 204 +; CHECK: ori r23, 204 +; CHECK: ori r24, 204 +; CHECK: ori r25, 204 + %result = or i64 %a, 14757395258967641292 + ret i64 %result +} + diff --git a/llvm/test/CodeGen/AVR/progmem.ll b/llvm/test/CodeGen/AVR/progmem.ll new file mode 100644 index 000000000000..07d21834f1ee --- /dev/null +++ b/llvm/test/CodeGen/AVR/progmem.ll @@ -0,0 +1,70 @@ +; RUN: llc < %s -march=avr -mattr=movw,lpm | FileCheck %s +; XFAIL: * + +; Tests the standard LPM instruction + +define i8 @test8(i8 addrspace(1)* %p) { +; CHECK-LABEL: test8: +; CHECK: movw r30, r24 +; CHECK: lpm r24, Z + %1 = load i8, i8 addrspace(1)* %p + ret i8 %1 +} + +define i16 @test16(i16 addrspace(1)* %p) { +; CHECK-LABEL: test16: +; CHECK: movw r30, r24 +; CHECK: lpm r24, Z+ +; CHECK: lpm r25, Z+ + %1 = load i16, i16 addrspace(1)* %p + ret i16 %1 +} + +define i8 @test8postinc(i8 addrspace(1)* %x, i8 %y) { +; CHECK-LABEL: test8postinc: +; CHECK: movw r30, r24 +; CHECK: lpm {{.*}}, Z+ +entry: + %cmp10 = icmp sgt i8 %y, 0 + br i1 %cmp10, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %ret.013 = phi i8 [ %add, %for.body ], [ 0, %entry ] + %i.012 = phi i8 [ %inc, %for.body ], [ 0, %entry ] + %x.addr.011 = phi i8 addrspace(1)* [ %incdec.ptr, %for.body ], [ %x, %entry ] + %incdec.ptr = getelementptr inbounds i8, i8 addrspace(1)* %x.addr.011, i16 1 + %0 = load i8, i8 addrspace(1)* %x.addr.011 + %add = add i8 %0, %ret.013 + %inc = add i8 %i.012, 1 + %exitcond = icmp eq i8 %inc, %y + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.body, %entry + %ret.0.lcssa = phi i8 [ 0, %entry ], [ %add, %for.body ] + ret i8 %ret.0.lcssa +} + +define i16 @test16postinc(i16 addrspace(1)* %x, i8 %y) { +; CHECK-LABEL: test16postinc: +; CHECK: movw r30, r24 +; CHECK: lpm {{.*}}, Z+ +; CHECK: lpm {{.*}}, Z+ +entry: + %cmp5 = icmp sgt i8 %y, 0 + br i1 %cmp5, label %for.body, label %for.end + +for.body: ; preds = %entry, %for.body + %ret.08 = phi i16 [ %add, %for.body ], [ 0, %entry ] + %i.07 = phi i8 [ %inc, %for.body ], [ 0, %entry ] + %x.addr.06 = phi i16 addrspace(1)* [ %incdec.ptr, %for.body ], [ %x, %entry ] + %incdec.ptr = getelementptr inbounds i16, i16 addrspace(1)* %x.addr.06, i16 1 + %0 = load i16, i16 addrspace(1)* %x.addr.06 + %add = add nsw i16 %0, %ret.08 + %inc = add i8 %i.07, 1 + %exitcond = icmp eq i8 %inc, %y + br i1 %exitcond, label %for.end, label %for.body + +for.end: ; preds = %for.body, %entry + %ret.0.lcssa = phi i16 [ 0, %entry ], [ %add, %for.body ] + ret i16 %ret.0.lcssa +} diff --git a/llvm/test/CodeGen/AVR/return.ll b/llvm/test/CodeGen/AVR/return.ll new file mode 100644 index 000000000000..d57f435fd11c --- /dev/null +++ b/llvm/test/CodeGen/AVR/return.ll @@ -0,0 +1,142 @@ +; RUN: llc -mattr=avr6,sram < %s -march=avr | FileCheck %s + +;TODO: test returning byval structs +; TODO: test naked functions + +define void @return_void() { +; CHECK: return_void:{{[a-zA-Z0-9 #@]*}} +; CHECK-NEXT: #{{[a-zA-Z0-9 #@]*}} +; CHECK-NEXT: ret + ret void +} + +define i8 @return8_imm() { +; CHECK-LABEL: return8_imm: +; CHECK: ldi r24, 5 + ret i8 5 +} + +define i8 @return8_arg(i8 %x) { +; CHECK: return8_arg:{{[a-zA-Z0-9 #@]*}} +; CHECK-NEXT: #{{[a-zA-Z0-9 #@]*}} +; CHECK-NEXT: ret + ret i8 %x +} + +define i8 @return8_arg2(i8 %x, i8 %y, i8 %z) { +; CHECK-LABEL: return8_arg2: +; CHECK: mov r24, r20 + ret i8 %z +} + +define i16 @return16_imm() { +; CHECK-LABEL: return16_imm: +; CHECK: ldi r24, 57 +; CHECK: ldi r25, 48 + ret i16 12345 +} + +define i16 @return16_arg(i16 %x) { +; CHECK: return16_arg:{{[a-zA-Z0-9 #@]*}} +; CHECK-NEXT: #{{[a-zA-Z0-9 #@]*}} +; CHECK-NEXT: ret + ret i16 %x +} + +define i16 @return16_arg2(i16 %x, i16 %y, i16 %z) { +; CHECK-LABEL: return16_arg2: +; CHECK: movw r24, r20 + ret i16 %z +} + +define i32 @return32_imm() { +; CHECK-LABEL: return32_imm: +; CHECK: ldi r22, 21 +; CHECK: ldi r23, 205 +; CHECK: ldi r24, 91 +; CHECK: ldi r25, 7 + ret i32 123456789 +} + +define i32 @return32_arg(i32 %x) { +; CHECK: return32_arg:{{[a-zA-Z0-9 #@]*}} +; CHECK-NEXT: #{{[a-zA-Z0-9 #@]*}} +; CHECK-NEXT: ret + ret i32 %x +} + +define i32 @return32_arg2(i32 %x, i32 %y, i32 %z) { +; CHECK-LABEL: return32_arg2: +; CHECK: movw r22, r14 +; CHECK: movw r24, r16 + ret i32 %z +} + +define i64 @return64_imm() { +; CHECK-LABEL: return64_imm: +; CHECK: ldi r18, 204 +; CHECK: ldi r19, 204 +; CHECK: ldi r20, 104 +; CHECK: ldi r21, 37 +; CHECK: ldi r22, 25 +; CHECK: ldi r23, 22 +; CHECK: ldi r24, 236 +; CHECK: ldi r25, 190 + ret i64 13757395258967641292 +} + +define i64 @return64_arg(i64 %x) { +; CHECK: return64_arg:{{[a-zA-Z0-9 #@]*}} +; CHECK-NEXT: #{{[a-zA-Z0-9 #@]*}} +; CHECK-NEXT: ret + ret i64 %x +} + +define i64 @return64_arg2(i64 %x, i64 %y, i64 %z) { +; CHECK-LABEL: return64_arg2: +; CHECK: push r28 +; CHECK: push r29 +; CHECK: ldd r18, Y+5 +; CHECK: ldd r19, Y+6 +; CHECK: ldd r20, Y+7 +; CHECK: ldd r21, Y+8 +; CHECK: ldd r22, Y+9 +; CHECK: ldd r23, Y+10 +; CHECK: ldd r24, Y+11 +; CHECK: ldd r25, Y+12 +; CHECK: pop r29 +; CHECK: pop r28 + ret i64 %z +} + +define i32 @return64_trunc(i32 %a, i32 %b, i32 %c, i64 %d) { +; CHECK-LABEL: return64_trunc: +; CHECK: push r28 +; CHECK: push r29 +; CHECK: ldd r22, Y+5 +; CHECK: ldd r23, Y+6 +; CHECK: ldd r24, Y+7 +; CHECK: ldd r25, Y+8 +; CHECK: pop r29 +; CHECK: pop r28 + %result = trunc i64 %d to i32 + ret i32 %result +} + +define i32 @naked(i32 %x) naked { +; CHECK-LABEL: naked: +; CHECK-NOT: ret + ret i32 %x +} + +define avr_intrcc void @interrupt_handler() { +; CHECK-LABEL: interrupt_handler: +; CHECK: reti + ret void +} + +define avr_signalcc void @signal_handler() { +; CHECK-LABEL: signal_handler: +; CHECK: reti + ret void +} diff --git a/llvm/test/CodeGen/AVR/sext.ll b/llvm/test/CodeGen/AVR/sext.ll new file mode 100644 index 000000000000..7fb6d84ec446 --- /dev/null +++ b/llvm/test/CodeGen/AVR/sext.ll @@ -0,0 +1,29 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +; sext R17:R16, R13 +; mov r16, r13 +; mov r17, r13 +; lsl r17 +; sbc r17, r17 +define i16 @sext1(i8 %x, i8 %y) { +; CHECK-LABEL: sext1: +; CHECK: mov r24, r22 +; CHECK: mov r25, r22 +; CHECK: lsl r25 +; CHECK: sbc r25, r25 + %1 = sext i8 %y to i16 + ret i16 %1 +} + +; sext R17:R16, R16 +; mov r17, r16 +; lsl r17 +; sbc r17, r17 +define i16 @sext2(i8 %x) { +; CHECK-LABEL: sext2: +; CHECK: mov r25, r24 +; CHECK: lsl r25 +; CHECK: sbc r25, r25 + %1 = sext i8 %x to i16 + ret i16 %1 +} diff --git a/llvm/test/CodeGen/AVR/store.ll b/llvm/test/CodeGen/AVR/store.ll new file mode 100644 index 000000000000..bad3f61a0135 --- /dev/null +++ b/llvm/test/CodeGen/AVR/store.ll @@ -0,0 +1,130 @@ +; RUN: llc -mattr=avr6,sram < %s -march=avr | FileCheck %s + +define void @store8(i8* %x, i8 %y) { +; CHECK-LABEL: store8: +; CHECK: st {{[XYZ]}}, r22 + store i8 %y, i8* %x + ret void +} + +define void @store16(i16* %x, i16 %y) { +; CHECK-LABEL: store16: +; CHECK: st {{[YZ]}}, r22 +; CHECK: std {{[YZ]}}+1, r23 + store i16 %y, i16* %x + ret void +} + +define void @store8disp(i8* %x, i8 %y) { +; CHECK-LABEL: store8disp: +; CHECK: std {{[YZ]}}+63, r22 + %arrayidx = getelementptr inbounds i8, i8* %x, i16 63 + store i8 %y, i8* %arrayidx + ret void +} + +define void @store8nodisp(i8* %x, i8 %y) { +; CHECK-LABEL: store8nodisp: +; CHECK: movw r26, r24 +; CHECK: subi r26, 192 +; CHECK: sbci r27, 255 +; CHECK: st {{[XYZ]}}, r22 + %arrayidx = getelementptr inbounds i8, i8* %x, i16 64 + store i8 %y, i8* %arrayidx + ret void +} + +define void @store16disp(i16* %x, i16 %y) { +; CHECK-LABEL: store16disp: +; CHECK: std {{[YZ]}}+62, r22 +; CHECK: std {{[YZ]}}+63, r23 + %arrayidx = getelementptr inbounds i16, i16* %x, i16 31 + store i16 %y, i16* %arrayidx + ret void +} + +define void @store16nodisp(i16* %x, i16 %y) { +; CHECK-LABEL: store16nodisp: +; CHECK: movw r30, r24 +; CHECK: subi r30, 192 +; CHECK: sbci r31, 255 +; CHECK: st {{[YZ]}}, r22 +; CHECK: std {{[YZ]}}+1, r23 + %arrayidx = getelementptr inbounds i16, i16* %x, i16 32 + store i16 %y, i16* %arrayidx + ret void +} + +define void @store8postinc(i8* %x, i8 %y) { +; CHECK-LABEL: store8postinc: +; CHECK: st {{[XYZ]}}+, {{.*}} +entry: + %tobool3 = icmp eq i8 %y, 0 + br i1 %tobool3, label %while.end, label %while.body +while.body: ; preds = %entry, %while.body + %dec5.in = phi i8 [ %dec5, %while.body ], [ %y, %entry ] + %x.addr.04 = phi i8* [ %incdec.ptr, %while.body ], [ %x, %entry ] + %dec5 = add i8 %dec5.in, -1 + %incdec.ptr = getelementptr inbounds i8, i8* %x.addr.04, i16 1 + store i8 %dec5, i8* %x.addr.04 + %tobool = icmp eq i8 %dec5, 0 + br i1 %tobool, label %while.end, label %while.body +while.end: ; preds = %while.body, %entry + ret void +} + +define void @store16postinc(i16* %x, i16 %y) { +; CHECK-LABEL: store16postinc: +; CHECK: st {{[XYZ]}}+, {{.*}} +; CHECK: st {{[XYZ]}}+, {{.*}} +entry: + %tobool3 = icmp eq i16 %y, 0 + br i1 %tobool3, label %while.end, label %while.body +while.body: ; preds = %entry, %while.body + %dec5.in = phi i16 [ %dec5, %while.body ], [ %y, %entry ] + %x.addr.04 = phi i16* [ %incdec.ptr, %while.body ], [ %x, %entry ] + %dec5 = add nsw i16 %dec5.in, -1 + %incdec.ptr = getelementptr inbounds i16, i16* %x.addr.04, i16 1 + store i16 %dec5, i16* %x.addr.04 + %tobool = icmp eq i16 %dec5, 0 + br i1 %tobool, label %while.end, label %while.body +while.end: ; preds = %while.body, %entry + ret void +} + +define void @store8predec(i8* %x, i8 %y) { +; CHECK-LABEL: store8predec: +; CHECK: st -{{[XYZ]}}, {{.*}} +entry: + %tobool3 = icmp eq i8 %y, 0 + br i1 %tobool3, label %while.end, label %while.body +while.body: ; preds = %entry, %while.body + %dec5.in = phi i8 [ %dec5, %while.body ], [ %y, %entry ] + %x.addr.04 = phi i8* [ %incdec.ptr, %while.body ], [ %x, %entry ] + %dec5 = add i8 %dec5.in, -1 + %incdec.ptr = getelementptr inbounds i8, i8* %x.addr.04, i16 -1 + store i8 %dec5, i8* %incdec.ptr + %tobool = icmp eq i8 %dec5, 0 + br i1 %tobool, label %while.end, label %while.body +while.end: ; preds = %while.body, %entry + ret void +} + +define void @store16predec(i16* %x, i16 %y) { +; CHECK-LABEL: store16predec: +; CHECK: st -{{[XYZ]}}, {{.*}} +; CHECK: st -{{[XYZ]}}, {{.*}} +entry: + %tobool3 = icmp eq i16 %y, 0 + br i1 %tobool3, label %while.end, label %while.body +while.body: ; preds = %entry, %while.body + %dec5.in = phi i16 [ %dec5, %while.body ], [ %y, %entry ] + %x.addr.04 = phi i16* [ %incdec.ptr, %while.body ], [ %x, %entry ] + %dec5 = add nsw i16 %dec5.in, -1 + %incdec.ptr = getelementptr inbounds i16, i16* %x.addr.04, i16 -1 + store i16 %dec5, i16* %incdec.ptr + %tobool = icmp eq i16 %dec5, 0 + br i1 %tobool, label %while.end, label %while.body +while.end: ; preds = %while.body, %entry + ret void +} diff --git a/llvm/test/CodeGen/AVR/sub.ll b/llvm/test/CodeGen/AVR/sub.ll new file mode 100644 index 000000000000..7a65a6004974 --- /dev/null +++ b/llvm/test/CodeGen/AVR/sub.ll @@ -0,0 +1,92 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +define i8 @sub8_reg_reg(i8 %a, i8 %b) { +; CHECK-LABEL: sub8_reg_reg: +; CHECK: sub r24, r22 + %result = sub i8 %a, %b + ret i8 %result +} + +define i8 @sub8_reg_imm(i8 %a) { +; CHECK-LABEL: sub8_reg_imm: +; CHECK: subi r24, 5 + %result = sub i8 %a, 5 + ret i8 %result +} + +define i8 @sub8_reg_decrement(i8 %a) { +; CHECK-LABEL: sub8_reg_decrement: +; CHECK: dec r24 + %result = sub i8 %a, 1 + ret i8 %result +} + +define i16 @sub16_reg_reg(i16 %a, i16 %b) { +; CHECK-LABEL: sub16_reg_reg: +; CHECK: sub r24, r22 +; CHECK: sbc r25, r23 + %result = sub i16 %a, %b + ret i16 %result +} + +define i16 @sub16_reg_imm(i16 %a) { +; CHECK-LABEL: sub16_reg_imm: +; CHECK: sbiw r24, 63 + %result = sub i16 %a, 63 + ret i16 %result +} + +define i16 @sub16_reg_imm_subi(i16 %a) { +; CHECK-LABEL: sub16_reg_imm_subi: +; CHECK: subi r24, 210 +; CHECK: sbci r25, 4 + %result = sub i16 %a, 1234 + ret i16 %result +} + +define i32 @sub32_reg_reg(i32 %a, i32 %b) { +; CHECK-LABEL: sub32_reg_reg: +; CHECK: sub r22, r18 +; CHECK: sbc r23, r19 +; CHECK: sbc r24, r20 +; CHECK: sbc r25, r21 + %result = sub i32 %a, %b + ret i32 %result +} + +define i32 @sub32_reg_imm(i32 %a) { +; CHECK-LABEL: sub32_reg_imm: +; CHECK: subi r22, 21 +; CHECK: sbci r23, 205 +; CHECK: sbci r24, 91 +; CHECK: sbci r25, 7 + %result = sub i32 %a, 123456789 + ret i32 %result +} + +define i64 @sub64_reg_reg(i64 %a, i64 %b) { +; CHECK-LABEL: sub64_reg_reg: +; CHECK: sub r18, r10 +; CHECK: sbc r20, r12 +; CHECK: sbc r21, r13 +; CHECK: sbc r22, r14 +; CHECK: sbc r23, r15 +; CHECK: sbc r24, r16 +; CHECK: sbc r25, r17 + %result = sub i64 %a, %b + ret i64 %result +} + +define i64 @sub64_reg_imm(i64 %a) { +; CHECK-LABEL: sub64_reg_imm: +; CHECK: subi r18, 204 +; CHECK: sbci r19, 204 +; CHECK: sbci r20, 104 +; CHECK: sbci r21, 37 +; CHECK: sbci r22, 25 +; CHECK: sbci r23, 22 +; CHECK: sbci r24, 236 +; CHECK: sbci r25, 190 + %result = sub i64 %a, 13757395258967641292 + ret i64 %result +} diff --git a/llvm/test/CodeGen/AVR/varargs.ll b/llvm/test/CodeGen/AVR/varargs.ll new file mode 100644 index 000000000000..b35ce4c0f7ae --- /dev/null +++ b/llvm/test/CodeGen/AVR/varargs.ll @@ -0,0 +1,59 @@ +; RUN: llc -mattr=sram,movw,addsubiw < %s -march=avr | FileCheck %s + +declare void @llvm.va_start(i8*) +declare i16 @vsprintf(i8* nocapture, i8* nocapture, i8*) +declare void @llvm.va_end(i8*) + +define i16 @varargs1(i8* nocapture %x, ...) { +; CHECK-LABEL: varargs1: +; CHECK: movw r20, r28 +; CHECK: subi r20, 215 +; CHECK: sbci r21, 255 +; CHECK: movw r24, r28 +; CHECK: adiw r24, 3 +; CHECK: ldd r22, Y+39 +; CHECK: ldd r23, Y+40 +; CHECK: call + %buffer = alloca [32 x i8] + %ap = alloca i8* + %ap1 = bitcast i8** %ap to i8* + call void @llvm.va_start(i8* %ap1) + %arraydecay = getelementptr inbounds [32 x i8], [32 x i8]* %buffer, i16 0, i16 0 + %1 = load i8*, i8** %ap + %call = call i16 @vsprintf(i8* %arraydecay, i8* %x, i8* %1) + call void @llvm.va_end(i8* %ap1) + ret i16 0 +} + +define i16 @varargs2(i8* nocapture %x, ...) { +; CHECK-LABEL: varargs2: +; CHECK: ld r24, Z +; CHECK: ldd r25, Z+1 + %ap = alloca i8* + %ap1 = bitcast i8** %ap to i8* + call void @llvm.va_start(i8* %ap1) + %1 = va_arg i8** %ap, i16 + call void @llvm.va_end(i8* %ap1) + ret i16 %1 +} + +declare void @var1223(i16, ...) +define void @varargcall() { +; CHECK-LABEL: varargcall: +; CHECK: ldi [[REG1:r[0-9]+]], 191 +; CHECK: ldi [[REG2:r[0-9]+]], 223 +; CHECK: push [[REG2]] +; CHECK: push [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 189 +; CHECK: ldi [[REG2:r[0-9]+]], 205 +; CHECK: push [[REG2]] +; CHECK: push [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 205 +; CHECK: ldi [[REG2:r[0-9]+]], 171 +; CHECK: push [[REG2]] +; CHECK: push [[REG1]] +; CHECK: call +; CHECK: adiw r30, 6 + tail call void (i16, ...) @var1223(i16 -21555, i16 -12867, i16 -8257) + ret void +} diff --git a/llvm/test/CodeGen/AVR/xor.ll b/llvm/test/CodeGen/AVR/xor.ll new file mode 100644 index 000000000000..3f04feb9e2ae --- /dev/null +++ b/llvm/test/CodeGen/AVR/xor.ll @@ -0,0 +1,41 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +define i8 @xor8_reg_reg(i8 %a, i8 %b) { +; CHECK-LABEL: xor8_reg_reg: +; CHECK: eor r24, r22 + %result = xor i8 %a, %b + ret i8 %result +} + +define i16 @xor16_reg_reg(i16 %a, i16 %b) { +; CHECK-LABEL: xor16_reg_reg: +; CHECK: eor r24, r22 +; CHECK: eor r25, r23 + %result = xor i16 %a, %b + ret i16 %result +} + +define i32 @xor32_reg_reg(i32 %a, i32 %b) { +; CHECK-LABEL: xor32_reg_reg: +; CHECK: eor r22, r18 +; CHECK: eor r23, r19 +; CHECK: eor r24, r20 +; CHECK: eor r25, r21 + %result = xor i32 %a, %b + ret i32 %result +} + +define i64 @xor64_reg_reg(i64 %a, i64 %b) { +; CHECK-LABEL: xor64_reg_reg: +; CHECK: eor r18, r10 +; CHECK: eor r19, r11 +; CHECK: eor r20, r12 +; CHECK: eor r21, r13 +; CHECK: eor r22, r14 +; CHECK: eor r23, r15 +; CHECK: eor r24, r16 +; CHECK: eor r25, r17 + %result = xor i64 %a, %b + ret i64 %result +} + diff --git a/llvm/test/CodeGen/AVR/zext.ll b/llvm/test/CodeGen/AVR/zext.ll new file mode 100644 index 000000000000..ff7a653c7efa --- /dev/null +++ b/llvm/test/CodeGen/AVR/zext.ll @@ -0,0 +1,31 @@ +; RUN: llc < %s -march=avr | FileCheck %s + +; zext R25:R24, R24 +; eor R25, R25 +define i16 @zext1(i8 %x) { +; CHECK-LABEL: zext1: +; CHECK: eor r25, r25 + %1 = zext i8 %x to i16 + ret i16 %1 +} + +; zext R25:R24, R20 +; mov R24, R20 +; eor R25, R25 +define i16 @zext2(i8 %x, i8 %y) { +; CHECK-LABEL: zext2: +; CHECK: mov r24, r22 +; CHECK: eor r25, r25 + %1 = zext i8 %y to i16 + ret i16 %1 +} + +; zext R25:R24, R24 +; eor R25, R25 +define i16 @zext_i1(i1 %x) { +; CHECK-LABEL: zext_i1: +; CHECK: eor r25, r25 + %1 = zext i1 %x to i16 + ret i16 %1 +} +