-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[ValueTracking] Infer known bits fromfrom (icmp eq (and/or x,y), C) #87143
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] Infer known bits fromfrom (icmp eq (and/or x,y), C) #87143
Conversation
…d/or x,y), C)`; NFC
In `(icmp eq (and x,y), C)` all 1s in `C` must also be set in both `x`/`y`. In `(icmp eq (or x,y), C)` all 0s in `C` must also be set in both `x`/`y`.
@llvm/pr-subscribers-llvm-analysis @llvm/pr-subscribers-llvm-transforms Author: None (goldsteinn) Changes
Full diff: https://github.com/llvm/llvm-project/pull/87143.diff 3 Files Affected:
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index b5e8a1d22f264b..33a69861cc3c57 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -648,6 +648,7 @@ static void computeKnownBitsFromCmp(const Value *V, CmpInst::Predicate Pred,
auto m_V =
m_CombineOr(m_Specific(V), m_PtrToIntSameSize(Q.DL, m_Specific(V)));
+ Value *Y;
const APInt *Mask, *C;
uint64_t ShAmt;
switch (Pred) {
@@ -656,16 +657,18 @@ static void computeKnownBitsFromCmp(const Value *V, CmpInst::Predicate Pred,
if (match(LHS, m_V) && match(RHS, m_APInt(C))) {
Known = Known.unionWith(KnownBits::makeConstant(*C));
// assume(V & Mask = C)
- } else if (match(LHS, m_And(m_V, m_APInt(Mask))) &&
+ } else if (match(LHS, m_c_And(m_V, m_Value(Y))) &&
match(RHS, m_APInt(C))) {
// For one bits in Mask, we can propagate bits from C to V.
- Known.Zero |= ~*C & *Mask;
- Known.One |= *C & *Mask;
+ Known.One |= *C;
+ if (match(Y, m_APInt(Mask)))
+ Known.Zero |= ~*C & *Mask;
// assume(V | Mask = C)
- } else if (match(LHS, m_Or(m_V, m_APInt(Mask))) && match(RHS, m_APInt(C))) {
+ } else if (match(LHS, m_c_Or(m_V, m_Value(Y))) && match(RHS, m_APInt(C))) {
// For zero bits in Mask, we can propagate bits from C to V.
- Known.Zero |= ~*C & ~*Mask;
- Known.One |= *C & ~*Mask;
+ Known.Zero |= ~*C;
+ if (match(Y, m_APInt(Mask)))
+ Known.One |= *C & ~*Mask;
// assume(V ^ Mask = C)
} else if (match(LHS, m_Xor(m_V, m_APInt(Mask))) &&
match(RHS, m_APInt(C))) {
@@ -9276,11 +9279,17 @@ void llvm::findValuesAffectedByCondition(
if (ICmpInst::isEquality(Pred)) {
if (match(B, m_ConstantInt())) {
+ Value *Y;
// (X & C) or (X | C) or (X ^ C).
// (X << C) or (X >>_s C) or (X >>_u C).
if (match(A, m_BitwiseLogic(m_Value(X), m_ConstantInt())) ||
match(A, m_Shift(m_Value(X), m_ConstantInt())))
AddAffected(X);
+ else if (match(A, m_And(m_Value(X), m_Value(Y))) ||
+ match(A, m_Or(m_Value(X), m_Value(Y)))) {
+ AddAffected(X);
+ AddAffected(Y);
+ }
}
} else {
// Handle (A + C1) u< C2, which is the canonical form of
diff --git a/llvm/test/Transforms/InstCombine/known-bits.ll b/llvm/test/Transforms/InstCombine/known-bits.ll
index 5305c78f691231..769f7661fc8dc0 100644
--- a/llvm/test/Transforms/InstCombine/known-bits.ll
+++ b/llvm/test/Transforms/InstCombine/known-bits.ll
@@ -124,7 +124,6 @@ exit:
ret i8 %or2
}
-
define i8 @test_cond_and_bothways(i8 %x) {
; CHECK-LABEL: @test_cond_and_bothways(
; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], 91
@@ -181,8 +180,6 @@ exit:
ret i8 %or2
}
-
-
define i8 @test_cond_and_commuted(i8 %x, i1 %c1, i1 %c2) {
; CHECK-LABEL: @test_cond_and_commuted(
; CHECK-NEXT: [[AND:%.*]] = and i8 [[X:%.*]], 3
@@ -343,7 +340,7 @@ exit:
ret i8 %or2
}
-define i32 @test_icmp_trunc1(i32 %x){
+define i32 @test_icmp_trunc1(i32 %x) {
; CHECK-LABEL: @test_icmp_trunc1(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[Y:%.*]] = trunc i32 [[X:%.*]] to i16
@@ -365,7 +362,7 @@ else:
ret i32 0
}
-define i32 @test_icmp_trunc_assume(i32 %x){
+define i32 @test_icmp_trunc_assume(i32 %x) {
; CHECK-LABEL: @test_icmp_trunc_assume(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[Y:%.*]] = trunc i32 [[X:%.*]] to i16
@@ -532,7 +529,106 @@ if.else:
ret i1 %other
}
+define i8 @and_eq_bits_must_be_set(i8 %x, i8 %y) {
+; CHECK-LABEL: @and_eq_bits_must_be_set(
+; CHECK-NEXT: [[XY:%.*]] = and i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[XY]], 123
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: ret i8 1
+;
+ %xy = and i8 %x, %y
+ %cmp = icmp eq i8 %xy, 123
+ call void @llvm.assume(i1 %cmp)
+ %r = and i8 %x, 1
+ ret i8 %r
+}
+
+define i8 @and_eq_bits_must_be_set2(i8 %x, i8 %y) {
+; CHECK-LABEL: @and_eq_bits_must_be_set2(
+; CHECK-NEXT: [[XY:%.*]] = and i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[XY]], 123
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: ret i8 11
+;
+ %xy = and i8 %x, %y
+ %cmp = icmp eq i8 %xy, 123
+ call void @llvm.assume(i1 %cmp)
+ %r = and i8 %y, 11
+ ret i8 %r
+}
+
+define i8 @and_eq_bits_must_be_set2_partial_fail(i8 %x, i8 %y) {
+; CHECK-LABEL: @and_eq_bits_must_be_set2_partial_fail(
+; CHECK-NEXT: [[XY:%.*]] = and i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[XY]], 123
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: [[R:%.*]] = and i8 [[Y]], 111
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %xy = and i8 %x, %y
+ %cmp = icmp eq i8 %xy, 123
+ call void @llvm.assume(i1 %cmp)
+ %r = and i8 %y, 111
+ ret i8 %r
+}
+
+define i8 @or_eq_bits_must_be_unset(i8 %x, i8 %y) {
+; CHECK-LABEL: @or_eq_bits_must_be_unset(
+; CHECK-NEXT: [[XY:%.*]] = or i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[XY]], 124
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: ret i8 0
+;
+ %xy = or i8 %x, %y
+ %cmp = icmp eq i8 %xy, 124
+ call void @llvm.assume(i1 %cmp)
+ %r = and i8 %x, 3
+ ret i8 %r
+}
+
+define i8 @or_eq_bits_must_be_unset2(i8 %x, i8 %y) {
+; CHECK-LABEL: @or_eq_bits_must_be_unset2(
+; CHECK-NEXT: [[XY:%.*]] = or i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[XY]], 124
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: ret i8 0
+;
+ %xy = or i8 %x, %y
+ %cmp = icmp eq i8 %xy, 124
+ call void @llvm.assume(i1 %cmp)
+ %r = and i8 %y, 1
+ ret i8 %r
+}
+define i8 @or_eq_bits_must_be_unset2_partial_fail(i8 %x, i8 %y) {
+; CHECK-LABEL: @or_eq_bits_must_be_unset2_partial_fail(
+; CHECK-NEXT: [[XY:%.*]] = or i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8 [[XY]], 124
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: [[R:%.*]] = and i8 [[Y]], 4
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %xy = or i8 %x, %y
+ %cmp = icmp eq i8 %xy, 124
+ call void @llvm.assume(i1 %cmp)
+ %r = and i8 %y, 7
+ ret i8 %r
+}
+
+define i8 @or_ne_bits_must_be_unset2_fail(i8 %x, i8 %y) {
+; CHECK-LABEL: @or_ne_bits_must_be_unset2_fail(
+; CHECK-NEXT: [[XY:%.*]] = or i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ne i8 [[XY]], 124
+; CHECK-NEXT: call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT: [[R:%.*]] = and i8 [[X]], 3
+; CHECK-NEXT: ret i8 [[R]]
+;
+ %xy = or i8 %x, %y
+ %cmp = icmp ne i8 %xy, 124
+ call void @llvm.assume(i1 %cmp)
+ %r = and i8 %x, 3
+ ret i8 %r
+}
declare void @use(i1)
declare void @sink(i8)
diff --git a/llvm/test/Transforms/InstCombine/zext-or-icmp.ll b/llvm/test/Transforms/InstCombine/zext-or-icmp.ll
index 661c36038a67ea..a4b74aa8cc7dc3 100644
--- a/llvm/test/Transforms/InstCombine/zext-or-icmp.ll
+++ b/llvm/test/Transforms/InstCombine/zext-or-icmp.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+; RUN: opt < %s -passes='instcombine<no-verify-fixpoint>' -S | FileCheck %s
define i8 @zext_or_icmp_icmp(i8 %a, i8 %b) {
; CHECK-LABEL: @zext_or_icmp_icmp(
@@ -180,11 +180,11 @@ define i8 @PR49475_infloop(i32 %t0, i16 %insert, i64 %e, i8 %i162) {
; CHECK-NEXT: [[SEXT:%.*]] = shl i64 [[SUB17]], 32
; CHECK-NEXT: [[CONV18:%.*]] = ashr exact i64 [[SEXT]], 32
; CHECK-NEXT: [[CMP:%.*]] = icmp sge i64 [[XOR]], [[CONV18]]
-; CHECK-NEXT: [[CONV19:%.*]] = zext i1 [[CMP]] to i16
-; CHECK-NEXT: [[OR21:%.*]] = or i16 [[CONV19]], [[INSERT]]
-; CHECK-NEXT: [[TOBOOL23_NOT:%.*]] = icmp eq i16 [[OR21]], 0
+; CHECK-NEXT: [[TRUNC44:%.*]] = zext i1 [[CMP]] to i8
+; CHECK-NEXT: [[INC:%.*]] = add i8 [[TRUNC44]], [[I162]]
+; CHECK-NEXT: [[TOBOOL23_NOT:%.*]] = xor i1 [[CMP]], true
; CHECK-NEXT: call void @llvm.assume(i1 [[TOBOOL23_NOT]])
-; CHECK-NEXT: ret i8 [[I162]]
+; CHECK-NEXT: ret i8 [[INC]]
;
%b = icmp eq i32 %t0, 0
%b2 = icmp eq i16 %insert, 0
|
@@ -1,5 +1,5 @@ | |||
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py | |||
; RUN: opt < %s -passes=instcombine -S | FileCheck %s | |||
; RUN: opt < %s -passes='instcombine<no-verify-fixpoint>' -S | FileCheck %s |
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 no longer reach fixed point because we end up unlocking a transform by creating a pattern we handle in knownbits.
If this is important, the fix here is to support sext/zext in the AssumptionCache.
You can test this locally with the following command:git-clang-format --diff c2f3a11dbe1a6bc2fc46b35c3fb4398e1d6a90c4 6a90629a32bf64571bbcd373e8d611361d3dbb92 -- llvm/lib/Analysis/ValueTracking.cpp View the diff from clang-format here.diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 33a69861cc..93ba231f74 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -657,8 +657,7 @@ static void computeKnownBitsFromCmp(const Value *V, CmpInst::Predicate Pred,
if (match(LHS, m_V) && match(RHS, m_APInt(C))) {
Known = Known.unionWith(KnownBits::makeConstant(*C));
// assume(V & Mask = C)
- } else if (match(LHS, m_c_And(m_V, m_Value(Y))) &&
- match(RHS, m_APInt(C))) {
+ } else if (match(LHS, m_c_And(m_V, m_Value(Y))) && match(RHS, m_APInt(C))) {
// For one bits in Mask, we can propagate bits from C to V.
Known.One |= *C;
if (match(Y, m_APInt(Mask)))
|
@goldsteinn Could you please have a look at dtcxzyw/llvm-opt-benchmark#476 (comment)? cc @nikic |
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. Nice catch!
@dtcxzyw If it's useful, we can move the fold from instsimplify to instcombine and create non-terminator unreachable there. |
(icmp eq (and/or x,y), C)
; NFC(icmp eq (and/or x,y), C)