diff --git a/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/lib/Target/AVR/AVRExpandPseudoInsts.cpp index a48d3d134bb..2f7e017e550 100644 --- a/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ b/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -1402,6 +1402,135 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return true; } +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + Register DstLoReg, DstHiReg; + Register DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // swap Rh + // swap Rl + buildMI(MBB, MBBI, AVR::SWAPRd) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + buildMI(MBB, MBBI, AVR::SWAPRd) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + // andi Rh, 0xf0 + auto MI0 = + buildMI(MBB, MBBI, AVR::ANDIRdK) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addImm(0xf0); + // SREG is implicitly dead. + MI0->getOperand(3).setIsDead(); + + // eor Rh, Rl + auto MI1 = + buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + // SREG is implicitly dead. + MI1->getOperand(3).setIsDead(); + + // andi Rl, 0xf0 + auto MI2 = + buildMI(MBB, MBBI, AVR::ANDIRdK) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addImm(0xf0); + // SREG is implicitly dead. + MI2->getOperand(3).setIsDead(); + + // eor Rh, Rl + auto MI3 = + buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + if (ImpIsDead) + MI3->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + Register DstLoReg, DstHiReg; + Register DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // mov Rh, Rl + buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + // clr Rl + auto MIBLO = + buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + if (ImpIsDead) + MIBLO->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + Register DstLoReg, DstHiReg; + Register DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // mov Rh, Rl + buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + // swap Rh + buildMI(MBB, MBBI, AVR::SWAPRd) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + + // andi Rh, 0xf0 + auto MI0 = + buildMI(MBB, MBBI, AVR::ANDIRdK) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addImm(0xf0); + // SREG is implicitly dead. + MI0->getOperand(3).setIsDead(); + + // clr Rl + auto MI1 = + buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + if (ImpIsDead) + MI1->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { MachineInstr &MI = *MBBI; @@ -1433,6 +1562,135 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return true; } +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + Register DstLoReg, DstHiReg; + Register DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // swap Rh + // swap Rl + buildMI(MBB, MBBI, AVR::SWAPRd) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + buildMI(MBB, MBBI, AVR::SWAPRd) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + // andi Rl, 0xf + auto MI0 = + buildMI(MBB, MBBI, AVR::ANDIRdK) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addImm(0xf); + // SREG is implicitly dead. + MI0->getOperand(3).setIsDead(); + + // eor Rl, Rh + auto MI1 = + buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + // SREG is implicitly dead. + MI1->getOperand(3).setIsDead(); + + // andi Rh, 0xf + auto MI2 = + buildMI(MBB, MBBI, AVR::ANDIRdK) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addImm(0xf); + // SREG is implicitly dead. + MI2->getOperand(3).setIsDead(); + + // eor Rl, Rh + auto MI3 = + buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + if (ImpIsDead) + MI3->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + Register DstLoReg, DstHiReg; + Register DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // Move upper byte to lower byte. + buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg); + + // Clear upper byte. + auto MIBHI = + buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + Register DstLoReg, DstHiReg; + Register DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // Move upper byte to lower byte. + buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg); + + // swap Rl + buildMI(MBB, MBBI, AVR::SWAPRd) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)); + + // andi Rl, 0xf + auto MI0 = + buildMI(MBB, MBBI, AVR::ANDIRdK) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstLoReg, getKillRegState(DstIsKill)) + .addImm(0xf); + // SREG is implicitly dead. + MI0->getOperand(3).setIsDead(); + + // Clear upper byte. + auto MIBHI = + buildMI(MBB, MBBI, AVR::EORRdRr) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { llvm_unreachable("RORW unimplemented"); @@ -1476,6 +1734,39 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return true; } +template <> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + Register DstLoReg, DstHiReg; + Register DstReg = MI.getOperand(0).getReg(); + bool DstIsDead = MI.getOperand(0).isDead(); + bool DstIsKill = MI.getOperand(1).isKill(); + bool ImpIsDead = MI.getOperand(2).isDead(); + TRI->splitReg(DstReg, DstLoReg, DstHiReg); + + // Move upper byte to lower byte. + buildMI(MBB, MBBI, AVR::MOVRdRr) + .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + + // Move the sign bit to the C flag. + buildMI(MBB, MBBI, AVR::ADDRdRr).addReg(DstHiReg) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + + // Set upper byte to 0 or -1. + auto MIBHI = + buildMI(MBB, MBBI, AVR::SBCRdRr) + .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) + .addReg(DstHiReg, getKillRegState(DstIsKill)) + .addReg(DstHiReg, getKillRegState(DstIsKill)); + if (ImpIsDead) + MIBHI->getOperand(3).setIsDead(); + + MI.eraseFromParent(); + return true; +} + template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { MachineInstr &MI = *MBBI; @@ -1798,10 +2089,17 @@ bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) { EXPAND(AVR::ROLBRd); EXPAND(AVR::RORBRd); EXPAND(AVR::LSLWRd); + EXPAND(AVR::LSLW4Rd); + EXPAND(AVR::LSLW8Rd); + EXPAND(AVR::LSLW12Rd); EXPAND(AVR::LSRWRd); + EXPAND(AVR::LSRW4Rd); + EXPAND(AVR::LSRW8Rd); + EXPAND(AVR::LSRW12Rd); EXPAND(AVR::RORWRd); EXPAND(AVR::ROLWRd); EXPAND(AVR::ASRWRd); + EXPAND(AVR::ASRW8Rd); EXPAND(AVR::LSLB7Rd); EXPAND(AVR::LSRB7Rd); EXPAND(AVR::ASRB7Rd); diff --git a/lib/Target/AVR/AVRISelLowering.cpp b/lib/Target/AVR/AVRISelLowering.cpp index 3e7c2984655..944f7b681b5 100644 --- a/lib/Target/AVR/AVRISelLowering.cpp +++ b/lib/Target/AVR/AVRISelLowering.cpp @@ -334,7 +334,7 @@ SDValue AVRTargetLowering::LowerShifts(SDValue Op, SelectionDAG &DAG) const { llvm_unreachable("Invalid shift opcode"); } - // Optimize int8 shifts. + // Optimize int8/int16 shifts. if (VT.getSizeInBits() == 8) { if (Op.getOpcode() == ISD::SHL && 4 <= ShiftAmount && ShiftAmount < 7) { // Optimize LSL when 4 <= ShiftAmount <= 6. @@ -362,6 +362,50 @@ SDValue AVRTargetLowering::LowerShifts(SDValue Op, SelectionDAG &DAG) const { Victim = DAG.getNode(AVRISD::ASR7, dl, VT, Victim); ShiftAmount = 0; } + } else if (VT.getSizeInBits() == 16) { + if (4 <= ShiftAmount && ShiftAmount < 8) + switch (Op.getOpcode()) { + case ISD::SHL: + Victim = DAG.getNode(AVRISD::LSL4, dl, VT, Victim); + ShiftAmount -= 4; + break; + case ISD::SRL: + Victim = DAG.getNode(AVRISD::LSR4, dl, VT, Victim); + ShiftAmount -= 4; + break; + default: + break; + } + else if (8 <= ShiftAmount && ShiftAmount < 12) + switch (Op.getOpcode()) { + case ISD::SHL: + Victim = DAG.getNode(AVRISD::LSL8, dl, VT, Victim); + ShiftAmount -= 8; + break; + case ISD::SRL: + Victim = DAG.getNode(AVRISD::LSR8, dl, VT, Victim); + ShiftAmount -= 8; + break; + case ISD::SRA: + Victim = DAG.getNode(AVRISD::ASR8, dl, VT, Victim); + ShiftAmount -= 8; + break; + default: + break; + } + else if (12 <= ShiftAmount) + switch (Op.getOpcode()) { + case ISD::SHL: + Victim = DAG.getNode(AVRISD::LSL12, dl, VT, Victim); + ShiftAmount -= 12; + break; + case ISD::SRL: + Victim = DAG.getNode(AVRISD::LSR12, dl, VT, Victim); + ShiftAmount -= 12; + break; + default: + break; + } } while (ShiftAmount--) { diff --git a/lib/Target/AVR/AVRISelLowering.h b/lib/Target/AVR/AVRISelLowering.h index 7aff4159211..3532b09a3ba 100644 --- a/lib/Target/AVR/AVRISelLowering.h +++ b/lib/Target/AVR/AVRISelLowering.h @@ -36,8 +36,15 @@ enum NodeType { /// TargetExternalSymbol, and TargetGlobalAddress. WRAPPER, LSL, ///< Logical shift left. + LSL4, ///< Logical shift left 4 bits. + LSL8, ///< Logical shift left 8 bits. + LSL12, ///< Logical shift left 12 bits. LSR, ///< Logical shift right. + LSR4, ///< Logical shift right 4 bits. + LSR8, ///< Logical shift right 8 bits. + LSR12, ///< Logical shift right 12 bits. ASR, ///< Arithmetic shift right. + ASR8, ///< Arithmetic shift right 8 bits. LSL7, ///< Logical shift left 7 bits. LSR7, ///< Logical shift right 7 bits. ASR7, ///< Arithmetic shift right 7 bits. diff --git a/lib/Target/AVR/AVRInstrInfo.td b/lib/Target/AVR/AVRInstrInfo.td index 9f7c16fc96d..e967e107a1c 100644 --- a/lib/Target/AVR/AVRInstrInfo.td +++ b/lib/Target/AVR/AVRInstrInfo.td @@ -55,10 +55,17 @@ def AVRselectcc: SDNode<"AVRISD::SELECT_CC", SDT_AVRSelectCC, [SDNPInGlue]>; // Shift nodes. def AVRlsl : SDNode<"AVRISD::LSL", SDTIntUnaryOp>; +def AVRlsl4 : SDNode<"AVRISD::LSL4", SDTIntUnaryOp>; +def AVRlsl8 : SDNode<"AVRISD::LSL8", SDTIntUnaryOp>; +def AVRlsl12 : SDNode<"AVRISD::LSL12", SDTIntUnaryOp>; def AVRlsr : SDNode<"AVRISD::LSR", SDTIntUnaryOp>; +def AVRlsr4 : SDNode<"AVRISD::LSR4", SDTIntUnaryOp>; +def AVRlsr8 : SDNode<"AVRISD::LSR8", SDTIntUnaryOp>; +def AVRlsr12 : SDNode<"AVRISD::LSR12", SDTIntUnaryOp>; def AVRrol : SDNode<"AVRISD::ROL", SDTIntUnaryOp>; def AVRror : SDNode<"AVRISD::ROR", SDTIntUnaryOp>; def AVRasr : SDNode<"AVRISD::ASR", SDTIntUnaryOp>; +def AVRasr8 : SDNode<"AVRISD::ASR8", SDTIntUnaryOp>; def AVRlsl7 : SDNode<"AVRISD::LSL7", SDTIntUnaryOp>; def AVRlsr7 : SDNode<"AVRISD::LSR7", SDTIntUnaryOp>; def AVRasr7 : SDNode<"AVRISD::ASR7", SDTIntUnaryOp>; @@ -1674,6 +1681,21 @@ Defs = [SREG] in "lslb7\t$rd", [(set i8:$rd, (AVRlsl7 i8:$src)), (implicit SREG)]>; + def LSLW4Rd : Pseudo<(outs DREGS:$rd), + (ins DREGS:$src), + "lslw4\t$rd", + [(set i16:$rd, (AVRlsl4 i16:$src)), (implicit SREG)]>; + + def LSLW8Rd : Pseudo<(outs DREGS:$rd), + (ins DREGS:$src), + "lslw8\t$rd", + [(set i16:$rd, (AVRlsl8 i16:$src)), (implicit SREG)]>; + + def LSLW12Rd : Pseudo<(outs DREGS:$rd), + (ins DREGS:$src), + "lslw12\t$rd", + [(set i16:$rd, (AVRlsl12 i16:$src)), (implicit SREG)]>; + def LSRRd : FRd<0b1001, 0b0100110, (outs GPR8:$rd), @@ -1691,6 +1713,21 @@ Defs = [SREG] in "lsrw\t$rd", [(set i16:$rd, (AVRlsr i16:$src)), (implicit SREG)]>; + def LSRW4Rd : Pseudo<(outs DREGS:$rd), + (ins DREGS:$src), + "lsrw4\t$rd", + [(set i16:$rd, (AVRlsr4 i16:$src)), (implicit SREG)]>; + + def LSRW8Rd : Pseudo<(outs DREGS:$rd), + (ins DREGS:$src), + "lsrw8\t$rd", + [(set i16:$rd, (AVRlsr8 i16:$src)), (implicit SREG)]>; + + def LSRW12Rd : Pseudo<(outs DREGS:$rd), + (ins DREGS:$src), + "lsrw12\t$rd", + [(set i16:$rd, (AVRlsr12 i16:$src)), (implicit SREG)]>; + def ASRRd : FRd<0b1001, 0b0100101, (outs GPR8:$rd), @@ -1708,6 +1745,11 @@ Defs = [SREG] in "asrw\t$rd", [(set i16:$rd, (AVRasr i16:$src)), (implicit SREG)]>; + def ASRW8Rd : Pseudo<(outs DREGS:$rd), + (ins DREGS:$src), + "asrw8\t$rd", + [(set i16:$rd, (AVRasr8 i16:$src)), (implicit SREG)]>; + // Bit rotate operations. let Uses = [SREG] in { @@ -2123,12 +2165,7 @@ def : Pat<(store i16:$src, (i16 (AVRWrapper tglobaladdr:$dst))), def : Pat<(i16 (AVRWrapper tblockaddress:$dst)), (LDIWRdK tblockaddress:$dst)>; -// hi-reg truncation : trunc(int16 >> 8) -//:FIXME: i think it's better to emit an extract subreg node in the DAG than -// all this mess once we get optimal shift code -// lol... I think so, too. [@agnat] -def : Pat<(i8 (trunc (AVRlsr (AVRlsr (AVRlsr (AVRlsr (AVRlsr (AVRlsr (AVRlsr - (AVRlsr DREGS:$src)))))))))), +def : Pat<(i8 (trunc (AVRlsr8 DREGS:$src))), (EXTRACT_SUBREG DREGS:$src, sub_hi)>; // :FIXME: DAGCombiner produces an shl node after legalization from these seq: diff --git a/test/CodeGen/AVR/shift.ll b/test/CodeGen/AVR/shift.ll index 7a21a016e5c..34042b3a119 100644 --- a/test/CodeGen/AVR/shift.ll +++ b/test/CodeGen/AVR/shift.ll @@ -178,3 +178,93 @@ define i8 @asr_i8_7(i8 %a) { %result = ashr i8 %a, 7 ret i8 %result } + +define i16 @lsl_i16_5(i16 %a) { +; CHECK-LABEL: lsl_i16_5 +; CHECK: swap r25 +; CHECK-NEXT: swap r24 +; CHECK-NEXT: andi r25, 240 +; CHECK-NEXT: eor r25, r24 +; CHECK-NEXT: andi r24, 240 +; CHECK-NEXT: eor r25, r24 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: rol r25 +; CHECK-NEXT: ret + %result = shl i16 %a, 5 + ret i16 %result +} + +define i16 @lsl_i16_9(i16 %a) { +; CHECK-LABEL: lsl_i16_9 +; CHECK: mov r25, r24 +; CHECK-NEXT: clr r24 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: rol r25 +; CHECK-NEXT: ret + %result = shl i16 %a, 9 + ret i16 %result +} + +define i16 @lsl_i16_13(i16 %a) { +; CHECK-LABEL: lsl_i16_13 +; CHECK: mov r25, r24 +; CHECK-NEXT: swap r25 +; CHECK-NEXT: andi r25, 240 +; CHECK-NEXT: clr r24 +; CHECK-NEXT: lsl r24 +; CHECK-NEXT: rol r25 +; CHECK-NEXT: ret + %result = shl i16 %a, 13 + ret i16 %result +} + +define i16 @lsr_i16_5(i16 %a) { +; CHECK-LABEL: lsr_i16_5 +; CHECK: swap r25 +; CHECK-NEXT: swap r24 +; CHECK-NEXT: andi r24, 15 +; CHECK-NEXT: eor r24, r25 +; CHECK-NEXT: andi r25, 15 +; CHECK-NEXT: eor r24, r25 +; CHECK-NEXT: lsr r25 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: ret + %result = lshr i16 %a, 5 + ret i16 %result +} + +define i16 @lsr_i16_9(i16 %a) { +; CHECK-LABEL: lsr_i16_9 +; CHECK: mov r24, r25 +; CHECK-NEXT: clr r25 +; CHECK-NEXT: lsr r25 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: ret + %result = lshr i16 %a, 9 + ret i16 %result +} + +define i16 @lsr_i16_13(i16 %a) { +; CHECK-LABEL: lsr_i16_13 +; CHECK: mov r24, r25 +; CHECK-NEXT: swap r24 +; CHECK-NEXT: andi r24, 15 +; CHECK-NEXT: clr r25 +; CHECK-NEXT: lsr r25 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: ret + %result = lshr i16 %a, 13 + ret i16 %result +} + +define i16 @asr_i16_9(i16 %a) { +; CHECK-LABEL: asr_i16_9 +; CHECK: mov r24, r25 +; CHECK-NEXT: lsl r25 +; CHECK-NEXT: sbc r25, r25 +; CHECK-NEXT: asr r25 +; CHECK-NEXT: ror r24 +; CHECK-NEXT: ret + %result = ashr i16 %a, 9 + ret i16 %result +}