Index: llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp =================================================================== --- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -818,13 +818,12 @@ Register DstReg = MI.getOperand(0).getReg(); Register SrcReg = MI.getOperand(1).getReg(); 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(); + bool HasX = IsExt ? STI.hasELPMX() : STI.hasLPMX(); TRI->splitReg(DstReg, DstLoReg, DstHiReg); // Set the I/O register RAMPZ for ELPM. if (IsExt) { - const AVRSubtarget &STI = MBB.getParent()->getSubtarget(); Register Bank = MI.getOperand(2).getReg(); // out RAMPZ, rtmp buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(Bank); @@ -833,18 +832,58 @@ // 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)); - - MIBLO.setMemRefs(MI.memoperands()); - MIBHI.setMemRefs(MI.memoperands()); + if (HasX) { + unsigned OpLo = IsExt ? AVR::ELPMRdZPi : AVR::LPMRdZPi; + unsigned OpHi = IsExt ? 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 = IsExt ? 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 + Register SrcLoReg, SrcHiReg; + TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg); + auto MIZLO = 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()); + } MI.eraseFromParent(); return true; Index: llvm/test/CodeGen/AVR/elpm.ll =================================================================== --- llvm/test/CodeGen/AVR/elpm.ll +++ llvm/test/CodeGen/AVR/elpm.ll @@ -28,6 +28,32 @@ ; 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 @@ ; 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 @@ ; 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 @@ ; 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 Index: llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir =================================================================== --- llvm/test/CodeGen/AVR/pseudo/ELPMWRdZ.mir +++ 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 @@ -24,6 +31,29 @@ ; 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