[llvm] acb4d14 - [AVR] Fix incorrect expansion of pseudo instructions LPMWRdZ/ELPMWRdZ

Ben Shi via llvm-commits llvm-commits at lists.llvm.org
Thu Apr 6 00:27:17 PDT 2023


Author: Ben Shi
Date: 2023-04-06T15:27:04+08:00
New Revision: acb4d143bdb75930eae8dc700e16e4950b8cc2c8

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

LOG: [AVR] Fix incorrect expansion of pseudo instructions LPMWRdZ/ELPMWRdZ

The 'ELPM' instruction has three forms:

--------------------------
| form        | feature  |
| ----------- | -------- |
| ELPM        | hasELPM  |
| ELPM Rd, Z  | hasELPMX |
| ELPM Rd, Z+ | hasELPMX |
--------------------------

The second form is always used in the expansion of pseudo instructions
LPMWRdZ/ELPMWRdZ. But for devices without ELPMX and with only ELPM,
only the first form can be used.

Reviewed By: aykevl, Miss_Grape

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

Added: 
    

Modified: 
    llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
    llvm/lib/Target/AVR/AVRInstrInfo.td
    llvm/test/CodeGen/AVR/elpm.ll
    llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
index b29eb87a55a01..5cdd98da768c9 100644
--- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
@@ -97,9 +97,9 @@ class AVRExpandPseudo : public MachineFunctionPass {
   bool expandASRW15Rd(Block &MBB, BlockIt MBBI);
 
   // Common implementation of LPMWRdZ and ELPMWRdZ.
-  bool expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsExt);
+  bool expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsELPM);
   // Common implementation of LPMBRdZ and ELPMBRdZ.
-  bool expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsExt);
+  bool expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsELPM);
 };
 
 char AVRExpandPseudo::ID = 0;
@@ -812,19 +812,21 @@ bool AVRExpandPseudo::expand<AVR::LDDWRdPtrQ>(Block &MBB, BlockIt MBBI) {
   return true;
 }
 
-bool AVRExpandPseudo::expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsExt) {
+bool AVRExpandPseudo::expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsELPM) {
   MachineInstr &MI = *MBBI;
   Register DstLoReg, DstHiReg;
   Register DstReg = MI.getOperand(0).getReg();
   Register SrcReg = MI.getOperand(1).getReg();
+  Register SrcLoReg, SrcHiReg;
   bool SrcIsKill = MI.getOperand(1).isKill();
-  unsigned OpLo = IsExt ? AVR::ELPMRdZPi : AVR::LPMRdZPi;
-  unsigned OpHi = IsExt ? AVR::ELPMRdZ : AVR::LPMRdZ;
+  const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
+  bool IsLPMRn = IsELPM ? STI.hasELPMX() : STI.hasLPMX();
+
   TRI->splitReg(DstReg, DstLoReg, DstHiReg);
+  TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
 
   // Set the I/O register RAMPZ for ELPM.
-  if (IsExt) {
-    const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
+  if (IsELPM) {
     Register Bank = MI.getOperand(2).getReg();
     // out RAMPZ, rtmp
     buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(Bank);
@@ -833,18 +835,81 @@ bool AVRExpandPseudo::expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsExt) {
   // This is enforced by the @earlyclobber constraint.
   assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same");
 
-  // Load low byte.
-  auto MIBLO = buildMI(MBB, MBBI, OpLo)
-                   .addReg(DstLoReg, RegState::Define)
-                   .addReg(SrcReg);
-
-  // Load high byte.
-  auto MIBHI = buildMI(MBB, MBBI, OpHi)
-                   .addReg(DstHiReg, RegState::Define)
-                   .addReg(SrcReg, getKillRegState(SrcIsKill));
+  if (IsLPMRn) {
+    unsigned OpLo = IsELPM ? AVR::ELPMRdZPi : AVR::LPMRdZPi;
+    unsigned OpHi = IsELPM ? AVR::ELPMRdZ : AVR::LPMRdZ;
+    // Load low byte.
+    auto MIBLO = buildMI(MBB, MBBI, OpLo)
+                     .addReg(DstLoReg, RegState::Define)
+                     .addReg(SrcReg);
+    // Load high byte.
+    auto MIBHI = buildMI(MBB, MBBI, OpHi)
+                     .addReg(DstHiReg, RegState::Define)
+                     .addReg(SrcReg, getKillRegState(SrcIsKill));
+    MIBLO.setMemRefs(MI.memoperands());
+    MIBHI.setMemRefs(MI.memoperands());
+  } else {
+    unsigned Opc = IsELPM ? AVR::ELPM : AVR::LPM;
+    // Load low byte, and copy to the low destination register.
+    auto MIBLO = buildMI(MBB, MBBI, Opc);
+    buildMI(MBB, MBBI, AVR::MOVRdRr)
+        .addReg(DstLoReg, RegState::Define)
+        .addReg(AVR::R0, RegState::Kill);
+    MIBLO.setMemRefs(MI.memoperands());
+    // Increase the Z register by 1.
+    if (STI.hasADDSUBIW()) {
+      // adiw r31:r30, 1
+      auto MIINC = buildMI(MBB, MBBI, AVR::ADIWRdK)
+                       .addReg(SrcReg, RegState::Define)
+                       .addReg(SrcReg, getKillRegState(SrcIsKill))
+                       .addImm(1);
+      MIINC->getOperand(3).setIsDead();
+    } else {
+      // subi r30, 255
+      // sbci r31, 255
+      buildMI(MBB, MBBI, AVR::SUBIRdK)
+          .addReg(SrcLoReg, RegState::Define)
+          .addReg(SrcLoReg, getKillRegState(SrcIsKill))
+          .addImm(255);
+      auto MIZHI = buildMI(MBB, MBBI, AVR::SBCIRdK)
+                       .addReg(SrcHiReg, RegState::Define)
+                       .addReg(SrcHiReg, getKillRegState(SrcIsKill))
+                       .addImm(255);
+      MIZHI->getOperand(3).setIsDead();
+      MIZHI->getOperand(4).setIsKill();
+    }
+    // Load high byte, and copy to the high destination register.
+    auto MIBHI = buildMI(MBB, MBBI, Opc);
+    buildMI(MBB, MBBI, AVR::MOVRdRr)
+        .addReg(DstHiReg, RegState::Define)
+        .addReg(AVR::R0, RegState::Kill);
+    MIBHI.setMemRefs(MI.memoperands());
+  }
 
-  MIBLO.setMemRefs(MI.memoperands());
-  MIBHI.setMemRefs(MI.memoperands());
+  // Restore the Z register if it is not killed.
+  if (!SrcIsKill) {
+    if (STI.hasADDSUBIW()) {
+      // sbiw r31:r30, 1
+      auto MIDEC = buildMI(MBB, MBBI, AVR::SBIWRdK)
+                       .addReg(SrcReg, RegState::Define)
+                       .addReg(SrcReg, getKillRegState(SrcIsKill))
+                       .addImm(1);
+      MIDEC->getOperand(3).setIsDead();
+    } else {
+      // subi r30, 1
+      // sbci r31, 0
+      buildMI(MBB, MBBI, AVR::SUBIRdK)
+          .addReg(SrcLoReg, RegState::Define)
+          .addReg(SrcLoReg, getKillRegState(SrcIsKill))
+          .addImm(1);
+      auto MIZHI = buildMI(MBB, MBBI, AVR::SBCIRdK)
+                       .addReg(SrcHiReg, RegState::Define)
+                       .addReg(SrcHiReg, getKillRegState(SrcIsKill))
+                       .addImm(0);
+      MIZHI->getOperand(3).setIsDead();
+      MIZHI->getOperand(4).setIsKill();
+    }
+  }
 
   MI.eraseFromParent();
   return true;
@@ -860,23 +925,23 @@ bool AVRExpandPseudo::expand<AVR::ELPMWRdZ>(Block &MBB, BlockIt MBBI) {
   return expandLPMWELPMW(MBB, MBBI, true);
 }
 
-bool AVRExpandPseudo::expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsExt) {
+bool AVRExpandPseudo::expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsELPM) {
   MachineInstr &MI = *MBBI;
   Register DstReg = MI.getOperand(0).getReg();
   Register SrcReg = MI.getOperand(1).getReg();
   bool SrcIsKill = MI.getOperand(1).isKill();
   const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
-  bool HasX = IsExt ? STI.hasELPMX() : STI.hasLPMX();
+  bool IsLPMRn = IsELPM ? STI.hasELPMX() : STI.hasLPMX();
 
   // Set the I/O register RAMPZ for ELPM (out RAMPZ, rtmp).
-  if (IsExt) {
+  if (IsELPM) {
     Register BankReg = MI.getOperand(2).getReg();
     buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(BankReg);
   }
 
   // Load byte.
-  if (HasX) {
-    unsigned Opc = IsExt ? AVR::ELPMRdZ : AVR::LPMRdZ;
+  if (IsLPMRn) {
+    unsigned Opc = IsELPM ? AVR::ELPMRdZ : AVR::LPMRdZ;
     auto MILB = buildMI(MBB, MBBI, Opc)
                     .addReg(DstReg, RegState::Define)
                     .addReg(SrcReg, getKillRegState(SrcIsKill));
@@ -884,7 +949,7 @@ bool AVRExpandPseudo::expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsExt) {
   } else {
     // For the basic ELPM/LPM instruction, its operand[0] is the implicit
     // 'Z' register, and its operand[1] is the implicit 'R0' register.
-    unsigned Opc = IsExt ? AVR::ELPM : AVR::LPM;
+    unsigned Opc = IsELPM ? AVR::ELPM : AVR::LPM;
     auto MILB = buildMI(MBB, MBBI, Opc);
     buildMI(MBB, MBBI, AVR::MOVRdRr)
         .addReg(DstReg, RegState::Define)

diff  --git a/llvm/lib/Target/AVR/AVRInstrInfo.td b/llvm/lib/Target/AVR/AVRInstrInfo.td
index d1a1fc9babbb1..0b35a95849b96 100644
--- a/llvm/lib/Target/AVR/AVRInstrInfo.td
+++ b/llvm/lib/Target/AVR/AVRInstrInfo.td
@@ -1690,10 +1690,15 @@ let canFoldAsLoad = 1, isReMaterializable = 1, mayLoad = 1,
       : F16<0b1001010111001000, (outs), (ins), "lpm", []>,
       Requires<[HasLPM]>;
 
-  // This pseudo is combination of LPM and MOV instructions.
-  let Defs = [R0] in
-  def LPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z), "lpmb\t$dst, $z", []>,
-                Requires<[HasLPM]>;
+  // These pseudo instructions are combination of the OUT and LPM instructions.
+  let Defs = [R0] in {
+    def LPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z), "lpmb\t$dst, $z", []>,
+                  Requires<[HasLPM]>;
+
+    let Constraints = "@earlyclobber $dst" in
+    def LPMWRdZ : Pseudo<(outs DREGS:$dst), (ins ZREG:$z), "lpmw\t$dst, $z", []>,
+                  Requires<[HasLPM]>;
+  }
 
   def LPMRdZ : FLPMX<0, 0,
                      (outs GPR8
@@ -1713,14 +1718,6 @@ let canFoldAsLoad = 1, isReMaterializable = 1, mayLoad = 1,
                          "lpm\t$rd, $z+", []>,
                    Requires<[HasLPMX]>;
 
-    let Constraints = "@earlyclobber $dst" in
-    def LPMWRdZ : Pseudo<(outs DREGS
-                          : $dst),
-                         (ins ZREG
-                          : $z),
-                         "lpmw\t$dst, $z", []>,
-                  Requires<[HasLPMX]>;
-
     def LPMWRdZPi : Pseudo<(outs DREGS
                             : $dst),
                            (ins ZREG
@@ -1747,19 +1744,20 @@ let mayLoad = 1, hasSideEffects = 0 in {
                     Requires<[HasELPMX]>;
   }
 
-  // This pseudo is combination of the OUT and ELPM instructions.
-  let Defs = [R0] in
-  def ELPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p),
-                        "elpmb\t$dst, $z, $p", []>,
-                 Requires<[HasELPM]>;
+  // These pseudo instructions are combination of the OUT and ELPM instructions.
+  let Defs = [R0] in {
+    def ELPMBRdZ : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p),
+                          "elpmb\t$dst, $z, $p", []>,
+                   Requires<[HasELPM]>;
 
-  // These pseudos are combination of the OUT and ELPM instructions.
-  let Defs = [R31R30], hasSideEffects = 1 in {
     let Constraints = "@earlyclobber $dst" in
     def ELPMWRdZ : Pseudo<(outs DREGS:$dst), (ins ZREG:$z, LD8:$p),
                           "elpmw\t$dst, $z, $p", []>,
-                   Requires<[HasELPMX]>;
+                   Requires<[HasELPM]>;
+  }
 
+  // These pseudos are combination of the OUT and ELPM instructions.
+  let Defs = [R31R30], hasSideEffects = 1 in {
     def ELPMBRdZPi : Pseudo<(outs GPR8:$dst), (ins ZREG:$z, LD8:$p),
                             "elpmb\t$dst, $z+, $p", []>,
                      Requires<[HasELPMX]>;

diff  --git a/llvm/test/CodeGen/AVR/elpm.ll b/llvm/test/CodeGen/AVR/elpm.ll
index 2e989d1442ffd..21f956aa2cb9d 100644
--- a/llvm/test/CodeGen/AVR/elpm.ll
+++ b/llvm/test/CodeGen/AVR/elpm.ll
@@ -28,6 +28,32 @@ define i16 @foo0(i16 %a, i16 %b) {
 ; CHECK-NEXT:    sub r24, r18
 ; CHECK-NEXT:    sbc r25, r19
 ; CHECK-NEXT:    ret
+;
+; NOX-LABEL: foo0:
+; NOX:       ; %bb.0: ; %entry
+; NOX-NEXT:    lsl r22
+; NOX-NEXT:    rol r23
+; NOX-NEXT:    subi r22, lo8(-(arr0))
+; NOX-NEXT:    sbci r23, hi8(-(arr0))
+; NOX-NEXT:    movw r30, r22
+; NOX-NEXT:    lpm
+; NOX-NEXT:    mov r18, r0
+; NOX-NEXT:    adiw r30, 1
+; NOX-NEXT:    lpm
+; NOX-NEXT:    mov r19, r0
+; NOX-NEXT:    lsl r24
+; NOX-NEXT:    rol r25
+; NOX-NEXT:    subi r24, lo8(-(arr0))
+; NOX-NEXT:    sbci r25, hi8(-(arr0))
+; NOX-NEXT:    movw r30, r24
+; NOX-NEXT:    lpm
+; NOX-NEXT:    mov r24, r0
+; NOX-NEXT:    adiw r30, 1
+; NOX-NEXT:    lpm
+; NOX-NEXT:    mov r25, r0
+; NOX-NEXT:    sub r24, r18
+; NOX-NEXT:    sbc r25, r19
+; NOX-NEXT:    ret
 entry:
   %arrayidx = getelementptr inbounds [4 x i16], [4 x i16] addrspace(1)* @arr0, i16 0, i16 %a
   %0 = load i16, i16 addrspace(1)* %arrayidx, align 1
@@ -59,6 +85,34 @@ define i16 @foo1(i16 %a, i16 %b) {
 ; CHECK-NEXT:    sub r24, r20
 ; CHECK-NEXT:    sbc r25, r21
 ; CHECK-NEXT:    ret
+;
+; NOX-LABEL: foo1:
+; NOX:       ; %bb.0: ; %entry
+; NOX-NEXT:    lsl r22
+; NOX-NEXT:    rol r23
+; NOX-NEXT:    subi r22, lo8(-(arr1))
+; NOX-NEXT:    sbci r23, hi8(-(arr1))
+; NOX-NEXT:    movw r30, r22
+; NOX-NEXT:    ldi r18, 1
+; NOX-NEXT:    out 59, r18
+; NOX-NEXT:    elpm
+; NOX-NEXT:    mov r20, r0
+; NOX-NEXT:    adiw r30, 1
+; NOX-NEXT:    elpm
+; NOX-NEXT:    mov r21, r0
+; NOX-NEXT:    lsl r24
+; NOX-NEXT:    rol r25
+; NOX-NEXT:    subi r24, lo8(-(arr0))
+; NOX-NEXT:    sbci r25, hi8(-(arr0))
+; NOX-NEXT:    movw r30, r24
+; NOX-NEXT:    lpm
+; NOX-NEXT:    mov r24, r0
+; NOX-NEXT:    adiw r30, 1
+; NOX-NEXT:    lpm
+; NOX-NEXT:    mov r25, r0
+; NOX-NEXT:    sub r24, r20
+; NOX-NEXT:    sbc r25, r21
+; NOX-NEXT:    ret
 entry:
   %arrayidx = getelementptr inbounds [4 x i16], [4 x i16] addrspace(1)* @arr0, i16 0, i16 %a
   %0 = load i16, i16 addrspace(1)* %arrayidx, align 1
@@ -90,6 +144,34 @@ define i16 @foo2(i16 %a, i16 %b) {
 ; CHECK-NEXT:    sub r24, r18
 ; CHECK-NEXT:    sbc r25, r19
 ; CHECK-NEXT:    ret
+;
+; NOX-LABEL: foo2:
+; NOX:       ; %bb.0: ; %entry
+; NOX-NEXT:    lsl r24
+; NOX-NEXT:    rol r25
+; NOX-NEXT:    subi r24, lo8(-(arr2))
+; NOX-NEXT:    sbci r25, hi8(-(arr2))
+; NOX-NEXT:    movw r30, r24
+; NOX-NEXT:    ldi r18, 2
+; NOX-NEXT:    out 59, r18
+; NOX-NEXT:    elpm
+; NOX-NEXT:    mov r24, r0
+; NOX-NEXT:    adiw r30, 1
+; NOX-NEXT:    elpm
+; NOX-NEXT:    mov r25, r0
+; NOX-NEXT:    lsl r22
+; NOX-NEXT:    rol r23
+; NOX-NEXT:    subi r22, lo8(-(arr0))
+; NOX-NEXT:    sbci r23, hi8(-(arr0))
+; NOX-NEXT:    movw r30, r22
+; NOX-NEXT:    lpm
+; NOX-NEXT:    mov r18, r0
+; NOX-NEXT:    adiw r30, 1
+; NOX-NEXT:    lpm
+; NOX-NEXT:    mov r19, r0
+; NOX-NEXT:    sub r24, r18
+; NOX-NEXT:    sbc r25, r19
+; NOX-NEXT:    ret
 entry:
   %arrayidx = getelementptr inbounds [4 x i16], [4 x i16] addrspace(3)* @arr2, i16 0, i16 %a
   %0 = load i16, i16 addrspace(3)* %arrayidx, align 1
@@ -123,6 +205,36 @@ define i16 @foo3(i16 %a, i16 %b) {
 ; CHECK-NEXT:    sub r24, r20
 ; CHECK-NEXT:    sbc r25, r21
 ; CHECK-NEXT:    ret
+;
+; NOX-LABEL: foo3:
+; NOX:       ; %bb.0: ; %entry
+; NOX-NEXT:    lsl r22
+; NOX-NEXT:    rol r23
+; NOX-NEXT:    subi r22, lo8(-(arr1))
+; NOX-NEXT:    sbci r23, hi8(-(arr1))
+; NOX-NEXT:    movw r30, r22
+; NOX-NEXT:    ldi r18, 1
+; NOX-NEXT:    out 59, r18
+; NOX-NEXT:    elpm
+; NOX-NEXT:    mov r20, r0
+; NOX-NEXT:    adiw r30, 1
+; NOX-NEXT:    elpm
+; NOX-NEXT:    mov r21, r0
+; NOX-NEXT:    lsl r24
+; NOX-NEXT:    rol r25
+; NOX-NEXT:    subi r24, lo8(-(arr2))
+; NOX-NEXT:    sbci r25, hi8(-(arr2))
+; NOX-NEXT:    movw r30, r24
+; NOX-NEXT:    ldi r18, 2
+; NOX-NEXT:    out 59, r18
+; NOX-NEXT:    elpm
+; NOX-NEXT:    mov r24, r0
+; NOX-NEXT:    adiw r30, 1
+; NOX-NEXT:    elpm
+; NOX-NEXT:    mov r25, r0
+; NOX-NEXT:    sub r24, r20
+; NOX-NEXT:    sbc r25, r21
+; NOX-NEXT:    ret
 entry:
   %arrayidx = getelementptr inbounds [4 x i16], [4 x i16] addrspace(3)* @arr2, i16 0, i16 %a
   %0 = load i16, i16 addrspace(3)* %arrayidx, align 1

diff  --git a/llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir b/llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir
index d4469fb467ac6..7a6bab0a03d68 100644
--- a/llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir
+++ b/llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir
@@ -1,4 +1,11 @@
-# RUN: llc -mcpu=atmega1284p -start-before=greedy %s -o - | FileCheck %s
+# RUN: llc -mtriple=avr -mattr=+lpm -mattr=+elpm -mattr=+lpmx -mattr=+elpmx \
+# RUN:     -mattr=+movw -start-before=greedy %s -o - | FileCheck %s
+# RUN: llc -mtriple=avr -mattr=+lpm -mattr=+elpm -mattr=-lpmx -mattr=-elpmx \
+# RUN:     -mattr=+addsubiw -mattr=+movw -start-before=greedy %s -o - \
+# RUN:     | FileCheck --check-prefix=NOX %s
+# RUN: llc -mtriple=avr -mattr=+lpm -mattr=+elpm -mattr=-lpmx -mattr=-elpmx \
+# RUN:     -mattr=-addsubiw -mattr=+movw -start-before=greedy %s -o - \
+# RUN:     | FileCheck --check-prefix=NOADIWNOX %s
 
 # This test checks the expansion of the 16-bit ELPM pseudo instruction and that
 # the register allocator won't use R31R30 as an output register (which would
@@ -10,6 +17,10 @@
   entry:
     ret void
   }
+  define void @test_elpmwrdz_2() {
+  entry:
+    ret void
+  }
 ...
 
 ---
@@ -24,9 +35,79 @@ body: |
     ; CHECK-NEXT: elpm r25, Z
     ; CHECK-NEXT: movw r30, r24
 
+    ; NOX-LABEL: test_elpmwrdz
+    ; NOX:       ; %bb.0:
+    ; NOX-NEXT:    ldi r18, 1
+    ; NOX-NEXT:    out
+    ; NOX-NEXT:    elpm
+    ; NOX-NEXT:    mov r24, r0
+    ; NOX-NEXT:    adiw r30, 1
+    ; NOX-NEXT:    elpm
+    ; NOX-NEXT:    mov r25, r0
+    ; NOX-NEXT:    movw r30, r24
+
+    ; NOADIWNOX-LABEL: test_elpmwrdz
+    ; NOADIWNOX:       ; %bb.0:
+    ; NOADIWNOX-NEXT:    ldi r18, 1
+    ; NOADIWNOX-NEXT:    out
+    ; NOADIWNOX-NEXT:    elpm
+    ; NOADIWNOX-NEXT:    mov r24, r0
+    ; NOADIWNOX-NEXT:    subi r30, 255
+    ; NOADIWNOX-NEXT:    sbci r31, 255
+    ; NOADIWNOX-NEXT:    elpm
+    ; NOADIWNOX-NEXT:    mov r25, r0
+    ; NOADIWNOX-NEXT:    movw r30, r24
+
     %1:zreg = COPY killed $r31r30
     %2:ld8 = LDIRdK 1
-    %3:dregs = ELPMWRdZ %1, %2, implicit-def dead $r31r30
+    %3:dregs = ELPMWRdZ %1, %2, implicit-def dead $r0
     $r31r30 = COPY %3
     RET implicit $r31r30
 ...
+
+---
+name:            test_elpmwrdz_2
+tracksRegLiveness: true
+body: |
+  bb.0.entry:
+    liveins: $r31r30
+
+    ; CHECK-LABEL: test_elpmwrdz_2
+    ; CHECK:       ; %bb.0:
+    ; CHECK-NEXT:    ldi r24, 1
+    ; CHECK-NEXT:    out 59, r24
+    ; CHECK-NEXT:    elpm r18, Z+
+    ; CHECK-NEXT:    elpm r19, Z
+    ; CHECK-NEXT:    sbiw r30, 1
+    ; CHECK-NEXT:    ret
+
+    ; NOX-LABEL: test_elpmwrdz_2
+    ; NOX:       ; %bb.0:
+    ; NOX-NEXT:    ldi r24, 1
+    ; NOX-NEXT:    out 59, r24
+    ; NOX-NEXT:    elpm
+    ; NOX-NEXT:    mov r18, r0
+    ; NOX-NEXT:    adiw r30, 1
+    ; NOX-NEXT:    elpm
+    ; NOX-NEXT:    mov r19, r0
+    ; NOX-NEXT:    sbiw r30, 1
+    ; NOX-NEXT:    ret
+
+    ; NOADIWNOX-LABEL: test_elpmwrdz_2
+    ; NOADIWNOX:       ; %bb.0:
+    ; NOADIWNOX-NEXT:    ldi r24, 1
+    ; NOADIWNOX-NEXT:    out  59, r24
+    ; NOADIWNOX-NEXT:    elpm
+    ; NOADIWNOX-NEXT:    mov r18, r0
+    ; NOADIWNOX-NEXT:    subi r30, 255
+    ; NOADIWNOX-NEXT:    sbci r31, 255
+    ; NOADIWNOX-NEXT:    elpm
+    ; NOADIWNOX-NEXT:    mov r19, r0
+    ; NOADIWNOX-NEXT:    subi r30, 1
+    ; NOADIWNOX-NEXT:    sbci r31, 0
+
+    %1:zreg = COPY $r31r30
+    %2:ld8 = LDIRdK 1
+    %3:dregs = ELPMWRdZ %1, %2, implicit-def dead $r0
+    RET implicit $r31r30
+...


        


More information about the llvm-commits mailing list