Skip to content
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

[InstSimplify] Fold icmp of X and/or C1 and X and/or C2 into constant #65905

Merged
merged 4 commits into from
Sep 18, 2023

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Sep 10, 2023

This patch simplifies the pattern icmp X and/or C1, X and/or C2 when one constant mask is the subset of the other.
If C1 & C2 == C1, A = X and/or C1, B = X and/or C2, we can do the following folds:
icmp ule A, B -> true
icmp ugt A, B -> false
We can apply similar folds for signed predicates when C1 and C2 are the same sign:
icmp sle A, B -> true
icmp sgt A, B -> false

Alive2: https://alive2.llvm.org/ce/z/Q4ekP5
Fixes #65833.

@llvmbot
Copy link
Collaborator

llvmbot commented Sep 10, 2023

@llvm/pr-subscribers-llvm-analysis

Changes

This patch simplifies the pattern icmp X & C1, X & C2 when one constant mask is the subset of the other.
Alive2: https://alive2.llvm.org/ce/z/s-IEK7
Fixes #65833.

--
Full diff: https://github.com/llvm/llvm-project/pull/65905.diff

3 Files Affected:

  • (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+27-1)
  • (modified) llvm/test/Transforms/InstSimplify/compare.ll (+194)
  • (modified) llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll (+97)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index d0cc56ebc2be319..734029f1ad0bef5 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -3427,7 +3427,7 @@ static Value *simplifyICmpWithBinOp(CmpInst::Predicate Pred, Value *LHS,
     switch (LBO->getOpcode()) {
     default:
       break;
-    case Instruction::Shl:
+    case Instruction::Shl: {
       bool NUW = Q.IIQ.hasNoUnsignedWrap(LBO) && Q.IIQ.hasNoUnsignedWrap(RBO);
       bool NSW = Q.IIQ.hasNoSignedWrap(LBO) && Q.IIQ.hasNoSignedWrap(RBO);
       if (!NUW || (ICmpInst::isSigned(Pred) && !NSW) ||
@@ -3436,6 +3436,32 @@ static Value *simplifyICmpWithBinOp(CmpInst::Predicate Pred, Value *LHS,
       if (Value *V = simplifyICmpInst(Pred, LBO->getOperand(1),
                                       RBO->getOperand(1), Q, MaxRecurse - 1))
         return V;
+      break;
+    }
+    // icmp X & C1, X & C2 where (C1 & C2) == C1/C2
+    // icmp X | C1, X | C2 where (C1 & C2) == C1/C2
+    case Instruction::And:
+    case Instruction::Or: {
+      if (ICmpInst::isUnsigned(Pred)) {
+        const APInt *C1, *C2;
+        if (match(LBO->getOperand(1), m_APInt(C1)) &&
+            match(RBO->getOperand(1), m_APInt(C2))) {
+          if (C1->isSubsetOf(*C2)) {
+            if (Pred == ICmpInst::ICMP_ULE)
+              return ConstantInt::getTrue(getCompareTy(LHS));
+            if (Pred == ICmpInst::ICMP_UGT)
+              return ConstantInt::getFalse(getCompareTy(LHS));
+          }
+          if (C2->isSubsetOf(*C1)) {
+            if (Pred == ICmpInst::ICMP_UGE)
+              return ConstantInt::getTrue(getCompareTy(LHS));
+            if (Pred == ICmpInst::ICMP_ULT)
+              return ConstantInt::getFalse(getCompareTy(LHS));
+          }
+        }
+      }
+      break;
+    }
     }
   }
 
diff --git a/llvm/test/Transforms/InstSimplify/compare.ll b/llvm/test/Transforms/InstSimplify/compare.ll
index c6c104a41c8be70..d21af97494c88bc 100644
--- a/llvm/test/Transforms/InstSimplify/compare.ll
+++ b/llvm/test/Transforms/InstSimplify/compare.ll
@@ -1919,6 +1919,200 @@ define i1 @tautological8(i32 %A, i32 %B) {
   ret i1 %D
 }
 
+define i1 @tautological9(i32 %A) {
+; CHECK-LABEL: @tautological9(
+; CHECK-NEXT:    ret i1 false
+;
+  %C1 = and i32 %A, 1
+  %C2 = and i32 %A, 3
+  %D = icmp ugt i32 %C1, %C2
+  ret i1 %D
+}
+
+define <2 x i1> @tautological9_vec(<2 x i32> %A) {
+; CHECK-LABEL: @tautological9_vec(
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
+;
+  %C1 = and <2 x i32> %A, 
+  %C2 = and <2 x i32> %A, 
+  %D = icmp ugt <2 x i32> %C1, %C2
+  ret <2 x i1> %D
+}
+
+define i1 @tautological10(i32 %A) {
+; CHECK-LABEL: @tautological10(
+; CHECK-NEXT:    ret i1 true
+;
+  %C1 = and i32 %A, 1
+  %C2 = and i32 %A, 3
+  %D = icmp ule i32 %C1, %C2
+  ret i1 %D
+}
+
+define i1 @tautological11(i32 %A) {
+; CHECK-LABEL: @tautological11(
+; CHECK-NEXT:    ret i1 true
+;
+  %C1 = or i32 %A, 1
+  %C2 = or i32 %A, 3
+  %D = icmp ule i32 %C1, %C2
+  ret i1 %D
+}
+
+define i1 @tautological12(i32 %A) {
+; CHECK-LABEL: @tautological12(
+; CHECK-NEXT:    ret i1 false
+;
+  %C1 = or i32 %A, 1
+  %C2 = or i32 %A, 3
+  %D = icmp ugt i32 %C1, %C2
+  ret i1 %D
+}
+
+define i1 @tautological13(i32 %A) {
+; CHECK-LABEL: @tautological13(
+; CHECK-NEXT:    ret i1 false
+;
+  %C1 = or i32 %A, 1
+  %C2 = or i32 %A, 3
+  %D = icmp ult i32 %C2, %C1
+  ret i1 %D
+}
+
+define i1 @tautological14(i32 %A) {
+; CHECK-LABEL: @tautological14(
+; CHECK-NEXT:    ret i1 true
+;
+  %C1 = or i32 %A, 1
+  %C2 = or i32 %A, 3
+  %D = icmp uge i32 %C2, %C1
+  ret i1 %D
+}
+
+define i1 @tautological15(i32 %A) {
+; CHECK-LABEL: @tautological15(
+; CHECK-NEXT:    ret i1 true
+;
+  %C1 = and i32 %A, 1
+  %C2 = and i32 %A, 3
+  %D = icmp uge i32 %C2, %C1
+  ret i1 %D
+}
+
+define i1 @tautological16(i32 %A) {
+; CHECK-LABEL: @tautological16(
+; CHECK-NEXT:    ret i1 false
+;
+  %C1 = and i32 %A, 1
+  %C2 = and i32 %A, 3
+  %D = icmp ult i32 %C2, %C1
+  ret i1 %D
+}
+
+define i1 @tautological9_negative(i32 %A) {
+; CHECK-LABEL: @tautological9_negative(
+; CHECK-NEXT:    [[C1:%.*]] = and i32 [[A:%.*]], 1
+; CHECK-NEXT:    [[C2:%.*]] = and i32 [[A]], 2
+; CHECK-NEXT:    [[D:%.*]] = icmp ugt i32 [[C1]], [[C2]]
+; CHECK-NEXT:    ret i1 [[D]]
+;
+  %C1 = and i32 %A, 1
+  %C2 = and i32 %A, 2
+  %D = icmp ugt i32 %C1, %C2
+  ret i1 %D
+}
+
+define i1 @tautological10_negative(i32 %A) {
+; CHECK-LABEL: @tautological10_negative(
+; CHECK-NEXT:    [[C1:%.*]] = and i32 [[A:%.*]], 1
+; CHECK-NEXT:    [[C2:%.*]] = and i32 [[A]], 2
+; CHECK-NEXT:    [[D:%.*]] = icmp ule i32 [[C1]], [[C2]]
+; CHECK-NEXT:    ret i1 [[D]]
+;
+  %C1 = and i32 %A, 1
+  %C2 = and i32 %A, 2
+  %D = icmp ule i32 %C1, %C2
+  ret i1 %D
+}
+
+define i1 @tautological11_negative(i32 %A) {
+; CHECK-LABEL: @tautological11_negative(
+; CHECK-NEXT:    [[C1:%.*]] = or i32 [[A:%.*]], 1
+; CHECK-NEXT:    [[C2:%.*]] = or i32 [[A]], 2
+; CHECK-NEXT:    [[D:%.*]] = icmp ule i32 [[C1]], [[C2]]
+; CHECK-NEXT:    ret i1 [[D]]
+;
+  %C1 = or i32 %A, 1
+  %C2 = or i32 %A, 2
+  %D = icmp ule i32 %C1, %C2
+  ret i1 %D
+}
+
+define i1 @tautological12_negative(i32 %A) {
+; CHECK-LABEL: @tautological12_negative(
+; CHECK-NEXT:    [[C1:%.*]] = or i32 [[A:%.*]], 1
+; CHECK-NEXT:    [[C2:%.*]] = or i32 [[A]], 2
+; CHECK-NEXT:    [[D:%.*]] = icmp ugt i32 [[C1]], [[C2]]
+; CHECK-NEXT:    ret i1 [[D]]
+;
+  %C1 = or i32 %A, 1
+  %C2 = or i32 %A, 2
+  %D = icmp ugt i32 %C1, %C2
+  ret i1 %D
+}
+
+define i1 @tautological13_negative(i32 %A) {
+; CHECK-LABEL: @tautological13_negative(
+; CHECK-NEXT:    [[C1:%.*]] = or i32 [[A:%.*]], 1
+; CHECK-NEXT:    [[C2:%.*]] = or i32 [[A]], 2
+; CHECK-NEXT:    [[D:%.*]] = icmp ult i32 [[C2]], [[C1]]
+; CHECK-NEXT:    ret i1 [[D]]
+;
+  %C1 = or i32 %A, 1
+  %C2 = or i32 %A, 2
+  %D = icmp ult i32 %C2, %C1
+  ret i1 %D
+}
+
+define i1 @tautological14_negative(i32 %A) {
+; CHECK-LABEL: @tautological14_negative(
+; CHECK-NEXT:    [[C1:%.*]] = or i32 [[A:%.*]], 1
+; CHECK-NEXT:    [[C2:%.*]] = or i32 [[A]], 2
+; CHECK-NEXT:    [[D:%.*]] = icmp uge i32 [[C2]], [[C1]]
+; CHECK-NEXT:    ret i1 [[D]]
+;
+  %C1 = or i32 %A, 1
+  %C2 = or i32 %A, 2
+  %D = icmp uge i32 %C2, %C1
+  ret i1 %D
+}
+
+define i1 @tautological15_negative(i32 %A) {
+; CHECK-LABEL: @tautological15_negative(
+; CHECK-NEXT:    [[C1:%.*]] = and i32 [[A:%.*]], 1
+; CHECK-NEXT:    [[C2:%.*]] = and i32 [[A]], 2
+; CHECK-NEXT:    [[D:%.*]] = icmp uge i32 [[C2]], [[C1]]
+; CHECK-NEXT:    ret i1 [[D]]
+;
+  %C1 = and i32 %A, 1
+  %C2 = and i32 %A, 2
+  %D = icmp uge i32 %C2, %C1
+  ret i1 %D
+}
+
+define i1 @tautological16_negative(i32 %A) {
+; CHECK-LABEL: @tautological16_negative(
+; CHECK-NEXT:    [[C1:%.*]] = and i32 [[A:%.*]], 1
+; CHECK-NEXT:    [[C2:%.*]] = and i32 [[A]], 2
+; CHECK-NEXT:    [[D:%.*]] = icmp ult i32 [[C2]], [[C1]]
+; CHECK-NEXT:    ret i1 [[D]]
+;
+  %C1 = and i32 %A, 1
+  %C2 = and i32 %A, 2
+  %D = icmp ult i32 %C2, %C1
+  ret i1 %D
+}
+
 declare void @helper_i1(i1)
 ; Series of tests for icmp s[lt|ge] (or A, B), A and icmp s[gt|le] A, (or A, B)
 define void @icmp_slt_sge_or(i32 %Ax, i32 %Bx) {
diff --git a/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll b/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll
index e7123b208084927..8828378b5315df0 100644
--- a/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll
+++ b/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll
@@ -2332,3 +2332,100 @@ false:
   %m2 = call i8 @llvm.smin.i8(i8 %x, i8 %y)
   ret i8 %m2
 }
+
+; Tests from PR65833
+define i8 @umin_and_mask(i8 %x) {
+; CHECK-LABEL: @umin_and_mask(
+; CHECK-NEXT:    [[AND1:%.*]] = and i8 [[X:%.*]], 1
+; CHECK-NEXT:    ret i8 [[AND1]]
+;
+  %and1 = and i8 %x, 1
+  %and2 = and i8 %x, 3
+  %val = call i8 @llvm.umin.i8(i8 %and1, i8 %and2)
+  ret i8 %val
+}
+
+define i8 @umax_and_mask(i8 %x) {
+; CHECK-LABEL: @umax_and_mask(
+; CHECK-NEXT:    [[AND2:%.*]] = and i8 [[X:%.*]], 3
+; CHECK-NEXT:    ret i8 [[AND2]]
+;
+  %and1 = and i8 %x, 1
+  %and2 = and i8 %x, 3
+  %val = call i8 @llvm.umax.i8(i8 %and1, i8 %and2)
+  ret i8 %val
+}
+
+define i8 @umin_or_mask(i8 %x) {
+; CHECK-LABEL: @umin_or_mask(
+; CHECK-NEXT:    [[AND1:%.*]] = or i8 [[X:%.*]], 1
+; CHECK-NEXT:    ret i8 [[AND1]]
+;
+  %and1 = or i8 %x, 1
+  %and2 = or i8 %x, 3
+  %val = call i8 @llvm.umin.i8(i8 %and1, i8 %and2)
+  ret i8 %val
+}
+
+define i8 @umax_or_mask(i8 %x) {
+; CHECK-LABEL: @umax_or_mask(
+; CHECK-NEXT:    [[AND2:%.*]] = or i8 [[X:%.*]], 3
+; CHECK-NEXT:    ret i8 [[AND2]]
+;
+  %and1 = or i8 %x, 1
+  %and2 = or i8 %x, 3
+  %val = call i8 @llvm.umax.i8(i8 %and1, i8 %and2)
+  ret i8 %val
+}
+
+define i8 @umin_and_mask_negative(i8 %x) {
+; CHECK-LABEL: @umin_and_mask_negative(
+; CHECK-NEXT:    [[AND1:%.*]] = and i8 [[X:%.*]], 1
+; CHECK-NEXT:    [[AND2:%.*]] = and i8 [[X]], 2
+; CHECK-NEXT:    [[VAL:%.*]] = call i8 @llvm.umin.i8(i8 [[AND1]], i8 [[AND2]])
+; CHECK-NEXT:    ret i8 [[VAL]]
+;
+  %and1 = and i8 %x, 1
+  %and2 = and i8 %x, 2
+  %val = call i8 @llvm.umin.i8(i8 %and1, i8 %and2)
+  ret i8 %val
+}
+
+define i8 @umax_and_mask_negative(i8 %x) {
+; CHECK-LABEL: @umax_and_mask_negative(
+; CHECK-NEXT:    [[AND1:%.*]] = and i8 [[X:%.*]], 1
+; CHECK-NEXT:    [[AND2:%.*]] = and i8 [[X]], 2
+; CHECK-NEXT:    [[VAL:%.*]] = call i8 @llvm.umax.i8(i8 [[AND1]], i8 [[AND2]])
+; CHECK-NEXT:    ret i8 [[VAL]]
+;
+  %and1 = and i8 %x, 1
+  %and2 = and i8 %x, 2
+  %val = call i8 @llvm.umax.i8(i8 %and1, i8 %and2)
+  ret i8 %val
+}
+
+define i8 @umin_or_mask_negative(i8 %x) {
+; CHECK-LABEL: @umin_or_mask_negative(
+; CHECK-NEXT:    [[AND1:%.*]] = or i8 [[X:%.*]], 1
+; CHECK-NEXT:    [[AND2:%.*]] = or i8 [[X]], 2
+; CHECK-NEXT:    [[VAL:%.*]] = call i8 @llvm.umin.i8(i8 [[AND1]], i8 [[AND2]])
+; CHECK-NEXT:    ret i8 [[VAL]]
+;
+  %and1 = or i8 %x, 1
+  %and2 = or i8 %x, 2
+  %val = call i8 @llvm.umin.i8(i8 %and1, i8 %and2)
+  ret i8 %val
+}
+
+define i8 @umax_or_mask_negative(i8 %x) {
+; CHECK-LABEL: @umax_or_mask_negative(
+; CHECK-NEXT:    [[AND1:%.*]] = or i8 [[X:%.*]], 1
+; CHECK-NEXT:    [[AND2:%.*]] = or i8 [[X]], 2
+; CHECK-NEXT:    [[VAL:%.*]] = call i8 @llvm.umax.i8(i8 [[AND1]], i8 [[AND2]])
+; CHECK-NEXT:    ret i8 [[VAL]]
+;
+  %and1 = or i8 %x, 1
+  %and2 = or i8 %x, 2
+  %val = call i8 @llvm.umax.i8(i8 %and1, i8 %and2)
+  ret i8 %val
+}

llvm/lib/Analysis/InstructionSimplify.cpp Outdated Show resolved Hide resolved
llvm/lib/Analysis/InstructionSimplify.cpp Outdated Show resolved Hide resolved
llvm/lib/Analysis/InstructionSimplify.cpp Outdated Show resolved Hide resolved
llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll Outdated Show resolved Hide resolved
@llvmbot
Copy link
Collaborator

llvmbot commented Sep 17, 2023

@llvm/pr-subscribers-llvm-transforms

Changes

This patch simplifies the pattern icmp X &amp; C1, X &amp; C2 when one constant mask is the subset of the other.
Alive2: https://alive2.llvm.org/ce/z/s-IEK7
Fixes #65833.


Full diff: https://github.com/llvm/llvm-project/pull/65905.diff

3 Files Affected:

  • (modified) llvm/lib/Analysis/InstructionSimplify.cpp (+27-1)
  • (modified) llvm/test/Transforms/InstSimplify/compare.ll (+9-36)
  • (modified) llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll (+6-14)
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 2a3011075e47ed7..1ca1f27b9ab992d 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -3427,7 +3427,7 @@ static Value *simplifyICmpWithBinOp(CmpInst::Predicate Pred, Value *LHS,
     switch (LBO->getOpcode()) {
     default:
       break;
-    case Instruction::Shl:
+    case Instruction::Shl: {
       bool NUW = Q.IIQ.hasNoUnsignedWrap(LBO) && Q.IIQ.hasNoUnsignedWrap(RBO);
       bool NSW = Q.IIQ.hasNoSignedWrap(LBO) && Q.IIQ.hasNoSignedWrap(RBO);
       if (!NUW || (ICmpInst::isSigned(Pred) && !NSW) ||
@@ -3436,6 +3436,32 @@ static Value *simplifyICmpWithBinOp(CmpInst::Predicate Pred, Value *LHS,
       if (Value *V = simplifyICmpInst(Pred, LBO->getOperand(1),
                                       RBO->getOperand(1), Q, MaxRecurse - 1))
         return V;
+      break;
+    }
+    // icmp X & C1, X & C2 where (C1 & C2) == C1/C2
+    // icmp X | C1, X | C2 where (C1 & C2) == C1/C2
+    case Instruction::And:
+    case Instruction::Or: {
+      if (ICmpInst::isUnsigned(Pred)) {
+        const APInt *C1, *C2;
+        if (match(LBO->getOperand(1), m_APInt(C1)) &&
+            match(RBO->getOperand(1), m_APInt(C2))) {
+          if (C1->isSubsetOf(*C2)) {
+            if (Pred == ICmpInst::ICMP_ULE)
+              return ConstantInt::getTrue(getCompareTy(LHS));
+            if (Pred == ICmpInst::ICMP_UGT)
+              return ConstantInt::getFalse(getCompareTy(LHS));
+          }
+          if (C2->isSubsetOf(*C1)) {
+            if (Pred == ICmpInst::ICMP_UGE)
+              return ConstantInt::getTrue(getCompareTy(LHS));
+            if (Pred == ICmpInst::ICMP_ULT)
+              return ConstantInt::getFalse(getCompareTy(LHS));
+          }
+        }
+      }
+      break;
+    }
     }
   }
 
diff --git a/llvm/test/Transforms/InstSimplify/compare.ll b/llvm/test/Transforms/InstSimplify/compare.ll
index cb3805932cbc5be..d21af97494c88bc 100644
--- a/llvm/test/Transforms/InstSimplify/compare.ll
+++ b/llvm/test/Transforms/InstSimplify/compare.ll
@@ -1921,10 +1921,7 @@ define i1 @tautological8(i32 %A, i32 %B) {
 
 define i1 @tautological9(i32 %A) {
 ; CHECK-LABEL: @tautological9(
-; CHECK-NEXT:    [[C1:%.*]] = and i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[C2:%.*]] = and i32 [[A]], 3
-; CHECK-NEXT:    [[D:%.*]] = icmp ugt i32 [[C1]], [[C2]]
-; CHECK-NEXT:    ret i1 [[D]]
+; CHECK-NEXT:    ret i1 false
 ;
   %C1 = and i32 %A, 1
   %C2 = and i32 %A, 3
@@ -1934,10 +1931,7 @@ define i1 @tautological9(i32 %A) {
 
 define <2 x i1> @tautological9_vec(<2 x i32> %A) {
 ; CHECK-LABEL: @tautological9_vec(
-; CHECK-NEXT:    [[C1:%.*]] = and <2 x i32> [[A:%.*]], <i32 1, i32 1>
-; CHECK-NEXT:    [[C2:%.*]] = and <2 x i32> [[A]], <i32 3, i32 3>
-; CHECK-NEXT:    [[D:%.*]] = icmp ugt <2 x i32> [[C1]], [[C2]]
-; CHECK-NEXT:    ret <2 x i1> [[D]]
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
 ;
   %C1 = and <2 x i32> %A, <i32 1, i32 1>
   %C2 = and <2 x i32> %A, <i32 3, i32 3>
@@ -1947,10 +1941,7 @@ define <2 x i1> @tautological9_vec(<2 x i32> %A) {
 
 define i1 @tautological10(i32 %A) {
 ; CHECK-LABEL: @tautological10(
-; CHECK-NEXT:    [[C1:%.*]] = and i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[C2:%.*]] = and i32 [[A]], 3
-; CHECK-NEXT:    [[D:%.*]] = icmp ule i32 [[C1]], [[C2]]
-; CHECK-NEXT:    ret i1 [[D]]
+; CHECK-NEXT:    ret i1 true
 ;
   %C1 = and i32 %A, 1
   %C2 = and i32 %A, 3
@@ -1960,10 +1951,7 @@ define i1 @tautological10(i32 %A) {
 
 define i1 @tautological11(i32 %A) {
 ; CHECK-LABEL: @tautological11(
-; CHECK-NEXT:    [[C1:%.*]] = or i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[C2:%.*]] = or i32 [[A]], 3
-; CHECK-NEXT:    [[D:%.*]] = icmp ule i32 [[C1]], [[C2]]
-; CHECK-NEXT:    ret i1 [[D]]
+; CHECK-NEXT:    ret i1 true
 ;
   %C1 = or i32 %A, 1
   %C2 = or i32 %A, 3
@@ -1973,10 +1961,7 @@ define i1 @tautological11(i32 %A) {
 
 define i1 @tautological12(i32 %A) {
 ; CHECK-LABEL: @tautological12(
-; CHECK-NEXT:    [[C1:%.*]] = or i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[C2:%.*]] = or i32 [[A]], 3
-; CHECK-NEXT:    [[D:%.*]] = icmp ugt i32 [[C1]], [[C2]]
-; CHECK-NEXT:    ret i1 [[D]]
+; CHECK-NEXT:    ret i1 false
 ;
   %C1 = or i32 %A, 1
   %C2 = or i32 %A, 3
@@ -1986,10 +1971,7 @@ define i1 @tautological12(i32 %A) {
 
 define i1 @tautological13(i32 %A) {
 ; CHECK-LABEL: @tautological13(
-; CHECK-NEXT:    [[C1:%.*]] = or i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[C2:%.*]] = or i32 [[A]], 3
-; CHECK-NEXT:    [[D:%.*]] = icmp ult i32 [[C2]], [[C1]]
-; CHECK-NEXT:    ret i1 [[D]]
+; CHECK-NEXT:    ret i1 false
 ;
   %C1 = or i32 %A, 1
   %C2 = or i32 %A, 3
@@ -1999,10 +1981,7 @@ define i1 @tautological13(i32 %A) {
 
 define i1 @tautological14(i32 %A) {
 ; CHECK-LABEL: @tautological14(
-; CHECK-NEXT:    [[C1:%.*]] = or i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[C2:%.*]] = or i32 [[A]], 3
-; CHECK-NEXT:    [[D:%.*]] = icmp uge i32 [[C2]], [[C1]]
-; CHECK-NEXT:    ret i1 [[D]]
+; CHECK-NEXT:    ret i1 true
 ;
   %C1 = or i32 %A, 1
   %C2 = or i32 %A, 3
@@ -2012,10 +1991,7 @@ define i1 @tautological14(i32 %A) {
 
 define i1 @tautological15(i32 %A) {
 ; CHECK-LABEL: @tautological15(
-; CHECK-NEXT:    [[C1:%.*]] = and i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[C2:%.*]] = and i32 [[A]], 3
-; CHECK-NEXT:    [[D:%.*]] = icmp uge i32 [[C2]], [[C1]]
-; CHECK-NEXT:    ret i1 [[D]]
+; CHECK-NEXT:    ret i1 true
 ;
   %C1 = and i32 %A, 1
   %C2 = and i32 %A, 3
@@ -2025,10 +2001,7 @@ define i1 @tautological15(i32 %A) {
 
 define i1 @tautological16(i32 %A) {
 ; CHECK-LABEL: @tautological16(
-; CHECK-NEXT:    [[C1:%.*]] = and i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[C2:%.*]] = and i32 [[A]], 3
-; CHECK-NEXT:    [[D:%.*]] = icmp ult i32 [[C2]], [[C1]]
-; CHECK-NEXT:    ret i1 [[D]]
+; CHECK-NEXT:    ret i1 false
 ;
   %C1 = and i32 %A, 1
   %C2 = and i32 %A, 3
diff --git a/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll b/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll
index 179de804a0c090c..8828378b5315df0 100644
--- a/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll
+++ b/llvm/test/Transforms/InstSimplify/maxmin_intrinsics.ll
@@ -2337,9 +2337,7 @@ false:
 define i8 @umin_and_mask(i8 %x) {
 ; CHECK-LABEL: @umin_and_mask(
 ; CHECK-NEXT:    [[AND1:%.*]] = and i8 [[X:%.*]], 1
-; CHECK-NEXT:    [[AND2:%.*]] = and i8 [[X]], 3
-; CHECK-NEXT:    [[VAL:%.*]] = call i8 @llvm.umin.i8(i8 [[AND1]], i8 [[AND2]])
-; CHECK-NEXT:    ret i8 [[VAL]]
+; CHECK-NEXT:    ret i8 [[AND1]]
 ;
   %and1 = and i8 %x, 1
   %and2 = and i8 %x, 3
@@ -2349,10 +2347,8 @@ define i8 @umin_and_mask(i8 %x) {
 
 define i8 @umax_and_mask(i8 %x) {
 ; CHECK-LABEL: @umax_and_mask(
-; CHECK-NEXT:    [[AND1:%.*]] = and i8 [[X:%.*]], 1
-; CHECK-NEXT:    [[AND2:%.*]] = and i8 [[X]], 3
-; CHECK-NEXT:    [[VAL:%.*]] = call i8 @llvm.umax.i8(i8 [[AND1]], i8 [[AND2]])
-; CHECK-NEXT:    ret i8 [[VAL]]
+; CHECK-NEXT:    [[AND2:%.*]] = and i8 [[X:%.*]], 3
+; CHECK-NEXT:    ret i8 [[AND2]]
 ;
   %and1 = and i8 %x, 1
   %and2 = and i8 %x, 3
@@ -2363,9 +2359,7 @@ define i8 @umax_and_mask(i8 %x) {
 define i8 @umin_or_mask(i8 %x) {
 ; CHECK-LABEL: @umin_or_mask(
 ; CHECK-NEXT:    [[AND1:%.*]] = or i8 [[X:%.*]], 1
-; CHECK-NEXT:    [[AND2:%.*]] = or i8 [[X]], 3
-; CHECK-NEXT:    [[VAL:%.*]] = call i8 @llvm.umin.i8(i8 [[AND1]], i8 [[AND2]])
-; CHECK-NEXT:    ret i8 [[VAL]]
+; CHECK-NEXT:    ret i8 [[AND1]]
 ;
   %and1 = or i8 %x, 1
   %and2 = or i8 %x, 3
@@ -2375,10 +2369,8 @@ define i8 @umin_or_mask(i8 %x) {
 
 define i8 @umax_or_mask(i8 %x) {
 ; CHECK-LABEL: @umax_or_mask(
-; CHECK-NEXT:    [[AND1:%.*]] = or i8 [[X:%.*]], 1
-; CHECK-NEXT:    [[AND2:%.*]] = or i8 [[X]], 3
-; CHECK-NEXT:    [[VAL:%.*]] = call i8 @llvm.umax.i8(i8 [[AND1]], i8 [[AND2]])
-; CHECK-NEXT:    ret i8 [[VAL]]
+; CHECK-NEXT:    [[AND2:%.*]] = or i8 [[X:%.*]], 3
+; CHECK-NEXT:    ret i8 [[AND2]]
 ;
   %and1 = or i8 %x, 1
   %and2 = or i8 %x, 3

@dtcxzyw dtcxzyw changed the title [InstSimplify] Simplify icmp X & C1, X & C2 when (C1 & C2) == C1/C2 [InstSimplify] Fold icmp of X and/or C1 and X and/or C2 into constant Sep 17, 2023
@goldsteinn
Copy link
Contributor

LGTM, but please cleanup commits.

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Sep 18, 2023

LGTM, but please cleanup commits.

GitHub will squash commits into a single commit.

@dtcxzyw dtcxzyw merged commit be2723d into llvm:main Sep 18, 2023
2 checks passed
@dtcxzyw dtcxzyw deleted the icmp-and-or-mask branch September 18, 2023 13:32
ZijunZhaoCCK pushed a commit to ZijunZhaoCCK/llvm-project that referenced this pull request Sep 19, 2023
…tant (llvm#65905)

This patch simplifies the pattern `icmp X and/or C1, X and/or C2` when
one constant mask is the subset of the other.
If `C1 & C2 == C1`, `A = X and/or C1`, `B = X and/or C2`, we can do the
following folds:
`icmp ule A, B -> true`
`icmp ugt A, B -> false`
We can apply similar folds for signed predicates when `C1` and `C2` are
the same sign:
`icmp sle A, B -> true`
`icmp sgt A, B -> false`

Alive2: https://alive2.llvm.org/ce/z/Q4ekP5
Fixes llvm#65833.
zahiraam pushed a commit to tahonermann/llvm-project that referenced this pull request Oct 24, 2023
…tant (llvm#65905)

This patch simplifies the pattern `icmp X and/or C1, X and/or C2` when
one constant mask is the subset of the other.
If `C1 & C2 == C1`, `A = X and/or C1`, `B = X and/or C2`, we can do the
following folds:
`icmp ule A, B -> true`
`icmp ugt A, B -> false`
We can apply similar folds for signed predicates when `C1` and `C2` are
the same sign:
`icmp sle A, B -> true`
`icmp sgt A, B -> false`

Alive2: https://alive2.llvm.org/ce/z/Q4ekP5
Fixes llvm#65833.
zahiraam pushed a commit to tahonermann/llvm-project that referenced this pull request Oct 24, 2023
…tant (llvm#65905)

This patch simplifies the pattern `icmp X and/or C1, X and/or C2` when
one constant mask is the subset of the other.
If `C1 & C2 == C1`, `A = X and/or C1`, `B = X and/or C2`, we can do the
following folds:
`icmp ule A, B -> true`
`icmp ugt A, B -> false`
We can apply similar folds for signed predicates when `C1` and `C2` are
the same sign:
`icmp sle A, B -> true`
`icmp sgt A, B -> false`

Alive2: https://alive2.llvm.org/ce/z/Q4ekP5
Fixes llvm#65833.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Simplifications of max/min (a & c0, a & c1) under (c0 & c1) == c1/c0
3 participants