[llvm] d1d3005 - [AVR] Do not emit instructions invalid for attiny10

Ayke van Laethem via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 22 08:05:13 PST 2022


Author: Ayke van Laethem
Date: 2022-12-22T17:04:53+01:00
New Revision: d1d3005c9fe6a1ded17d4cad373cae85c743e7f3

URL: https://github.com/llvm/llvm-project/commit/d1d3005c9fe6a1ded17d4cad373cae85c743e7f3
DIFF: https://github.com/llvm/llvm-project/commit/d1d3005c9fe6a1ded17d4cad373cae85c743e7f3.diff

LOG: [AVR] Do not emit instructions invalid for attiny10

The attiny4/attiny5/attiny9/attiny10 have a slightly modified
instruction set that drops a number of useful instructions. This patch
makes sure to not emit them on these "reduced tiny" cores.

The affected instructions are:

  * lds and sts (load/store directly from data)
  * ldd and std (load/store with displacement)
  * adiw and sbiw (add/sub register pairs)
  * various other instructions that were emitted without checking
    whether the chip actually supports them (movw, adiw, etc)

There is a variant on lds and sts on these chips, but it can only
address a limited portion of the address space and is mainly useful to
load/store I/O registers (as an extension to the in and out
instructions). I have not implemented it here, implementing it can be
done in a separate patch.

This patch is not optimal. I'm sure it can be improved a lot. For
example, we could teach the instruction selector to not select lddw/stdw
instructions so that the weird pointer adjustments are not necessary.
But for now I've focused just on correctness, not on code quality.

Updates: https://github.com/llvm/llvm-project/issues/53459

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

Added: 
    

Modified: 
    llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
    llvm/lib/Target/AVR/AVRFrameLowering.cpp
    llvm/lib/Target/AVR/AVRInstrInfo.td
    llvm/lib/Target/AVR/AVRRegisterInfo.cpp
    llvm/test/CodeGen/AVR/calling-conv/c/tiny.ll
    llvm/test/CodeGen/AVR/directmem.ll
    llvm/test/CodeGen/AVR/pseudo/LDDWRdPtrQ.mir
    llvm/test/CodeGen/AVR/pseudo/LDWRdPtr.mir
    llvm/test/CodeGen/AVR/pseudo/STDWPtrQRr.mir
    llvm/test/CodeGen/AVR/pseudo/STWPtrRr.mir
    llvm/test/CodeGen/AVR/return.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
index b474f065924b9..f471e72b8f06f 100644
--- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
@@ -635,30 +635,42 @@ bool AVRExpandPseudo::expand<AVR::LDSWRdK>(Block &MBB, BlockIt MBBI) {
 template <>
 bool AVRExpandPseudo::expand<AVR::LDWRdPtr>(Block &MBB, BlockIt MBBI) {
   MachineInstr &MI = *MBBI;
-  Register DstLoReg, DstHiReg;
   Register DstReg = MI.getOperand(0).getReg();
   Register SrcReg = MI.getOperand(1).getReg();
+  bool DstIsKill = MI.getOperand(0).isKill();
   bool SrcIsKill = MI.getOperand(1).isKill();
-  unsigned OpLo = AVR::LDRdPtr;
-  unsigned OpHi = AVR::LDDRdPtrQ;
-  TRI->splitReg(DstReg, DstLoReg, DstHiReg);
+  const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
 
   // DstReg has an earlyclobber so the register allocator will allocate them in
   // separate registers.
   assert(DstReg != SrcReg && "Dst and Src registers are the same!");
 
-  // Load low byte.
-  buildMI(MBB, MBBI, OpLo)
-      .addReg(DstLoReg, RegState::Define)
-      .addReg(SrcReg)
-      .setMemRefs(MI.memoperands());
+  if (STI.hasTinyEncoding()) {
+    // Handle this case in the expansion of LDDWRdPtrQ because it is very
+    // similar.
+    buildMI(MBB, MBBI, AVR::LDDWRdPtrQ)
+        .addDef(DstReg, getKillRegState(DstIsKill))
+        .addReg(SrcReg, getKillRegState(SrcIsKill))
+        .addImm(0)
+        .setMemRefs(MI.memoperands());
 
-  // Load high byte.
-  buildMI(MBB, MBBI, OpHi)
-      .addReg(DstHiReg, RegState::Define)
-      .addReg(SrcReg, getKillRegState(SrcIsKill))
-      .addImm(1)
-      .setMemRefs(MI.memoperands());
+  } else {
+    Register DstLoReg, DstHiReg;
+    TRI->splitReg(DstReg, DstLoReg, DstHiReg);
+
+    // Load low byte.
+    buildMI(MBB, MBBI, AVR::LDRdPtr)
+        .addReg(DstLoReg, RegState::Define)
+        .addReg(SrcReg)
+        .setMemRefs(MI.memoperands());
+
+    // Load high byte.
+    buildMI(MBB, MBBI, AVR::LDDRdPtrQ)
+        .addReg(DstHiReg, RegState::Define)
+        .addReg(SrcReg, getKillRegState(SrcIsKill))
+        .addImm(1)
+        .setMemRefs(MI.memoperands());
+  }
 
   MI.eraseFromParent();
   return true;
@@ -733,14 +745,12 @@ bool AVRExpandPseudo::expand<AVR::LDWRdPtrPd>(Block &MBB, BlockIt MBBI) {
 template <>
 bool AVRExpandPseudo::expand<AVR::LDDWRdPtrQ>(Block &MBB, BlockIt MBBI) {
   MachineInstr &MI = *MBBI;
-  Register DstLoReg, DstHiReg;
   Register DstReg = MI.getOperand(0).getReg();
   Register SrcReg = MI.getOperand(1).getReg();
   unsigned Imm = MI.getOperand(2).getImm();
+  bool DstIsKill = MI.getOperand(0).isKill();
   bool SrcIsKill = MI.getOperand(1).isKill();
-  unsigned OpLo = AVR::LDDRdPtrQ;
-  unsigned OpHi = AVR::LDDRdPtrQ;
-  TRI->splitReg(DstReg, DstLoReg, DstHiReg);
+  const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
 
   // Since we add 1 to the Imm value for the high byte below, and 63 is the
   // highest Imm value allowed for the instruction, 62 is the limit here.
@@ -750,19 +760,51 @@ bool AVRExpandPseudo::expand<AVR::LDDWRdPtrQ>(Block &MBB, BlockIt MBBI) {
   // separate registers.
   assert(DstReg != SrcReg && "Dst and Src registers are the same!");
 
-  // Load low byte.
-  buildMI(MBB, MBBI, OpLo)
-      .addReg(DstLoReg, RegState::Define)
-      .addReg(SrcReg)
-      .addImm(Imm)
-      .setMemRefs(MI.memoperands());
+  if (STI.hasTinyEncoding()) {
+    // Reduced tiny cores don't support load/store with displacement. However,
+    // they do support postincrement. So we'll simply adjust the pointer before
+    // and after and use postincrement to load multiple registers.
+
+    // Add offset. The offset can be 0 when expanding this instruction from the
+    // more specific LDWRdPtr instruction.
+    if (Imm != 0) {
+      buildMI(MBB, MBBI, AVR::SUBIWRdK, SrcReg)
+          .addReg(SrcReg)
+          .addImm(0x10000 - Imm);
+    }
 
-  // Load high byte.
-  buildMI(MBB, MBBI, OpHi)
-      .addReg(DstHiReg, RegState::Define)
-      .addReg(SrcReg, getKillRegState(SrcIsKill))
-      .addImm(Imm + 1)
-      .setMemRefs(MI.memoperands());
+    // Do a word load with postincrement. This will be lowered to a two byte
+    // load.
+    buildMI(MBB, MBBI, AVR::LDWRdPtrPi)
+        .addDef(DstReg, getKillRegState(DstIsKill))
+        .addReg(SrcReg, getKillRegState(SrcIsKill))
+        .addImm(0)
+        .setMemRefs(MI.memoperands());
+
+    // If the pointer is used after the store instruction, subtract the new
+    // offset (with 2 added after the postincrement instructions) so it is the
+    // same as before.
+    if (!SrcIsKill) {
+      buildMI(MBB, MBBI, AVR::SUBIWRdK, SrcReg).addReg(SrcReg).addImm(Imm + 2);
+    }
+  } else {
+    Register DstLoReg, DstHiReg;
+    TRI->splitReg(DstReg, DstLoReg, DstHiReg);
+
+    // Load low byte.
+    buildMI(MBB, MBBI, AVR::LDDRdPtrQ)
+        .addReg(DstLoReg, RegState::Define)
+        .addReg(SrcReg)
+        .addImm(Imm)
+        .setMemRefs(MI.memoperands());
+
+    // Load high byte.
+    buildMI(MBB, MBBI, AVR::LDDRdPtrQ)
+        .addReg(DstHiReg, RegState::Define)
+        .addReg(SrcReg, getKillRegState(SrcIsKill))
+        .addImm(Imm + 1)
+        .setMemRefs(MI.memoperands());
+  }
 
   MI.eraseFromParent();
   return true;
@@ -972,27 +1014,39 @@ bool AVRExpandPseudo::expand<AVR::STSWKRr>(Block &MBB, BlockIt MBBI) {
 template <>
 bool AVRExpandPseudo::expand<AVR::STWPtrRr>(Block &MBB, BlockIt MBBI) {
   MachineInstr &MI = *MBBI;
-  Register SrcLoReg, SrcHiReg;
   Register DstReg = MI.getOperand(0).getReg();
   Register SrcReg = MI.getOperand(1).getReg();
+  bool DstIsKill = MI.getOperand(0).isKill();
   bool DstIsUndef = MI.getOperand(0).isUndef();
   bool SrcIsKill = MI.getOperand(1).isKill();
-  unsigned OpLo = AVR::STPtrRr;
-  unsigned OpHi = AVR::STDPtrQRr;
-  TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
+  const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
 
   //: TODO: need to reverse this order like inw and stsw?
-  auto MIBLO = buildMI(MBB, MBBI, OpLo)
-                   .addReg(DstReg, getUndefRegState(DstIsUndef))
-                   .addReg(SrcLoReg, getKillRegState(SrcIsKill));
 
-  auto MIBHI = buildMI(MBB, MBBI, OpHi)
-                   .addReg(DstReg, getUndefRegState(DstIsUndef))
-                   .addImm(1)
-                   .addReg(SrcHiReg, getKillRegState(SrcIsKill));
+  if (STI.hasTinyEncoding()) {
+    // Handle this case in the expansion of STDWPtrQRr because it is very
+    // similar.
+    buildMI(MBB, MBBI, AVR::STDWPtrQRr)
+        .addReg(DstReg,
+                getKillRegState(DstIsKill) | getUndefRegState(DstIsUndef))
+        .addImm(0)
+        .addReg(SrcReg, getKillRegState(SrcIsKill))
+        .setMemRefs(MI.memoperands());
 
-  MIBLO.setMemRefs(MI.memoperands());
-  MIBHI.setMemRefs(MI.memoperands());
+  } else {
+    Register SrcLoReg, SrcHiReg;
+    TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
+    buildMI(MBB, MBBI, AVR::STPtrRr)
+        .addReg(DstReg, getUndefRegState(DstIsUndef))
+        .addReg(SrcLoReg, getKillRegState(SrcIsKill))
+        .setMemRefs(MI.memoperands());
+
+    buildMI(MBB, MBBI, AVR::STDPtrQRr)
+        .addReg(DstReg, getUndefRegState(DstIsUndef))
+        .addImm(1)
+        .addReg(SrcHiReg, getKillRegState(SrcIsKill))
+        .setMemRefs(MI.memoperands());
+  }
 
   MI.eraseFromParent();
   return true;
@@ -1071,6 +1125,7 @@ bool AVRExpandPseudo::expand<AVR::STWPtrPdRr>(Block &MBB, BlockIt MBBI) {
 template <>
 bool AVRExpandPseudo::expand<AVR::STDWPtrQRr>(Block &MBB, BlockIt MBBI) {
   MachineInstr &MI = *MBBI;
+  const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
 
   Register DstReg = MI.getOperand(0).getReg();
   bool DstIsKill = MI.getOperand(0).isKill();
@@ -1079,23 +1134,32 @@ bool AVRExpandPseudo::expand<AVR::STDWPtrQRr>(Block &MBB, BlockIt MBBI) {
   bool SrcIsKill = MI.getOperand(2).isKill();
 
   // STD's maximum displacement is 63, so larger stores have to be split into a
-  // set of operations
-  if (Imm >= 63) {
-    if (!DstIsKill) {
-      buildMI(MBB, MBBI, AVR::PUSHWRr).addReg(DstReg);
+  // set of operations.
+  // For avrtiny chips, STD is not available at all so we always have to fall
+  // back to manual pointer adjustments.
+  if (Imm >= 63 || STI.hasTinyEncoding()) {
+    // Add offset. The offset can be 0 when expanding this instruction from the
+    // more specific STWPtrRr instruction.
+    if (Imm != 0) {
+      buildMI(MBB, MBBI, AVR::SUBIWRdK, DstReg)
+          .addReg(DstReg, RegState::Kill)
+          .addImm(0x10000 - Imm);
     }
 
-    buildMI(MBB, MBBI, AVR::SUBIWRdK)
-        .addReg(DstReg, RegState::Define)
-        .addReg(DstReg, RegState::Kill)
-        .addImm(-Imm);
-
-    buildMI(MBB, MBBI, AVR::STWPtrRr)
-        .addReg(DstReg, RegState::Kill)
-        .addReg(SrcReg, getKillRegState(SrcIsKill));
+    // Do the store. This is a word store, that will be expanded further.
+    buildMI(MBB, MBBI, AVR::STWPtrPiRr, DstReg)
+        .addReg(DstReg, getKillRegState(DstIsKill))
+        .addReg(SrcReg, getKillRegState(SrcIsKill))
+        .addImm(0)
+        .setMemRefs(MI.memoperands());
 
+    // If the pointer is used after the store instruction, subtract the new
+    // offset (with 2 added after the postincrement instructions) so it is the
+    // same as before.
     if (!DstIsKill) {
-      buildMI(MBB, MBBI, AVR::POPWRd).addDef(DstReg, RegState::Define);
+      buildMI(MBB, MBBI, AVR::SUBIWRdK, DstReg)
+          .addReg(DstReg, RegState::Kill)
+          .addImm(Imm + 2);
     }
   } else {
     unsigned OpLo = AVR::STDPtrQRr;

diff  --git a/llvm/lib/Target/AVR/AVRFrameLowering.cpp b/llvm/lib/Target/AVR/AVRFrameLowering.cpp
index 3cbe691282f42..904cdf8420eb0 100644
--- a/llvm/lib/Target/AVR/AVRFrameLowering.cpp
+++ b/llvm/lib/Target/AVR/AVRFrameLowering.cpp
@@ -121,7 +121,8 @@ void AVRFrameLowering::emitPrologue(MachineFunction &MF,
   }
 
   // Reserve the necessary frame memory by doing FP -= <size>.
-  unsigned Opcode = (isUInt<6>(FrameSize)) ? AVR::SBIWRdK : AVR::SUBIWRdK;
+  unsigned Opcode = (isUInt<6>(FrameSize) && STI.hasADDSUBIW()) ? AVR::SBIWRdK
+                                                                : AVR::SUBIWRdK;
 
   MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(Opcode), AVR::R29R28)
                          .addReg(AVR::R29R28, RegState::Kill)
@@ -202,7 +203,7 @@ void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
     unsigned Opcode;
 
     // Select the optimal opcode depending on how big it is.
-    if (isUInt<6>(FrameSize)) {
+    if (isUInt<6>(FrameSize) && STI.hasADDSUBIW()) {
       Opcode = AVR::ADIWRdK;
     } else {
       Opcode = AVR::SUBIWRdK;
@@ -384,7 +385,7 @@ MachineBasicBlock::iterator AVRFrameLowering::eliminateCallFramePseudoInstr(
     // Select the best opcode to adjust SP based on the offset size.
     unsigned AddOpcode;
 
-    if (isUInt<6>(Amount)) {
+    if (isUInt<6>(Amount) && STI.hasADDSUBIW()) {
       AddOpcode = AVR::ADIWRdK;
     } else {
       AddOpcode = AVR::SUBIWRdK;
@@ -457,7 +458,8 @@ struct AVRFrameAnalyzer : public MachineFunctionPass {
         int Opcode = MI.getOpcode();
 
         if ((Opcode != AVR::LDDRdPtrQ) && (Opcode != AVR::LDDWRdPtrQ) &&
-            (Opcode != AVR::STDPtrQRr) && (Opcode != AVR::STDWPtrQRr)) {
+            (Opcode != AVR::STDPtrQRr) && (Opcode != AVR::STDWPtrQRr) &&
+            (Opcode != AVR::FRMIDX)) {
           continue;
         }
 

diff  --git a/llvm/lib/Target/AVR/AVRInstrInfo.td b/llvm/lib/Target/AVR/AVRInstrInfo.td
index 4d79ce755c9c8..412cb7ebb31b5 100644
--- a/llvm/lib/Target/AVR/AVRInstrInfo.td
+++ b/llvm/lib/Target/AVR/AVRInstrInfo.td
@@ -320,6 +320,9 @@ def HasBREAK : Predicate<"Subtarget->hasBREAK()">,
 def HasTinyEncoding : Predicate<"Subtarget->hasTinyEncoding()">,
                       AssemblerPredicate<(all_of FeatureTinyEncoding)>;
 
+def HasNonTinyEncoding : Predicate<"!Subtarget->hasTinyEncoding()">,
+                         AssemblerPredicate<(all_of (not FeatureTinyEncoding))>;
+
 // AVR specific condition code. These correspond to AVR_*_COND in
 // AVRInstrInfo.td. They must be kept in synch.
 def AVR_COND_EQ : PatLeaf<(i8 0)>;
@@ -1283,7 +1286,7 @@ let canFoldAsLoad = 1, isReMaterializable = 1 in {
                      "lds\t$rd, $k", [(set i8
                                        : $rd, (load imm
                                                : $k))]>,
-               Requires<[HasSRAM]>;
+               Requires<[HasSRAM, HasNonTinyEncoding]>;
 
   // LDSW Rd+1:Rd, K+1:K
   //
@@ -1297,7 +1300,7 @@ let canFoldAsLoad = 1, isReMaterializable = 1 in {
                        "ldsw\t$dst, $src", [(set i16
                                              : $dst, (load imm
                                                       : $src))]>,
-                Requires<[HasSRAM]>;
+                Requires<[HasSRAM, HasNonTinyEncoding]>;
 }
 
 // Indirect loads.
@@ -1315,8 +1318,12 @@ let canFoldAsLoad = 1, isReMaterializable = 1 in {
   // LDW Rd+1:Rd, P
   //
   // Expands to:
-  // ld  Rd,   P
-  // ldd Rd+1, P+1
+  //   ld  Rd,   P
+  //   ldd Rd+1, P+1
+  // On reduced tiny cores, this instruction expands to:
+  //   ld    Rd,   P+
+  //   ld    Rd+1, P+
+  //   subiw P,    2
   let Constraints = "@earlyclobber $reg" in def LDWRdPtr
       : Pseudo<(outs DREGS
                 : $reg),
@@ -1386,13 +1393,18 @@ let canFoldAsLoad = 1, isReMaterializable = 1 in {
                 "ldd\t$reg, $memri", [(set i8
                                        : $reg, (load addr
                                                 : $memri))]>,
-      Requires<[HasSRAM]>;
+      Requires<[HasSRAM, HasNonTinyEncoding]>;
 
   // LDDW Rd+1:Rd, P+q
   //
   // Expands to:
-  // ldd Rd,   P+q
-  // ldd Rd+1, P+q+1
+  //   ldd Rd,   P+q
+  //   ldd Rd+1, P+q+1
+  // On reduced tiny cores, this instruction expands to:
+  //   subiw P,    -q
+  //   ld    Rd,   P+
+  //   ld    Rd+1, P+
+  //   subiw P,    q+2
   let Constraints = "@earlyclobber $dst" in def LDDWRdPtrQ
       : Pseudo<(outs DREGS
                 : $dst),
@@ -1492,7 +1504,7 @@ def STSKRr : F32DM<0b1, (outs),
                    "sts\t$k, $rd", [(store i8
                                      : $rd, imm
                                      : $k)]>,
-             Requires<[HasSRAM]>;
+             Requires<[HasSRAM, HasNonTinyEncoding]>;
 
 // STSW K+1:K, Rr+1:Rr
 //
@@ -1506,7 +1518,7 @@ def STSWKRr : Pseudo<(outs),
                      "stsw\t$dst, $src", [(store i16
                                            : $src, imm
                                            : $dst)]>,
-              Requires<[HasSRAM]>;
+              Requires<[HasSRAM, HasNonTinyEncoding]>;
 
 // Indirect stores.
 // ST P, Rr
@@ -1524,8 +1536,12 @@ def STPtrRr : FSTLD<1, 0b00, (outs),
 // Stores the value of Rr into the location addressed by pointer P.
 //
 // Expands to:
-// st P, Rr
-// std P+1, Rr+1
+//   st P, Rr
+//   std P+1, Rr+1
+// On reduced tiny cores, this instruction expands to:
+//   st    P+, Rr
+//   st    P+, Rr+1
+//   subiw P,  q+2
 def STWPtrRr : Pseudo<(outs),
                       (ins PTRDISPREGS
                        : $ptrreg, DREGS
@@ -1624,15 +1640,20 @@ def STDPtrQRr : FSTDLDD<1, (outs),
                         "std\t$memri, $reg", [(store i8
                                                : $reg, addr
                                                : $memri)]>,
-                Requires<[HasSRAM]>;
+                Requires<[HasSRAM, HasNonTinyEncoding]>;
 
 // STDW P+q, Rr+1:Rr
 // Stores the value of Rr into the location addressed by pointer P with a
 // displacement of q. Does not modify P.
 //
 // Expands to:
-// std P+q,   Rr
-// std P+q+1, Rr+1
+//   std P+q,   Rr
+//   std P+q+1, Rr+1
+// On reduced tiny cores, this instruction expands to:
+//   subiw P,  -q
+//   st    P+, Rr
+//   st    P+, Rr+1
+//   subiw P,  q+2
 def STDWPtrQRr : Pseudo<(outs),
                         (ins memri
                          : $memri, DREGS
@@ -2413,7 +2434,8 @@ def : Pat<(add i16
            : $src2),
           (SBIWRdK i16
            : $src1, (imm0_63_neg
-                     : $src2))>;
+                     : $src2))>,
+          Requires<[HasADDSUBIW]>;
 def : Pat<(add i16
            : $src1, imm
            : $src2),
@@ -2481,26 +2503,18 @@ def : Pat<(add i16
           (SUBIWRdK i16
            : $src, tglobaladdr
            : $src2)>;
-def : Pat<(i8(load(AVRWrapper tglobaladdr
-                   : $dst))),
-          (LDSRdK tglobaladdr
-           : $dst)>;
-def : Pat<(i16(load(AVRWrapper tglobaladdr
-                    : $dst))),
-          (LDSWRdK tglobaladdr
-           : $dst)>;
-def : Pat<(store i8
-           : $src, (i16(AVRWrapper tglobaladdr
-                        : $dst))),
-          (STSKRr tglobaladdr
-           : $dst, i8
-           : $src)>;
-def : Pat<(store i16
-           : $src, (i16(AVRWrapper tglobaladdr
-                        : $dst))),
-          (STSWKRr tglobaladdr
-           : $dst, i16
-           : $src)>;
+def : Pat<(i8(load(AVRWrapper tglobaladdr:$dst))),
+          (LDSRdK tglobaladdr:$dst)>,
+          Requires<[HasSRAM, HasNonTinyEncoding]>;
+def : Pat<(i16(load(AVRWrapper tglobaladdr:$dst))),
+          (LDSWRdK tglobaladdr:$dst)>,
+          Requires<[HasSRAM, HasNonTinyEncoding]>;
+def : Pat<(store i8:$src, (i16(AVRWrapper tglobaladdr:$dst))),
+          (STSKRr tglobaladdr:$dst, i8:$src)>,
+          Requires<[HasSRAM, HasNonTinyEncoding]>;
+def : Pat<(store i16:$src, (i16(AVRWrapper tglobaladdr:$dst))),
+          (STSWKRr tglobaladdr:$dst, i16:$src)>,
+          Requires<[HasSRAM, HasNonTinyEncoding]>;
 
 // BlockAddress
 def : Pat<(i16(AVRWrapper tblockaddress

diff  --git a/llvm/lib/Target/AVR/AVRRegisterInfo.cpp b/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
index 327f406718a55..d345c50488a29 100644
--- a/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
+++ b/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
@@ -166,18 +166,28 @@ bool AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
   // instruction. We have only two-address instructions, thus we need to
   // expand it into move + add.
   if (MI.getOpcode() == AVR::FRMIDX) {
-    MI.setDesc(TII.get(AVR::MOVWRdRr));
-    MI.getOperand(FIOperandNum).ChangeToRegister(AVR::R29R28, false);
-    MI.removeOperand(2);
+    Register DstReg = MI.getOperand(0).getReg();
+    assert(DstReg != AVR::R29R28 && "Dest reg cannot be the frame pointer");
+
+    // Copy the frame pointer.
+    if (STI.hasMOVW()) {
+      BuildMI(MBB, MI, dl, TII.get(AVR::MOVWRdRr), DstReg)
+          .addReg(AVR::R29R28);
+    } else {
+      Register DstLoReg, DstHiReg;
+      splitReg(DstReg, DstLoReg, DstHiReg);
+      BuildMI(MBB, MI, dl, TII.get(AVR::MOVRdRr), DstLoReg)
+          .addReg(AVR::R28);
+      BuildMI(MBB, MI, dl, TII.get(AVR::MOVRdRr), DstHiReg)
+          .addReg(AVR::R29);
+    }
 
     assert(Offset > 0 && "Invalid offset");
 
     // We need to materialize the offset via an add instruction.
     unsigned Opcode;
-    Register DstReg = MI.getOperand(0).getReg();
-    assert(DstReg != AVR::R29R28 && "Dest reg cannot be the frame pointer");
 
-    II++; // Skip over the FRMIDX (and now MOVW) instruction.
+    II++; // Skip over the FRMIDX instruction.
 
     // Generally, to load a frame address two add instructions are emitted that
     // could get folded into a single one:
@@ -195,7 +205,7 @@ bool AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
     case AVR::R25R24:
     case AVR::R27R26:
     case AVR::R31R30: {
-      if (isUInt<6>(Offset)) {
+      if (isUInt<6>(Offset) && STI.hasADDSUBIW()) {
         Opcode = AVR::ADIWRdK;
         break;
       }
@@ -214,19 +224,28 @@ bool AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
                             .addImm(Offset);
     New->getOperand(3).setIsDead();
 
+    MI.eraseFromParent(); // remove FRMIDX
+
     return false;
   }
 
+  // On most AVRs, we can use an offset up to 62 for load/store with
+  // displacement (63 for byte values, 62 for word values). However, the
+  // "reduced tiny" cores don't support load/store with displacement. So for
+  // them, we force an offset of 0 meaning that any positive offset will require
+  // adjusting the frame pointer.
+  int MaxOffset = STI.hasTinyEncoding() ? 0 : 62;
+
   // If the offset is too big we have to adjust and restore the frame pointer
   // to materialize a valid load/store with displacement.
   //: TODO: consider using only one adiw/sbiw chain for more than one frame
   //: index
-  if (Offset > 62) {
+  if (Offset > MaxOffset) {
     unsigned AddOpc = AVR::ADIWRdK, SubOpc = AVR::SBIWRdK;
-    int AddOffset = Offset - 63 + 1;
+    int AddOffset = Offset - MaxOffset;
 
     // For huge offsets where adiw/sbiw cannot be used use a pair of subi/sbci.
-    if ((Offset - 63 + 1) > 63) {
+    if ((Offset - MaxOffset) > 63 || !STI.hasADDSUBIW()) {
       AddOpc = AVR::SUBIWRdK;
       SubOpc = AVR::SUBIWRdK;
       AddOffset = -AddOffset;
@@ -253,9 +272,9 @@ bool AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
     // cond branch it will be using a dead register.
     BuildMI(MBB, std::next(II), dl, TII.get(SubOpc), AVR::R29R28)
         .addReg(AVR::R29R28, RegState::Kill)
-        .addImm(Offset - 63 + 1);
+        .addImm(Offset - MaxOffset);
 
-    Offset = 62;
+    Offset = MaxOffset;
   }
 
   MI.getOperand(FIOperandNum).ChangeToRegister(AVR::R29R28, false);

diff  --git a/llvm/test/CodeGen/AVR/calling-conv/c/tiny.ll b/llvm/test/CodeGen/AVR/calling-conv/c/tiny.ll
index 2c6e97e6221a3..ab1035d086021 100644
--- a/llvm/test/CodeGen/AVR/calling-conv/c/tiny.ll
+++ b/llvm/test/CodeGen/AVR/calling-conv/c/tiny.ll
@@ -40,8 +40,6 @@ define i16 @foo2(i16 %a, i16 %b, i16 %c) {
 
 ; NOTE:  %a(i16), %b(i16) and %c(i16) each costs two registers,
 ;        while %d(i16) is passed via the stack.
-; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows
-;        how arguments are passed.
 define i16 @foo3(i16 %a, i16 %b, i16 %c, i16 %d) {
 ; CHECK-LABEL: foo3:
 ; CHECK:       ; %bb.0:
@@ -49,8 +47,16 @@ define i16 @foo3(i16 %a, i16 %b, i16 %c, i16 %d) {
 ; CHECK-NEXT:    push r29
 ; CHECK-NEXT:    in r28, 61
 ; CHECK-NEXT:    in r29, 62
-; CHECK-NEXT:    ldd r30, Y+5
-; CHECK-NEXT:    ldd r31, Y+6
+; CHECK-NEXT:    in r16, 63
+; CHECK-NEXT:    subi r28, 251
+; CHECK-NEXT:    sbci r29, 255
+; CHECK-NEXT:    ld r30, Y+
+; CHECK-NEXT:    ld r31, Y+
+; CHECK-NEXT:    subi r28, 2
+; CHECK-NEXT:    sbci r29, 0
+; CHECK-NEXT:    subi r28, 5
+; CHECK-NEXT:    sbci r29, 0
+; CHECK-NEXT:    out 63, r16
 ; CHECK-NEXT:    sub r20, r30
 ; CHECK-NEXT:    sbc r21, r31
 ; CHECK-NEXT:    sub r24, r22
@@ -67,8 +73,6 @@ define i16 @foo3(i16 %a, i16 %b, i16 %c, i16 %d) {
 }
 
 ; NOTE: %a(i32) costs four registers, while %b(i32) is passed via the stack.
-; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows
-;        how arguments are passed.
 define i32 @foo4(i32 %a, i32 %b) {
 ; CHECK-LABEL: foo4:
 ; CHECK:       ; %bb.0:
@@ -76,10 +80,26 @@ define i32 @foo4(i32 %a, i32 %b) {
 ; CHECK-NEXT:    push r29
 ; CHECK-NEXT:    in r28, 61
 ; CHECK-NEXT:    in r29, 62
-; CHECK-NEXT:    ldd r20, Y+5
-; CHECK-NEXT:    ldd r21, Y+6
-; CHECK-NEXT:    ldd r30, Y+7
-; CHECK-NEXT:    ldd r31, Y+8
+; CHECK-NEXT:    in r16, 63
+; CHECK-NEXT:    subi r28, 251
+; CHECK-NEXT:    sbci r29, 255
+; CHECK-NEXT:    ld r20, Y+
+; CHECK-NEXT:    ld r21, Y+
+; CHECK-NEXT:    subi r28, 2
+; CHECK-NEXT:    sbci r29, 0
+; CHECK-NEXT:    subi r28, 5
+; CHECK-NEXT:    sbci r29, 0
+; CHECK-NEXT:    out 63, r16
+; CHECK-NEXT:    in r16, 63
+; CHECK-NEXT:    subi r28, 249
+; CHECK-NEXT:    sbci r29, 255
+; CHECK-NEXT:    ld r30, Y+
+; CHECK-NEXT:    ld r31, Y+
+; CHECK-NEXT:    subi r28, 2
+; CHECK-NEXT:    sbci r29, 0
+; CHECK-NEXT:    subi r28, 7
+; CHECK-NEXT:    sbci r29, 0
+; CHECK-NEXT:    out 63, r16
 ; CHECK-NEXT:    sub r20, r22
 ; CHECK-NEXT:    sbc r21, r23
 ; CHECK-NEXT:    sbc r30, r24
@@ -96,8 +116,6 @@ define i32 @foo4(i32 %a, i32 %b) {
 }
 
 ; NOTE: %0 costs six registers, while %1 is passed via the stack.
-; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows
-;        how arguments are passed.
 define i8 @foo5([5 x i8] %0, i8 %1) {
 ; CHECK-LABEL: foo5:
 ; CHECK:       ; %bb.0:
@@ -105,7 +123,11 @@ define i8 @foo5([5 x i8] %0, i8 %1) {
 ; CHECK-NEXT:    push r29
 ; CHECK-NEXT:    in r28, 61
 ; CHECK-NEXT:    in r29, 62
-; CHECK-NEXT:    ldd r24, Y+5
+; CHECK-NEXT:    mov r26, r28
+; CHECK-NEXT:    mov r27, r29
+; CHECK-NEXT:    subi r26, 251
+; CHECK-NEXT:    sbci r27, 255
+; CHECK-NEXT:    ld r24, X
 ; CHECK-NEXT:    add r24, r20
 ; CHECK-NEXT:    pop r29
 ; CHECK-NEXT:    pop r28
@@ -129,8 +151,6 @@ define i8 @foo6([2 x i8] %0, [4 x i8] %1) {
 
 ; NOTE: %0 cost four registers, while %1 is passed via the stack,
 ;       though there are two vacant registers.
-; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows
-;        how arguments are passed.
 define i8 @foo7([3 x i8] %0, [3 x i8] %1) {
 ; CHECK-LABEL: foo7:
 ; CHECK:       ; %bb.0:
@@ -138,7 +158,11 @@ define i8 @foo7([3 x i8] %0, [3 x i8] %1) {
 ; CHECK-NEXT:    push r29
 ; CHECK-NEXT:    in r28, 61
 ; CHECK-NEXT:    in r29, 62
-; CHECK-NEXT:    ldd r24, Y+5
+; CHECK-NEXT:    mov r26, r28
+; CHECK-NEXT:    mov r27, r29
+; CHECK-NEXT:    subi r26, 251
+; CHECK-NEXT:    sbci r27, 255
+; CHECK-NEXT:    ld r24, X
 ; CHECK-NEXT:    add r24, r22
 ; CHECK-NEXT:    pop r29
 ; CHECK-NEXT:    pop r28
@@ -151,8 +175,6 @@ define i8 @foo7([3 x i8] %0, [3 x i8] %1) {
 
 ; NOTE: %0 costs four registers, and %1 costs two registers, while %2 is
 ;       passed via the stack, though there is one vacant register.
-; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows
-;        how arguments are passed.
 define i8 @foo8([3 x i8] %0, i8 %1, i8 %2) {
 ; CHECK-LABEL: foo8:
 ; CHECK:       ; %bb.0:
@@ -161,7 +183,11 @@ define i8 @foo8([3 x i8] %0, i8 %1, i8 %2) {
 ; CHECK-NEXT:    in r28, 61
 ; CHECK-NEXT:    in r29, 62
 ; CHECK-NEXT:    add r22, r20
-; CHECK-NEXT:    ldd r24, Y+5
+; CHECK-NEXT:    mov r26, r28
+; CHECK-NEXT:    mov r27, r29
+; CHECK-NEXT:    subi r26, 251
+; CHECK-NEXT:    sbci r27, 255
+; CHECK-NEXT:    ld r24, X
 ; CHECK-NEXT:    sub r24, r22
 ; CHECK-NEXT:    pop r29
 ; CHECK-NEXT:    pop r28
@@ -173,8 +199,6 @@ define i8 @foo8([3 x i8] %0, i8 %1, i8 %2) {
 }
 
 ; NOTE: %0 is passed via registers, though there are 6 vacant registers.
-; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows
-;        how arguments are passed.
 define i8 @foo9([7 x i8] %0) {
 ; CHECK-LABEL: foo9:
 ; CHECK:       ; %bb.0:
@@ -182,8 +206,16 @@ define i8 @foo9([7 x i8] %0) {
 ; CHECK-NEXT:    push r29
 ; CHECK-NEXT:    in r28, 61
 ; CHECK-NEXT:    in r29, 62
-; CHECK-NEXT:    ldd r25, Y+6
-; CHECK-NEXT:    ldd r24, Y+5
+; CHECK-NEXT:    mov r26, r28
+; CHECK-NEXT:    mov r27, r29
+; CHECK-NEXT:    subi r26, 250
+; CHECK-NEXT:    sbci r27, 255
+; CHECK-NEXT:    ld r25, X
+; CHECK-NEXT:    mov r26, r28
+; CHECK-NEXT:    mov r27, r29
+; CHECK-NEXT:    subi r26, 251
+; CHECK-NEXT:    sbci r27, 255
+; CHECK-NEXT:    ld r24, X
 ; CHECK-NEXT:    add r24, r25
 ; CHECK-NEXT:    pop r29
 ; CHECK-NEXT:    pop r28
@@ -195,8 +227,6 @@ define i8 @foo9([7 x i8] %0) {
 }
 
 ; NOTE: %0 costs six registers, while %1 and %2 are passed via the stack.
-; FIXME: The `ldd` instruction is invalid on avrtiny, this test just shows
-;        how arguments are passed.
 define i8 @fooa([6 x i8] %0, i8 %1, i8 %2) {
 ; CHECK-LABEL: fooa:
 ; CHECK:       ; %bb.0:
@@ -204,8 +234,16 @@ define i8 @fooa([6 x i8] %0, i8 %1, i8 %2) {
 ; CHECK-NEXT:    push r29
 ; CHECK-NEXT:    in r28, 61
 ; CHECK-NEXT:    in r29, 62
-; CHECK-NEXT:    ldd r25, Y+5
-; CHECK-NEXT:    ldd r24, Y+6
+; CHECK-NEXT:    mov r26, r28
+; CHECK-NEXT:    mov r27, r29
+; CHECK-NEXT:    subi r26, 251
+; CHECK-NEXT:    sbci r27, 255
+; CHECK-NEXT:    ld r25, X
+; CHECK-NEXT:    mov r26, r28
+; CHECK-NEXT:    mov r27, r29
+; CHECK-NEXT:    subi r26, 250
+; CHECK-NEXT:    sbci r27, 255
+; CHECK-NEXT:    ld r24, X
 ; CHECK-NEXT:    sub r24, r25
 ; CHECK-NEXT:    sub r24, r20
 ; CHECK-NEXT:    pop r29

diff  --git a/llvm/test/CodeGen/AVR/directmem.ll b/llvm/test/CodeGen/AVR/directmem.ll
index 6e1f72eceb81c..256325ce67e98 100644
--- a/llvm/test/CodeGen/AVR/directmem.ll
+++ b/llvm/test/CodeGen/AVR/directmem.ll
@@ -1,4 +1,5 @@
 ; RUN: llc -mattr=sram,addsubiw < %s -march=avr | FileCheck %s
+; RUN: llc -mattr=sram,avrtiny < %s -march=avr | FileCheck %s --check-prefix=CHECK-TINY
 
 @char = common global i8 0
 @char.array = common global [3 x i8] zeroinitializer
@@ -20,6 +21,11 @@ define void @global8_store() {
 ; CHECK-LABEL: global8_store:
 ; CHECK: ldi [[REG:r[0-9]+]], 6
 ; CHECK: sts char, [[REG]]
+; CHECK-TINY-LABEL: global8_store:
+; CHECK-TINY: ldi [[REG1:r[0-9]+]], 6
+; CHECK-TINY: ldi [[REG2:r[0-9]+]], lo8(char)
+; CHECK-TINY: ldi [[REG3:r[0-9]+]], hi8(char)
+; CHECK-TINY: st [[REG4:[X-Z]]], [[REG1]]
   store i8 6, i8* @char
   ret void
 }
@@ -27,6 +33,10 @@ define void @global8_store() {
 define i8 @global8_load() {
 ; CHECK-LABEL: global8_load:
 ; CHECK: lds r24, char
+; CHECK-TINY-LABEL: global8_load:
+; CHECK-TINY: ldi [[REG1:r[0-9]+]], lo8(char)
+; CHECK-TINY: ldi [[REG2:r[0-9]+]], hi8(char)
+; CHECK-TINY: ld [[REG3:r[0-9]+]], [[REG4:[X-Z]]]
   %result = load i8, i8* @char
   ret i8 %result
 }

diff  --git a/llvm/test/CodeGen/AVR/pseudo/LDDWRdPtrQ.mir b/llvm/test/CodeGen/AVR/pseudo/LDDWRdPtrQ.mir
index d13bf118b98d3..920d332134a66 100644
--- a/llvm/test/CodeGen/AVR/pseudo/LDDWRdPtrQ.mir
+++ b/llvm/test/CodeGen/AVR/pseudo/LDDWRdPtrQ.mir
@@ -1,4 +1,5 @@
-# RUN: llc -O0  %s -o - -march=avr | FileCheck %s
+# RUN: llc -O0 -run-pass=avr-expand-pseudo %s -o - | FileCheck %s
+# RUN: llc -O0 -run-pass=avr-expand-pseudo -mattr=avrtiny %s -o - | FileCheck %s --check-prefix=CHECK-TINY
 
 # This test checks the expansion of the 16-bit 'LDDWRdPtrQ' pseudo instruction.
 
@@ -19,8 +20,15 @@ body: |
 
     ; CHECK-LABEL: test_lddwrdptrq
 
-    ; CHECK:      ldd     r24, Z+10
-    ; CHECK-NEXT: ldd     r25, Z+11
+    ; CHECK:      $r24 = LDDRdPtrQ $r31r30, 10
+    ; CHECK-NEXT: $r25 = LDDRdPtrQ $r31r30, 11
+
+    ; CHECK-TINY:      $r30 = SUBIRdK $r30, 246, implicit-def $sreg
+    ; CHECK-TINY-NEXT: $r31 = SBCIRdK $r31, 255, implicit-def $sreg, implicit killed $sreg
+    ; CHECK-TINY-NEXT: $r24, $r31r30 = LDRdPtrPi killed $r31r30
+    ; CHECK-TINY-NEXT: $r25, $r31r30 = LDRdPtrPi killed $r31r30
+    ; CHECK-TINY-NEXT: $r30 = SUBIRdK $r30, 12, implicit-def $sreg
+    ; CHECK-TINY-NEXT: $r31 = SBCIRdK $r31, 0, implicit-def $sreg, implicit killed $sreg
 
     early-clobber $r25r24 = LDDWRdPtrQ undef $r31r30, 10
 ...

diff  --git a/llvm/test/CodeGen/AVR/pseudo/LDWRdPtr.mir b/llvm/test/CodeGen/AVR/pseudo/LDWRdPtr.mir
index d86b60b97e347..6a240b829f978 100644
--- a/llvm/test/CodeGen/AVR/pseudo/LDWRdPtr.mir
+++ b/llvm/test/CodeGen/AVR/pseudo/LDWRdPtr.mir
@@ -1,4 +1,5 @@
 # RUN: llc -O0 -run-pass=avr-expand-pseudo %s -o - | FileCheck %s
+# RUN: llc -O0 -run-pass=avr-expand-pseudo -mattr=avrtiny %s -o - | FileCheck %s --check-prefix=CHECK-TINY
 
 # This test checks the expansion of the 16-bit LDWRdPtr pseudo instruction.
 
@@ -21,5 +22,10 @@ body: |
     ; CHECK:               $r0 = LDRdPtr $r31r30
     ; CHECK-NEXT:          $r1 = LDDRdPtrQ $r31r30, 1
 
+    ; CHECK-TINY:      $r0, $r31r30 = LDRdPtrPi killed $r31r30
+    ; CHECK-TINY-NEXT: $r1, $r31r30 = LDRdPtrPi killed $r31r30
+    ; CHECK-TINY-NEXT: $r30 = SUBIRdK $r30, 2, implicit-def $sreg
+    ; CHECK-TINY-NEXT: $r31 = SBCIRdK $r31, 0, implicit-def $sreg, implicit killed $sreg
+
     $r1r0 = LDWRdPtr $r31r30
 ...

diff  --git a/llvm/test/CodeGen/AVR/pseudo/STDWPtrQRr.mir b/llvm/test/CodeGen/AVR/pseudo/STDWPtrQRr.mir
index fbec684516ee7..535296d53b37e 100644
--- a/llvm/test/CodeGen/AVR/pseudo/STDWPtrQRr.mir
+++ b/llvm/test/CodeGen/AVR/pseudo/STDWPtrQRr.mir
@@ -1,4 +1,5 @@
 # RUN: llc -O0 -run-pass=avr-expand-pseudo -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -O0 -run-pass=avr-expand-pseudo -verify-machineinstrs -mattr=avrtiny %s -o - | FileCheck %s --check-prefix=CHECK-TINY
 
 --- |
   target triple = "avr--"
@@ -18,6 +19,12 @@ body: |
     ; Small displacement (<63):
     ; CHECK:      STDPtrQRr $r29r28, 3, $r0
     ; CHECK-NEXT: STDPtrQRr $r29r28, 4, $r1
+    ; CHECK-TINY:      $r28 = SUBIRdK killed $r28, 253, implicit-def $sreg
+    ; CHECK-TINY-NEXT: $r29 = SBCIRdK killed $r29, 255, implicit-def $sreg, implicit killed $sreg
+    ; CHECK-TINY-NEXT: early-clobber $r29r28 = STPtrPiRr killed $r29r28, $r0, 0
+    ; CHECK-TINY-NEXT: early-clobber $r29r28 = STPtrPiRr killed $r29r28, $r1, 0
+    ; CHECK-TINY-NEXT: $r28 = SUBIRdK killed $r28, 5, implicit-def $sreg
+    ; CHECK-TINY-NEXT: $r29 = SBCIRdK killed $r29, 0, implicit-def $sreg, implicit killed $sreg
     STDWPtrQRr $r29r28, 3, $r1r0
 
     ; Small displacement where the destination register is killed:
@@ -36,31 +43,33 @@ body: |
     STDWPtrQRr $r29r28, 62, $r1r0
 
     ; Large displacement (>=63):
-    ; CHECK: PUSHRr $r28, implicit-def $sp, implicit $sp
-    ; CHECK-NEXT: PUSHRr $r29, implicit-def $sp, implicit $sp
-    ; CHECK-NEXT: $r28 = SUBIRdK killed $r28, 193, implicit-def $sreg
+    ; CHECK:      $r28 = SUBIRdK killed $r28, 193, implicit-def $sreg
     ; CHECK-NEXT: $r29 = SBCIRdK killed $r29, 255, implicit-def $sreg, implicit killed $sreg
-    ; CHECK-NEXT: STPtrRr $r29r28, $r0
-    ; CHECK-NEXT: STDPtrQRr $r29r28, 1, $r1
-    ; CHECK-NEXT: $r29 = POPRd implicit-def $sp, implicit $sp
-    ; CHECK-NEXT: $r28 = POPRd implicit-def $sp, implicit $sp
+    ; CHECK-NEXT: $r29r28 = STPtrPiRr killed $r29r28, $r0, 0
+    ; CHECK-NEXT: $r29r28 = STPtrPiRr killed $r29r28, $r1, 0
+    ; CHECK-NEXT: $r28 = SUBIRdK killed $r28, 65, implicit-def $sreg
+    ; CHECK-NEXT: $r29 = SBCIRdK killed $r29, 0, implicit-def $sreg, implicit killed $sreg
+    ; CHECK-TINY:      $r28 = SUBIRdK killed $r28, 193, implicit-def $sreg
+    ; CHECK-TINY-NEXT: $r29 = SBCIRdK killed $r29, 255, implicit-def $sreg, implicit killed $sreg
+    ; CHECK-TINY-NEXT: $r29r28 = STPtrPiRr killed $r29r28, $r0, 0
+    ; CHECK-TINY-NEXT: $r29r28 = STPtrPiRr killed $r29r28, $r1, 0
+    ; CHECK-TINY-NEXT: $r28 = SUBIRdK killed $r28, 65, implicit-def $sreg
+    ; CHECK-TINY-NEXT: $r29 = SBCIRdK killed $r29, 0, implicit-def $sreg, implicit killed $sreg
     STDWPtrQRr $r29r28, 63, $r1r0
 
     ; Large displacement where the destination register is killed:
     ; CHECK: $r28 = SUBIRdK killed $r28, 193, implicit-def $sreg
     ; CHECK-NEXT: $r29 = SBCIRdK killed $r29, 255, implicit-def $sreg, implicit killed $sreg
-    ; CHECK-NEXT: STPtrRr $r29r28, $r0
-    ; CHECK-NEXT: STDPtrQRr $r29r28, 1, $r1
+    ; CHECK-NEXT: $r29r28 = STPtrPiRr killed $r29r28, $r0
+    ; CHECK-NEXT: $r29r28 = STPtrPiRr killed $r29r28, $r1
     STDWPtrQRr killed $r29r28, 63, $r1r0
 
     ; Large displacement where the source register is killed:
-    ; CHECK: PUSHRr $r28, implicit-def $sp, implicit $sp
-    ; CHECK-NEXT: PUSHRr $r29, implicit-def $sp, implicit $sp
-    ; CHECK-NEXT: $r28 = SUBIRdK killed $r28, 193, implicit-def $sreg
+    ; CHECK:      $r28 = SUBIRdK killed $r28, 193, implicit-def $sreg
     ; CHECK-NEXT: $r29 = SBCIRdK killed $r29, 255, implicit-def $sreg, implicit killed $sreg
-    ; CHECK-NEXT: STPtrRr $r29r28, killed $r0
-    ; CHECK-NEXT: STDPtrQRr $r29r28, 1, killed $r1
-    ; CHECK-NEXT: $r29 = POPRd implicit-def $sp, implicit $sp
-    ; CHECK-NEXT: $r28 = POPRd implicit-def $sp, implicit $sp
+    ; CHECK-NEXT: STPtrPiRr killed $r29r28, killed $r0, 0
+    ; CHECK-NEXT: STPtrPiRr killed $r29r28, killed $r1, 0
+    ; CHECK-NEXT: $r28 = SUBIRdK killed $r28, 65, implicit-def $sreg
+    ; CHECK-NEXT: $r29 = SBCIRdK killed $r29, 0, implicit-def $sreg, implicit killed $sreg
     STDWPtrQRr $r29r28, 63, killed $r1r0
 ...

diff  --git a/llvm/test/CodeGen/AVR/pseudo/STWPtrRr.mir b/llvm/test/CodeGen/AVR/pseudo/STWPtrRr.mir
index 203eb6b0401dc..b6449ae1d438c 100644
--- a/llvm/test/CodeGen/AVR/pseudo/STWPtrRr.mir
+++ b/llvm/test/CodeGen/AVR/pseudo/STWPtrRr.mir
@@ -1,4 +1,5 @@
 # RUN: llc -O0 -run-pass=avr-expand-pseudo %s -o - | FileCheck %s
+# RUN: llc -O0 -run-pass=avr-expand-pseudo -mattr=avrtiny %s -o - | FileCheck %s --check-prefix=CHECK-TINY
 
 # This test checks the expansion of the 16-bit STSWRdK pseudo instruction.
 
@@ -21,5 +22,10 @@ body: |
     ; CHECK:      STPtrRr $r31r30, $r16
     ; CHECK-NEXT: STDPtrQRr $r31r30, 1, $r17
 
+    ; CHECK-TINY:      $r31r30 = STPtrPiRr killed $r31r30, $r16, 0
+    ; CHECK-TINY-NEXT: $r31r30 = STPtrPiRr killed $r31r30, $r17, 0
+    ; CHECK-TINY-NEXT: $r30 = SUBIRdK killed $r30, 2, implicit-def $sreg
+    ; CHECK-TINY-NEXT: $r31 = SBCIRdK killed $r31, 0, implicit-def $sreg, implicit killed $sreg
+
     STWPtrRr $r31r30, $r17r16
 ...

diff  --git a/llvm/test/CodeGen/AVR/return.ll b/llvm/test/CodeGen/AVR/return.ll
index afe358026b96a..8cb9f1427c77a 100644
--- a/llvm/test/CodeGen/AVR/return.ll
+++ b/llvm/test/CodeGen/AVR/return.ll
@@ -1,6 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: llc -mattr=avr6,sram -mtriple=avr < %s | FileCheck %s --check-prefix=AVR
-; RUN: llc -mattr=tinyencoding -mtriple=avr < %s | FileCheck %s --check-prefix=TINY
+; RUN: llc -mcpu=attiny10 -mtriple=avr < %s | FileCheck %s --check-prefix=TINY
 
 ;TODO: test returning byval structs
 ; TODO: test naked functions
@@ -136,10 +136,26 @@ define i32 @return32_arg2(i32 %x, i32 %y, i32 %z) {
 ; TINY-NEXT:    push r29
 ; TINY-NEXT:    in r28, 61
 ; TINY-NEXT:    in r29, 62
-; TINY-NEXT:    ldd r22, Y+9
-; TINY-NEXT:    ldd r23, Y+10
-; TINY-NEXT:    ldd r24, Y+11
-; TINY-NEXT:    ldd r25, Y+12
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 247
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r22, Y+
+; TINY-NEXT:    ld r23, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 9
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 245
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r24, Y+
+; TINY-NEXT:    ld r25, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 11
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
 ; TINY-NEXT:    pop r29
 ; TINY-NEXT:    pop r28
 ; TINY-NEXT:    ret
@@ -165,20 +181,32 @@ define i64 @return64_imm() {
 ; TINY-NEXT:    ldi r21, 190
 ; TINY-NEXT:    mov r30, r24
 ; TINY-NEXT:    mov r31, r25
-; TINY-NEXT:    std Z+6, r20
-; TINY-NEXT:    std Z+7, r21
+; TINY-NEXT:    subi r30, 250
+; TINY-NEXT:    sbci r31, 255
+; TINY-NEXT:    st Z+, r20
+; TINY-NEXT:    st Z+, r21
+; TINY-NEXT:    subi r30, 8
+; TINY-NEXT:    sbci r31, 0
 ; TINY-NEXT:    ldi r24, 25
 ; TINY-NEXT:    ldi r25, 22
-; TINY-NEXT:    std Z+4, r24
-; TINY-NEXT:    std Z+5, r25
+; TINY-NEXT:    subi r30, 252
+; TINY-NEXT:    sbci r31, 255
+; TINY-NEXT:    st Z+, r24
+; TINY-NEXT:    st Z+, r25
+; TINY-NEXT:    subi r30, 6
+; TINY-NEXT:    sbci r31, 0
 ; TINY-NEXT:    ldi r24, 104
 ; TINY-NEXT:    ldi r25, 37
-; TINY-NEXT:    std Z+2, r24
-; TINY-NEXT:    std Z+3, r25
+; TINY-NEXT:    subi r30, 254
+; TINY-NEXT:    sbci r31, 255
+; TINY-NEXT:    st Z+, r24
+; TINY-NEXT:    st Z+, r25
+; TINY-NEXT:    subi r30, 4
+; TINY-NEXT:    sbci r31, 0
 ; TINY-NEXT:    ldi r24, 204
 ; TINY-NEXT:    ldi r25, 204
-; TINY-NEXT:    st Z, r24
-; TINY-NEXT:    std Z+1, r25
+; TINY-NEXT:    st Z+, r24
+; TINY-NEXT:    st Z+, r25
 ; TINY-NEXT:    ret
     ret i64 13757395258967641292
 }
@@ -194,24 +222,68 @@ define i64 @return64_arg(i64 %x) {
 ; TINY-NEXT:    push r29
 ; TINY-NEXT:    in r28, 61
 ; TINY-NEXT:    in r29, 62
-; TINY-NEXT:    ldd r20, Y+11
-; TINY-NEXT:    ldd r21, Y+12
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 245
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r20, Y+
+; TINY-NEXT:    ld r21, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 11
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
 ; TINY-NEXT:    mov r30, r24
 ; TINY-NEXT:    mov r31, r25
-; TINY-NEXT:    std Z+6, r20
-; TINY-NEXT:    std Z+7, r21
-; TINY-NEXT:    ldd r24, Y+9
-; TINY-NEXT:    ldd r25, Y+10
-; TINY-NEXT:    std Z+4, r24
-; TINY-NEXT:    std Z+5, r25
-; TINY-NEXT:    ldd r24, Y+7
-; TINY-NEXT:    ldd r25, Y+8
-; TINY-NEXT:    std Z+2, r24
-; TINY-NEXT:    std Z+3, r25
-; TINY-NEXT:    ldd r24, Y+5
-; TINY-NEXT:    ldd r25, Y+6
-; TINY-NEXT:    st Z, r24
-; TINY-NEXT:    std Z+1, r25
+; TINY-NEXT:    subi r30, 250
+; TINY-NEXT:    sbci r31, 255
+; TINY-NEXT:    st Z+, r20
+; TINY-NEXT:    st Z+, r21
+; TINY-NEXT:    subi r30, 8
+; TINY-NEXT:    sbci r31, 0
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 247
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r24, Y+
+; TINY-NEXT:    ld r25, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 9
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
+; TINY-NEXT:    subi r30, 252
+; TINY-NEXT:    sbci r31, 255
+; TINY-NEXT:    st Z+, r24
+; TINY-NEXT:    st Z+, r25
+; TINY-NEXT:    subi r30, 6
+; TINY-NEXT:    sbci r31, 0
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 249
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r24, Y+
+; TINY-NEXT:    ld r25, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 7
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
+; TINY-NEXT:    subi r30, 254
+; TINY-NEXT:    sbci r31, 255
+; TINY-NEXT:    st Z+, r24
+; TINY-NEXT:    st Z+, r25
+; TINY-NEXT:    subi r30, 4
+; TINY-NEXT:    sbci r31, 0
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 251
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r24, Y+
+; TINY-NEXT:    ld r25, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 5
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
+; TINY-NEXT:    st Z+, r24
+; TINY-NEXT:    st Z+, r25
 ; TINY-NEXT:    pop r29
 ; TINY-NEXT:    pop r28
 ; TINY-NEXT:    ret
@@ -243,24 +315,68 @@ define i64 @return64_arg2(i64 %x, i64 %y, i64 %z) {
 ; TINY-NEXT:    push r29
 ; TINY-NEXT:    in r28, 61
 ; TINY-NEXT:    in r29, 62
-; TINY-NEXT:    ldd r20, Y+27
-; TINY-NEXT:    ldd r21, Y+28
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 229
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r20, Y+
+; TINY-NEXT:    ld r21, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 27
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
 ; TINY-NEXT:    mov r30, r24
 ; TINY-NEXT:    mov r31, r25
-; TINY-NEXT:    std Z+6, r20
-; TINY-NEXT:    std Z+7, r21
-; TINY-NEXT:    ldd r24, Y+25
-; TINY-NEXT:    ldd r25, Y+26
-; TINY-NEXT:    std Z+4, r24
-; TINY-NEXT:    std Z+5, r25
-; TINY-NEXT:    ldd r24, Y+23
-; TINY-NEXT:    ldd r25, Y+24
-; TINY-NEXT:    std Z+2, r24
-; TINY-NEXT:    std Z+3, r25
-; TINY-NEXT:    ldd r24, Y+21
-; TINY-NEXT:    ldd r25, Y+22
-; TINY-NEXT:    st Z, r24
-; TINY-NEXT:    std Z+1, r25
+; TINY-NEXT:    subi r30, 250
+; TINY-NEXT:    sbci r31, 255
+; TINY-NEXT:    st Z+, r20
+; TINY-NEXT:    st Z+, r21
+; TINY-NEXT:    subi r30, 8
+; TINY-NEXT:    sbci r31, 0
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 231
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r24, Y+
+; TINY-NEXT:    ld r25, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 25
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
+; TINY-NEXT:    subi r30, 252
+; TINY-NEXT:    sbci r31, 255
+; TINY-NEXT:    st Z+, r24
+; TINY-NEXT:    st Z+, r25
+; TINY-NEXT:    subi r30, 6
+; TINY-NEXT:    sbci r31, 0
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 233
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r24, Y+
+; TINY-NEXT:    ld r25, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 23
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
+; TINY-NEXT:    subi r30, 254
+; TINY-NEXT:    sbci r31, 255
+; TINY-NEXT:    st Z+, r24
+; TINY-NEXT:    st Z+, r25
+; TINY-NEXT:    subi r30, 4
+; TINY-NEXT:    sbci r31, 0
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 235
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r24, Y+
+; TINY-NEXT:    ld r25, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 21
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
+; TINY-NEXT:    st Z+, r24
+; TINY-NEXT:    st Z+, r25
 ; TINY-NEXT:    pop r29
 ; TINY-NEXT:    pop r28
 ; TINY-NEXT:    ret
@@ -288,10 +404,26 @@ define i32 @return64_trunc(i32 %a, i32 %b, i32 %c, i64 %d) {
 ; TINY-NEXT:    push r29
 ; TINY-NEXT:    in r28, 61
 ; TINY-NEXT:    in r29, 62
-; TINY-NEXT:    ldd r22, Y+13
-; TINY-NEXT:    ldd r23, Y+14
-; TINY-NEXT:    ldd r24, Y+15
-; TINY-NEXT:    ldd r25, Y+16
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 243
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r22, Y+
+; TINY-NEXT:    ld r23, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 13
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
+; TINY-NEXT:    in r16, 63
+; TINY-NEXT:    subi r28, 241
+; TINY-NEXT:    sbci r29, 255
+; TINY-NEXT:    ld r24, Y+
+; TINY-NEXT:    ld r25, Y+
+; TINY-NEXT:    subi r28, 2
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    subi r28, 15
+; TINY-NEXT:    sbci r29, 0
+; TINY-NEXT:    out 63, r16
 ; TINY-NEXT:    pop r29
 ; TINY-NEXT:    pop r28
 ; TINY-NEXT:    ret


        


More information about the llvm-commits mailing list