Index: llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -1877,6 +1877,19 @@ } } + // If this 'and 'masks out high bits, we may be able to move a trunc before + // a shift-right. This will reduce the shift width and potentially eliminate + // the 'and' completely: + // and (trunc (lshr X, ShiftC)), C --> and (lshr (trunc X), ShiftC), C + if (match(Op0, m_OneUse(m_Trunc( + m_OneUse(m_LShr(m_Value(X), m_APInt(ShiftC)))))) && + ShiftC->ult(Width) && + C->countLeadingZeros() >= ShiftC->getZExtValue()) { + Value *TruncX = Builder.CreateTrunc(X, Ty); + Value *NewShift = Builder.CreateLShr(TruncX, ShiftC->getZExtValue()); + return BinaryOperator::CreateAnd(NewShift, Op1); + } + const APInt *AddC; if (match(Op0, m_Add(m_Value(X), m_APInt(AddC)))) { // If we add zeros to every bit below a mask, the add has no effect: Index: llvm/test/Transforms/InstCombine/and-narrow.ll =================================================================== --- llvm/test/Transforms/InstCombine/and-narrow.ll +++ llvm/test/Transforms/InstCombine/and-narrow.ll @@ -215,9 +215,9 @@ define i6 @trunc_lshr(i8 %x) { ; CHECK-LABEL: @trunc_lshr( -; CHECK-NEXT: [[S:%.*]] = lshr i8 [[X:%.*]], 2 -; CHECK-NEXT: [[T:%.*]] = trunc i8 [[S]] to i6 -; CHECK-NEXT: [[R:%.*]] = and i6 [[T]], 14 +; CHECK-NEXT: [[TMP1:%.*]] = trunc i8 [[X:%.*]] to i6 +; CHECK-NEXT: [[TMP2:%.*]] = lshr i6 [[TMP1]], 2 +; CHECK-NEXT: [[R:%.*]] = and i6 [[TMP2]], 14 ; CHECK-NEXT: ret i6 [[R]] ; %s = lshr i8 %x, 2 @@ -226,12 +226,13 @@ ret i6 %r } +; The 'and' is eliminated. + define i6 @trunc_lshr_exact_mask(i8 %x) { ; CHECK-LABEL: @trunc_lshr_exact_mask( -; CHECK-NEXT: [[S:%.*]] = lshr i8 [[X:%.*]], 2 -; CHECK-NEXT: [[T:%.*]] = trunc i8 [[S]] to i6 -; CHECK-NEXT: [[R:%.*]] = and i6 [[T]], 15 -; CHECK-NEXT: ret i6 [[R]] +; CHECK-NEXT: [[TMP1:%.*]] = trunc i8 [[X:%.*]] to i6 +; CHECK-NEXT: [[TMP2:%.*]] = lshr i6 [[TMP1]], 2 +; CHECK-NEXT: ret i6 [[TMP2]] ; %s = lshr i8 %x, 2 %t = trunc i8 %s to i6 @@ -239,6 +240,8 @@ ret i6 %r } +; negative test - a high bit of x is in the result + define i6 @trunc_lshr_big_mask(i8 %x) { ; CHECK-LABEL: @trunc_lshr_big_mask( ; CHECK-NEXT: [[S:%.*]] = lshr i8 [[X:%.*]], 2 @@ -252,6 +255,8 @@ ret i6 %r } +; negative test + define i6 @trunc_lshr_use1(i8 %x) { ; CHECK-LABEL: @trunc_lshr_use1( ; CHECK-NEXT: [[S:%.*]] = lshr i8 [[X:%.*]], 2 @@ -267,6 +272,8 @@ ret i6 %r } +; negative test + define i6 @trunc_lshr_use2(i8 %x) { ; CHECK-LABEL: @trunc_lshr_use2( ; CHECK-NEXT: [[S:%.*]] = lshr i8 [[X:%.*]], 2 @@ -282,11 +289,13 @@ ret i6 %r } +; splat vectors are ok + define <2 x i7> @trunc_lshr_vec_splat(<2 x i16> %x) { ; CHECK-LABEL: @trunc_lshr_vec_splat( -; CHECK-NEXT: [[S:%.*]] = lshr <2 x i16> [[X:%.*]], -; CHECK-NEXT: [[T:%.*]] = trunc <2 x i16> [[S]] to <2 x i7> -; CHECK-NEXT: [[R:%.*]] = and <2 x i7> [[T]], +; CHECK-NEXT: [[TMP1:%.*]] = trunc <2 x i16> [[X:%.*]] to <2 x i7> +; CHECK-NEXT: [[TMP2:%.*]] = lshr <2 x i7> [[TMP1]], +; CHECK-NEXT: [[R:%.*]] = and <2 x i7> [[TMP2]], ; CHECK-NEXT: ret <2 x i7> [[R]] ; %s = lshr <2 x i16> %x, @@ -295,12 +304,13 @@ ret <2 x i7> %r } +; The 'and' is eliminated. + define <2 x i7> @trunc_lshr_vec_splat_exact_mask(<2 x i16> %x) { ; CHECK-LABEL: @trunc_lshr_vec_splat_exact_mask( -; CHECK-NEXT: [[S:%.*]] = lshr <2 x i16> [[X:%.*]], -; CHECK-NEXT: [[T:%.*]] = trunc <2 x i16> [[S]] to <2 x i7> -; CHECK-NEXT: [[R:%.*]] = and <2 x i7> [[T]], -; CHECK-NEXT: ret <2 x i7> [[R]] +; CHECK-NEXT: [[TMP1:%.*]] = trunc <2 x i16> [[X:%.*]] to <2 x i7> +; CHECK-NEXT: [[TMP2:%.*]] = lshr <2 x i7> [[TMP1]], +; CHECK-NEXT: ret <2 x i7> [[TMP2]] ; %s = lshr <2 x i16> %x, %t = trunc <2 x i16> %s to <2 x i7> @@ -308,6 +318,8 @@ ret <2 x i7> %r } +; negative test - the high bit of x is in the result + define <2 x i7> @trunc_lshr_big_shift(<2 x i16> %x) { ; CHECK-LABEL: @trunc_lshr_big_shift( ; CHECK-NEXT: [[S:%.*]] = lshr <2 x i16> [[X:%.*]],