Index: llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp =================================================================== --- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -98,6 +98,8 @@ // Common implementation of LPMWRdZ and ELPMWRdZ. bool expandLPMWELPMW(Block &MBB, BlockIt MBBI, bool IsExt); + // Common implementation of LPMBRdZ and ELPMBRdZ. + bool expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsExt); }; char AVRExpandPseudo::ID = 0; @@ -858,28 +860,32 @@ return expandLPMWELPMW(MBB, MBBI, true); } -template <> -bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { +bool AVRExpandPseudo::expandLPMBELPMB(Block &MBB, BlockIt MBBI, bool IsExt) { MachineInstr &MI = *MBBI; Register DstReg = MI.getOperand(0).getReg(); Register SrcReg = MI.getOperand(1).getReg(); - Register BankReg = MI.getOperand(2).getReg(); bool SrcIsKill = MI.getOperand(1).isKill(); const AVRSubtarget &STI = MBB.getParent()->getSubtarget(); + bool HasX = IsExt ? STI.hasELPMX() : STI.hasLPMX(); // Set the I/O register RAMPZ for ELPM (out RAMPZ, rtmp). - buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(BankReg); + if (IsExt) { + Register BankReg = MI.getOperand(2).getReg(); + buildMI(MBB, MBBI, AVR::OUTARr).addImm(STI.getIORegRAMPZ()).addReg(BankReg); + } // Load byte. - if (STI.hasELPMX()) { - auto MILB = buildMI(MBB, MBBI, AVR::ELPMRdZ) + if (HasX) { + unsigned Opc = IsExt ? AVR::ELPMRdZ : AVR::LPMRdZ; + auto MILB = buildMI(MBB, MBBI, Opc) .addReg(DstReg, RegState::Define) .addReg(SrcReg, getKillRegState(SrcIsKill)); MILB.setMemRefs(MI.memoperands()); } else { - // For the basic 'ELPM' instruction, its operand[0] is the implicit + // For the basic ELPM/LPM instruction, its operand[0] is the implicit // 'Z' register, and its operand[1] is the implicit 'R0' register. - auto MILB = buildMI(MBB, MBBI, AVR::ELPM); + unsigned Opc = IsExt ? AVR::ELPM : AVR::LPM; + auto MILB = buildMI(MBB, MBBI, Opc); buildMI(MBB, MBBI, AVR::MOVRdRr) .addReg(DstReg, RegState::Define) .addReg(AVR::R0, RegState::Kill); @@ -890,6 +896,16 @@ return true; } +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandLPMBELPMB(MBB, MBBI, true); +} + +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandLPMBELPMB(MBB, MBBI, false); +} + template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { llvm_unreachable("16-bit LPMPi is unimplemented"); @@ -2437,6 +2453,7 @@ EXPAND(AVR::LDWRdPtrPd); case AVR::LDDWRdYQ: //: FIXME: remove this once PR13375 gets fixed EXPAND(AVR::LDDWRdPtrQ); + EXPAND(AVR::LPMBRdZ); EXPAND(AVR::LPMWRdZ); EXPAND(AVR::LPMWRdZPi); EXPAND(AVR::ELPMBRdZ); Index: llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp =================================================================== --- llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp +++ llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp @@ -400,8 +400,9 @@ switch (VT.SimpleTy) { case MVT::i8: if (ProgMemBank == 0) { + unsigned Opc = Subtarget->hasLPMX() ? AVR::LPMRdZ : AVR::LPMBRdZ; ResNode = - CurDAG->getMachineNode(AVR::LPMRdZ, DL, MVT::i8, MVT::Other, Ptr); + CurDAG->getMachineNode(Opc, DL, MVT::i8, MVT::Other, Ptr); } else { // Do not combine the LDI instruction into the ELPM pseudo instruction, // since it may be reused by other ELPM pseudo instructions. Index: llvm/lib/Target/AVR/AVRInstrInfo.td =================================================================== --- llvm/lib/Target/AVR/AVRInstrInfo.td +++ llvm/lib/Target/AVR/AVRInstrInfo.td @@ -1690,6 +1690,11 @@ : 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]>; + def LPMRdZ : FLPMX<0, 0, (outs GPR8 : $rd), Index: llvm/test/CodeGen/AVR/elpm.ll =================================================================== --- llvm/test/CodeGen/AVR/elpm.ll +++ llvm/test/CodeGen/AVR/elpm.ll @@ -152,6 +152,24 @@ ; CHECK-NEXT: lsl r25 ; CHECK-NEXT: sbc r25, r25 ; CHECK-NEXT: ret +; +; NOX-LABEL: foob0: +; NOX: ; %bb.0: ; %entry +; NOX-NEXT: subi r22, lo8(-(arrb1)) +; NOX-NEXT: sbci r23, hi8(-(arrb1)) +; NOX-NEXT: movw r30, r22 +; NOX-NEXT: lpm +; NOX-NEXT: mov r18, r0 +; NOX-NEXT: subi r24, lo8(-(arrb1)) +; NOX-NEXT: sbci r25, hi8(-(arrb1)) +; NOX-NEXT: movw r30, r24 +; NOX-NEXT: lpm +; NOX-NEXT: mov r24, r0 +; NOX-NEXT: sub r24, r18 +; NOX-NEXT: mov r25, r24 +; NOX-NEXT: lsl r25 +; NOX-NEXT: sbc r25, r25 +; NOX-NEXT: ret entry: %arrayidx = getelementptr inbounds [4 x i8], [4 x i8] addrspace(1)* @arrb1, i16 0, i16 %a %0 = load i8, i8 addrspace(1)* %arrayidx, align 1 @@ -179,6 +197,26 @@ ; CHECK-NEXT: lsl r25 ; CHECK-NEXT: sbc r25, r25 ; CHECK-NEXT: ret +; +; NOX-LABEL: foob1: +; NOX: ; %bb.0: ; %entry +; NOX-NEXT: subi r22, lo8(-(arrb3)) +; NOX-NEXT: sbci r23, hi8(-(arrb3)) +; NOX-NEXT: movw r30, r22 +; NOX-NEXT: ldi r18, 2 +; NOX-NEXT: out 59, r18 +; NOX-NEXT: elpm +; NOX-NEXT: mov r18, r0 +; NOX-NEXT: subi r24, lo8(-(arrb1)) +; NOX-NEXT: sbci r25, hi8(-(arrb1)) +; NOX-NEXT: movw r30, r24 +; NOX-NEXT: lpm +; NOX-NEXT: mov r24, r0 +; NOX-NEXT: sub r24, r18 +; NOX-NEXT: mov r25, r24 +; NOX-NEXT: lsl r25 +; NOX-NEXT: sbc r25, r25 +; NOX-NEXT: ret entry: %arrayidx = getelementptr inbounds [4 x i8], [4 x i8] addrspace(1)* @arrb1, i16 0, i16 %a %0 = load i8, i8 addrspace(1)* %arrayidx, align 1 @@ -206,6 +244,26 @@ ; CHECK-NEXT: lsl r25 ; CHECK-NEXT: sbc r25, r25 ; CHECK-NEXT: ret +; +; NOX-LABEL: foob2: +; NOX: ; %bb.0: ; %entry +; NOX-NEXT: subi r24, lo8(-(arrb5)) +; NOX-NEXT: sbci r25, hi8(-(arrb5)) +; NOX-NEXT: movw r30, r24 +; NOX-NEXT: ldi r24, 4 +; NOX-NEXT: out 59, r24 +; NOX-NEXT: elpm +; NOX-NEXT: mov r24, r0 +; NOX-NEXT: subi r22, lo8(-(arrb1)) +; NOX-NEXT: sbci r23, hi8(-(arrb1)) +; NOX-NEXT: movw r30, r22 +; NOX-NEXT: lpm +; NOX-NEXT: mov r25, r0 +; NOX-NEXT: sub r24, r25 +; NOX-NEXT: mov r25, r24 +; NOX-NEXT: lsl r25 +; NOX-NEXT: sbc r25, r25 +; NOX-NEXT: ret entry: %arrayidx = getelementptr inbounds [4 x i8], [4 x i8] addrspace(5)* @arrb5, i16 0, i16 %a %0 = load i8, i8 addrspace(5)* %arrayidx, align 1 Index: llvm/test/CodeGen/AVR/pseudo/LPMBRdZ.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/AVR/pseudo/LPMBRdZ.mir @@ -0,0 +1,40 @@ +# RUN: llc -mtriple=avr -mattr=+lpm -mattr=+lpmx -start-before=greedy %s -o - \ +# RUN: | FileCheck %s +# RUN: llc -mtriple=avr -mattr=+lpm -mattr=-lpmx -start-before=greedy %s -o - \ +# RUN: | FileCheck --check-prefix=NOX %s + +# This test checks the expansion of the 8-bit LPMBRdZ pseudo instruction and that +# the register allocator won't use R31R30 as an output register (which would +# lead to undefined behavior). + +--- | + target triple = "avr--" + define void @test_lpmbrdz() { + entry: + ret void + } +... + +--- +name: test_lpmbrdz +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $r31r30 + + ; CHECK-LABEL: test_lpmbrdz: + ; CHECK: ; %bb.0: + ; CHECK-NEXT: lpm r30, Z + ; CHECK-NEXT: ret + + ; NOX-LABEL: test_lpmbrdz + ; NOX: ; %bb.0: + ; NOX-NEXT: lpm + ; NOX-NEXT: mov r30, r0 + ; NOX-NEXT: ret + + %1:zreg = COPY killed $r31r30 + %2:gpr8 = LPMBRdZ %1, implicit-def dead $r0 + $r30 = COPY %2 + RET implicit $r30 +...