diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp @@ -2589,23 +2589,29 @@ // with binop identity constant. But creating a select with non-constant // arm may not be reversible due to poison semantics. Is that a good // canonicalization? - Value *A; - if (match(Op0, m_OneUse(m_SExt(m_Value(A)))) && - A->getType()->isIntOrIntVectorTy(1)) - return SelectInst::Create(A, Op1, Constant::getNullValue(Ty)); - if (match(Op1, m_OneUse(m_SExt(m_Value(A)))) && + Value *A, *B; + if (match(&I, m_c_And(m_OneUse(m_SExt(m_Value(A))), m_Value(B))) && A->getType()->isIntOrIntVectorTy(1)) - return SelectInst::Create(A, Op0, Constant::getNullValue(Ty)); + return SelectInst::Create(A, B, Constant::getNullValue(Ty)); // Similarly, a 'not' of the bool translates to a swap of the select arms: - // ~sext(A) & Op1 --> A ? 0 : Op1 - // Op0 & ~sext(A) --> A ? 0 : Op0 - if (match(Op0, m_Not(m_SExt(m_Value(A)))) && - A->getType()->isIntOrIntVectorTy(1)) - return SelectInst::Create(A, Constant::getNullValue(Ty), Op1); - if (match(Op1, m_Not(m_SExt(m_Value(A)))) && + // ~sext(A) & B / B & ~sext(A) --> A ? 0 : B + if (match(&I, m_c_And(m_Not(m_SExt(m_Value(A))), m_Value(B))) && A->getType()->isIntOrIntVectorTy(1)) - return SelectInst::Create(A, Constant::getNullValue(Ty), Op0); + return SelectInst::Create(A, Constant::getNullValue(Ty), B); + + // (-1 + A) & B --> A ? 0 : B where A is effectively a bool (zext of i1/, or inst with !range {0, 2}). + if (match(&I, m_c_And(m_OneUse(m_Add(m_ZExtOrSelf(m_Value(A)), m_AllOnes())), + m_Value(B)))) { + if (A->getType()->isIntOrIntVectorTy(1)) + return SelectInst::Create(A, Constant::getNullValue(Ty), B); + if (computeKnownBits(A, /* Depth */ 0, &I).countMaxActiveBits() <= 1) { + return SelectInst::Create( + Builder.CreateICmpEQ(A, Constant::getNullValue(A->getType())), B, + Constant::getNullValue(Ty)); + } + } // (iN X s>> (N-1)) & Y --> (X s< 0) ? Y : 0 -- with optional sext if (match(&I, m_c_And(m_OneUse(m_SExtOrSelf( diff --git a/llvm/test/Transforms/InstCombine/binop-cast.ll b/llvm/test/Transforms/InstCombine/binop-cast.ll --- a/llvm/test/Transforms/InstCombine/binop-cast.ll +++ b/llvm/test/Transforms/InstCombine/binop-cast.ll @@ -261,9 +261,8 @@ define i64 @PR63321(ptr %ptr, i64 %c) { ; CHECK-LABEL: @PR63321( ; CHECK-NEXT: [[VAL:%.*]] = load i8, ptr [[PTR:%.*]], align 1, !range [[RNG0:![0-9]+]] -; CHECK-NEXT: [[RHS:%.*]] = zext i8 [[VAL]] to i64 -; CHECK-NEXT: [[MASK:%.*]] = add nsw i64 [[RHS]], -1 -; CHECK-NEXT: [[RES:%.*]] = and i64 [[MASK]], [[C:%.*]] +; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i8 [[VAL]], 0 +; CHECK-NEXT: [[RES:%.*]] = select i1 [[TMP1]], i64 [[C:%.*]], i64 0 ; CHECK-NEXT: ret i64 [[RES]] ; %val = load i8, ptr %ptr, align 1, !range !{i8 0, i8 2} @@ -304,8 +303,8 @@ define i32 @and_add_bool_no_fold(i32 %y) { ; CHECK-LABEL: @and_add_bool_no_fold( ; CHECK-NEXT: [[X:%.*]] = and i32 [[Y:%.*]], 1 -; CHECK-NEXT: [[MASK:%.*]] = add nsw i32 [[X]], -1 -; CHECK-NEXT: [[RES:%.*]] = and i32 [[MASK]], [[Y]] +; CHECK-NEXT: [[TMP1:%.*]] = icmp eq i32 [[X]], 0 +; CHECK-NEXT: [[RES:%.*]] = select i1 [[TMP1]], i32 [[Y]], i32 0 ; CHECK-NEXT: ret i32 [[RES]] ; %x = and i32 %y, 1