diff --git a/lib/Transforms/InstCombine/InstCombineCasts.cpp b/lib/Transforms/InstCombine/InstCombineCasts.cpp index 72f0c1fc6a7..71ce74959c2 100644 --- a/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -499,6 +499,26 @@ Instruction *InstCombiner::visitTrunc(TruncInst &CI) { return CastInst::CreateIntegerCast(Shift, CI.getType(), false); } + // Transform trunc(lshr (sext A), Cst) to ashr A, Cst to eliminate type + // conversion. + // It works because bits coming from sign extension have the same value as + // sign bit of the original value; performing ashr instead of lshr + // generates bits of the same value as the sign bit. + if (Src->hasOneUse() && + match(Src, m_LShr(m_SExt(m_Value(A)), m_ConstantInt(Cst))) && + cast(Src)->getOperand(0)->hasOneUse()) { + const unsigned ASize = A->getType()->getPrimitiveSizeInBits(); + // This optimization can be only performed when zero bits generated by + // the original lshr aren't pulled into the value after truncation, so we + // can only shift by values smaller then the size of destination type (in + // bits). + if (Cst->getValue().ult(ASize)) { + Value *Shift = Builder->CreateAShr(A, Cst->getZExtValue()); + Shift->takeName(Src); + return CastInst::CreateIntegerCast(Shift, CI.getType(), false); + } + } + // Transform "trunc (and X, cst)" -> "and (trunc X), cst" so long as the dest // type isn't non-native. if (Src->hasOneUse() && isa(Src->getType()) && diff --git a/test/Transforms/InstCombine/cast.ll b/test/Transforms/InstCombine/cast.ll index 7fe54ef8469..6edcbe2bea3 100644 --- a/test/Transforms/InstCombine/cast.ll +++ b/test/Transforms/InstCombine/cast.ll @@ -1062,6 +1062,43 @@ define i8 @test85(i32 %a) { ; CHECK: [[CST:%.*]] = trunc i32 [[SHR]] to i8 } +define i16 @test86(i16 %v) { + %a = sext i16 %v to i32 + %s = ashr i32 %a, 4 + %t = trunc i32 %s to i16 + ret i16 %t + +; CHECK-LABEL: @test86( +; CHECK: [[ASHR:%.*]] = ashr i16 %v, 4 +; CHECK-NEXT: ret i16 [[ASHR]] +} + +define i16 @test87(i16 %v) { + %c = sext i16 %v to i32 + %m = mul nsw i32 %c, 16 + %a = ashr i32 %m, 16 + %t = trunc i32 %a to i16 + ret i16 %t + +; CHECK-LABEL: @test87( +; CHECK: [[ASHR:%.*]] = ashr i16 %v, 12 +; CHECK-NEXT: ret i16 [[ASHR]] +} + +define i16 @test88(i16 %v) { + %a = sext i16 %v to i32 + %s = ashr i32 %a, 18 + %t = trunc i32 %s to i16 + ret i16 %t + +; Do not optimize to ashr i16 (shift by 18) +; CHECK-LABEL: @test88( +; CHECK: [[SEXT:%.*]] = sext i16 %v to i32 +; CHECK-NEXT: [[ASHR:%.*]] = ashr i32 [[SEXT]], 18 +; CHECK-NEXT: [[TRUNC:%.*]] = trunc i32 [[ASHR]] to i16 +; CHECK-NEXT: ret i16 [[TRUNC]] +} + ; Overflow on a float to int or int to float conversion is undefined (PR21130). define i8 @overflow_fptosi() {