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 @@ -2546,23 +2546,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 @@ -257,3 +257,54 @@ %r = xor i32 %sext, 42 ret i32 %r } + +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: [[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} + %rhs = zext i8 %val to i64 + %mask = add i64 -1, %rhs + %res = and i64 %mask, %c + ret i64 %res +} + +define i32 @and_add_bool_to_select(i1 %x, i32 %y) { +; CHECK-LABEL: @and_add_bool_to_select( +; CHECK-NEXT: [[RES:%.*]] = select i1 [[X:%.*]], i32 0, i32 [[Y:%.*]] +; CHECK-NEXT: ret i32 [[RES]] +; + %val = zext i1 %x to i32 + %mask = add i32 -1, %val + %res = and i32 %mask, %y + ret i32 %res +} + +define <2 x i32> @and_add_bool_vec_to_select(<2 x i1> %x, <2 x i32> %y) { +; CHECK-LABEL: @and_add_bool_vec_to_select( +; CHECK-NEXT: [[RES:%.*]] = select <2 x i1> [[X:%.*]], <2 x i32> zeroinitializer, <2 x i32> [[Y:%.*]] +; CHECK-NEXT: ret <2 x i32> [[RES]] +; + %val = zext <2 x i1> %x to <2 x i32> + %mask = add <2 x i32> , %val + %res = and <2 x i32> %mask, %y + ret <2 x i32> %res +} + +define i32 @and_add_bool_to_select_multi_use(i1 %x, i32 %y) { +; CHECK-LABEL: @and_add_bool_to_select_multi_use( +; CHECK-NEXT: [[NOT_X:%.*]] = xor i1 [[X:%.*]], true +; CHECK-NEXT: [[MASK:%.*]] = sext i1 [[NOT_X]] to i32 +; CHECK-NEXT: [[RES:%.*]] = and i32 [[MASK]], [[Y:%.*]] +; CHECK-NEXT: [[RET:%.*]] = add i32 [[RES]], [[MASK]] +; CHECK-NEXT: ret i32 [[RET]] +; + %val = zext i1 %x to i32 + %mask = add i32 -1, %val + %res = and i32 %mask, %y + %ret = add i32 %res, %mask + ret i32 %ret +}