-
Notifications
You must be signed in to change notification settings - Fork 10.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ValueTracking] Recognize X op (X != 0)
as non-zero
#88579
Conversation
@llvm/pr-subscribers-llvm-transforms Author: None (goldsteinn) Changes
Full diff: https://github.com/llvm/llvm-project/pull/88579.diff 2 Files Affected:
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index b83e7e6769c200..e25f2f99a69950 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -2420,9 +2420,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, Depth, Q) ||
isKnownNonZero(X, DemandedElts, Depth, Q);
@@ -2466,6 +2477,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<Constant>(X))
if (C->isNullValue() && isKnownNonZero(Y, DemandedElts, Depth, Q))
@@ -2618,7 +2634,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, Depth, Q) ||
isKnownNonZero(I->getOperand(0), DemandedElts, Depth, Q);
@@ -2909,6 +2933,11 @@ static bool isKnownNonZeroFromOperator(const Operator *I,
return isKnownNonZero(II->getArgOperand(0), Depth, Q);
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, Depth, Q) ||
isKnownNonZero(II->getArgOperand(0), DemandedElts, Depth, Q);
case Intrinsic::smax: {
diff --git a/llvm/test/Transforms/InstSimplify/known-non-zero.ll b/llvm/test/Transforms/InstSimplify/known-non-zero.ll
index d9b8f5eed32390..f39c4cc7f59933 100644
--- a/llvm/test/Transforms/InstSimplify/known-non-zero.ll
+++ b/llvm/test/Transforms/InstSimplify/known-non-zero.ll
@@ -377,3 +377,186 @@ define <2 x i1> @insert_nonzero_any_idx_fail(<2 x i8> %xx, i8 %yy, i32 %idx) {
%r = icmp eq <2 x i8> %ins, zeroinitializer
ret <2 x 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
+}
+
|
@llvm/pr-subscribers-llvm-analysis Author: None (goldsteinn) Changes
Full diff: https://github.com/llvm/llvm-project/pull/88579.diff 2 Files Affected:
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index b83e7e6769c200..e25f2f99a69950 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -2420,9 +2420,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, Depth, Q) ||
isKnownNonZero(X, DemandedElts, Depth, Q);
@@ -2466,6 +2477,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<Constant>(X))
if (C->isNullValue() && isKnownNonZero(Y, DemandedElts, Depth, Q))
@@ -2618,7 +2634,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, Depth, Q) ||
isKnownNonZero(I->getOperand(0), DemandedElts, Depth, Q);
@@ -2909,6 +2933,11 @@ static bool isKnownNonZeroFromOperator(const Operator *I,
return isKnownNonZero(II->getArgOperand(0), Depth, Q);
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, Depth, Q) ||
isKnownNonZero(II->getArgOperand(0), DemandedElts, Depth, Q);
case Intrinsic::smax: {
diff --git a/llvm/test/Transforms/InstSimplify/known-non-zero.ll b/llvm/test/Transforms/InstSimplify/known-non-zero.ll
index d9b8f5eed32390..f39c4cc7f59933 100644
--- a/llvm/test/Transforms/InstSimplify/known-non-zero.ll
+++ b/llvm/test/Transforms/InstSimplify/known-non-zero.ll
@@ -377,3 +377,186 @@ define <2 x i1> @insert_nonzero_any_idx_fail(<2 x i8> %xx, i8 %yy, i32 %idx) {
%r = icmp eq <2 x i8> %ins, zeroinitializer
ret <2 x 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
+}
+
|
X op (X != 0)
as non-zero
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it is a common pattern. But I would like to canonicalize it into umax(X, 1)
(or vice versa).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think umax would make sense, although imo this patch still makes sense b.c this will be a single-user only transform
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would umax canonicalization cover the original SPEC case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there are two transform here.
- fold
(X == C) Op X
->X == C ? (X Op 0) : (C Op 1)
. assuming thatX Op 0
simplifies. - recognize something like
X == 0 ? X : 1
asumax(X, 1)
.
I'm happy to write patches for both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already handle the X == 0 ? 1 : X
-> umax(X, 1)
. Ill post patch for the select case soon. That being said, still prefer to get this in as I don't think we should rely on single-use constrained canonicalization in ValueTracking
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made PR for the folding #89020
Would still like to get this in for the reason of the fold requiring single-use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM.
I don't think the completeness is valuable here. |
NB: Seen |
ping edit: |
ping2 |
The ops supported are: `add`, `sub`, `xor`, `or`, `umax`, `uadd.sat` Proofs: https://alive2.llvm.org/ce/z/8ZMSRg The `add` case actually comes up in SPECInt, the rest are here mostly for completeness.
a756fce
to
2b8a9a4
Compare
Since this was approved by @dtcxzyw im going to push this in coming week unless any objections |
Pretty sure that this will miscompile if X is undef and isKnownNonZero() is used in a transform that retains the original expression. |
Hmm? The proofs allow for |
isKnowNonZero
ofX op (X != 0)
; NFCX op (X != 0)
as non-zeroThe ops supported are:
add
,sub
,xor
,or
,umax
,uadd.sat
Proofs: https://alive2.llvm.org/ce/z/8ZMSRg
The
add
case actually comes up in SPECInt,xor
/or
cases show up in LLVM,the rest are here mostly for completeness.