diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 375385aca7a39..51ddc0ad72106 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -2449,9 +2449,20 @@ static bool isNonZeroRecurrence(const PHINode *PN) { } } +static bool matchOpWithOpEqZero(Value *Op0, Value *Op1) { + ICmpInst::Predicate Pred; + return (match(Op0, m_ZExtOrSExt(m_ICmp(Pred, m_Specific(Op1), m_Zero()))) || + match(Op1, m_ZExtOrSExt(m_ICmp(Pred, m_Specific(Op0), m_Zero())))) && + Pred == ICmpInst::ICMP_EQ; +} + static bool isNonZeroAdd(const APInt &DemandedElts, unsigned Depth, const SimplifyQuery &Q, unsigned BitWidth, Value *X, Value *Y, bool NSW, bool NUW) { + // (X + (X != 0)) is non zero + if (matchOpWithOpEqZero(X, Y)) + return true; + if (NUW) return isKnownNonZero(Y, DemandedElts, Q, Depth) || isKnownNonZero(X, DemandedElts, Q, Depth); @@ -2495,6 +2506,11 @@ static bool isNonZeroAdd(const APInt &DemandedElts, unsigned Depth, static bool isNonZeroSub(const APInt &DemandedElts, unsigned Depth, const SimplifyQuery &Q, unsigned BitWidth, Value *X, Value *Y) { + // (X - (X != 0)) is non zero + // ((X != 0) - X) is non zero + if (matchOpWithOpEqZero(X, Y)) + return true; + // TODO: Move this case into isKnownNonEqual(). if (auto *C = dyn_cast(X)) if (C->isNullValue() && isKnownNonZero(Y, DemandedElts, Q, Depth)) @@ -2654,7 +2670,15 @@ static bool isKnownNonZeroFromOperator(const Operator *I, case Instruction::Sub: return isNonZeroSub(DemandedElts, Depth, Q, BitWidth, I->getOperand(0), I->getOperand(1)); + case Instruction::Xor: + // (X ^ (X != 0)) is non zero + if (matchOpWithOpEqZero(I->getOperand(0), I->getOperand(1))) + return true; + break; case Instruction::Or: + // (X | (X != 0)) is non zero + if (matchOpWithOpEqZero(I->getOperand(0), I->getOperand(1))) + return true; // X | Y != 0 if X != 0 or Y != 0. return isKnownNonZero(I->getOperand(1), DemandedElts, Q, Depth) || isKnownNonZero(I->getOperand(0), DemandedElts, Q, Depth); @@ -2945,6 +2969,11 @@ static bool isKnownNonZeroFromOperator(const Operator *I, return isKnownNonZero(II->getArgOperand(0), Q, Depth); case Intrinsic::umax: case Intrinsic::uadd_sat: + // umax(X, (X != 0)) is non zero + // X +usat (X != 0) is non zero + if (matchOpWithOpEqZero(II->getArgOperand(0), II->getArgOperand(1))) + return true; + return isKnownNonZero(II->getArgOperand(1), DemandedElts, Q, Depth) || isKnownNonZero(II->getArgOperand(0), DemandedElts, Q, Depth); case Intrinsic::smax: { diff --git a/llvm/test/Transforms/InstSimplify/known-non-zero.ll b/llvm/test/Transforms/InstSimplify/known-non-zero.ll index fd2862eb04a24..965c333d306d1 100644 --- a/llvm/test/Transforms/InstSimplify/known-non-zero.ll +++ b/llvm/test/Transforms/InstSimplify/known-non-zero.ll @@ -400,3 +400,186 @@ define i1 @nonzero_reduce_or_fail(<2 x i8> %xx) { %r = icmp eq i8 %v, 0 ret i1 %r } + +define i1 @src_x_add_x_eq_0(i8 %x) { +; CHECK-LABEL: @src_x_add_x_eq_0( +; CHECK-NEXT: ret i1 false +; + %x_eq_0 = icmp eq i8 %x, 0 + %y = zext i1 %x_eq_0 to i8 + %v = add i8 %x, %y + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_add_x_eq_1_fail(i8 %x) { +; CHECK-LABEL: @src_x_add_x_eq_1_fail( +; CHECK-NEXT: [[X_EQ_1:%.*]] = icmp eq i8 [[X:%.*]], 1 +; CHECK-NEXT: [[Y:%.*]] = zext i1 [[X_EQ_1]] to i8 +; CHECK-NEXT: [[V:%.*]] = add i8 [[X]], [[Y]] +; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 0 +; CHECK-NEXT: ret i1 [[R]] +; + %x_eq_1 = icmp eq i8 %x, 1 + %y = zext i1 %x_eq_1 to i8 + %v = add i8 %x, %y + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_or_x_eq_0(i8 %x) { +; CHECK-LABEL: @src_x_or_x_eq_0( +; CHECK-NEXT: ret i1 false +; + %x_eq_0 = icmp eq i8 %x, 0 + %y = sext i1 %x_eq_0 to i8 + %v = or i8 %x, %y + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_or_x_sle_0_fail(i8 %x) { +; CHECK-LABEL: @src_x_or_x_sle_0_fail( +; CHECK-NEXT: [[X_EQ_0:%.*]] = icmp sle i8 [[X:%.*]], 0 +; CHECK-NEXT: [[Y:%.*]] = sext i1 [[X_EQ_0]] to i8 +; CHECK-NEXT: [[V:%.*]] = or i8 [[X]], [[Y]] +; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 0 +; CHECK-NEXT: ret i1 [[R]] +; + %x_eq_0 = icmp sle i8 %x, 0 + %y = sext i1 %x_eq_0 to i8 + %v = or i8 %x, %y + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_xor_x_eq_0(i8 %x) { +; CHECK-LABEL: @src_x_xor_x_eq_0( +; CHECK-NEXT: ret i1 false +; + %x_eq_0 = icmp eq i8 %x, 0 + %y = zext i1 %x_eq_0 to i8 + %v = xor i8 %x, %y + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_xor_x_ne_0_fail(i8 %x) { +; CHECK-LABEL: @src_x_xor_x_ne_0_fail( +; CHECK-NEXT: [[X_NE_0:%.*]] = icmp ne i8 [[X:%.*]], 0 +; CHECK-NEXT: [[Y:%.*]] = zext i1 [[X_NE_0]] to i8 +; CHECK-NEXT: [[V:%.*]] = xor i8 [[X]], [[Y]] +; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 0 +; CHECK-NEXT: ret i1 [[R]] +; + %x_ne_0 = icmp ne i8 %x, 0 + %y = zext i1 %x_ne_0 to i8 + %v = xor i8 %x, %y + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_sub0_x_eq_0(i8 %x) { +; CHECK-LABEL: @src_x_sub0_x_eq_0( +; CHECK-NEXT: ret i1 false +; + %x_eq_0 = icmp eq i8 %x, 0 + %y = sext i1 %x_eq_0 to i8 + %v = sub i8 %x, %y + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_sub0_z_eq_0_fail(i8 %x, i8 %z) { +; CHECK-LABEL: @src_x_sub0_z_eq_0_fail( +; CHECK-NEXT: [[Z_EQ_0:%.*]] = icmp eq i8 [[Z:%.*]], 0 +; CHECK-NEXT: [[Y:%.*]] = sext i1 [[Z_EQ_0]] to i8 +; CHECK-NEXT: [[V:%.*]] = sub i8 [[X:%.*]], [[Y]] +; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 0 +; CHECK-NEXT: ret i1 [[R]] +; + %z_eq_0 = icmp eq i8 %z, 0 + %y = sext i1 %z_eq_0 to i8 + %v = sub i8 %x, %y + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_sub1_x_eq_0(i8 %x) { +; CHECK-LABEL: @src_x_sub1_x_eq_0( +; CHECK-NEXT: ret i1 false +; + %x_eq_0 = icmp eq i8 %x, 0 + %y = zext i1 %x_eq_0 to i8 + %v = sub i8 %y, %x + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_sub1_x_eq_0_or_fail(i8 %x, i1 %c1) { +; CHECK-LABEL: @src_x_sub1_x_eq_0_or_fail( +; CHECK-NEXT: [[X_EQ_0:%.*]] = icmp eq i8 [[X:%.*]], 0 +; CHECK-NEXT: [[X_EQ_0_OR:%.*]] = or i1 [[X_EQ_0]], [[C1:%.*]] +; CHECK-NEXT: [[Y:%.*]] = zext i1 [[X_EQ_0_OR]] to i8 +; CHECK-NEXT: [[V:%.*]] = sub i8 [[Y]], [[X]] +; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 0 +; CHECK-NEXT: ret i1 [[R]] +; + %x_eq_0 = icmp eq i8 %x, 0 + %x_eq_0_or = or i1 %x_eq_0, %c1 + %y = zext i1 %x_eq_0_or to i8 + %v = sub i8 %y, %x + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_umax_x_eq_0(i8 %x) { +; CHECK-LABEL: @src_x_umax_x_eq_0( +; CHECK-NEXT: ret i1 false +; + %x_eq_0 = icmp eq i8 %x, 0 + %y = sext i1 %x_eq_0 to i8 + %v = call i8 @llvm.umax.i8(i8 %y, i8 %x) + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_umax_x_ugt_10_fail(i8 %x) { +; CHECK-LABEL: @src_x_umax_x_ugt_10_fail( +; CHECK-NEXT: [[X_UGT_10:%.*]] = icmp ugt i8 [[X:%.*]], 10 +; CHECK-NEXT: [[Y:%.*]] = sext i1 [[X_UGT_10]] to i8 +; CHECK-NEXT: [[V:%.*]] = call i8 @llvm.umax.i8(i8 [[Y]], i8 [[X]]) +; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 0 +; CHECK-NEXT: ret i1 [[R]] +; + %x_ugt_10 = icmp ugt i8 %x, 10 + %y = sext i1 %x_ugt_10 to i8 + %v = call i8 @llvm.umax.i8(i8 %y, i8 %x) + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_uadd.sat_x_eq_0(i8 %x) { +; CHECK-LABEL: @src_x_uadd.sat_x_eq_0( +; CHECK-NEXT: ret i1 false +; + %x_eq_0 = icmp eq i8 %x, 0 + %y = zext i1 %x_eq_0 to i8 + %v = call i8 @llvm.uadd.sat.i8(i8 %y, i8 %x) + %r = icmp eq i8 %v, 0 + ret i1 %r +} + +define i1 @src_x_uadd.sat_c1_fail(i8 %x, i1 %c1) { +; CHECK-LABEL: @src_x_uadd.sat_c1_fail( +; CHECK-NEXT: [[Y:%.*]] = zext i1 [[C1:%.*]] to i8 +; CHECK-NEXT: [[V:%.*]] = call i8 @llvm.uadd.sat.i8(i8 [[Y]], i8 [[X:%.*]]) +; CHECK-NEXT: [[R:%.*]] = icmp eq i8 [[V]], 0 +; CHECK-NEXT: ret i1 [[R]] +; + %y = zext i1 %c1 to i8 + %v = call i8 @llvm.uadd.sat.i8(i8 %y, i8 %x) + %r = icmp eq i8 %v, 0 + ret i1 %r +} +