Index: llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp =================================================================== --- llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -1364,6 +1364,135 @@ 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; @@ -1395,6 +1524,135 @@ 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"); @@ -1438,6 +1696,39 @@ 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; Register DstLoReg, DstHiReg; @@ -1654,10 +1945,17 @@ 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::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 @@ -36,8 +36,15 @@ /// 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. 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,53 @@ llvm_unreachable("Invalid shift opcode"); } + // Optimize 16-bit int shifts. + 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--) { 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 @@ -55,10 +55,17 @@ // 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>; // Pseudo shift nodes for non-constant shift amounts. def AVRlslLoop : SDNode<"AVRISD::LSLLOOP", SDTIntShiftOp>; @@ -1653,6 +1660,21 @@ "lslw\t$rd", [(set i16:$rd, (AVRlsl i16:$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), @@ -1665,6 +1687,21 @@ "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), @@ -1677,6 +1714,11 @@ "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 { @@ -2092,12 +2134,7 @@ 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: Index: llvm/test/CodeGen/AVR/shift.ll =================================================================== --- llvm/test/CodeGen/AVR/shift.ll +++ llvm/test/CodeGen/AVR/shift.ll @@ -6,3 +6,93 @@ %result = shl i64 %a, %b ret i64 %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 +}