From 836f4bd0903294ae7976356d7d507c8602e1044b Mon Sep 17 00:00:00 2001 From: Juergen Ributzka Date: Wed, 27 Aug 2014 00:58:26 +0000 Subject: [PATCH] [FastISel][AArch64] Fold Sign-/Zero-Extend into the shift immediate instruction. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216510 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/AArch64/AArch64FastISel.cpp | 304 +++++++++++++++++++----- test/CodeGen/AArch64/fast-isel-shift.ll | 170 +++++++++++++ 2 files changed, 419 insertions(+), 55 deletions(-) diff --git a/lib/Target/AArch64/AArch64FastISel.cpp b/lib/Target/AArch64/AArch64FastISel.cpp index 14089f50160..4811691732e 100644 --- a/lib/Target/AArch64/AArch64FastISel.cpp +++ b/lib/Target/AArch64/AArch64FastISel.cpp @@ -195,13 +195,16 @@ private: unsigned Op1, bool Op1IsKill); unsigned emitLSL_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, unsigned Op1Reg, bool Op1IsKill); - unsigned emitLSL_ri(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, uint64_t Imm); + unsigned emitLSL_ri(MVT RetVT, MVT SrcVT, unsigned Op0Reg, bool Op0IsKill, + uint64_t Imm, bool IsZExt = true); unsigned emitLSR_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, unsigned Op1Reg, bool Op1IsKill); - unsigned emitLSR_ri(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, uint64_t Imm); + unsigned emitLSR_ri(MVT RetVT, MVT SrcVT, unsigned Op0Reg, bool Op0IsKill, + uint64_t Imm, bool IsZExt = true); unsigned emitASR_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, unsigned Op1Reg, bool Op1IsKill); - unsigned emitASR_ri(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, uint64_t Imm); + unsigned emitASR_ri(MVT RetVT, MVT SrcVT, unsigned Op0Reg, bool Op0IsKill, + uint64_t Imm, bool IsZExt = false); unsigned AArch64MaterializeInt(const ConstantInt *CI, MVT VT); unsigned AArch64MaterializeFP(const ConstantFP *CFP, MVT VT); @@ -718,8 +721,8 @@ bool AArch64FastISel::SimplifyAddress(Address &Addr, MVT VT) { Addr.getOffsetReg(), /*TODO:IsKill=*/false, Addr.getShift()); else - ResultReg = emitLSL_ri(MVT::i64, Addr.getOffsetReg(), /*Op0IsKill=*/false, - Addr.getShift()); + ResultReg = emitLSL_ri(MVT::i64, MVT::i64, Addr.getOffsetReg(), + /*Op0IsKill=*/false, Addr.getShift()); if (!ResultReg) return false; @@ -2381,7 +2384,8 @@ bool AArch64FastISel::FastLowerIntrinsicCall(const IntrinsicInst *II) { if (VT == MVT::i32) { MulReg = Emit_SMULL_rr(MVT::i64, LHSReg, LHSIsKill, RHSReg, RHSIsKill); - unsigned ShiftReg = emitLSR_ri(MVT::i64, MulReg, /*IsKill=*/false, 32); + unsigned ShiftReg = emitLSR_ri(MVT::i64, MVT::i64, MulReg, + /*IsKill=*/false, 32); MulReg = FastEmitInst_extractsubreg(VT, MulReg, /*IsKill=*/true, AArch64::sub_32); ShiftReg = FastEmitInst_extractsubreg(VT, ShiftReg, /*IsKill=*/true, @@ -2694,23 +2698,70 @@ unsigned AArch64FastISel::emitLSL_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, return ResultReg; } -unsigned AArch64FastISel::emitLSL_ri(MVT RetVT, unsigned Op0, bool Op0IsKill, - uint64_t Shift) { - unsigned Opc, ImmR, ImmS; - switch (RetVT.SimpleTy) { - default: return 0; - case MVT::i8: - Opc = AArch64::UBFMWri; ImmR = -Shift % 32; ImmS = 7 - Shift; break; - case MVT::i16: - Opc = AArch64::UBFMWri; ImmR = -Shift % 32; ImmS = 15 - Shift; break; - case MVT::i32: - Opc = AArch64::UBFMWri; ImmR = -Shift % 32; ImmS = 31 - Shift; break; - case MVT::i64: - Opc = AArch64::UBFMXri; ImmR = -Shift % 64; ImmS = 63 - Shift; break; - } +unsigned AArch64FastISel::emitLSL_ri(MVT RetVT, MVT SrcVT, unsigned Op0, + bool Op0IsKill, uint64_t Shift, + bool IsZext) { + assert(RetVT.SimpleTy >= SrcVT.SimpleTy && + "Unexpected source/return type pair."); + assert((SrcVT == MVT::i8 || SrcVT == MVT::i16 || SrcVT == MVT::i32 || + SrcVT == MVT::i64) && "Unexpected source value type."); + assert((RetVT == MVT::i8 || RetVT == MVT::i16 || RetVT == MVT::i32 || + RetVT == MVT::i64) && "Unexpected return value type."); + + bool Is64Bit = (RetVT == MVT::i64); + unsigned RegSize = Is64Bit ? 64 : 32; + unsigned DstBits = RetVT.getSizeInBits(); + unsigned SrcBits = SrcVT.getSizeInBits(); + // Don't deal with undefined shifts. + if (Shift >= DstBits) + return 0; + + // For immediate shifts we can fold the zero-/sign-extension into the shift. + // {S|U}BFM Wd, Wn, #r, #s + // Wd<32+s-r,32-r> = Wn when r > s + + // %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16 + // %2 = shl i16 %1, 4 + // Wd<32+7-28,32-28> = Wn<7:0> <- clamp s to 7 + // 0b1111_1111_1111_1111__1111_1010_1010_0000 sext + // 0b0000_0000_0000_0000__0000_0101_0101_0000 sext | zext + // 0b0000_0000_0000_0000__0000_1010_1010_0000 zext + + // %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16 + // %2 = shl i16 %1, 8 + // Wd<32+7-24,32-24> = Wn<7:0> + // 0b1111_1111_1111_1111__1010_1010_0000_0000 sext + // 0b0000_0000_0000_0000__0101_0101_0000_0000 sext | zext + // 0b0000_0000_0000_0000__1010_1010_0000_0000 zext + + // %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16 + // %2 = shl i16 %1, 12 + // Wd<32+3-20,32-20> = Wn<3:0> + // 0b1111_1111_1111_1111__1010_0000_0000_0000 sext + // 0b0000_0000_0000_0000__0101_0000_0000_0000 sext | zext + // 0b0000_0000_0000_0000__1010_0000_0000_0000 zext + + unsigned ImmR = RegSize - Shift; + // Limit the width to the length of the source type. + unsigned ImmS = std::min(SrcBits - 1, DstBits - 1 - Shift); + static const unsigned OpcTable[2][2] = { + {AArch64::SBFMWri, AArch64::SBFMXri}, + {AArch64::UBFMWri, AArch64::UBFMXri} + }; + unsigned Opc = OpcTable[IsZext][Is64Bit]; const TargetRegisterClass *RC = - (RetVT == MVT::i64) ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass; + Is64Bit ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass; + if (SrcVT.SimpleTy <= MVT::i32 && RetVT == MVT::i64) { + unsigned TmpReg = MRI.createVirtualRegister(RC); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, + TII.get(AArch64::SUBREG_TO_REG), TmpReg) + .addImm(0) + .addReg(Op0, getKillRegState(Op0IsKill)) + .addImm(AArch64::sub_32); + Op0 = TmpReg; + Op0IsKill = true; + } return FastEmitInst_rii(Opc, RC, Op0, Op0IsKill, ImmR, ImmS); } @@ -2741,20 +2792,86 @@ unsigned AArch64FastISel::emitLSR_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, return ResultReg; } -unsigned AArch64FastISel::emitLSR_ri(MVT RetVT, unsigned Op0, bool Op0IsKill, - uint64_t Shift) { - unsigned Opc, ImmS; - switch (RetVT.SimpleTy) { - default: return 0; - case MVT::i8: Opc = AArch64::UBFMWri; ImmS = 7; break; - case MVT::i16: Opc = AArch64::UBFMWri; ImmS = 15; break; - case MVT::i32: Opc = AArch64::UBFMWri; ImmS = 31; break; - case MVT::i64: Opc = AArch64::UBFMXri; ImmS = 63; break; +unsigned AArch64FastISel::emitLSR_ri(MVT RetVT, MVT SrcVT, unsigned Op0, + bool Op0IsKill, uint64_t Shift, + bool IsZExt) { + assert(RetVT.SimpleTy >= SrcVT.SimpleTy && + "Unexpected source/return type pair."); + assert((SrcVT == MVT::i8 || SrcVT == MVT::i16 || SrcVT == MVT::i32 || + SrcVT == MVT::i64) && "Unexpected source value type."); + assert((RetVT == MVT::i8 || RetVT == MVT::i16 || RetVT == MVT::i32 || + RetVT == MVT::i64) && "Unexpected return value type."); + + bool Is64Bit = (RetVT == MVT::i64); + unsigned RegSize = Is64Bit ? 64 : 32; + unsigned DstBits = RetVT.getSizeInBits(); + unsigned SrcBits = SrcVT.getSizeInBits(); + + // Don't deal with undefined shifts. + if (Shift >= DstBits) + return 0; + + // For immediate shifts we can fold the zero-/sign-extension into the shift. + // {S|U}BFM Wd, Wn, #r, #s + // Wd = Wn when r <= s + + // %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16 + // %2 = lshr i16 %1, 4 + // Wd<7-4:0> = Wn<7:4> + // 0b0000_0000_0000_0000__0000_1111_1111_1010 sext + // 0b0000_0000_0000_0000__0000_0000_0000_0101 sext | zext + // 0b0000_0000_0000_0000__0000_0000_0000_1010 zext + + // %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16 + // %2 = lshr i16 %1, 8 + // Wd<7-7,0> = Wn<7:7> + // 0b0000_0000_0000_0000__0000_0000_1111_1111 sext + // 0b0000_0000_0000_0000__0000_0000_0000_0000 sext + // 0b0000_0000_0000_0000__0000_0000_0000_0000 zext + + // %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16 + // %2 = lshr i16 %1, 12 + // Wd<7-7,0> = Wn<7:7> <- clamp r to 7 + // 0b0000_0000_0000_0000__0000_0000_0000_1111 sext + // 0b0000_0000_0000_0000__0000_0000_0000_0000 sext + // 0b0000_0000_0000_0000__0000_0000_0000_0000 zext + + if (Shift >= SrcBits && IsZExt) + return AArch64MaterializeInt(ConstantInt::get(*Context, APInt(RegSize, 0)), + RetVT); + + // It is not possible to fold a sign-extend into the LShr instruction. In this + // case emit a sign-extend. + if (!IsZExt) { + Op0 = EmitIntExt(SrcVT, Op0, RetVT, IsZExt); + if (!Op0) + return 0; + Op0IsKill = true; + SrcVT = RetVT; + SrcBits = SrcVT.getSizeInBits(); + IsZExt = true; } + unsigned ImmR = std::min(SrcBits - 1, Shift); + unsigned ImmS = SrcBits - 1; + static const unsigned OpcTable[2][2] = { + {AArch64::SBFMWri, AArch64::SBFMXri}, + {AArch64::UBFMWri, AArch64::UBFMXri} + }; + unsigned Opc = OpcTable[IsZExt][Is64Bit]; const TargetRegisterClass *RC = - (RetVT == MVT::i64) ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass; - return FastEmitInst_rii(Opc, RC, Op0, Op0IsKill, Shift, ImmS); + Is64Bit ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass; + if (SrcVT.SimpleTy <= MVT::i32 && RetVT == MVT::i64) { + unsigned TmpReg = MRI.createVirtualRegister(RC); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, + TII.get(AArch64::SUBREG_TO_REG), TmpReg) + .addImm(0) + .addReg(Op0, getKillRegState(Op0IsKill)) + .addImm(AArch64::sub_32); + Op0 = TmpReg; + Op0IsKill = true; + } + return FastEmitInst_rii(Opc, RC, Op0, Op0IsKill, ImmR, ImmS); } unsigned AArch64FastISel::emitASR_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, @@ -2784,20 +2901,74 @@ unsigned AArch64FastISel::emitASR_rr(MVT RetVT, unsigned Op0Reg, bool Op0IsKill, return ResultReg; } -unsigned AArch64FastISel::emitASR_ri(MVT RetVT, unsigned Op0, bool Op0IsKill, - uint64_t Shift) { - unsigned Opc, ImmS; - switch (RetVT.SimpleTy) { - default: return 0; - case MVT::i8: Opc = AArch64::SBFMWri; ImmS = 7; break; - case MVT::i16: Opc = AArch64::SBFMWri; ImmS = 15; break; - case MVT::i32: Opc = AArch64::SBFMWri; ImmS = 31; break; - case MVT::i64: Opc = AArch64::SBFMXri; ImmS = 63; break; - } +unsigned AArch64FastISel::emitASR_ri(MVT RetVT, MVT SrcVT, unsigned Op0, + bool Op0IsKill, uint64_t Shift, + bool IsZExt) { + assert(RetVT.SimpleTy >= SrcVT.SimpleTy && + "Unexpected source/return type pair."); + assert((SrcVT == MVT::i8 || SrcVT == MVT::i16 || SrcVT == MVT::i32 || + SrcVT == MVT::i64) && "Unexpected source value type."); + assert((RetVT == MVT::i8 || RetVT == MVT::i16 || RetVT == MVT::i32 || + RetVT == MVT::i64) && "Unexpected return value type."); + + bool Is64Bit = (RetVT == MVT::i64); + unsigned RegSize = Is64Bit ? 64 : 32; + unsigned DstBits = RetVT.getSizeInBits(); + unsigned SrcBits = SrcVT.getSizeInBits(); + // Don't deal with undefined shifts. + if (Shift >= DstBits) + return 0; + + // For immediate shifts we can fold the zero-/sign-extension into the shift. + // {S|U}BFM Wd, Wn, #r, #s + // Wd = Wn when r <= s + + // %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16 + // %2 = ashr i16 %1, 4 + // Wd<7-4:0> = Wn<7:4> + // 0b1111_1111_1111_1111__1111_1111_1111_1010 sext + // 0b0000_0000_0000_0000__0000_0000_0000_0101 sext | zext + // 0b0000_0000_0000_0000__0000_0000_0000_1010 zext + + // %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16 + // %2 = ashr i16 %1, 8 + // Wd<7-7,0> = Wn<7:7> + // 0b1111_1111_1111_1111__1111_1111_1111_1111 sext + // 0b0000_0000_0000_0000__0000_0000_0000_0000 sext + // 0b0000_0000_0000_0000__0000_0000_0000_0000 zext + + // %1 = {s|z}ext i8 {0b1010_1010|0b0101_0101} to i16 + // %2 = ashr i16 %1, 12 + // Wd<7-7,0> = Wn<7:7> <- clamp r to 7 + // 0b1111_1111_1111_1111__1111_1111_1111_1111 sext + // 0b0000_0000_0000_0000__0000_0000_0000_0000 sext + // 0b0000_0000_0000_0000__0000_0000_0000_0000 zext + + if (Shift >= SrcBits && IsZExt) + return AArch64MaterializeInt(ConstantInt::get(*Context, APInt(RegSize, 0)), + RetVT); + + unsigned ImmR = std::min(SrcBits - 1, Shift); + unsigned ImmS = SrcBits - 1; + static const unsigned OpcTable[2][2] = { + {AArch64::SBFMWri, AArch64::SBFMXri}, + {AArch64::UBFMWri, AArch64::UBFMXri} + }; + unsigned Opc = OpcTable[IsZExt][Is64Bit]; const TargetRegisterClass *RC = - (RetVT == MVT::i64) ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass; - return FastEmitInst_rii(Opc, RC, Op0, Op0IsKill, Shift, ImmS); + Is64Bit ? &AArch64::GPR64RegClass : &AArch64::GPR32RegClass; + if (SrcVT.SimpleTy <= MVT::i32 && RetVT == MVT::i64) { + unsigned TmpReg = MRI.createVirtualRegister(RC); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, + TII.get(AArch64::SUBREG_TO_REG), TmpReg) + .addImm(0) + .addReg(Op0, getKillRegState(Op0IsKill)) + .addImm(AArch64::sub_32); + Op0 = TmpReg; + Op0IsKill = true; + } + return FastEmitInst_rii(Opc, RC, Op0, Op0IsKill, ImmR, ImmS); } unsigned AArch64FastISel::EmitIntExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, @@ -2988,29 +3159,47 @@ bool AArch64FastISel::SelectMul(const Instruction *I) { } bool AArch64FastISel::SelectShift(const Instruction *I) { - EVT RetEVT = TLI.getValueType(I->getType(), true); - if (!RetEVT.isSimple()) - return false; - MVT RetVT = RetEVT.getSimpleVT(); - - unsigned Op0Reg = getRegForValue(I->getOperand(0)); - if (!Op0Reg) + MVT RetVT; + if (!isLoadStoreTypeLegal(I->getType(), RetVT)) return false; - bool Op0IsKill = hasTrivialKill(I->getOperand(0)); if (const auto *C = dyn_cast(I->getOperand(1))) { unsigned ResultReg = 0; uint64_t ShiftVal = C->getZExtValue(); + MVT SrcVT = RetVT; + bool IsZExt = (I->getOpcode() == Instruction::AShr) ? false : true; + const Value * Op0 = I->getOperand(0); + if (const auto *ZExt = dyn_cast(Op0)) { + MVT TmpVT; + if (isLoadStoreTypeLegal(ZExt->getSrcTy(), TmpVT)) { + SrcVT = TmpVT; + IsZExt = true; + Op0 = ZExt->getOperand(0); + } + } else if (const auto *SExt = dyn_cast(Op0)) { + MVT TmpVT; + if (isLoadStoreTypeLegal(SExt->getSrcTy(), TmpVT)) { + SrcVT = TmpVT; + IsZExt = false; + Op0 = SExt->getOperand(0); + } + } + + unsigned Op0Reg = getRegForValue(Op0); + if (!Op0Reg) + return false; + bool Op0IsKill = hasTrivialKill(Op0); + switch (I->getOpcode()) { default: llvm_unreachable("Unexpected instruction."); case Instruction::Shl: - ResultReg = emitLSL_ri(RetVT, Op0Reg, Op0IsKill, ShiftVal); + ResultReg = emitLSL_ri(RetVT, SrcVT, Op0Reg, Op0IsKill, ShiftVal, IsZExt); break; case Instruction::AShr: - ResultReg = emitASR_ri(RetVT, Op0Reg, Op0IsKill, ShiftVal); + ResultReg = emitASR_ri(RetVT, SrcVT, Op0Reg, Op0IsKill, ShiftVal, IsZExt); break; case Instruction::LShr: - ResultReg = emitLSR_ri(RetVT, Op0Reg, Op0IsKill, ShiftVal); + ResultReg = emitLSR_ri(RetVT, SrcVT, Op0Reg, Op0IsKill, ShiftVal, IsZExt); break; } if (!ResultReg) @@ -3020,6 +3209,11 @@ bool AArch64FastISel::SelectShift(const Instruction *I) { return true; } + unsigned Op0Reg = getRegForValue(I->getOperand(0)); + if (!Op0Reg) + return false; + bool Op0IsKill = hasTrivialKill(I->getOperand(0)); + unsigned Op1Reg = getRegForValue(I->getOperand(1)); if (!Op1Reg) return false; diff --git a/test/CodeGen/AArch64/fast-isel-shift.ll b/test/CodeGen/AArch64/fast-isel-shift.ll index 210b4ed9b92..2a4fffcf0f9 100644 --- a/test/CodeGen/AArch64/fast-isel-shift.ll +++ b/test/CodeGen/AArch64/fast-isel-shift.ll @@ -16,6 +16,56 @@ define zeroext i8 @lsl_i8(i8 %a) { ret i8 %1 } +; CHECK-LABEL: lsl_zext_i8_i16 +; CHECK: ubfiz {{w[0-9]*}}, {{w[0-9]*}}, #4, #8 +define zeroext i16 @lsl_zext_i8_i16(i8 %b) { + %1 = zext i8 %b to i16 + %2 = shl i16 %1, 4 + ret i16 %2 +} + +; CHECK-LABEL: lsl_sext_i8_i16 +; CHECK: sbfiz {{w[0-9]*}}, {{w[0-9]*}}, #4, #8 +define signext i16 @lsl_sext_i8_i16(i8 %b) { + %1 = sext i8 %b to i16 + %2 = shl i16 %1, 4 + ret i16 %2 +} + +; CHECK-LABEL: lsl_zext_i8_i32 +; CHECK: ubfiz {{w[0-9]*}}, {{w[0-9]*}}, #4, #8 +define i32 @lsl_zext_i8_i32(i8 %b) { + %1 = zext i8 %b to i32 + %2 = shl i32 %1, 4 + ret i32 %2 +} + +; CHECK-LABEL: lsl_sext_i8_i32 +; CHECK: sbfiz {{w[0-9]*}}, {{w[0-9]*}}, #4, #8 +define i32 @lsl_sext_i8_i32(i8 %b) { + %1 = sext i8 %b to i32 + %2 = shl i32 %1, 4 + ret i32 %2 +} + +; FIXME: Cannot test this yet, because the target-independent instruction +; selector handles this. +; CHECK-LABEL: lsl_zext_i8_i64 +define i64 @lsl_zext_i8_i64(i8 %b) { + %1 = zext i8 %b to i64 + %2 = shl i64 %1, 4 + ret i64 %2 +} + +; FIXME: Cannot test this yet, because the target-independent instruction +; selector handles this. +; CHECK-LABEL: lsl_sext_i8_i64 +define i64 @lsl_sext_i8_i64(i8 %b) { + %1 = sext i8 %b to i64 + %2 = shl i64 %1, 4 + ret i64 %2 +} + ; CHECK-LABEL: lslv_i16 ; CHECK: and [[REG1:w[0-9]+]], w1, #0xffff ; CHECK-NEXT: lsl [[REG2:w[0-9]+]], w0, [[REG1]] @@ -32,6 +82,40 @@ define zeroext i16 @lsl_i16(i16 %a) { ret i16 %1 } +; CHECK-LABEL: lsl_zext_i16_i32 +; CHECK: ubfiz {{w[0-9]*}}, {{w[0-9]*}}, #8, #16 +define i32 @lsl_zext_i16_i32(i16 %b) { + %1 = zext i16 %b to i32 + %2 = shl i32 %1, 8 + ret i32 %2 +} + +; CHECK-LABEL: lsl_sext_i16_i32 +; CHECK: sbfiz {{w[0-9]*}}, {{w[0-9]*}}, #8, #16 +define i32 @lsl_sext_i16_i32(i16 %b) { + %1 = sext i16 %b to i32 + %2 = shl i32 %1, 8 + ret i32 %2 +} + +; FIXME: Cannot test this yet, because the target-independent instruction +; selector handles this. +; CHECK-LABEL: lsl_zext_i16_i64 +define i64 @lsl_zext_i16_i64(i16 %b) { + %1 = zext i16 %b to i64 + %2 = shl i64 %1, 8 + ret i64 %2 +} + +; FIXME: Cannot test this yet, because the target-independent instruction +; selector handles this. +; CHECK-LABEL: lsl_sext_i16_i64 +define i64 @lsl_sext_i16_i64(i16 %b) { + %1 = sext i16 %b to i64 + %2 = shl i64 %1, 8 + ret i64 %2 +} + ; CHECK-LABEL: lslv_i32 ; CHECK: lsl {{w[0-9]*}}, w0, w1 define zeroext i32 @lslv_i32(i32 %a, i32 %b) { @@ -46,6 +130,26 @@ define zeroext i32 @lsl_i32(i32 %a) { ret i32 %1 } +; FIXME: Cannot test this yet, because the target-independent instruction +; selector handles this. +; CHECK-LABEL: lsl_zext_i32_i64 +define i64 @lsl_zext_i32_i64(i32 %b) { + %1 = zext i32 %b to i64 + %2 = shl i64 %1, 16 + ret i64 %2 +} + +; FIXME: Cannot test this yet, because the target-independent instruction +; selector handles this. +; CHECK-LABEL: lsl_sext_i32_i64 +define i64 @lsl_sext_i32_i64(i32 %b) { + %1 = sext i32 %b to i64 + %2 = shl i64 %1, 16 + ret i64 %2 +} + +; FIXME: Cannot test this yet, because the target-independent instruction +; selector handles this. ; CHECK-LABEL: lslv_i64 ; CHECK: lsl {{x[0-9]*}}, x0, x1 define i64 @lslv_i64(i64 %a, i64 %b) { @@ -78,6 +182,40 @@ define zeroext i8 @lsr_i8(i8 %a) { ret i8 %1 } +; CHECK-LABEL: lsr_zext_i8_i16 +; CHECK: ubfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4 +define zeroext i16 @lsr_zext_i8_i16(i8 %b) { + %1 = zext i8 %b to i16 + %2 = lshr i16 %1, 4 + ret i16 %2 +} + +; CHECK-LABEL: lsr_sext_i8_i16 +; CHECK: sxtb [[REG:w[0-9]+]], w0 +; CHECK-NEXT: ubfx {{w[0-9]*}}, [[REG]], #4, #12 +define signext i16 @lsr_sext_i8_i16(i8 %b) { + %1 = sext i8 %b to i16 + %2 = lshr i16 %1, 4 + ret i16 %2 +} + +; CHECK-LABEL: lsr_zext_i8_i32 +; CHECK: ubfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4 +define i32 @lsr_zext_i8_i32(i8 %b) { + %1 = zext i8 %b to i32 + %2 = lshr i32 %1, 4 + ret i32 %2 +} + +; CHECK-LABEL: lsr_sext_i8_i32 +; CHECK: sxtb [[REG:w[0-9]+]], w0 +; CHECK-NEXT: lsr {{w[0-9]*}}, [[REG]], #4 +define i32 @lsr_sext_i8_i32(i8 %b) { + %1 = sext i8 %b to i32 + %2 = lshr i32 %1, 4 + ret i32 %2 +} + ; CHECK-LABEL: lsrv_i16 ; CHECK: and [[REG1:w[0-9]+]], w0, #0xffff ; CHECK-NEXT: and [[REG2:w[0-9]+]], w1, #0xffff @@ -141,6 +279,38 @@ define zeroext i8 @asr_i8(i8 %a) { ret i8 %1 } +; CHECK-LABEL: asr_zext_i8_i16 +; CHECK: ubfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4 +define zeroext i16 @asr_zext_i8_i16(i8 %b) { + %1 = zext i8 %b to i16 + %2 = ashr i16 %1, 4 + ret i16 %2 +} + +; CHECK-LABEL: asr_sext_i8_i16 +; CHECK: sbfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4 +define signext i16 @asr_sext_i8_i16(i8 %b) { + %1 = sext i8 %b to i16 + %2 = ashr i16 %1, 4 + ret i16 %2 +} + +; CHECK-LABEL: asr_zext_i8_i32 +; CHECK: ubfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4 +define i32 @asr_zext_i8_i32(i8 %b) { + %1 = zext i8 %b to i32 + %2 = ashr i32 %1, 4 + ret i32 %2 +} + +; CHECK-LABEL: asr_sext_i8_i32 +; CHECK: sbfx {{w[0-9]*}}, {{w[0-9]*}}, #4, #4 +define i32 @asr_sext_i8_i32(i8 %b) { + %1 = sext i8 %b to i32 + %2 = ashr i32 %1, 4 + ret i32 %2 +} + ; CHECK-LABEL: asrv_i16 ; CHECK: sxth [[REG1:w[0-9]+]], w0 ; CHECK-NEXT: and [[REG2:w[0-9]+]], w1, #0xffff -- 2.34.1