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,28 @@ // 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_c_Add(m_ZExtOrSelf(m_Value(A)), m_AllOnes()), + m_Value(B))) && + (A->getType()->isIntOrIntVectorTy(1) || + KnownBits::ule(computeKnownBits(A, /* Depth */ 0, &I), + KnownBits::makeConstant(APInt( + A->getType()->getScalarSizeInBits(), 1))) == true)) + return SelectInst::Create( + Builder.CreateTrunc(A, A->getType()->getWithNewBitWidth(1)), + Constant::getNullValue(Ty), B); // (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,17 @@ %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: [[DOTNOT:%.*]] = icmp eq i8 [[VAL]], 0 +; CHECK-NEXT: [[RES:%.*]] = select i1 [[DOTNOT]], 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 +}