diff --git a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineShifts.cpp @@ -1150,6 +1150,14 @@ return BinaryOperator::CreateLShr( ConstantInt::get(Ty, APInt::getSignMask(BitWidth)), X); + // Canonicalize "extract lowest set bit" using cttz to and-with-negate: + // 1 << (cttz X) --> -X & X + if (match(Op1, + m_OneUse(m_Intrinsic(m_Value(X), m_Value())))) { + Value *NegX = Builder.CreateNeg(X, "neg"); + return BinaryOperator::CreateAnd(NegX, X); + } + // The only way to shift out the 1 is with an over-shift, so that would // be poison with or without "nuw". Undef is excluded because (undef << X) // is not undef (it is zero). diff --git a/llvm/test/Transforms/InstCombine/shift.ll b/llvm/test/Transforms/InstCombine/shift.ll --- a/llvm/test/Transforms/InstCombine/shift.ll +++ b/llvm/test/Transforms/InstCombine/shift.ll @@ -2009,8 +2009,8 @@ define i32 @shl1_cttz(i32 %x) { ; CHECK-LABEL: @shl1_cttz( -; CHECK-NEXT: [[TZ:%.*]] = call i32 @llvm.cttz.i32(i32 [[X:%.*]], i1 true), !range [[RNG0:![0-9]+]] -; CHECK-NEXT: [[SHL:%.*]] = shl nuw i32 1, [[TZ]] +; CHECK-NEXT: [[NEG:%.*]] = sub i32 0, [[X:%.*]] +; CHECK-NEXT: [[SHL:%.*]] = and i32 [[NEG]], [[X]] ; CHECK-NEXT: ret i32 [[SHL]] ; %tz = call i32 @llvm.cttz.i32(i32 %x, i1 true) @@ -2020,8 +2020,8 @@ define <2 x i8> @shl1_cttz_vec(<2 x i8> %x) { ; CHECK-LABEL: @shl1_cttz_vec( -; CHECK-NEXT: [[TZ:%.*]] = call <2 x i8> @llvm.cttz.v2i8(<2 x i8> [[X:%.*]], i1 false) -; CHECK-NEXT: [[SHL:%.*]] = shl nuw <2 x i8> , [[TZ]] +; CHECK-NEXT: [[NEG:%.*]] = sub <2 x i8> zeroinitializer, [[X:%.*]] +; CHECK-NEXT: [[SHL:%.*]] = and <2 x i8> [[NEG]], [[X]] ; CHECK-NEXT: ret <2 x i8> [[SHL]] ; %tz = call <2 x i8> @llvm.cttz.v2i8(<2 x i8> %x, i1 false) @@ -2031,8 +2031,8 @@ define <2 x i8> @shl1_cttz_vec_poison(<2 x i8> %x) { ; CHECK-LABEL: @shl1_cttz_vec_poison( -; CHECK-NEXT: [[TZ:%.*]] = call <2 x i8> @llvm.cttz.v2i8(<2 x i8> [[X:%.*]], i1 false) -; CHECK-NEXT: [[SHL:%.*]] = shl nuw <2 x i8> , [[TZ]] +; CHECK-NEXT: [[NEG:%.*]] = sub <2 x i8> zeroinitializer, [[X:%.*]] +; CHECK-NEXT: [[SHL:%.*]] = and <2 x i8> [[NEG]], [[X]] ; CHECK-NEXT: ret <2 x i8> [[SHL]] ; %tz = call <2 x i8> @llvm.cttz.v2i8(<2 x i8> %x, i1 false) @@ -2040,9 +2040,11 @@ ret <2 x i8> %shl } +; negative test - extra use + define i32 @shl1_cttz_extra_use(i32 %x) { ; CHECK-LABEL: @shl1_cttz_extra_use( -; CHECK-NEXT: [[TZ:%.*]] = call i32 @llvm.cttz.i32(i32 [[X:%.*]], i1 false), !range [[RNG0]] +; CHECK-NEXT: [[TZ:%.*]] = call i32 @llvm.cttz.i32(i32 [[X:%.*]], i1 false), !range [[RNG0:![0-9]+]] ; CHECK-NEXT: call void @use_i32(i32 [[TZ]]) ; CHECK-NEXT: [[SHL:%.*]] = shl nuw i32 1, [[TZ]] ; CHECK-NEXT: ret i32 [[SHL]] @@ -2053,6 +2055,8 @@ ret i32 %shl } +; negative test - must be shift-left of 1 + define i32 @shl2_cttz(i32 %x) { ; CHECK-LABEL: @shl2_cttz( ; CHECK-NEXT: [[TZ:%.*]] = call i32 @llvm.cttz.i32(i32 [[X:%.*]], i1 true), !range [[RNG0]]