Index: llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp =================================================================== --- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -97,9 +97,9 @@ 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 @@ 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(); + 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(); + 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 @@ // 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 @@ 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(); - 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 @@ } 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) Index: llvm/lib/Target/AVR/AVRInstrInfo.td =================================================================== --- llvm/lib/Target/AVR/AVRInstrInfo.td +++ llvm/lib/Target/AVR/AVRInstrInfo.td @@ -1690,10 +1690,15 @@ : 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 @@ "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 @@ 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]>; 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 @@ -10,6 +17,10 @@ entry: ret void } + define void @test_elpmwrdz_2() { + entry: + ret void + } ... --- @@ -24,9 +35,79 @@ ; 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 +...