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