Index: llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp =================================================================== --- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -1438,6 +1438,111 @@ return true; } +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + Register DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + + // ror r24 + // clr r24 + // ror r24 + + buildMI(MBB, MBBI, AVR::RORRd) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, getKillRegState(DstIsKill)); + + buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addReg(DstReg, getKillRegState(DstIsKill)); + + auto MIRRC = + buildMI(MBB, MBBI, AVR::RORRd) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, getKillRegState(DstIsKill)); + + if (ImpIsDead) + MIRRC->getOperand(2).setIsDead(); + + // SREG is always implicitly killed + MIRRC->getOperand(3).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + Register DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + + // rol r24 + // clr r24 + // rol r24 + + buildMI(MBB, MBBI, AVR::ADCRdRr) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addReg(DstReg, getKillRegState(DstIsKill)); + + buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addReg(DstReg, getKillRegState(DstIsKill)); + + auto MIRRC = + buildMI(MBB, MBBI, AVR::ADCRdRr) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addReg(DstReg, getKillRegState(DstIsKill)); + + if (ImpIsDead) + MIRRC->getOperand(3).setIsDead(); + + // SREG is always implicitly killed + MIRRC->getOperand(4).setIsKill(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + Register DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + + // lsl r24 + // sbc r24, r24 + + buildMI(MBB, MBBI, AVR::ADDRdRr) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addReg(DstReg, getKillRegState(DstIsKill)); + + auto MIRRC = buildMI(MBB, MBBI, AVR::SBCRdRr) + .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstReg, getKillRegState(DstIsKill)) + .addReg(DstReg, getKillRegState(DstIsKill)); + + if (ImpIsDead) + MIRRC->getOperand(3).setIsDead(); + + // SREG is always implicitly killed + MIRRC->getOperand(4).setIsKill(); + + MI.eraseFromParent(); + return true; +} + template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { MachineInstr &MI = *MBBI; Register DstLoReg, DstHiReg; @@ -1658,6 +1763,9 @@ EXPAND(AVR::RORWRd); EXPAND(AVR::ROLWRd); EXPAND(AVR::ASRWRd); + EXPAND(AVR::LSLB7Rd); + EXPAND(AVR::LSRB7Rd); + EXPAND(AVR::ASRB7Rd); EXPAND(AVR::SEXT); EXPAND(AVR::ZEXT); EXPAND(AVR::SPREAD); Index: llvm/lib/Target/AVR/AVRISelLowering.h =================================================================== --- llvm/lib/Target/AVR/AVRISelLowering.h +++ llvm/lib/Target/AVR/AVRISelLowering.h @@ -38,6 +38,9 @@ LSL, ///< Logical shift left. LSR, ///< Logical shift right. ASR, ///< Arithmetic shift right. + LSL7, ///< Logical shift left 7 bits. + LSR7, ///< Logical shift right 7 bits. + ASR7, ///< Arithmetic shift right 7 bits. ROR, ///< Bit rotate right. ROL, ///< Bit rotate left. LSLLOOP, ///< A loop of single logical shift left instructions. Index: llvm/lib/Target/AVR/AVRISelLowering.cpp =================================================================== --- llvm/lib/Target/AVR/AVRISelLowering.cpp +++ llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -334,6 +334,18 @@ llvm_unreachable("Invalid shift opcode"); } + // Optimize int8 shifts. + if (VT.getSizeInBits() == 8 && ShiftAmount == 7) { + if (Op.getOpcode() == ISD::SHL) { + Victim = DAG.getNode(AVRISD::LSL7, dl, VT, Victim); + } else if (Op.getOpcode() == ISD::SRL) { + Victim = DAG.getNode(AVRISD::LSR7, dl, VT, Victim); + } else if (Op.getOpcode() == ISD::SRA) { + Victim = DAG.getNode(AVRISD::ASR7, dl, VT, Victim); + } + ShiftAmount = 0; + } + while (ShiftAmount--) { Victim = DAG.getNode(Opc8, dl, VT, Victim); } Index: llvm/lib/Target/AVR/AVRInstrInfo.td =================================================================== --- llvm/lib/Target/AVR/AVRInstrInfo.td +++ llvm/lib/Target/AVR/AVRInstrInfo.td @@ -59,6 +59,9 @@ def AVRrol : SDNode<"AVRISD::ROL", SDTIntUnaryOp>; def AVRror : SDNode<"AVRISD::ROR", SDTIntUnaryOp>; def AVRasr : SDNode<"AVRISD::ASR", SDTIntUnaryOp>; +def AVRlsl7 : SDNode<"AVRISD::LSL7", SDTIntUnaryOp>; +def AVRlsr7 : SDNode<"AVRISD::LSR7", SDTIntUnaryOp>; +def AVRasr7 : SDNode<"AVRISD::ASR7", SDTIntUnaryOp>; // Pseudo shift nodes for non-constant shift amounts. def AVRlslLoop : SDNode<"AVRISD::LSLLOOP", SDTIntShiftOp>; @@ -1653,6 +1656,11 @@ "lslw\t$rd", [(set i16:$rd, (AVRlsl i16:$src)), (implicit SREG)]>; + def LSLB7Rd : Pseudo<(outs GPR8:$rd), + (ins GPR8:$src), + "lslb7\t$rd", + [(set i8:$rd, (AVRlsl7 i8:$src)), (implicit SREG)]>; + def LSRRd : FRd<0b1001, 0b0100110, (outs GPR8:$rd), @@ -1660,6 +1668,11 @@ "lsr\t$rd", [(set i8:$rd, (AVRlsr i8:$src)), (implicit SREG)]>; + def LSRB7Rd : Pseudo<(outs GPR8:$rd), + (ins GPR8:$src), + "lsrb7\t$rd", + [(set i8:$rd, (AVRlsr7 i8:$src)), (implicit SREG)]>; + def LSRWRd : Pseudo<(outs DREGS:$rd), (ins DREGS:$src), "lsrw\t$rd", @@ -1672,6 +1685,11 @@ "asr\t$rd", [(set i8:$rd, (AVRasr i8:$src)), (implicit SREG)]>; + def ASRB7Rd : Pseudo<(outs GPR8:$rd), + (ins GPR8:$src), + "asrb7\t$rd", + [(set i8:$rd, (AVRasr7 i8:$src)), (implicit SREG)]>; + def ASRWRd : Pseudo<(outs DREGS:$rd), (ins DREGS:$src), "asrw\t$rd", Index: llvm/test/CodeGen/AVR/shift.ll =================================================================== --- llvm/test/CodeGen/AVR/shift.ll +++ llvm/test/CodeGen/AVR/shift.ll @@ -50,3 +50,29 @@ %result = shl i64 %a, %b ret i64 %result } + +; CHECK-LABEL: lsl_i8_7 +define i8 @lsl_i8_7(i8 %a) { +; CHECK: ror r24 +; CHECK-NEXT: clr r24 +; CHECK-NEXT: ror r24 + %result = shl i8 %a, 7 + ret i8 %result +} + +; CHECK-LABEL: lsr_i8_7 +define i8 @lsr_i8_7(i8 %a) { +; CHECK: rol r24 +; CHECK-NEXT: clr r24 +; CHECK-NEXT: rol r24 + %result = lshr i8 %a, 7 + ret i8 %result +} + +; CHECK-LABEL: asr_i8_7 +define i8 @asr_i8_7(i8 %a) { +; CHECK: lsl r24 +; CHECK-NEXT: sbc r24, r24 + %result = ashr i8 %a, 7 + ret i8 %result +} Index: llvm/test/CodeGen/AVR/smul-with-overflow.ll =================================================================== --- llvm/test/CodeGen/AVR/smul-with-overflow.ll +++ llvm/test/CodeGen/AVR/smul-with-overflow.ll @@ -13,13 +13,8 @@ ; CHECK: muls r24, r22 ; CHECK: mov [[HIGH:r[0-9]+]], r1 ; CHECK: mov [[LOW:r[0-9]+]], r0 -; CHECK: asr {{.*}}[[LOW]] -; CHECK: asr {{.*}}[[LOW]] -; CHECK: asr {{.*}}[[LOW]] -; CHECK: asr {{.*}}[[LOW]] -; CHECK: asr {{.*}}[[LOW]] -; CHECK: asr {{.*}}[[LOW]] -; CHECK: asr {{.*}}[[LOW]] +; CHECK: lsl {{.*}}[[LOW]] +; CHECK: sbc {{.*}}[[LOW]], {{.*}}[[LOW]] ; CHECK: ldi [[RET:r[0-9]+]], 1 ; CHECK: cp {{.*}}[[HIGH]], {{.*}}[[LOW]] ; CHECK: brne [[LABEL:.LBB[_0-9]+]]