[llvm] r287162 - [AVR] Add the pseudo instruction expansion pass

Dylan McKay via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 16 13:58:05 PST 2016


Author: dylanmckay
Date: Wed Nov 16 15:58:04 2016
New Revision: 287162

URL: http://llvm.org/viewvc/llvm-project?rev=287162&view=rev
Log:
[AVR] Add the pseudo instruction expansion pass

Summary:
A lot of the pseudo instructions are required because LLVM assumes that
all integers of the same size as the pointer size are legal. This means
that it will not currently expand 16-bit instructions to their 8-bit
variants because it thinks 16-bit types are legal for the operations.

This also adds all of the CodeGen tests that required the pass to run.

Reviewers: arsenm, kparzysz

Subscribers: wdng, mgorny, modocache, llvm-commits

Differential Revision: https://reviews.llvm.org/D26577

Added:
    llvm/trunk/lib/Target/AVR/AVRExpandPseudoInsts.cpp
    llvm/trunk/test/CodeGen/AVR/add.ll
    llvm/trunk/test/CodeGen/AVR/alloca.ll
    llvm/trunk/test/CodeGen/AVR/and.ll
    llvm/trunk/test/CodeGen/AVR/atomics/fence.ll
    llvm/trunk/test/CodeGen/AVR/atomics/load16.ll
    llvm/trunk/test/CodeGen/AVR/atomics/load8.ll
    llvm/trunk/test/CodeGen/AVR/atomics/store.ll
    llvm/trunk/test/CodeGen/AVR/atomics/store16.ll
    llvm/trunk/test/CodeGen/AVR/brind.ll
    llvm/trunk/test/CodeGen/AVR/call.ll
    llvm/trunk/test/CodeGen/AVR/cmp.ll
    llvm/trunk/test/CodeGen/AVR/com.ll
    llvm/trunk/test/CodeGen/AVR/directmem.ll
    llvm/trunk/test/CodeGen/AVR/dynalloca.ll
    llvm/trunk/test/CodeGen/AVR/eor.ll
    llvm/trunk/test/CodeGen/AVR/error-srcreg-destreg-same.ll
    llvm/trunk/test/CodeGen/AVR/expand-integer-failure.ll
    llvm/trunk/test/CodeGen/AVR/frame.ll
    llvm/trunk/test/CodeGen/AVR/inline-asm.ll
    llvm/trunk/test/CodeGen/AVR/interrupts.ll
    llvm/trunk/test/CodeGen/AVR/io.ll
    llvm/trunk/test/CodeGen/AVR/load.ll
    llvm/trunk/test/CodeGen/AVR/or.ll
    llvm/trunk/test/CodeGen/AVR/progmem.ll
    llvm/trunk/test/CodeGen/AVR/return.ll
    llvm/trunk/test/CodeGen/AVR/sext.ll
    llvm/trunk/test/CodeGen/AVR/store.ll
    llvm/trunk/test/CodeGen/AVR/sub.ll
    llvm/trunk/test/CodeGen/AVR/varargs.ll
    llvm/trunk/test/CodeGen/AVR/xor.ll
    llvm/trunk/test/CodeGen/AVR/zext.ll
Modified:
    llvm/trunk/lib/Target/AVR/AVRTargetMachine.cpp
    llvm/trunk/lib/Target/AVR/CMakeLists.txt
    llvm/trunk/test/CodeGen/AVR/high-pressure-on-ptrregs.ll
    llvm/trunk/test/CodeGen/AVR/lit.local.cfg

Added: llvm/trunk/lib/Target/AVR/AVRExpandPseudoInsts.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AVR/AVRExpandPseudoInsts.cpp?rev=287162&view=auto
==============================================================================
--- llvm/trunk/lib/Target/AVR/AVRExpandPseudoInsts.cpp (added)
+++ llvm/trunk/lib/Target/AVR/AVRExpandPseudoInsts.cpp Wed Nov 16 15:58:04 2016
@@ -0,0 +1,1431 @@
+//===-- AVRExpandPseudoInsts.cpp - Expand pseudo instructions -------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains a pass that expands pseudo instructions into target
+// instructions. This pass should be run after register allocation but before
+// the post-regalloc scheduling pass.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AVR.h"
+#include "AVRInstrInfo.h"
+#include "AVRTargetMachine.h"
+#include "MCTargetDesc/AVRMCTargetDesc.h"
+
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/Target/TargetRegisterInfo.h"
+
+using namespace llvm;
+
+namespace {
+
+/// Expands "placeholder" instructions marked as pseudo into
+/// actual AVR instructions.
+class AVRExpandPseudo : public MachineFunctionPass {
+public:
+  static char ID;
+
+  AVRExpandPseudo() : MachineFunctionPass(ID) {}
+
+  bool runOnMachineFunction(MachineFunction &MF) override;
+
+  StringRef getPassName() const override {
+    return "AVR pseudo instruction expansion pass";
+  }
+
+private:
+  typedef MachineBasicBlock Block;
+  typedef Block::iterator BlockIt;
+
+  const AVRRegisterInfo *TRI;
+  const TargetInstrInfo *TII;
+
+  /// The register to be used for temporary storage.
+  const unsigned SCRATCH_REGISTER = AVR::R0;
+  /// The IO address of the status register.
+  const unsigned SREG_ADDR = 0x3f;
+
+  bool expandMBB(Block &MBB);
+  bool expandMI(Block &MBB, BlockIt MBBI);
+  template <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

Modified: llvm/trunk/lib/Target/AVR/AVRTargetMachine.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AVR/AVRTargetMachine.cpp?rev=287162&r1=287161&r2=287162&view=diff
==============================================================================
--- llvm/trunk/lib/Target/AVR/AVRTargetMachine.cpp (original)
+++ llvm/trunk/lib/Target/AVR/AVRTargetMachine.cpp Wed Nov 16 15:58:04 2016
@@ -106,7 +106,7 @@ void AVRPassConfig::addPreRegAlloc() {
   addPass(createAVRDynAllocaSRPass());
 }
 
-void AVRPassConfig::addPreSched2() { }
+void AVRPassConfig::addPreSched2() { addPass(createAVRExpandPseudoPass()); }
 
 void AVRPassConfig::addPreEmitPass() { }
 

Modified: llvm/trunk/lib/Target/AVR/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Target/AVR/CMakeLists.txt?rev=287162&r1=287161&r2=287162&view=diff
==============================================================================
--- llvm/trunk/lib/Target/AVR/CMakeLists.txt (original)
+++ llvm/trunk/lib/Target/AVR/CMakeLists.txt Wed Nov 16 15:58:04 2016
@@ -18,6 +18,7 @@ add_public_tablegen_target(AVRCommonTabl
 
 add_llvm_target(AVRCodeGen
   AVRAsmPrinter.cpp
+  AVRExpandPseudoInsts.cpp
   AVRFrameLowering.cpp
   AVRInstrInfo.cpp
   AVRISelDAGToDAG.cpp

Added: llvm/trunk/test/CodeGen/AVR/add.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/add.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/add.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/add.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,93 @@
+; RUN: llc -mattr=addsubiw < %s -march=avr | FileCheck %s
+
+define i8 @add8_reg_reg(i8 %a, i8 %b) {
+; CHECK-LABEL: add8_reg_reg:
+; CHECK: add r24, r22
+    %result = add i8 %a, %b
+    ret i8 %result
+}
+
+define i8 @add8_reg_imm(i8 %a) {
+; CHECK-LABEL: add8_reg_imm:
+; CHECK: subi r24, -5
+    %result = add i8 %a, 5
+    ret i8 %result
+}
+
+define i8 @add8_reg_increment(i8 %a) {
+; CHECK-LABEL: add8_reg_increment:
+; CHECK: inc r24
+    %result = add i8 %a, 1
+    ret i8 %result
+}
+
+
+define i16 @add16_reg_reg(i16 %a, i16 %b) {
+; CHECK-LABEL: add16_reg_reg:
+; CHECK: add r24, r22
+; CHECK: adc r25, r23
+    %result = add i16 %a, %b
+    ret i16 %result
+}
+
+define i16 @add16_reg_imm(i16 %a) {
+; CHECK-LABEL: add16_reg_imm:
+; CHECK: adiw r24, 63
+    %result = add i16 %a, 63
+    ret i16 %result
+}
+
+define i16 @add16_reg_imm_subi(i16 %a) {
+; CHECK-LABEL: add16_reg_imm_subi:
+; CHECK: subi r24, 133
+; CHECK: sbci r25, 255
+    %result = add i16 %a, 123
+    ret i16 %result
+}
+
+define i32 @add32_reg_reg(i32 %a, i32 %b) {
+; CHECK-LABEL: add32_reg_reg:
+; CHECK: add r22, r18
+; CHECK: adc r23, r19
+; CHECK: adc r24, r20
+; CHECK: adc r25, r21
+    %result = add i32 %a, %b
+    ret i32 %result
+}
+
+define i32 @add32_reg_imm(i32 %a) {
+; CHECK-LABEL: add32_reg_imm:
+; CHECK: subi r22, 251
+; CHECK: sbci r23, 255
+; CHECK: sbci r24, 255
+; CHECK: sbci r25, 255
+    %result = add i32 %a, 5
+    ret i32 %result
+}
+
+define i64 @add64_reg_reg(i64 %a, i64 %b) {
+; CHECK-LABEL: add64_reg_reg:
+; CHECK: add r18, r10
+; CHECK: adc r20, r12
+; CHECK: adc r21, r13
+; CHECK: adc r22, r14
+; CHECK: adc r23, r15
+; CHECK: adc r24, r16
+; CHECK: adc r25, r17
+    %result = add i64 %a, %b
+    ret i64 %result
+}
+
+define i64 @add64_reg_imm(i64 %a) {
+; CHECK-LABEL: add64_reg_imm:
+; CHECK: subi r18, 251
+; CHECK: sbci r19, 255
+; CHECK: sbci r20, 255
+; CHECK: sbci r21, 255
+; CHECK: sbci r22, 255
+; CHECK: sbci r23, 255
+; CHECK: sbci r24, 255
+; CHECK: sbci r25, 255
+    %result = add i64 %a, 5
+    ret i64 %result
+}

Added: llvm/trunk/test/CodeGen/AVR/alloca.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/alloca.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/alloca.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/alloca.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,84 @@
+; RUN: llc < %s -march=avr -mattr=avr6 | FileCheck %s
+
+declare i16 @allocate(i16*, i16*)
+
+; Test taking an address of an alloca with a small offset (adiw)
+define i16 @alloca_addressof_small() {
+entry:
+; CHECK-LABEL: alloca_addressof_small:
+; Test that Y is saved
+; CHECK: push r28
+; CHECK: push r29
+; CHECK: movw r24, r28
+; CHECK: adiw r24, 17
+; CHECK: movw {{.*}}, r28
+; CHECK: adiw {{.*}}, 39
+; CHECK: movw r22, {{.*}}
+; CHECK: pop r29
+; CHECK: pop r28
+  %p = alloca [18 x i16]
+  %k = alloca [14 x i16]
+  %arrayidx = getelementptr inbounds [14 x i16], [14 x i16]* %k, i16 0, i16 8
+  %arrayidx1 = getelementptr inbounds [18 x i16], [18 x i16]* %p, i16 0, i16 5
+  %call = call i16 @allocate(i16* %arrayidx, i16* %arrayidx1)
+  ret i16 %call
+}
+
+; Test taking an address of an alloca with a big offset (subi/sbci pair)
+define i16 @alloca_addressof_big() {
+entry:
+; CHECK-LABEL: alloca_addressof_big:
+; CHECK: movw r24, r28
+; CHECK: adiw r24, 17
+; CHECK: movw r22, r28
+; CHECK: subi r22, 145
+; CHECK: sbci r23, 255
+  %p = alloca [55 x i16]
+  %k = alloca [14 x i16]
+  %arrayidx = getelementptr inbounds [14 x i16], [14 x i16]* %k, i16 0, i16 8
+  %arrayidx1 = getelementptr inbounds [55 x i16], [55 x i16]* %p, i16 0, i16 41
+  %call = call i16 @allocate(i16* %arrayidx, i16* %arrayidx1)
+  ret i16 %call
+}
+
+; Test writing to an allocated variable with a small and a big offset
+define i16 @alloca_write(i16 %x) {
+entry:
+; CHECK-LABEL: alloca_write:
+; Big offset here
+; CHECK: adiw r28, 57
+; CHECK: std Y+62, {{.*}}
+; CHECK: std Y+63, {{.*}}
+; CHECK: sbiw r28, 57
+; Small offset here
+; CHECK: std Y+23, {{.*}}
+; CHECK: std Y+24, {{.*}}
+  %p = alloca [15 x i16]
+  %k = alloca [14 x i16]
+  %arrayidx = getelementptr inbounds [15 x i16], [15 x i16]* %p, i16 0, i16 45
+  store i16 22, i16* %arrayidx
+  %arrayidx1 = getelementptr inbounds [14 x i16], [14 x i16]* %k, i16 0, i16 11
+  store i16 42, i16* %arrayidx1
+  %arrayidx2 = getelementptr inbounds [14 x i16], [14 x i16]* %k, i16 0, i16 0
+  %arrayidx3 = getelementptr inbounds [15 x i16], [15 x i16]* %p, i16 0, i16 0
+  %call = call i16 @allocate(i16* %arrayidx2, i16* %arrayidx3)
+  ret i16 %call
+}
+
+; Test writing to an allocated variable with a huge offset that cant be
+; materialized with adiw/sbiw but with a subi/sbci pair.
+define void @alloca_write_huge() {
+; CHECK-LABEL: alloca_write_huge:
+; CHECK: subi r28, 41
+; CHECK: sbci r29, 255
+; CHECK: std Y+62, {{.*}}
+; CHECK: std Y+63, {{.*}}
+; CHECK: subi r28, 215
+; CHECK: sbci r29, 0
+  %k = alloca [140 x i16]
+  %arrayidx = getelementptr inbounds [140 x i16], [140 x i16]* %k, i16 0, i16 138
+  store i16 22, i16* %arrayidx
+  %arraydecay = getelementptr inbounds [140 x i16], [140 x i16]* %k, i16 0, i16 0
+  call i16 @allocate(i16* %arraydecay, i16* null)
+  ret void
+}

Added: llvm/trunk/test/CodeGen/AVR/and.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/and.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/and.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/and.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,80 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+define i8 @and8_reg_reg(i8 %a, i8 %b) {
+; CHECK-LABEL: and8_reg_reg:
+; CHECK: and r24, r22
+    %result = and i8 %a, %b
+    ret i8 %result
+}
+
+define i8 @and8_reg_imm(i8 %a) {
+; CHECK-LABEL: and8_reg_imm:
+; CHECK: andi r24, 5
+    %result = and i8 %a, 5
+    ret i8 %result
+}
+
+define i16 @and16_reg_reg(i16 %a, i16 %b) {
+; CHECK-LABEL: and16_reg_reg:
+; CHECK: and r24, r22
+; CHECK: and r25, r23
+    %result = and i16 %a, %b
+    ret i16 %result
+}
+
+define i16 @and16_reg_imm(i16 %a) {
+; CHECK-LABEL: and16_reg_imm:
+; CHECK: andi r24, 210
+; CHECK: andi r25, 4
+    %result = and i16 %a, 1234
+    ret i16 %result
+}
+
+define i32 @and32_reg_reg(i32 %a, i32 %b) {
+; CHECK-LABEL: and32_reg_reg:
+; CHECK: and r22, r18
+; CHECK: and r23, r19
+; CHECK: and r24, r20
+; CHECK: and r25, r21
+    %result = and i32 %a, %b
+    ret i32 %result
+}
+
+define i32 @and32_reg_imm(i32 %a) {
+; CHECK-LABEL: and32_reg_imm:
+; CHECK: andi r22, 21
+; CHECK: andi r23, 205
+; CHECK: andi r24, 91
+; CHECK: andi r25, 7
+    %result = and i32 %a, 123456789
+    ret i32 %result
+}
+
+define i64 @and64_reg_reg(i64 %a, i64 %b) {
+; CHECK-LABEL: and64_reg_reg:
+; CHECK: and r18, r10
+; CHECK: and r19, r11
+; CHECK: and r20, r12
+; CHECK: and r21, r13
+; CHECK: and r22, r14
+; CHECK: and r23, r15
+; CHECK: and r24, r16
+; CHECK: and r25, r17
+    %result = and i64 %a, %b
+    ret i64 %result
+}
+
+define i64 @and64_reg_imm(i64 %a) {
+; CHECK-LABEL: and64_reg_imm:
+; CHECK: andi r18, 253
+; CHECK: andi r19, 255
+; CHECK: andi r20, 155
+; CHECK: andi r21, 88
+; CHECK: andi r22, 76
+; CHECK: andi r23, 73
+; CHECK: andi r24, 31
+; CHECK: andi r25, 242
+    %result = and i64 %a, 17446744073709551613
+    ret i64 %result
+}
+

Added: llvm/trunk/test/CodeGen/AVR/atomics/fence.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/atomics/fence.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/atomics/fence.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/atomics/fence.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,13 @@
+; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s
+
+; Checks that atomic fences are simply removed from IR.
+; AVR is always singlethreaded so fences do nothing.
+
+; CHECK_LABEL: atomic_fence8
+; CHECK:      ; BB#0:
+; CHECK-NEXT:   ret
+define void @atomic_fence8() {
+  fence acquire
+  ret void
+}
+

Added: llvm/trunk/test/CodeGen/AVR/atomics/load16.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/atomics/load16.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/atomics/load16.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/atomics/load16.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,137 @@
+; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s
+
+; CHECK-LABEL: atomic_load16
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RR:r[0-9]+]], [[RD:(X|Y|Z)]]
+; CHECK-NEXT: ldd [[RR:r[0-9]+]], [[RD:(X|Y|Z)]]+
+; CHECK-NEXT: out 63, r0
+define i16 @atomic_load16(i16* %foo) {
+  %val = load atomic i16, i16* %foo unordered, align 2
+  ret i16 %val
+}
+
+; CHECK-LABEL: atomic_load_swap16
+; CHECK: call __sync_lock_test_and_set_2
+define i16 @atomic_load_swap16(i16* %foo) {
+  %val = atomicrmw xchg i16* %foo, i16 13 seq_cst
+  ret i16 %val
+}
+
+; CHECK-LABEL: atomic_load_cmp_swap16
+; CHECK: call __sync_val_compare_and_swap_2
+define i16 @atomic_load_cmp_swap16(i16* %foo) {
+  %val = cmpxchg i16* %foo, i16 5, i16 10 acq_rel monotonic
+  %value_loaded = extractvalue { i16, i1 } %val, 0
+  ret i16 %value_loaded
+}
+
+; CHECK-LABEL: atomic_load_add16
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]]
+; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+
+; CHECK-NEXT: add [[RR1]], [[TMP:r[0-9]+]]
+; CHECK-NEXT: adc [[RR2]], [[TMP:r[0-9]+]]
+; CHECK-NEXT: st [[RD1]], [[RR1]]
+; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]]
+; CHECK-NEXT: out 63, r0
+define i16 @atomic_load_add16(i16* %foo) {
+  %val = atomicrmw add i16* %foo, i16 13 seq_cst
+  ret i16 %val
+}
+
+; CHECK-LABEL: atomic_load_sub16
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]]
+; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+
+; CHECK-NEXT: sub [[RR1]], [[TMP:r[0-9]+]]
+; CHECK-NEXT: sbc [[RR2]], [[TMP:r[0-9]+]]
+; CHECK-NEXT: st [[RD1]], [[RR1]]
+; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]]
+; CHECK-NEXT: out 63, r0
+define i16 @atomic_load_sub16(i16* %foo) {
+  %val = atomicrmw sub i16* %foo, i16 13 seq_cst
+  ret i16 %val
+}
+
+; CHECK-LABEL: atomic_load_and16
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]]
+; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+
+; CHECK-NEXT: and [[RR1]], [[TMP:r[0-9]+]]
+; CHECK-NEXT: and [[RR2]], [[TMP:r[0-9]+]]
+; CHECK-NEXT: st [[RD1]], [[RR1]]
+; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]]
+; CHECK-NEXT: out 63, r0
+define i16 @atomic_load_and16(i16* %foo) {
+  %val = atomicrmw and i16* %foo, i16 13 seq_cst
+  ret i16 %val
+}
+
+; CHECK-LABEL: atomic_load_or16
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]]
+; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+
+; CHECK-NEXT: or [[RR1]], [[TMP:r[0-9]+]]
+; CHECK-NEXT: or [[RR2]], [[TMP:r[0-9]+]]
+; CHECK-NEXT: st [[RD1]], [[RR1]]
+; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]]
+; CHECK-NEXT: out 63, r0
+define i16 @atomic_load_or16(i16* %foo) {
+  %val = atomicrmw or i16* %foo, i16 13 seq_cst
+  ret i16 %val
+}
+
+; CHECK-LABEL: atomic_load_xor16
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]]
+; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+
+; CHECK-NEXT: eor [[RR1]], [[TMP:r[0-9]+]]
+; CHECK-NEXT: eor [[RR2]], [[TMP:r[0-9]+]]
+; CHECK-NEXT: st [[RD1]], [[RR1]]
+; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]]
+; CHECK-NEXT: out 63, r0
+define i16 @atomic_load_xor16(i16* %foo) {
+  %val = atomicrmw xor i16* %foo, i16 13 seq_cst
+  ret i16 %val
+}
+
+; CHECK-LABEL: atomic_load_nand16
+; CHECK: call __sync_fetch_and_nand_2
+define i16 @atomic_load_nand16(i16* %foo) {
+  %val = atomicrmw nand i16* %foo, i16 13 seq_cst
+  ret i16 %val
+}
+
+; CHECK-LABEL: atomic_load_max16
+; CHECK: call __sync_fetch_and_max_2
+define i16 @atomic_load_max16(i16* %foo) {
+  %val = atomicrmw max i16* %foo, i16 13 seq_cst
+  ret i16 %val
+}
+
+; CHECK-LABEL: atomic_load_min16
+; CHECK: call __sync_fetch_and_min_2
+define i16 @atomic_load_min16(i16* %foo) {
+  %val = atomicrmw min i16* %foo, i16 13 seq_cst
+  ret i16 %val
+}
+
+; CHECK-LABEL: atomic_load_umax16
+; CHECK: call __sync_fetch_and_umax_2
+define i16 @atomic_load_umax16(i16* %foo) {
+  %val = atomicrmw umax i16* %foo, i16 13 seq_cst
+  ret i16 %val
+}
+
+; CHECK-LABEL: atomic_load_umin16
+; CHECK: call __sync_fetch_and_umin_2
+define i16 @atomic_load_umin16(i16* %foo) {
+  %val = atomicrmw umin i16* %foo, i16 13 seq_cst
+  ret i16 %val
+}

Added: llvm/trunk/test/CodeGen/AVR/atomics/load8.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/atomics/load8.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/atomics/load8.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/atomics/load8.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,124 @@
+; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s
+
+; Tests atomic operations on AVR
+
+; CHECK-LABEL: atomic_load8
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RR:r[0-9]+]], [[RD:(X|Y|Z)]]
+; CHECK-NEXT: out 63, r0
+define i8 @atomic_load8(i8* %foo) {
+  %val = load atomic i8, i8* %foo unordered, align 1
+  ret i8 %val
+}
+
+; CHECK-LABEL: atomic_load_swap8
+; CHECK: call __sync_lock_test_and_set_1
+define i8 @atomic_load_swap8(i8* %foo) {
+  %val = atomicrmw xchg i8* %foo, i8 13 seq_cst
+  ret i8 %val
+}
+
+; CHECK-LABEL: atomic_load_cmp_swap8
+; CHECK: call __sync_val_compare_and_swap_1
+define i8 @atomic_load_cmp_swap8(i8* %foo) {
+  %val = cmpxchg i8* %foo, i8 5, i8 10 acq_rel monotonic
+  %value_loaded = extractvalue { i8, i1 } %val, 0
+  ret i8 %value_loaded
+}
+
+; CHECK-LABEL: atomic_load_add8
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]]
+; CHECK-NEXT: add [[RD]], [[RR1:r[0-9]+]]
+; CHECK-NEXT: st [[RR]], [[RD]]
+; CHECK-NEXT: out 63, r0
+define i8 @atomic_load_add8(i8* %foo) {
+  %val = atomicrmw add i8* %foo, i8 13 seq_cst
+  ret i8 %val
+}
+
+; CHECK-LABEL: atomic_load_sub8
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]]
+; CHECK-NEXT: sub [[RD]], [[RR1:r[0-9]+]]
+; CHECK-NEXT: st [[RR]], [[RD]]
+; CHECK-NEXT: out 63, r0
+define i8 @atomic_load_sub8(i8* %foo) {
+  %val = atomicrmw sub i8* %foo, i8 13 seq_cst
+  ret i8 %val
+}
+
+; CHECK-LABEL: atomic_load_and8
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]]
+; CHECK-NEXT: and [[RD]], [[RR1:r[0-9]+]]
+; CHECK-NEXT: st [[RR]], [[RD]]
+; CHECK-NEXT: out 63, r0
+define i8 @atomic_load_and8(i8* %foo) {
+  %val = atomicrmw and i8* %foo, i8 13 seq_cst
+  ret i8 %val
+}
+
+; CHECK-LABEL: atomic_load_or8
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]]
+; CHECK-NEXT: or [[RD]], [[RR1:r[0-9]+]]
+; CHECK-NEXT: st [[RR]], [[RD]]
+; CHECK-NEXT: out 63, r0
+define i8 @atomic_load_or8(i8* %foo) {
+  %val = atomicrmw or i8* %foo, i8 13 seq_cst
+  ret i8 %val
+}
+
+; CHECK-LABEL: atomic_load_xor8
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]]
+; CHECK-NEXT: eor [[RD]], [[RR1:r[0-9]+]]
+; CHECK-NEXT: st [[RR]], [[RD]]
+; CHECK-NEXT: out 63, r0
+define i8 @atomic_load_xor8(i8* %foo) {
+  %val = atomicrmw xor i8* %foo, i8 13 seq_cst
+  ret i8 %val
+}
+
+; CHECK-LABEL: atomic_load_nand8
+; CHECK: call __sync_fetch_and_nand_1
+define i8 @atomic_load_nand8(i8* %foo) {
+  %val = atomicrmw nand i8* %foo, i8 13 seq_cst
+  ret i8 %val
+}
+
+; CHECK-LABEL: atomic_load_max8
+; CHECK: call __sync_fetch_and_max_1
+define i8 @atomic_load_max8(i8* %foo) {
+  %val = atomicrmw max i8* %foo, i8 13 seq_cst
+  ret i8 %val
+}
+
+; CHECK-LABEL: atomic_load_min8
+; CHECK: call __sync_fetch_and_min_1
+define i8 @atomic_load_min8(i8* %foo) {
+  %val = atomicrmw min i8* %foo, i8 13 seq_cst
+  ret i8 %val
+}
+
+; CHECK-LABEL: atomic_load_umax8
+; CHECK: call __sync_fetch_and_umax_1
+define i8 @atomic_load_umax8(i8* %foo) {
+  %val = atomicrmw umax i8* %foo, i8 13 seq_cst
+  ret i8 %val
+}
+
+; CHECK-LABEL: atomic_load_umin8
+; CHECK: call __sync_fetch_and_umin_1
+define i8 @atomic_load_umin8(i8* %foo) {
+  %val = atomicrmw umin i8* %foo, i8 13 seq_cst
+  ret i8 %val
+}
+

Added: llvm/trunk/test/CodeGen/AVR/atomics/store.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/atomics/store.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/atomics/store.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/atomics/store.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,37 @@
+; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s
+
+; CHECK-LABEL: atomic_store8
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]]
+; CHECK-NEXT: out 63, r0
+define void @atomic_store8(i8* %foo) {
+  store atomic i8 1, i8* %foo unordered, align 1
+  ret void
+}
+
+; CHECK-LABEL: atomic_store16
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]]
+; CHECK-NEXT: std [[RD]]+1, [[RR:r[0-9]+]]
+; CHECK-NEXT: out 63, r0
+define void @atomic_store16(i16* %foo) {
+  store atomic i16 1, i16* %foo unordered, align 2
+  ret void
+}
+
+; CHECK-LABEL: atomic_store32
+; CHECK: call __sync_lock_test_and_set_4
+define void @atomic_store32(i32* %foo) {
+  store atomic i32 1, i32* %foo unordered, align 4
+  ret void
+}
+
+; CHECK-LABEL: atomic_store64
+; CHECK: call __sync_lock_test_and_set_8
+define void @atomic_store64(i64* %foo) {
+  store atomic i64 1, i64* %foo unordered, align 8
+  ret void
+}
+

Added: llvm/trunk/test/CodeGen/AVR/atomics/store16.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/atomics/store16.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/atomics/store16.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/atomics/store16.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,25 @@
+; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s
+
+; CHECK-LABEL: atomic_store16
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]]
+; CHECK-NEXT: std [[RD:(X|Y|Z)]]+1, [[RR:r[0-9]+]]
+; CHECK-NEXT: out 63, r0
+define void @atomic_store16(i16* %foo) {
+  store atomic i16 1, i16* %foo unordered, align 2
+  ret void
+}
+
+; CHECK-LABEL: monotonic
+; CHECK:      in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: st Z, r24
+; CHECK-NEXT: std Z+1, r25
+; CHECK-NEXT: out 63, r0
+define void @monotonic(i16) {
+entry-block:
+  store atomic i16 %0, i16* undef monotonic, align 2
+  ret void
+}
+

Added: llvm/trunk/test/CodeGen/AVR/brind.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/brind.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/brind.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/brind.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,20 @@
+; RUN: llc -mattr=sram,eijmpcall < %s -march=avr | FileCheck %s
+
+ at 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
+}

Added: llvm/trunk/test/CodeGen/AVR/call.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/call.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/call.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/call.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,211 @@
+; RUN: llc < %s -march=avr -mattr=avr6 | FileCheck %s
+
+; TODO: test returning byval structs
+
+declare i8 @foo8_1(i8)
+declare i8 @foo8_2(i8, i8, i8)
+declare i8 @foo8_3(i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8)
+
+declare i16 @foo16_1(i16, i16)
+declare i16 @foo16_2(i16, i16, i16, i16, i16, i16, i16, i16, i16, i16, i16)
+
+declare i32 @foo32_1(i32, i32)
+declare i32 @foo32_2(i32, i32, i32, i32, i32)
+
+declare i64 @foo64_1(i64)
+declare i64 @foo64_2(i64, i64, i64)
+
+define i8 @calli8_reg() {
+; CHECK-LABEL: calli8_reg:
+; CHECK: ldi r24, 12
+; CHECK: call foo8_1
+; CHECK: ldi r24, 12
+; CHECK: ldi r22, 13
+; CHECK: ldi r20, 14
+; CHECK: call foo8_2
+    %result1 = call i8 @foo8_1(i8 12)
+    %result2 = call i8 @foo8_2(i8 12, i8 13, i8 14)
+    ret i8 %result2
+}
+
+define i8 @calli8_stack() {
+; CHECK-LABEL: calli8_stack:
+; CHECK: ldi [[REG1:r[0-9]+]], 11
+; CHECK: push [[REG1]]
+; CHECK: ldi [[REG1]], 10
+; CHECK: push [[REG1]]
+; CHECK: call foo8_3
+    %result1 = call i8 @foo8_3(i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9, i8 10, i8 11)
+    ret i8 %result1
+}
+
+define i16 @calli16_reg() {
+; CHECK-LABEL: calli16_reg:
+; CHECK: ldi r24, 1
+; CHECK: ldi r25, 2
+; CHECK: ldi r22, 2
+; CHECK: ldi r23, 2
+; CHECK: call foo16_1
+    %result1 = call i16 @foo16_1(i16 513, i16 514)
+    ret i16 %result1
+}
+
+define i16 @calli16_stack() {
+; CHECK-LABEL: calli16_stack:
+; CHECK: ldi [[REG1:r[0-9]+]], 10
+; CHECK: ldi [[REG2:r[0-9]+]], 2
+; CHECK: push [[REG2]]
+; CHECK: push [[REG1]]
+; CHECK: ldi [[REG1:r[0-9]+]], 9
+; CHECK: ldi [[REG2:r[0-9]+]], 2 
+; CHECK: push [[REG2]]
+; CHECK: push [[REG1]]
+; CHECK: call foo16_2
+    %result1 = call i16 @foo16_2(i16 512, i16 513, i16 514, i16 515, i16 516, i16 517, i16 518, i16 519, i16 520, i16 521, i16 522)
+    ret i16 %result1
+}
+
+define i32 @calli32_reg() {
+; CHECK-LABEL: calli32_reg:
+; CHECK: ldi r22, 64
+; CHECK: ldi r23, 66
+; CHECK: ldi r24, 15
+; CHECK: ldi r25, 2
+; CHECK: ldi r18, 128
+; CHECK: ldi r19, 132
+; CHECK: ldi r20, 30
+; CHECK: ldi r21, 2
+; CHECK: call foo32_1
+    %result1 = call i32 @foo32_1(i32 34554432, i32 35554432)
+    ret i32 %result1
+}
+
+define i32 @calli32_stack() {
+; CHECK-LABEL: calli32_stack:
+; CHECK: ldi [[REG1:r[0-9]+]], 15
+; CHECK: ldi [[REG2:r[0-9]+]], 2
+; CHECK: push [[REG2]]
+; CHECK: push [[REG1]]
+; CHECK: ldi [[REG1:r[0-9]+]], 64
+; CHECK: ldi [[REG2:r[0-9]+]], 66
+; CHECK: push [[REG2]]
+; CHECK: push [[REG1]]
+; CHECK: call foo32_2
+    %result1 = call i32 @foo32_2(i32 1, i32 2, i32 3, i32 4, i32 34554432)
+    ret i32 %result1
+}
+
+define i64 @calli64_reg() {
+; CHECK-LABEL: calli64_reg:
+; CHECK: ldi r18, 255
+; CHECK: ldi r19, 255
+; CHECK: ldi r20, 155
+; CHECK: ldi r21, 88
+; CHECK: ldi r22, 76
+; CHECK: ldi r23, 73
+; CHECK: ldi r24, 31
+; CHECK: ldi r25, 242
+; CHECK: call foo64_1
+    %result1 = call i64 @foo64_1(i64 17446744073709551615)
+    ret i64 %result1
+}
+
+define i64 @calli64_stack() {
+; CHECK-LABEL: calli64_stack:
+; CHECK: ldi [[REG1:r[0-9]+]], 31
+; CHECK: ldi [[REG2:r[0-9]+]], 242
+; CHECK: push [[REG2]]
+; CHECK: push [[REG1]]
+; CHECK: ldi [[REG1:r[0-9]+]], 76
+; CHECK: ldi [[REG2:r[0-9]+]], 73
+; CHECK: push [[REG2]]
+; CHECK: push [[REG1]]
+; CHECK: ldi [[REG1:r[0-9]+]], 155
+; CHECK: ldi [[REG2:r[0-9]+]], 88
+; CHECK: push [[REG2]]
+; CHECK: push [[REG1]]
+; CHECK: ldi [[REG1:r[0-9]+]], 255
+; CHECK: ldi [[REG2:r[0-9]+]], 255
+; CHECK: push [[REG2]]
+; CHECK: push [[REG1]]
+; CHECK: call foo64_2
+    %result1 = call i64 @foo64_2(i64 1, i64 2, i64 17446744073709551615)
+    ret i64 %result1
+}
+
+; Test passing arguments through the stack when the call frame is allocated
+; in the prologue.
+declare void @foo64_3(i64, i64, i64, i8, i16*)
+
+define void @testcallprologue() {
+; CHECK-LABEL: testcallprologue:
+; CHECK: push r28
+; CHECK: push r29
+; CHECK: sbiw r28, 28
+; CHECK: ldi [[REG1:r[0-9]+]], 88
+; CHECK: std Y+9, [[REG1]]
+; CHECK: ldi [[REG1:r[0-9]+]], 11
+; CHECK: ldi [[REG2:r[0-9]+]], 10
+; CHECK: std Y+7, [[REG1]]
+; CHECK: std Y+8, [[REG2]]
+; CHECK: ldi [[REG1:r[0-9]+]], 13
+; CHECK: ldi [[REG2:r[0-9]+]], 12
+; CHECK: std Y+5, [[REG1]]
+; CHECK: std Y+6, [[REG2]]
+; CHECK: ldi [[REG1:r[0-9]+]], 15
+; CHECK: ldi [[REG2:r[0-9]+]], 14
+; CHECK: std Y+3, [[REG1]]
+; CHECK: std Y+4, [[REG2]]
+; CHECK: ldi [[REG1:r[0-9]+]], 8
+; CHECK: ldi [[REG2:r[0-9]+]], 9
+; CHECK: std Y+1, [[REG1]]
+; CHECK: std Y+2, [[REG2]]
+; CHECK: pop r29
+; CHECK: pop r28
+  %p = alloca [8 x i16]
+  %arraydecay = getelementptr inbounds [8 x i16], [8 x i16]* %p, i16 0, i16 0
+  call void @foo64_3(i64 723685415333071112, i64 723685415333071112, i64 723685415333071112, i8 88, i16* %arraydecay)
+  ret void
+}
+
+define i32 @icall(i32 (i32)* %foo) {
+; CHECK-LABEL: icall:
+; CHECK: movw [[REG:r[0-9]+]], r24
+; CHECK: ldi r22, 147
+; CHECK: ldi r23, 248
+; CHECK: ldi r24, 214
+; CHECK: ldi r25, 198
+; CHECK: movw r30, [[REG]]
+; CHECK: icall
+; CHECK: subi r22, 251
+; CHECK: sbci r23, 255
+; CHECK: sbci r24, 255
+; CHECK: sbci r25, 255
+  %1 = call i32 %foo(i32 3335977107)
+  %2 = add nsw i32 %1, 5
+  ret i32 %2
+}
+
+; Calling external functions (like __divsf3) require extra processing for
+; arguments and return values in the LowerCall function.
+declare i32 @foofloat(float)
+
+define i32 @externcall(float %a, float %b) {
+; CHECK-LABEL: externcall:
+; CHECK: movw [[REG1:(r[0-9]+|[XYZ])]], r24
+; CHECK: movw [[REG2:(r[0-9]+|[XYZ])]], r22
+; CHECK: movw r22, r18
+; CHECK: movw r24, r20
+; CHECK: movw r18, [[REG2]]
+; CHECK: movw r20, [[REG1]]
+; CHECK: call __divsf3
+; CHECK: call foofloat
+; CHECK: subi r22, 251
+; CHECK: sbci r23, 255
+; CHECK: sbci r24, 255
+; CHECK: sbci r25, 255
+  %1 = fdiv float %b, %a
+  %2 = call i32 @foofloat(float %1)
+  %3 = add nsw i32 %2, 5
+  ret i32 %3
+}

Added: llvm/trunk/test/CodeGen/AVR/cmp.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/cmp.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/cmp.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/cmp.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,148 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+declare void @f1(i8)
+declare void @f2(i8)
+define void @cmp8(i8 %a, i8 %b) {
+; CHECK-LABEL: cmp8:
+; CHECK: cp
+; CHECK-NOT: cpc
+  %cmp = icmp eq i8 %a, %b
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  tail call void @f1(i8 %a)
+  br label %if.end
+if.else:
+  tail call void @f2(i8 %b)
+  br label %if.end
+if.end:
+  ret void
+}
+
+declare void @f3(i16)
+declare void @f4(i16)
+define void @cmp16(i16 %a, i16 %b) {
+; CHECK-LABEL: cmp16:
+; CHECK: cp
+; CHECK-NEXT: cpc
+  %cmp = icmp eq i16 %a, %b
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  tail call void @f3(i16 %a)
+  br label %if.end
+if.else:
+  tail call void @f4(i16 %b)
+  br label %if.end
+if.end:
+  ret void
+}
+
+declare void @f5(i32)
+declare void @f6(i32)
+define void @cmp32(i32 %a, i32 %b) {
+; CHECK-LABEL: cmp32:
+; CHECK: cp
+; CHECK-NEXT: cpc
+; CHECK-NEXT: cpc
+; CHECK-NEXT: cpc
+  %cmp = icmp eq i32 %a, %b
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  tail call void @f5(i32 %a)
+  br label %if.end
+if.else:
+  tail call void @f6(i32 %b)
+  br label %if.end
+if.end:
+  ret void
+}
+
+declare void @f7(i64)
+declare void @f8(i64)
+define void @cmp64(i64 %a, i64 %b) {
+; CHECK-LABEL: cmp64:
+; CHECK: cp
+; CHECK-NEXT: cpc
+; CHECK-NEXT: cpc
+; CHECK-NEXT: cpc
+; CHECK-NEXT: cpc
+; CHECK-NEXT: cpc
+; CHECK-NEXT: cpc
+; CHECK-NEXT: cpc
+  %cmp = icmp eq i64 %a, %b
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  tail call void @f7(i64 %a)
+  br label %if.end
+if.else:
+  tail call void @f8(i64 %b)
+  br label %if.end
+if.end:
+  ret void
+}
+
+declare void @f9()
+declare void @f10()
+
+define void @tst8(i8 %a) {
+; CHECK-LABEL: tst8:
+; CHECK: tst r24
+; CHECK-NEXT: brmi
+  %cmp = icmp sgt i8 %a, -1
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  tail call void @f9()
+  br label %if.end
+if.else:
+  tail call void @f10()
+  br label %if.end
+if.end:
+  ret void
+}
+
+define void @tst16(i16 %a) {
+; CHECK-LABEL: tst16:
+; CHECK: tst r25
+; CHECK-NEXT: brmi
+  %cmp = icmp sgt i16 %a, -1
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  tail call void @f9()
+  br label %if.end
+if.else:
+  tail call void @f10()
+  br label %if.end
+if.end:
+  ret void
+}
+
+define void @tst32(i32 %a) {
+; CHECK-LABEL: tst32:
+; CHECK: tst r25
+; CHECK-NEXT: brmi
+  %cmp = icmp sgt i32 %a, -1
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  tail call void @f9()
+  br label %if.end
+if.else:
+  tail call void @f10()
+  br label %if.end
+if.end:
+  ret void
+}
+
+define void @tst64(i64 %a) {
+; CHECK-LABEL: tst64:
+; CHECK: tst r25
+; CHECK-NEXT: brmi
+  %cmp = icmp sgt i64 %a, -1
+  br i1 %cmp, label %if.then, label %if.else
+if.then:
+  tail call void @f9()
+  br label %if.end
+if.else:
+  tail call void @f10()
+  br label %if.end
+if.end:
+  ret void
+}

Added: llvm/trunk/test/CodeGen/AVR/com.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/com.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/com.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/com.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,40 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+define i8 @com8(i8 %x) {
+; CHECK-LABEL: com8:
+; CHECK: com r24
+  %neg = xor i8 %x, -1
+  ret i8 %neg
+}
+
+define i16 @com16(i16 %x) {
+; CHECK-LABEL: com16:
+; CHECK: com r24
+; CHECK: com r25
+  %neg = xor i16 %x, -1
+  ret i16 %neg
+}
+
+define i32 @com32(i32 %x) {
+; CHECK-LABEL: com32:
+; CHECK: com r22
+; CHECK: com r23
+; CHECK: com r24
+; CHECK: com r25
+  %neg = xor i32 %x, -1
+  ret i32 %neg
+}
+
+define i64 @com64(i64 %x) {
+; CHECK-LABEL: com64:
+; CHECK: com r18
+; CHECK: com r19
+; CHECK: com r20
+; CHECK: com r21
+; CHECK: com r22
+; CHECK: com r23
+; CHECK: com r24
+; CHECK: com r25
+  %neg = xor i64 %x, -1
+  ret i64 %neg
+}

Added: llvm/trunk/test/CodeGen/AVR/directmem.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/directmem.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/directmem.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/directmem.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,345 @@
+; RUN: llc -mattr=sram,addsubiw < %s -march=avr | FileCheck %s
+
+ at char = common global i8 0
+ at char.array = common global [3 x i8] zeroinitializer
+ at char.static = internal global i8 0
+
+ at int = common global i16 0
+ at int.array = common global [3 x i16] zeroinitializer
+ at int.static = internal global i16 0
+
+ at long = common global i32 0
+ at long.array = common global [3 x i32] zeroinitializer
+ at long.static = internal global i32 0
+
+ at longlong = common global i64 0
+ at longlong.array = common global [3 x i64] zeroinitializer
+ at 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
+}

Added: llvm/trunk/test/CodeGen/AVR/dynalloca.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/dynalloca.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/dynalloca.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/dynalloca.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,78 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+declare void @foo(i16*, i16*, i8*)
+
+define void @test1(i16 %x) {
+; CHECK-LABEL: test1:
+; CHECK: out 61, r28
+; SP copy
+; CHECK-NEXT: in [[SPCOPY1:r[0-9]+]], 61
+; CHECK-NEXT: in [[SPCOPY2:r[0-9]+]], 62
+; allocate first dynalloca
+; CHECK: in {{.*}}, 61
+; CHECK: in {{.*}}, 62
+; CHECK: sub
+; CHECK: sbc
+; CHECK: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, {{.*}}
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, {{.*}}
+; Test writes
+; CHECK: std Z+12, {{.*}}
+; CHECK: std Z+13, {{.*}}
+; CHECK: std Z+7, {{.*}}
+; CHECK-NOT: std
+; Test SP restore
+; CHECK: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, [[SPCOPY2]]
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, [[SPCOPY1]]
+  %a = alloca [8 x i16]
+  %vla = alloca i16, i16 %x
+  %add = shl nsw i16 %x, 1
+  %vla1 = alloca i8, i16 %add
+  %arrayidx = getelementptr inbounds [8 x i16], [8 x i16]* %a, i16 0, i16 2
+  store i16 3, i16* %arrayidx
+  %arrayidx2 = getelementptr inbounds i16, i16* %vla, i16 6
+  store i16 4, i16* %arrayidx2
+  %arrayidx3 = getelementptr inbounds i8, i8* %vla1, i16 7
+  store i8 44, i8* %arrayidx3
+  %arraydecay = getelementptr inbounds [8 x i16], [8 x i16]* %a, i16 0, i16 0
+  call void @foo(i16* %arraydecay, i16* %vla, i8* %vla1)
+  ret void
+}
+
+declare void @foo2(i16*, i64, i64, i64)
+
+; Test that arguments are passed through pushes into the call instead of
+; allocating the call frame space in the prologue. Also test that SP is restored
+; after the call frame is restored and not before.
+define void @dynalloca2(i16 %x) {
+; CHECK-LABEL: dynalloca2:
+; CHECK: in [[SPCOPY1:r[0-9]+]], 61
+; CHECK: in [[SPCOPY2:r[0-9]+]], 62
+; CHECK: push
+; CHECK-NOT: st
+; CHECK-NOT: std
+; CHECK: call
+; Call frame restore
+; CHECK-NEXT: in r30, 61
+; CHECK-NEXT: in r31, 62
+; CHECK-NEXT: adiw r30, 8
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r31
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r30
+; SP restore
+; CHECK: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r29
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r28
+  %vla = alloca i16, i16 %x
+  call void @foo2(i16* %vla, i64 0, i64 0, i64 0)
+  ret void
+}

Added: llvm/trunk/test/CodeGen/AVR/eor.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/eor.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/eor.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/eor.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,92 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+; Tests for the exclusive OR operation.
+
+define i8 @eor8_reg_reg(i8 %a, i8 %b) {
+; CHECK-LABEL: eor8_reg_reg:
+; CHECK: eor r24, r22
+    %result = xor i8 %a, %b
+    ret i8 %result
+}
+
+define i8 @eor8_reg_imm(i8 %a) {
+; CHECK-LABEL: eor8_reg_imm:
+; CHECK: ldi r25, 5
+; CHECK: eor r24, r25
+    %result = xor i8 %a, 5
+    ret i8 %result
+}
+
+define i16 @eor16_reg_reg(i16 %a, i16 %b) {
+; CHECK-LABEL: eor16_reg_reg:
+; CHECK: eor r24, r22
+; CHECK: eor r25, r23
+    %result = xor i16 %a, %b
+    ret i16 %result
+}
+
+define i16 @eor16_reg_imm(i16 %a) {
+; CHECK-LABEL: eor16_reg_imm:
+; CHECK: ldi r18, 210
+; CHECK: ldi r19, 4
+; CHECK: eor r24, r18
+; CHECK: eor r25, r19
+    %result = xor i16 %a, 1234
+    ret i16 %result
+}
+
+define i32 @eor32_reg_reg(i32 %a, i32 %b) {
+; CHECK-LABEL: eor32_reg_reg:
+; CHECK: eor r22, r18
+; CHECK: eor r23, r19
+; CHECK: eor r24, r20
+; CHECK: eor r25, r21
+    %result = xor i32 %a, %b
+    ret i32 %result
+}
+
+define i32 @eor32_reg_imm(i32 %a) {
+; CHECK-LABEL: eor32_reg_imm:
+; CHECK: ldi r18, 210
+; CHECK: ldi r19, 4
+; CHECK: eor r22, r18
+; CHECK: eor r23, r19
+    %result = xor i32 %a, 1234
+    ret i32 %result
+}
+
+define i64 @eor64_reg_reg(i64 %a, i64 %b) {
+; CHECK-LABEL: eor64_reg_reg:
+; CHECK: eor r18, r10
+; CHECK: eor r19, r11
+; CHECK: eor r20, r12
+; CHECK: eor r21, r13
+; CHECK: eor r22, r14
+; CHECK: eor r23, r15
+; CHECK: eor r24, r16
+; CHECK: eor r25, r17
+    %result = xor i64 %a, %b
+    ret i64 %result
+}
+
+define i64 @eor64_reg_imm(i64 %a) {
+; CHECK-LABEL: eor64_reg_imm:
+; CHECK: ldi r30, 253
+; CHECK: ldi r31, 255
+; CHECK: eor r18, r30
+; CHECK: eor r19, r31
+; CHECK: ldi r30, 155
+; CHECK: ldi r31, 88
+; CHECK: eor r20, r30
+; CHECK: eor r21, r31
+; CHECK: ldi r30, 76
+; CHECK: ldi r31, 73
+; CHECK: eor r22, r30
+; CHECK: eor r23, r31
+; CHECK: ldi r30, 31
+; CHECK: ldi r31, 242
+; CHECK: eor r24, r30
+; CHECK: eor r25, r31
+    %result = xor i64 %a, 17446744073709551613
+    ret i64 %result
+}

Added: llvm/trunk/test/CodeGen/AVR/error-srcreg-destreg-same.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/error-srcreg-destreg-same.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/error-srcreg-destreg-same.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/error-srcreg-destreg-same.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,56 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+; XFAIL: *
+
+; This occurs when compiling Rust libcore.
+;
+; Assertion failed:
+; (DstReg != SrcReg && "SrcReg and DstReg cannot be the same")
+;   lib/Target/AVR/AVRExpandPseudoInsts.cpp, line 817
+;
+; https://github.com/avr-llvm/llvm/issues/229
+
+; CHECK-LABEL: rust_eh_personality
+declare void @rust_eh_personality()
+
+; CHECK-LABEL: __udivmoddi4
+define void @__udivmoddi4(i64 %arg, i64 %arg1) personality i32 (...)* bitcast (void ()* @rust_eh_personality to i32 (...)*) {
+entry-block:
+  %tmp = lshr i64 %arg, 32
+  %tmp2 = trunc i64 %tmp to i32
+  %tmp3 = trunc i64 %arg to i32
+  %tmp4 = add i64 %arg1, -1
+  br label %bb135
+
+bb133.loopexit:
+  ret void
+
+bb135:
+  %carry.0120 = phi i64 [ 0, %entry-block ], [ %phitmp, %bb135 ]
+  %q.sroa.12.1119 = phi i32 [ %tmp3, %entry-block ], [ %q.sroa.12.0.extract.trunc, %bb135 ]
+  %q.sroa.0.1118 = phi i32 [ 0, %entry-block ], [ %q.sroa.0.0.extract.trunc, %bb135 ]
+  %r.sroa.0.1116 = phi i32 [ %tmp2, %entry-block ], [ undef, %bb135 ]
+  %r.sroa.0.0.insert.ext62 = zext i32 %r.sroa.0.1116 to i64
+  %r.sroa.0.0.insert.insert64 = or i64 0, %r.sroa.0.0.insert.ext62
+  %tmp5 = shl nuw nsw i64 %r.sroa.0.0.insert.ext62, 1
+  %q.sroa.12.0.insert.ext101 = zext i32 %q.sroa.12.1119 to i64
+  %q.sroa.12.0.insert.shift102 = shl nuw i64 %q.sroa.12.0.insert.ext101, 32
+  %q.sroa.0.0.insert.ext87 = zext i32 %q.sroa.0.1118 to i64
+  %q.sroa.0.0.insert.insert89 = or i64 %q.sroa.12.0.insert.shift102, %q.sroa.0.0.insert.ext87
+  %tmp6 = lshr i64 %q.sroa.12.0.insert.ext101, 31
+  %tmp7 = lshr i64 %r.sroa.0.0.insert.insert64, 31
+  %tmp8 = shl nuw nsw i64 %q.sroa.0.0.insert.ext87, 1
+  %tmp9 = or i64 %tmp8, %carry.0120
+  %q.sroa.0.0.extract.trunc = trunc i64 %tmp9 to i32
+  %tmp10 = lshr i64 %q.sroa.0.0.insert.insert89, 31
+  %q.sroa.12.0.extract.trunc = trunc i64 %tmp10 to i32
+  %r.sroa.13.0.insert.shift72 = shl i64 %tmp7, 32
+  %.masked114 = and i64 %tmp5, 4294967294
+  %r.sroa.0.0.insert.ext57 = or i64 %tmp6, %.masked114
+  %r.sroa.0.0.insert.insert59 = or i64 %r.sroa.0.0.insert.ext57, %r.sroa.13.0.insert.shift72
+  %tmp11 = sub i64 %tmp4, %r.sroa.0.0.insert.insert59
+  %tmp12 = ashr i64 %tmp11, 63
+  %phitmp = and i64 %tmp12, 1
+  %tmp13 = icmp ult i32 undef, 32
+  br i1 %tmp13, label %bb135, label %bb133.loopexit
+}
+

Added: llvm/trunk/test/CodeGen/AVR/expand-integer-failure.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/expand-integer-failure.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/expand-integer-failure.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/expand-integer-failure.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,23 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+; XFAIL: *
+
+; Causes an assertion error
+; Assertion failed: (Lo.getValueType() == TLI.getTypeToTransformTo(*DAG.getContext(), Op.getValueType()) &&
+;   Hi.getValueType() == Lo.getValueType() &&
+;   "Invalid type for expanded integer"),
+; function SetExpandedInteger
+; file lib/CodeGen/SelectionDAG/LegalizeTypes.cpp
+
+; CHECK-LABEL: foo
+define void @foo(i16 %a) {
+ifcont:
+  %cmp_result = icmp eq i16 %a, 255
+  %bool_result = uitofp i1 %cmp_result to double
+  %result = fcmp one double 0.000000e+00, %bool_result
+  br i1 %result, label %then, label %else
+then:
+  ret void
+else:
+  ret void
+}
+

Added: llvm/trunk/test/CodeGen/AVR/frame.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/frame.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/frame.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/frame.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,65 @@
+; RUN: llc -mattr=mul < %s -march=avr | FileCheck %s
+
+declare float @dsin(float)
+declare float @dcos(float)
+declare float @dasin(float)
+
+; Test prologue and epilogue insertion
+define float @f3(float %days) {
+entry:
+; CHECK-LABEL: f3:
+; prologue code:
+; CHECK: push r28
+; CHECK: push r29
+; CHECK: in r28, 61
+; CHECK-NEXT: in r29, 62
+; CHECK-NEXT: sbiw r28, [[SIZE:[0-9]+]]
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r29
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r28
+; epilogue code:
+; CHECK: adiw r28, [[SIZE]]
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r29
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r28
+; CHECK: pop r29
+; CHECK: pop r28
+  %mul = fmul float %days, 0x3FEF8A6C60000000
+  %add = fadd float %mul, 0x40718776A0000000
+  %mul1 = fmul float %days, 0x3FEF8A09A0000000
+  %add2 = fadd float %mul1, 0x4076587740000000
+  %mul3 = fmul float %days, 0x3E81B35CC0000000
+  %sub = fsub float 0x3FFEA235C0000000, %mul3
+  %call = call float @dsin(float %add2)
+  %mul4 = fmul float %sub, %call
+  %mul5 = fmul float %days, 0x3E27C04CA0000000
+  %sub6 = fsub float 0x3F94790B80000000, %mul5
+  %mul7 = fmul float %add2, 2.000000e+00
+  %call8 = call float @dsin(float %mul7)
+  %mul9 = fmul float %sub6, %call8
+  %add10 = fadd float %mul4, %mul9
+  %add11 = fadd float %add, %add10
+  %mul12 = fmul float %days, 0x3E13C5B640000000
+  %sub13 = fsub float 0x3F911C1180000000, %mul12
+  %mul14 = fmul float %add, 2.000000e+00
+  %call15 = call float @dsin(float %mul14)
+  %mul16 = fmul float %call15, 0x3FF1F736C0000000
+  %mul17 = fmul float %sub13, 2.000000e+00
+  %mul19 = fmul float %mul17, %call
+  %sub20 = fsub float %mul16, %mul19
+  %mul21 = fmul float %sub13, 4.000000e+00
+  %mul22 = fmul float %mul21, 0x3FF1F736C0000000
+  %mul24 = fmul float %mul22, %call
+  %call26 = call float @dcos(float %mul14)
+  %mul27 = fmul float %mul24, %call26
+  %add28 = fadd float %sub20, %mul27
+  %call29 = call float @dsin(float %add11)
+  %mul30 = fmul float %call29, 0x3FF0AB6960000000
+  %call31 = call float @dasin(float %mul30)
+  %add32 = fadd float %call31, %add28
+  ret float %add32
+}

Modified: llvm/trunk/test/CodeGen/AVR/high-pressure-on-ptrregs.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/high-pressure-on-ptrregs.ll?rev=287162&r1=287161&r2=287162&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/high-pressure-on-ptrregs.ll (original)
+++ llvm/trunk/test/CodeGen/AVR/high-pressure-on-ptrregs.ll Wed Nov 16 15:58:04 2016
@@ -1,4 +1,5 @@
 ; RUN: llc < %s -march=avr | FileCheck %s
+; XFAIL: *
 
 ; This tests how LLVM handles IR which puts very high
 ; presure on the PTRREGS class for the register allocator.

Added: llvm/trunk/test/CodeGen/AVR/inline-asm.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/inline-asm.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/inline-asm.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/inline-asm.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,337 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+; XFAIL: *
+
+;CHECK-LABEL: no_operands:
+define void @no_operands() {
+  ;CHECK: add r24, r22
+  call void asm sideeffect "add r24, r22", ""() nounwind
+  ret void
+}
+
+;CHECK-LABEL: input_operand:
+define void @input_operand(i8 %a) {
+  ;CHECK: add r24, r24
+  call void asm sideeffect "add $0, $0", "r"(i8 %a) nounwind
+  ret void
+}
+
+;CHECK-LABEL: simple_upper_regs:
+define void @simple_upper_regs(i8 %p0, i8 %p1, i8 %p2, i8 %p3,
+                               i8 %p4, i8 %p5, i8 %p6, i8 %p7) {
+  ;CHECK: some_instr r17, r22, r20, r18, r16, r19, r21, r23
+  call void asm sideeffect "some_instr $0, $1, $2, $3, $4, $5, $6, $7",
+                           "a,a,a,a,a,a,a,a" (i8 %p0, i8 %p1, i8 %p2, i8 %p3,
+                                              i8 %p4, i8 %p5, i8 %p6, i8 %p7) nounwind
+  ret void
+}
+
+;CHECK-LABEL: upper_regs:
+define void @upper_regs(i8 %p0) {
+  ;CHECK: some_instr r24
+  call void asm sideeffect "some_instr $0", "d" (i8 %p0) nounwind
+  ret void
+}
+
+;CHECK-LABEL: lower_regs:
+define void @lower_regs(i8 %p0) {
+  ;CHECK: some_instr r15
+  call void asm sideeffect "some_instr $0", "l" (i8 %p0) nounwind
+  ret void
+}
+
+;CHECK-LABEL: special_upper_regs:
+define void @special_upper_regs(i8 %p0, i8 %p1, i8 %p2, i8 %p3) {
+  ;CHECK: some_instr r24,r28,r26,r30
+  call void asm sideeffect "some_instr $0,$1,$2,$3", "w,w,w,w" (i8 %p0, i8 %p1, i8 %p2, i8 %p3) nounwind
+  ret void
+}
+
+;CHECK-LABEL: xyz_reg:
+define void @xyz_reg(i16 %var) {
+  ;CHECK: some_instr r26, r28, r30
+  call void asm sideeffect "some_instr $0, $1, $2", "x,y,z" (i16 %var, i16 %var, i16 %var) nounwind
+  ret void
+}
+
+;TODO
+; How to use SP reg properly in inline asm??
+; define void @sp_reg(i16 %var) 
+
+;CHECK-LABEL: ptr_reg:
+define void @ptr_reg(i16 %var0, i16 %var1, i16 %var2) {
+  ;CHECK: some_instr r28, r26, r30
+  call void asm sideeffect "some_instr $0, $1, $2", "e,e,e" (i16 %var0, i16 %var1, i16 %var2) nounwind
+  ret void
+}
+
+;CHECK-LABEL: base_ptr_reg:
+define void @base_ptr_reg(i16 %var0, i16 %var1) {
+  ;CHECK: some_instr r28, r30
+  call void asm sideeffect "some_instr $0, $1", "b,b" (i16 %var0, i16 %var1) nounwind
+  ret void
+}
+
+;CHECK-LABEL: input_output_operand:
+define i8 @input_output_operand(i8 %a, i8 %b) {
+  ;CHECK: add r24, r24
+  %1 = call i8 asm "add $0, $1", "=r,r"(i8 %a) nounwind
+  ret i8 %1
+}
+
+;CHECK-LABEL: temp_reg:
+define void @temp_reg(i8 %a) {
+  ;CHECK: some_instr r0
+  call void asm sideeffect "some_instr $0", "t" (i8 %a) nounwind
+  ret void
+}
+
+;CHECK-LABEL: int_0_63:
+define void @int_0_63() {
+  ;CHECK: some_instr 5
+  call void asm sideeffect "some_instr $0", "I" (i8 5) nounwind
+  ret void
+}
+
+;CHECK-LABEL: int_minus63_0:
+define void @int_minus63_0() {
+  ;CHECK: some_instr -5
+  call void asm sideeffect "some_instr $0", "J" (i8 -5) nounwind
+  ret void
+}
+
+;CHECK-LABEL: int_2_2:
+define void @int_2_2() {
+  ;CHECK: some_instr 2
+  call void asm sideeffect "some_instr $0", "K" (i8 2) nounwind
+  ret void
+}
+
+;CHECK-LABEL: int_0_0:
+define void @int_0_0() {
+  ;CHECK: some_instr 0
+  call void asm sideeffect "some_instr $0", "L" (i8 0) nounwind
+  ret void
+}
+
+;CHECK-LABEL: int_0_255:
+define void @int_0_255() {
+  ;CHECK: some_instr 254
+  call void asm sideeffect "some_instr $0", "M" (i8 254) nounwind
+  ret void
+}
+
+;CHECK-LABEL: int_minus1_minus1:
+define void @int_minus1_minus1() {
+  ;CHECK: some_instr -1
+  call void asm sideeffect "some_instr $0", "N" (i8 -1) nounwind
+  ret void
+}
+
+;CHECK-LABEL: int_8_or_16_or_24:
+define void @int_8_or_16_or_24() {
+  ;CHECK: some_instr 8, 16, 24
+  call void asm sideeffect "some_instr $0, $1, $2", "O,O,O" (i8 8, i8 16, i8 24) nounwind
+  ret void
+}
+
+;CHECK-LABEL: int_1_1:
+define void @int_1_1() {
+  ;CHECK: some_instr 1
+  call void asm sideeffect "some_instr $0", "P" (i8 1) nounwind
+  ret void
+}
+
+;CHECK-LABEL: int_minus6_5:
+define void @int_minus6_5() {
+  ;CHECK: some_instr -6
+  call void asm sideeffect "some_instr $0", "R" (i8 -6) nounwind
+  ret void
+}
+
+;CHECK-LABEL: float_0_0:
+define void @float_0_0() {
+  ;CHECK: some_instr 0
+  call void asm sideeffect "some_instr $0", "G" (float 0.0) nounwind
+  ret void
+}
+
+
+; Memory constraint
+
+ at a = internal global i16 0, align 4
+ at 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
+}
+
+ at 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
+}
+
+

Added: llvm/trunk/test/CodeGen/AVR/interrupts.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/interrupts.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/interrupts.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/interrupts.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,33 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+define avr_intrcc void @interrupt_handler() {
+; CHECK-LABEL: interrupt_handler:
+; CHECK: sei
+; CHECK-NEXT: push r0
+; CHECK-NEXT: push r1
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: push r0
+; CHECK: eor r0, r0
+; CHECK: pop r0
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: pop r1
+; CHECK-NEXT: pop r0
+; CHECK-NEXT: reti
+  ret void
+}
+
+define avr_signalcc void @signal_handler() {
+; CHECK-LABEL: signal_handler:
+; CHECK-NOT: sei
+; CHECK: push r0
+; CHECK-NEXT: push r1
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: push r0
+; CHECK: eor r0, r0
+; CHECK: pop r0
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: pop r1
+; CHECK-NEXT: pop r0
+; CHECK-NEXT: reti
+  ret void
+}

Added: llvm/trunk/test/CodeGen/AVR/io.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/io.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/io.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/io.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,97 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+define i8 @read8() {
+; CHECK-LABEL: read8
+; CHECK: in r24, 8
+  %1 = load i8, i8* inttoptr (i16 40 to i8*)
+  ret i8 %1
+}
+
+define i16 @read16() {
+; CHECK-LABEL: read16
+; CHECK: in r24, 8
+; CHECK: in r25, 9
+  %1 = load i16, i16* inttoptr (i16 40 to i16*)
+  ret i16 %1
+}
+
+define i32 @read32() {
+; CHECK-LABEL: read32
+; CHECK: in r22, 8
+; CHECK: in r23, 9
+; CHECK: in r24, 10
+; CHECK: in r25, 11
+  %1 = load i32, i32* inttoptr (i16 40 to i32*)
+  ret i32 %1
+}
+
+define i64 @read64() {
+; CHECK-LABEL: read64
+; CHECK: in r18, 8
+; CHECK: in r19, 9
+; CHECK: in r20, 10
+; CHECK: in r21, 11
+; CHECK: in r22, 12
+; CHECK: in r23, 13
+; CHECK: in r24, 14
+; CHECK: in r25, 15
+  %1 = load i64, i64* inttoptr (i16 40 to i64*)
+  ret i64 %1
+}
+
+define void @write8() {
+; CHECK-LABEL: write8
+; CHECK: out 8
+  store i8 22, i8* inttoptr (i16 40 to i8*)
+  ret void
+}
+
+define void @write16() {
+; CHECK-LABEL: write16
+; CHECK: out 9
+; CHECK: out 8
+  store i16 1234, i16* inttoptr (i16 40 to i16*)
+  ret void
+}
+
+define void @write32() {
+; CHECK-LABEL: write32
+; CHECK: out 11
+; CHECK: out 10
+; CHECK: out 9
+; CHECK: out 8
+  store i32 12345678, i32* inttoptr (i16 40 to i32*)
+  ret void
+}
+
+define void @write64() {
+; CHECK-LABEL: write64
+; CHECK: out 15
+; CHECK: out 14
+; CHECK: out 13
+; CHECK: out 12
+; CHECK: out 11
+; CHECK: out 10
+; CHECK: out 9
+; CHECK: out 8
+  store i64 1234567891234567, i64* inttoptr (i16 40 to i64*)
+  ret void
+}
+
+define void @sbi8() {
+; CHECK-LABEL: sbi8
+; CHECK: sbi 8, 5
+  %1 = load i8, i8* inttoptr (i16 40 to i8*)
+  %or = or i8 %1, 32
+  store i8 %or, i8* inttoptr (i16 40 to i8*)
+  ret void
+}
+
+define void @cbi8() {
+; CHECK-LABEL: cbi8
+; CHECK: cbi 8, 5
+  %1 = load volatile i8, i8* inttoptr (i16 40 to i8*)
+  %and = and i8 %1, -33
+  store volatile i8 %and, i8* inttoptr (i16 40 to i8*)
+  ret void
+}

Modified: llvm/trunk/test/CodeGen/AVR/lit.local.cfg
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/lit.local.cfg?rev=287162&r1=287161&r2=287162&view=diff
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/lit.local.cfg (original)
+++ llvm/trunk/test/CodeGen/AVR/lit.local.cfg Wed Nov 16 15:58:04 2016
@@ -1,3 +1,27 @@
 if not 'AVR' in config.root.targets:
     config.unsupported = True
 
+config.suffixes = ['.ll', '.cpp']
+
+import os, lit.TestRunner
+from lit.formats import ShTest
+
+targets = set(config.root.targets_to_build.split())
+if not 'AVR' in targets:
+  config.unsupported = True
+
+if 'AVRLIT_PORT' in os.environ:
+  config.environment['AVRLIT_PORT'] = os.environ['AVRLIT_PORT']
+
+class AVRCodeGenTest(ShTest):
+  def __init__(self):
+    ShTest.__init__(self)
+
+  def execute(self, test, litConfig):
+    if test.getSourcePath().endswith('.cpp') and not 'AVRLIT_PORT' in os.environ:
+      return (lit.Test.UNSUPPORTED, 'AVRLIT_PORT environment variable is not set')
+
+    return ShTest.execute(self, test, litConfig)
+
+
+config.test_format = AVRCodeGenTest()

Added: llvm/trunk/test/CodeGen/AVR/load.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/load.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/load.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/load.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,142 @@
+; RUN: llc -mattr=avr6,sram < %s -march=avr | FileCheck %s
+
+define i8 @load8(i8* %x) {
+; CHECK-LABEL: load8:
+; CHECK: ld r24, {{[XYZ]}}
+  %1 = load i8, i8* %x
+  ret i8 %1
+}
+
+define i16 @load16(i16* %x) {
+; CHECK-LABEL: load16:
+; CHECK: ld r24, {{[YZ]}}
+; CHECK: ldd r25, {{[YZ]}}+1
+  %1 = load i16, i16* %x
+  ret i16 %1
+}
+
+define i8 @load8disp(i8* %x) {
+; CHECK-LABEL: load8disp:
+; CHECK: ldd r24, {{[YZ]}}+63
+  %1 = getelementptr inbounds i8, i8* %x, i64 63
+  %2 = load i8, i8* %1
+  ret i8 %2
+}
+
+define i8 @load8nodisp(i8* %x) {
+; CHECK-LABEL: load8nodisp:
+; CHECK: movw r26, r24
+; CHECK: subi r26, 192
+; CHECK: sbci r27, 255
+; CHECK: ld r24, {{[XYZ]}}
+  %1 = getelementptr inbounds i8, i8* %x, i64 64
+  %2 = load i8, i8* %1
+  ret i8 %2
+}
+
+define i16 @load16disp(i16* %x) {
+; CHECK-LABEL: load16disp:
+; CHECK: ldd r24, {{[YZ]}}+62
+; CHECK: ldd r25, {{[YZ]}}+63
+  %1 = getelementptr inbounds i16, i16* %x, i64 31
+  %2 = load i16, i16* %1
+  ret i16 %2
+}
+
+define i16 @load16nodisp(i16* %x) {
+; CHECK-LABEL: load16nodisp:
+; CHECK: movw r30, r24
+; CHECK: subi r30, 192
+; CHECK: sbci r31, 255
+; CHECK: ld r24, {{[YZ]}}
+; CHECK: ldd r25, {{[YZ]}}+1
+  %1 = getelementptr inbounds i16, i16* %x, i64 32
+  %2 = load i16, i16* %1
+  ret i16 %2
+}
+
+define i8 @load8postinc(i8* %x, i8 %y) {
+; CHECK-LABEL: load8postinc:
+; CHECK: ld {{.*}}, {{[XYZ]}}+
+entry:
+  %tobool6 = icmp eq i8 %y, 0
+  br i1 %tobool6, label %while.end, label %while.body
+while.body:                                       ; preds = %entry, %while.body
+  %r.09 = phi i8 [ %add, %while.body ], [ 0, %entry ]
+  %y.addr.08 = phi i8 [ %dec, %while.body ], [ %y, %entry ]
+  %x.addr.07 = phi i8* [ %incdec.ptr, %while.body ], [ %x, %entry ]
+  %dec = add i8 %y.addr.08, -1
+  %incdec.ptr = getelementptr inbounds i8, i8* %x.addr.07, i16 1
+  %0 = load i8, i8* %x.addr.07
+  %add = add i8 %0, %r.09
+  %tobool = icmp eq i8 %dec, 0
+  br i1 %tobool, label %while.end, label %while.body
+while.end:                                        ; preds = %while.body, %entry
+  %r.0.lcssa = phi i8 [ 0, %entry ], [ %add, %while.body ]
+  ret i8 %r.0.lcssa
+}
+
+define i16 @load16postinc(i16* %x, i16 %y) {
+; CHECK-LABEL: load16postinc:
+; CHECK: ld {{.*}}, {{[XYZ]}}+
+; CHECK: ld {{.*}}, {{[XYZ]}}+
+entry:
+  %tobool2 = icmp eq i16 %y, 0
+  br i1 %tobool2, label %while.end, label %while.body
+while.body:                                       ; preds = %entry, %while.body
+  %r.05 = phi i16 [ %add, %while.body ], [ 0, %entry ]
+  %y.addr.04 = phi i16 [ %dec, %while.body ], [ %y, %entry ]
+  %x.addr.03 = phi i16* [ %incdec.ptr, %while.body ], [ %x, %entry ]
+  %dec = add nsw i16 %y.addr.04, -1
+  %incdec.ptr = getelementptr inbounds i16, i16* %x.addr.03, i16 1
+  %0 = load i16, i16* %x.addr.03
+  %add = add nsw i16 %0, %r.05
+  %tobool = icmp eq i16 %dec, 0
+  br i1 %tobool, label %while.end, label %while.body
+while.end:                                        ; preds = %while.body, %entry
+  %r.0.lcssa = phi i16 [ 0, %entry ], [ %add, %while.body ]
+  ret i16 %r.0.lcssa
+}
+
+define i8 @load8predec(i8* %x, i8 %y) {
+; CHECK-LABEL: load8predec:
+; CHECK: ld {{.*}}, -{{[XYZ]}}
+entry:
+  %tobool6 = icmp eq i8 %y, 0
+  br i1 %tobool6, label %while.end, label %while.body
+while.body:                                       ; preds = %entry, %while.body
+  %r.09 = phi i8 [ %add, %while.body ], [ 0, %entry ]
+  %y.addr.08 = phi i8 [ %dec, %while.body ], [ %y, %entry ]
+  %x.addr.07 = phi i8* [ %incdec.ptr, %while.body ], [ %x, %entry ]
+  %dec = add i8 %y.addr.08, -1
+  %incdec.ptr = getelementptr inbounds i8, i8* %x.addr.07, i16 -1
+  %0 = load i8, i8* %incdec.ptr
+  %add = add i8 %0, %r.09
+  %tobool = icmp eq i8 %dec, 0
+  br i1 %tobool, label %while.end, label %while.body
+while.end:                                        ; preds = %while.body, %entry
+  %r.0.lcssa = phi i8 [ 0, %entry ], [ %add, %while.body ]
+  ret i8 %r.0.lcssa
+}
+
+define i16 @load16predec(i16* %x, i16 %y) {
+; CHECK-LABEL: load16predec:
+; CHECK: ld {{.*}}, -{{[XYZ]}}
+; CHECK: ld {{.*}}, -{{[XYZ]}}
+entry:
+  %tobool2 = icmp eq i16 %y, 0
+  br i1 %tobool2, label %while.end, label %while.body
+while.body:                                       ; preds = %entry, %while.body
+  %r.05 = phi i16 [ %add, %while.body ], [ 0, %entry ]
+  %y.addr.04 = phi i16 [ %dec, %while.body ], [ %y, %entry ]
+  %x.addr.03 = phi i16* [ %incdec.ptr, %while.body ], [ %x, %entry ]
+  %dec = add nsw i16 %y.addr.04, -1
+  %incdec.ptr = getelementptr inbounds i16, i16* %x.addr.03, i16 -1
+  %0 = load i16, i16* %incdec.ptr
+  %add = add nsw i16 %0, %r.05
+  %tobool = icmp eq i16 %dec, 0
+  br i1 %tobool, label %while.end, label %while.body
+while.end:                                        ; preds = %while.body, %entry
+  %r.0.lcssa = phi i16 [ 0, %entry ], [ %add, %while.body ]
+  ret i16 %r.0.lcssa
+}

Added: llvm/trunk/test/CodeGen/AVR/or.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/or.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/or.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/or.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,80 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+define i8 @or8_reg_reg(i8 %a, i8 %b) {
+; CHECK-LABEL: or8_reg_reg:
+; CHECK: or r24, r22
+    %result = or i8 %a, %b
+    ret i8 %result
+}
+
+define i8 @or8_reg_imm(i8 %a) {
+; CHECK-LABEL: or8_reg_imm:
+; CHECK: ori r24, 5
+    %result = or i8 %a, 5
+    ret i8 %result
+}
+
+define i16 @or16_reg_reg(i16 %a, i16 %b) {
+; CHECK-LABEL: or16_reg_reg:
+; CHECK: or r24, r22
+; CHECK: or r25, r23
+    %result = or i16 %a, %b
+    ret i16 %result
+}
+
+define i16 @or16_reg_imm(i16 %a) {
+; CHECK-LABEL: or16_reg_imm:
+; CHECK: ori r24, 210
+; CHECK: ori r25, 4
+    %result = or i16 %a, 1234
+    ret i16 %result
+}
+
+define i32 @or32_reg_reg(i32 %a, i32 %b) {
+; CHECK-LABEL: or32_reg_reg:
+; CHECK: or r22, r18
+; CHECK: or r23, r19
+; CHECK: or r24, r20
+; CHECK: or r25, r21
+    %result = or i32 %a, %b
+    ret i32 %result
+}
+
+define i32 @or32_reg_imm(i32 %a) {
+; CHECK-LABEL: or32_reg_imm:
+; CHECK: ori r22, 21
+; CHECK: ori r23, 205
+; CHECK: ori r24, 91
+; CHECK: ori r25, 7
+    %result = or i32 %a, 123456789
+    ret i32 %result
+}
+
+define i64 @or64_reg_reg(i64 %a, i64 %b) {
+; CHECK-LABEL: or64_reg_reg:
+; CHECK: or r18, r10
+; CHECK: or r19, r11
+; CHECK: or r20, r12
+; CHECK: or r21, r13
+; CHECK: or r22, r14
+; CHECK: or r23, r15
+; CHECK: or r24, r16
+; CHECK: or r25, r17
+    %result = or i64 %a, %b
+    ret i64 %result
+}
+
+define i64 @or64_reg_imm(i64 %a) {
+; CHECK-LABEL: or64_reg_imm:
+; CHECK: ori r18, 204
+; CHECK: ori r19, 204
+; CHECK: ori r20, 204
+; CHECK: ori r21, 204
+; CHECK: ori r22, 204
+; CHECK: ori r23, 204
+; CHECK: ori r24, 204
+; CHECK: ori r25, 204
+    %result = or i64 %a, 14757395258967641292
+    ret i64 %result
+}
+

Added: llvm/trunk/test/CodeGen/AVR/progmem.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/progmem.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/progmem.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/progmem.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,70 @@
+; RUN: llc < %s -march=avr -mattr=movw,lpm | FileCheck %s
+; XFAIL: *
+
+; Tests the standard LPM instruction
+
+define i8 @test8(i8 addrspace(1)* %p) {
+; CHECK-LABEL: test8:
+; CHECK: movw r30, r24
+; CHECK: lpm r24, Z
+  %1 = load i8, i8 addrspace(1)* %p
+  ret i8 %1
+}
+
+define i16 @test16(i16 addrspace(1)* %p) {
+; CHECK-LABEL: test16:
+; CHECK: movw r30, r24
+; CHECK: lpm r24, Z+
+; CHECK: lpm r25, Z+
+  %1 = load i16, i16 addrspace(1)* %p
+  ret i16 %1
+}
+
+define i8 @test8postinc(i8 addrspace(1)* %x, i8 %y) {
+; CHECK-LABEL: test8postinc:
+; CHECK: movw r30, r24
+; CHECK: lpm {{.*}}, Z+
+entry:
+  %cmp10 = icmp sgt i8 %y, 0
+  br i1 %cmp10, label %for.body, label %for.end
+
+for.body:                                         ; preds = %entry, %for.body
+  %ret.013 = phi i8 [ %add, %for.body ], [ 0, %entry ]
+  %i.012 = phi i8 [ %inc, %for.body ], [ 0, %entry ]
+  %x.addr.011 = phi i8 addrspace(1)* [ %incdec.ptr, %for.body ], [ %x, %entry ]
+  %incdec.ptr = getelementptr inbounds i8, i8 addrspace(1)* %x.addr.011, i16 1
+  %0 = load i8, i8 addrspace(1)* %x.addr.011
+  %add = add i8 %0, %ret.013
+  %inc = add i8 %i.012, 1
+  %exitcond = icmp eq i8 %inc, %y
+  br i1 %exitcond, label %for.end, label %for.body
+
+for.end:                                          ; preds = %for.body, %entry
+  %ret.0.lcssa = phi i8 [ 0, %entry ], [ %add, %for.body ]
+  ret i8 %ret.0.lcssa
+}
+
+define i16 @test16postinc(i16 addrspace(1)* %x, i8 %y) {
+; CHECK-LABEL: test16postinc:
+; CHECK: movw r30, r24
+; CHECK: lpm {{.*}}, Z+
+; CHECK: lpm {{.*}}, Z+
+entry:
+  %cmp5 = icmp sgt i8 %y, 0
+  br i1 %cmp5, label %for.body, label %for.end
+
+for.body:                                         ; preds = %entry, %for.body
+  %ret.08 = phi i16 [ %add, %for.body ], [ 0, %entry ]
+  %i.07 = phi i8 [ %inc, %for.body ], [ 0, %entry ]
+  %x.addr.06 = phi i16 addrspace(1)* [ %incdec.ptr, %for.body ], [ %x, %entry ]
+  %incdec.ptr = getelementptr inbounds i16, i16 addrspace(1)* %x.addr.06, i16 1
+  %0 = load i16, i16 addrspace(1)* %x.addr.06
+  %add = add nsw i16 %0, %ret.08
+  %inc = add i8 %i.07, 1
+  %exitcond = icmp eq i8 %inc, %y
+  br i1 %exitcond, label %for.end, label %for.body
+
+for.end:                                          ; preds = %for.body, %entry
+  %ret.0.lcssa = phi i16 [ 0, %entry ], [ %add, %for.body ]
+  ret i16 %ret.0.lcssa
+}

Added: llvm/trunk/test/CodeGen/AVR/return.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/return.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/return.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/return.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,142 @@
+; RUN: llc -mattr=avr6,sram < %s -march=avr | FileCheck %s
+
+;TODO: test returning byval structs
+; TODO: test naked functions
+
+define void @return_void() {
+; CHECK: return_void:{{[a-zA-Z0-9 #@]*}}
+; CHECK-NEXT: #{{[a-zA-Z0-9 #@]*}}
+; CHECK-NEXT: ret
+    ret void
+}
+
+define i8 @return8_imm() {
+; CHECK-LABEL: return8_imm:
+; CHECK: ldi r24, 5
+    ret i8 5
+}
+
+define i8 @return8_arg(i8 %x) {
+; CHECK: return8_arg:{{[a-zA-Z0-9 #@]*}}
+; CHECK-NEXT: #{{[a-zA-Z0-9 #@]*}}
+; CHECK-NEXT: ret
+    ret i8 %x
+}
+
+define i8 @return8_arg2(i8 %x, i8 %y, i8 %z) {
+; CHECK-LABEL: return8_arg2:
+; CHECK: mov r24, r20
+    ret i8 %z
+}
+
+define i16 @return16_imm() {
+; CHECK-LABEL: return16_imm:
+; CHECK: ldi r24, 57
+; CHECK: ldi r25, 48
+    ret i16 12345
+}
+
+define i16 @return16_arg(i16 %x) {
+; CHECK: return16_arg:{{[a-zA-Z0-9 #@]*}}
+; CHECK-NEXT: #{{[a-zA-Z0-9 #@]*}}
+; CHECK-NEXT: ret
+    ret i16 %x
+}
+
+define i16 @return16_arg2(i16 %x, i16 %y, i16 %z) {
+; CHECK-LABEL: return16_arg2:
+; CHECK: movw r24, r20
+    ret i16 %z
+}
+
+define i32 @return32_imm() {
+; CHECK-LABEL: return32_imm:
+; CHECK: ldi r22, 21
+; CHECK: ldi r23, 205
+; CHECK: ldi r24, 91
+; CHECK: ldi r25, 7
+    ret i32 123456789
+}
+
+define i32 @return32_arg(i32 %x) {
+; CHECK: return32_arg:{{[a-zA-Z0-9 #@]*}}
+; CHECK-NEXT: #{{[a-zA-Z0-9 #@]*}}
+; CHECK-NEXT: ret
+    ret i32 %x
+}
+
+define i32 @return32_arg2(i32 %x, i32 %y, i32 %z) {
+; CHECK-LABEL: return32_arg2:
+; CHECK: movw r22, r14
+; CHECK: movw r24, r16
+    ret i32 %z
+}
+
+define i64 @return64_imm() {
+; CHECK-LABEL: return64_imm:
+; CHECK: ldi r18, 204
+; CHECK: ldi r19, 204
+; CHECK: ldi r20, 104
+; CHECK: ldi r21, 37
+; CHECK: ldi r22, 25
+; CHECK: ldi r23, 22
+; CHECK: ldi r24, 236
+; CHECK: ldi r25, 190
+    ret i64 13757395258967641292
+}
+
+define i64 @return64_arg(i64 %x) {
+; CHECK: return64_arg:{{[a-zA-Z0-9 #@]*}}
+; CHECK-NEXT: #{{[a-zA-Z0-9 #@]*}}
+; CHECK-NEXT: ret
+    ret i64 %x
+}
+
+define i64 @return64_arg2(i64 %x, i64 %y, i64 %z) {
+; CHECK-LABEL: return64_arg2:
+; CHECK: push r28
+; CHECK: push r29
+; CHECK: ldd r18, Y+5
+; CHECK: ldd r19, Y+6
+; CHECK: ldd r20, Y+7
+; CHECK: ldd r21, Y+8
+; CHECK: ldd r22, Y+9
+; CHECK: ldd r23, Y+10
+; CHECK: ldd r24, Y+11
+; CHECK: ldd r25, Y+12
+; CHECK: pop r29
+; CHECK: pop r28
+    ret i64 %z
+}
+
+define i32 @return64_trunc(i32 %a, i32 %b, i32 %c, i64 %d) {
+; CHECK-LABEL: return64_trunc:
+; CHECK: push r28
+; CHECK: push r29
+; CHECK: ldd r22, Y+5
+; CHECK: ldd r23, Y+6
+; CHECK: ldd r24, Y+7
+; CHECK: ldd r25, Y+8
+; CHECK: pop r29
+; CHECK: pop r28
+  %result = trunc i64 %d to i32
+  ret i32 %result
+}
+
+define i32 @naked(i32 %x) naked {
+; CHECK-LABEL: naked:
+; CHECK-NOT: ret
+  ret i32 %x
+}
+
+define avr_intrcc void @interrupt_handler() {
+; CHECK-LABEL: interrupt_handler:
+; CHECK: reti
+  ret void
+}
+
+define avr_signalcc void @signal_handler() {
+; CHECK-LABEL: signal_handler:
+; CHECK: reti
+  ret void
+}

Added: llvm/trunk/test/CodeGen/AVR/sext.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/sext.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/sext.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/sext.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,29 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+; sext R17:R16, R13
+; mov r16, r13
+; mov r17, r13
+; lsl r17
+; sbc r17, r17
+define i16 @sext1(i8 %x, i8 %y) {
+; CHECK-LABEL: sext1:
+; CHECK: mov r24, r22
+; CHECK: mov r25, r22
+; CHECK: lsl r25
+; CHECK: sbc r25, r25
+  %1 = sext i8 %y to i16
+  ret i16 %1
+}
+
+; sext R17:R16, R16
+; mov r17, r16
+; lsl r17
+; sbc r17, r17
+define i16 @sext2(i8 %x) {
+; CHECK-LABEL: sext2:
+; CHECK: mov r25, r24
+; CHECK: lsl r25
+; CHECK: sbc r25, r25
+  %1 = sext i8 %x to i16
+  ret i16 %1
+}

Added: llvm/trunk/test/CodeGen/AVR/store.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/store.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/store.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/store.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,130 @@
+; RUN: llc -mattr=avr6,sram < %s -march=avr | FileCheck %s
+
+define void @store8(i8* %x, i8 %y) {
+; CHECK-LABEL: store8:
+; CHECK: st {{[XYZ]}}, r22
+  store i8 %y, i8* %x
+  ret void
+}
+
+define void @store16(i16* %x, i16 %y) {
+; CHECK-LABEL: store16:
+; CHECK: st {{[YZ]}}, r22
+; CHECK: std {{[YZ]}}+1, r23
+  store i16 %y, i16* %x
+  ret void
+}
+
+define void @store8disp(i8* %x, i8 %y) {
+; CHECK-LABEL: store8disp:
+; CHECK: std {{[YZ]}}+63, r22
+  %arrayidx = getelementptr inbounds i8, i8* %x, i16 63
+  store i8 %y, i8* %arrayidx
+  ret void
+}
+
+define void @store8nodisp(i8* %x, i8 %y) {
+; CHECK-LABEL: store8nodisp:
+; CHECK: movw r26, r24
+; CHECK: subi r26, 192
+; CHECK: sbci r27, 255
+; CHECK: st {{[XYZ]}}, r22
+  %arrayidx = getelementptr inbounds i8, i8* %x, i16 64
+  store i8 %y, i8* %arrayidx
+  ret void
+}
+
+define void @store16disp(i16* %x, i16 %y) {
+; CHECK-LABEL: store16disp:
+; CHECK: std {{[YZ]}}+62, r22
+; CHECK: std {{[YZ]}}+63, r23
+  %arrayidx = getelementptr inbounds i16, i16* %x, i16 31
+  store i16 %y, i16* %arrayidx
+  ret void
+}
+
+define void @store16nodisp(i16* %x, i16 %y) {
+; CHECK-LABEL: store16nodisp:
+; CHECK: movw r30, r24
+; CHECK: subi r30, 192
+; CHECK: sbci r31, 255
+; CHECK: st {{[YZ]}}, r22
+; CHECK: std {{[YZ]}}+1, r23
+  %arrayidx = getelementptr inbounds i16, i16* %x, i16 32
+  store i16 %y, i16* %arrayidx
+  ret void
+}
+
+define void @store8postinc(i8* %x, i8 %y) {
+; CHECK-LABEL: store8postinc:
+; CHECK: st {{[XYZ]}}+, {{.*}}
+entry:
+  %tobool3 = icmp eq i8 %y, 0
+  br i1 %tobool3, label %while.end, label %while.body
+while.body:                                       ; preds = %entry, %while.body
+  %dec5.in = phi i8 [ %dec5, %while.body ], [ %y, %entry ]
+  %x.addr.04 = phi i8* [ %incdec.ptr, %while.body ], [ %x, %entry ]
+  %dec5 = add i8 %dec5.in, -1
+  %incdec.ptr = getelementptr inbounds i8, i8* %x.addr.04, i16 1
+  store i8 %dec5, i8* %x.addr.04
+  %tobool = icmp eq i8 %dec5, 0
+  br i1 %tobool, label %while.end, label %while.body
+while.end:                                        ; preds = %while.body, %entry
+  ret void
+}
+
+define void @store16postinc(i16* %x, i16 %y) {
+; CHECK-LABEL: store16postinc:
+; CHECK: st {{[XYZ]}}+, {{.*}}
+; CHECK: st {{[XYZ]}}+, {{.*}}
+entry:
+  %tobool3 = icmp eq i16 %y, 0
+  br i1 %tobool3, label %while.end, label %while.body
+while.body:                                       ; preds = %entry, %while.body
+  %dec5.in = phi i16 [ %dec5, %while.body ], [ %y, %entry ]
+  %x.addr.04 = phi i16* [ %incdec.ptr, %while.body ], [ %x, %entry ]
+  %dec5 = add nsw i16 %dec5.in, -1
+  %incdec.ptr = getelementptr inbounds i16, i16* %x.addr.04, i16 1
+  store i16 %dec5, i16* %x.addr.04
+  %tobool = icmp eq i16 %dec5, 0
+  br i1 %tobool, label %while.end, label %while.body
+while.end:                                        ; preds = %while.body, %entry
+  ret void
+}
+
+define void @store8predec(i8* %x, i8 %y) {
+; CHECK-LABEL: store8predec:
+; CHECK: st -{{[XYZ]}}, {{.*}}
+entry:
+  %tobool3 = icmp eq i8 %y, 0
+  br i1 %tobool3, label %while.end, label %while.body
+while.body:                                       ; preds = %entry, %while.body
+  %dec5.in = phi i8 [ %dec5, %while.body ], [ %y, %entry ]
+  %x.addr.04 = phi i8* [ %incdec.ptr, %while.body ], [ %x, %entry ]
+  %dec5 = add i8 %dec5.in, -1
+  %incdec.ptr = getelementptr inbounds i8, i8* %x.addr.04, i16 -1
+  store i8 %dec5, i8* %incdec.ptr
+  %tobool = icmp eq i8 %dec5, 0
+  br i1 %tobool, label %while.end, label %while.body
+while.end:                                        ; preds = %while.body, %entry
+  ret void
+}
+
+define void @store16predec(i16* %x, i16 %y) {
+; CHECK-LABEL: store16predec:
+; CHECK: st -{{[XYZ]}}, {{.*}}
+; CHECK: st -{{[XYZ]}}, {{.*}}
+entry:
+  %tobool3 = icmp eq i16 %y, 0
+  br i1 %tobool3, label %while.end, label %while.body
+while.body:                                       ; preds = %entry, %while.body
+  %dec5.in = phi i16 [ %dec5, %while.body ], [ %y, %entry ]
+  %x.addr.04 = phi i16* [ %incdec.ptr, %while.body ], [ %x, %entry ]
+  %dec5 = add nsw i16 %dec5.in, -1
+  %incdec.ptr = getelementptr inbounds i16, i16* %x.addr.04, i16 -1
+  store i16 %dec5, i16* %incdec.ptr
+  %tobool = icmp eq i16 %dec5, 0
+  br i1 %tobool, label %while.end, label %while.body
+while.end:                                        ; preds = %while.body, %entry
+  ret void
+}

Added: llvm/trunk/test/CodeGen/AVR/sub.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/sub.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/sub.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/sub.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,92 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+define i8 @sub8_reg_reg(i8 %a, i8 %b) {
+; CHECK-LABEL: sub8_reg_reg:
+; CHECK: sub r24, r22
+    %result = sub i8 %a, %b
+    ret i8 %result
+}
+
+define i8 @sub8_reg_imm(i8 %a) {
+; CHECK-LABEL: sub8_reg_imm:
+; CHECK: subi r24, 5
+    %result = sub i8 %a, 5
+    ret i8 %result
+}
+
+define i8 @sub8_reg_decrement(i8 %a) {
+; CHECK-LABEL: sub8_reg_decrement:
+; CHECK: dec r24
+    %result = sub i8 %a, 1
+    ret i8 %result
+}
+
+define i16 @sub16_reg_reg(i16 %a, i16 %b) {
+; CHECK-LABEL: sub16_reg_reg:
+; CHECK: sub r24, r22
+; CHECK: sbc r25, r23
+    %result = sub i16 %a, %b
+    ret i16 %result
+}
+
+define i16 @sub16_reg_imm(i16 %a) {
+; CHECK-LABEL: sub16_reg_imm:
+; CHECK: sbiw r24, 63
+    %result = sub i16 %a, 63
+    ret i16 %result
+}
+
+define i16 @sub16_reg_imm_subi(i16 %a) {
+; CHECK-LABEL: sub16_reg_imm_subi:
+; CHECK: subi r24, 210
+; CHECK: sbci r25, 4
+    %result = sub i16 %a, 1234
+    ret i16 %result
+}
+
+define i32 @sub32_reg_reg(i32 %a, i32 %b) {
+; CHECK-LABEL: sub32_reg_reg:
+; CHECK: sub r22, r18
+; CHECK: sbc r23, r19
+; CHECK: sbc r24, r20
+; CHECK: sbc r25, r21
+    %result = sub i32 %a, %b
+    ret i32 %result
+}
+
+define i32 @sub32_reg_imm(i32 %a) {
+; CHECK-LABEL: sub32_reg_imm:
+; CHECK: subi r22, 21
+; CHECK: sbci r23, 205
+; CHECK: sbci r24, 91
+; CHECK: sbci r25, 7
+    %result = sub i32 %a, 123456789
+    ret i32 %result
+}
+
+define i64 @sub64_reg_reg(i64 %a, i64 %b) {
+; CHECK-LABEL: sub64_reg_reg:
+; CHECK: sub r18, r10
+; CHECK: sbc r20, r12
+; CHECK: sbc r21, r13
+; CHECK: sbc r22, r14
+; CHECK: sbc r23, r15
+; CHECK: sbc r24, r16
+; CHECK: sbc r25, r17
+    %result = sub i64 %a, %b
+    ret i64 %result
+}
+
+define i64 @sub64_reg_imm(i64 %a) {
+; CHECK-LABEL: sub64_reg_imm:
+; CHECK: subi r18, 204
+; CHECK: sbci r19, 204
+; CHECK: sbci r20, 104
+; CHECK: sbci r21, 37
+; CHECK: sbci r22, 25
+; CHECK: sbci r23, 22
+; CHECK: sbci r24, 236
+; CHECK: sbci r25, 190
+    %result = sub i64 %a, 13757395258967641292
+    ret i64 %result
+}

Added: llvm/trunk/test/CodeGen/AVR/varargs.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/varargs.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/varargs.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/varargs.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,59 @@
+; RUN: llc -mattr=sram,movw,addsubiw < %s -march=avr | FileCheck %s
+
+declare void @llvm.va_start(i8*)
+declare i16 @vsprintf(i8* nocapture, i8* nocapture, i8*)
+declare void @llvm.va_end(i8*)
+
+define i16 @varargs1(i8* nocapture %x, ...) {
+; CHECK-LABEL: varargs1:
+; CHECK: movw r20, r28
+; CHECK: subi r20, 215
+; CHECK: sbci r21, 255
+; CHECK: movw r24, r28
+; CHECK: adiw r24, 3
+; CHECK: ldd r22, Y+39
+; CHECK: ldd r23, Y+40
+; CHECK: call
+  %buffer = alloca [32 x i8]
+  %ap = alloca i8*
+  %ap1 = bitcast i8** %ap to i8*
+  call void @llvm.va_start(i8* %ap1)
+  %arraydecay = getelementptr inbounds [32 x i8], [32 x i8]* %buffer, i16 0, i16 0
+  %1 = load i8*, i8** %ap
+  %call = call i16 @vsprintf(i8* %arraydecay, i8* %x, i8* %1)
+  call void @llvm.va_end(i8* %ap1)
+  ret i16 0
+}
+
+define i16 @varargs2(i8* nocapture %x, ...) {
+; CHECK-LABEL: varargs2:
+; CHECK: ld r24, Z
+; CHECK: ldd r25, Z+1
+  %ap = alloca i8*
+  %ap1 = bitcast i8** %ap to i8*
+  call void @llvm.va_start(i8* %ap1)
+  %1 = va_arg i8** %ap, i16
+  call void @llvm.va_end(i8* %ap1)
+  ret i16 %1
+}
+
+declare void @var1223(i16, ...)
+define void @varargcall() {
+; CHECK-LABEL: varargcall:
+; CHECK: ldi [[REG1:r[0-9]+]], 191
+; CHECK: ldi [[REG2:r[0-9]+]], 223
+; CHECK: push [[REG2]]
+; CHECK: push [[REG1]]
+; CHECK: ldi [[REG1:r[0-9]+]], 189
+; CHECK: ldi [[REG2:r[0-9]+]], 205
+; CHECK: push [[REG2]]
+; CHECK: push [[REG1]]
+; CHECK: ldi [[REG1:r[0-9]+]], 205
+; CHECK: ldi [[REG2:r[0-9]+]], 171
+; CHECK: push [[REG2]]
+; CHECK: push [[REG1]]
+; CHECK: call
+; CHECK: adiw r30, 6
+  tail call void (i16, ...) @var1223(i16 -21555, i16 -12867, i16 -8257)
+  ret void
+}

Added: llvm/trunk/test/CodeGen/AVR/xor.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/xor.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/xor.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/xor.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,41 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+define i8 @xor8_reg_reg(i8 %a, i8 %b) {
+; CHECK-LABEL: xor8_reg_reg:
+; CHECK: eor r24, r22
+    %result = xor i8 %a, %b
+    ret i8 %result
+}
+
+define i16 @xor16_reg_reg(i16 %a, i16 %b) {
+; CHECK-LABEL: xor16_reg_reg:
+; CHECK: eor r24, r22
+; CHECK: eor r25, r23
+    %result = xor i16 %a, %b
+    ret i16 %result
+}
+
+define i32 @xor32_reg_reg(i32 %a, i32 %b) {
+; CHECK-LABEL: xor32_reg_reg:
+; CHECK: eor r22, r18
+; CHECK: eor r23, r19
+; CHECK: eor r24, r20
+; CHECK: eor r25, r21
+    %result = xor i32 %a, %b
+    ret i32 %result
+}
+
+define i64 @xor64_reg_reg(i64 %a, i64 %b) {
+; CHECK-LABEL: xor64_reg_reg:
+; CHECK: eor r18, r10
+; CHECK: eor r19, r11
+; CHECK: eor r20, r12
+; CHECK: eor r21, r13
+; CHECK: eor r22, r14
+; CHECK: eor r23, r15
+; CHECK: eor r24, r16
+; CHECK: eor r25, r17
+    %result = xor i64 %a, %b
+    ret i64 %result
+}
+

Added: llvm/trunk/test/CodeGen/AVR/zext.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/CodeGen/AVR/zext.ll?rev=287162&view=auto
==============================================================================
--- llvm/trunk/test/CodeGen/AVR/zext.ll (added)
+++ llvm/trunk/test/CodeGen/AVR/zext.ll Wed Nov 16 15:58:04 2016
@@ -0,0 +1,31 @@
+; RUN: llc < %s -march=avr | FileCheck %s
+
+; zext R25:R24, R24
+; eor R25, R25
+define i16 @zext1(i8 %x) {
+; CHECK-LABEL: zext1:
+; CHECK: eor r25, r25
+  %1 = zext i8 %x to i16
+  ret i16 %1
+}
+
+; zext R25:R24, R20
+; mov R24, R20
+; eor R25, R25
+define i16 @zext2(i8 %x, i8 %y) {
+; CHECK-LABEL: zext2:
+; CHECK: mov r24, r22
+; CHECK: eor r25, r25
+  %1 = zext i8 %y to i16
+  ret i16 %1
+}
+
+; zext R25:R24, R24
+; eor R25, R25
+define i16 @zext_i1(i1 %x) {
+; CHECK-LABEL: zext_i1:
+; CHECK: eor r25, r25
+  %1 = zext i1 %x to i16
+  ret i16 %1
+}
+




More information about the llvm-commits mailing list