[llvm] Issue 180492 (PR #180596)

Gergo Stomfai via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 10 06:32:42 PST 2026


https://github.com/stomfaig updated https://github.com/llvm/llvm-project/pull/180596

>From e6148684f09e548747feda71e3679584188d006c Mon Sep 17 00:00:00 2001
From: stomfaig <stomfaig at gmail.com>
Date: Mon, 9 Feb 2026 19:00:04 +0000
Subject: [PATCH 1/3] combine icmp-shl-zext-zext-1

---
 .../InstCombine/InstCombineCompares.cpp       |  22 ++++
 .../InstCombine/icmp-shl-zext-zext-1.ll       | 123 ++++++++++++++++++
 2 files changed, 145 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index bce609275ffa9..2ed6f36a771b5 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7894,6 +7894,28 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
     }
   }
 
+  // icmp eq (shl (zext i1 x), (zext i1 y)), 1 --> and i1 x, (not i1 y)
+  // icmp ne (shl (zext i1 x), (zext i1 y)), 1 --> or i1 (not i1 x), y
+  {
+    Value *A, *B;
+    CmpPredicate CmpPred;
+
+    if (match(&I,
+              m_c_ICmp(CmpPred, m_Shl(m_ZExt(m_Value(A)), m_ZExt(m_Value(B))),
+                       m_One())) &&
+        A->getType()->isIntegerTy(1) && B->getType()->isIntegerTy(1)) {
+
+      if (CmpPred == ICmpInst::ICMP_EQ) {
+        return replaceInstUsesWith(I,
+                                   Builder.CreateAnd(A, Builder.CreateNot(B)));
+      }
+      if (CmpPred == ICmpInst::ICMP_NE) {
+        return replaceInstUsesWith(I,
+                                   Builder.CreateOr(Builder.CreateNot(A), B));
+      }
+    }
+  }
+
   // Try to optimize equality comparisons against alloca-based pointers.
   if (Op0->getType()->isPointerTy() && I.isEquality()) {
     assert(Op1->getType()->isPointerTy() &&
diff --git a/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll b/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
new file mode 100644
index 0000000000000..9f9f09b444e14
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
@@ -0,0 +1,123 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; Basic test cases
+define i1 @icmp_eq(i1 %x, i1 %y) {
+; CHECK-LABEL: define i1 @icmp_eq(
+; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[Y]], true
+; CHECK-NEXT:    [[RESULT:%.*]] = and i1 [[X]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+  %x.ext = zext i1 %x to i32
+  %y.ext = zext i1 %y to i32
+  %shl = shl i32 %x.ext, %y.ext
+  %result = icmp eq i32 %shl, 1
+  ret i1 %result
+}
+
+define i1 @icmp_ne(i1 %x, i1 %y) {
+; CHECK-LABEL: define i1 @icmp_ne(
+; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[X]], true
+; CHECK-NEXT:    [[RESULT:%.*]] = or i1 [[Y]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+  %x.ext = zext i1 %x to i32
+  %y.ext = zext i1 %y to i32
+  %shl = shl i32 %x.ext, %y.ext
+  %result = icmp ne i32 %shl, 1
+  ret i1 %result
+}
+
+define i1 @icmp_eq_comm(i1 %x, i1 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_comm(
+; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[Y]], true
+; CHECK-NEXT:    [[RESULT:%.*]] = and i1 [[X]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+  %x.ext = zext i1 %x to i32
+  %y.ext = zext i1 %y to i32
+  %shl = shl i32 %x.ext, %y.ext
+  %result = icmp eq i32 1, %shl
+  ret i1 %result
+}
+
+define i1 @icmp_ne_comm(i1 %x, i1 %y) {
+; CHECK-LABEL: define i1 @icmp_ne_comm(
+; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[X]], true
+; CHECK-NEXT:    [[RESULT:%.*]] = or i1 [[Y]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+  %x.ext = zext i1 %x to i32
+  %y.ext = zext i1 %y to i32
+  %shl = shl i32 %x.ext, %y.ext
+  %result = icmp ne i32 1, %shl
+  ret i1 %result
+}
+
+; Different bit zext target bit width
+
+define i1 @icmp_eq_64(i1 %x, i1 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_64(
+; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[Y]], true
+; CHECK-NEXT:    [[RESULT:%.*]] = and i1 [[X]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+  %x.ext = zext i1 %x to i64
+  %y.ext = zext i1 %y to i64
+  %shl = shl i64 %x.ext, %y.ext
+  %result = icmp eq i64 %shl, 1
+  ret i1 %result
+}
+
+define i1 @icmp_ne_64(i1 %x, i1 %y) {
+; CHECK-LABEL: define i1 @icmp_ne_64(
+; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[X]], true
+; CHECK-NEXT:    [[RESULT:%.*]] = or i1 [[Y]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+  %x.ext = zext i1 %x to i64
+  %y.ext = zext i1 %y to i64
+  %shl = shl i64 %x.ext, %y.ext
+  %result = icmp ne i64 %shl, 1
+  ret i1 %result
+}
+
+; Negative test: Comparing against 3, not 1
+define i1 @icmp_eq_zero_negative(i1 %x, i1 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_zero_negative(
+; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]]) {
+; CHECK-NEXT:    [[X_EXT:%.*]] = zext i1 [[X]] to i32
+; CHECK-NEXT:    [[Y_EXT:%.*]] = zext i1 [[Y]] to i32
+; CHECK-NEXT:    [[SHL:%.*]] = shl nuw nsw i32 [[X_EXT]], [[Y_EXT]]
+; CHECK-NEXT:    [[RESULT:%.*]] = icmp eq i32 [[SHL]], 3
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+  %x.ext = zext i1 %x to i32
+  %y.ext = zext i1 %y to i32
+  %shl = shl i32 %x.ext, %y.ext
+  %result = icmp eq i32 %shl, 3
+  ret i1 %result
+}
+
+; Negative test: Non-i1 source type
+define i1 @icmp_eq_non_i1_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_non_i1_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT:    [[X_EXT:%.*]] = zext i8 [[X]] to i32
+; CHECK-NEXT:    [[Y_EXT:%.*]] = zext nneg i8 [[Y]] to i32
+; CHECK-NEXT:    [[SHL:%.*]] = shl i32 [[X_EXT]], [[Y_EXT]]
+; CHECK-NEXT:    [[RESULT:%.*]] = icmp eq i32 [[SHL]], 1
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+  %x.ext = zext i8 %x to i32
+  %y.ext = zext i8 %y to i32
+  %shl = shl i32 %x.ext, %y.ext
+  %result = icmp eq i32 %shl, 1
+  ret i1 %result
+}

>From 9e5adfaec1ec97e0a3ae72796f69f65b5af15cc7 Mon Sep 17 00:00:00 2001
From: stomfaig <stomfaig at gmail.com>
Date: Mon, 9 Feb 2026 19:29:02 +0000
Subject: [PATCH 2/3] clarify comment

---
 llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll b/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
index 9f9f09b444e14..18b6ad25e8bd7 100644
--- a/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
@@ -58,8 +58,7 @@ define i1 @icmp_ne_comm(i1 %x, i1 %y) {
   ret i1 %result
 }
 
-; Different bit zext target bit width
-
+; Different zext target bit width
 define i1 @icmp_eq_64(i1 %x, i1 %y) {
 ; CHECK-LABEL: define i1 @icmp_eq_64(
 ; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]]) {

>From 9546bc3ff1df983c238930d75af43449c642f963 Mon Sep 17 00:00:00 2001
From: stomfaig <stomfaig at gmail.com>
Date: Tue, 10 Feb 2026 14:32:14 +0000
Subject: [PATCH 3/3] resolve comments

---
 .../InstCombine/InstCombineCompares.cpp       |  15 ++-
 .../InstCombine/icmp-shl-zext-zext-1.ll       | 109 ++++++++++++++----
 2 files changed, 92 insertions(+), 32 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 2ed6f36a771b5..c509d5f894fab 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7900,19 +7900,18 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
     Value *A, *B;
     CmpPredicate CmpPred;
 
-    if (match(&I,
-              m_c_ICmp(CmpPred, m_Shl(m_ZExt(m_Value(A)), m_ZExt(m_Value(B))),
-                       m_One())) &&
-        A->getType()->isIntegerTy(1) && B->getType()->isIntegerTy(1)) {
+    if (match(&I, m_ICmp(CmpPred, m_Shl(m_ZExt(m_Value(A)), m_ZExt(m_Value(B))),
+                         m_One())) &&
+        A->getType()->isIntOrIntVectorTy(1) &&
+        B->getType()->isIntOrIntVectorTy(1)) {
 
-      if (CmpPred == ICmpInst::ICMP_EQ) {
+      if (CmpPred == ICmpInst::ICMP_EQ)
         return replaceInstUsesWith(I,
                                    Builder.CreateAnd(A, Builder.CreateNot(B)));
-      }
-      if (CmpPred == ICmpInst::ICMP_NE) {
+
+      if (CmpPred == ICmpInst::ICMP_NE)
         return replaceInstUsesWith(I,
                                    Builder.CreateOr(Builder.CreateNot(A), B));
-      }
     }
   }
 
diff --git a/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll b/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
index 18b6ad25e8bd7..0f3e8b42014d8 100644
--- a/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
@@ -30,32 +30,33 @@ define i1 @icmp_ne(i1 %x, i1 %y) {
   ret i1 %result
 }
 
-define i1 @icmp_eq_comm(i1 %x, i1 %y) {
-; CHECK-LABEL: define i1 @icmp_eq_comm(
-; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]]) {
-; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[Y]], true
-; CHECK-NEXT:    [[RESULT:%.*]] = and i1 [[X]], [[TMP1]]
-; CHECK-NEXT:    ret i1 [[RESULT]]
+; Basic test cases with vector types
+define <4 x i1> @icmp_eq_vec(<4 x i1> %x, <4 x i1> %y) {
+; CHECK-LABEL: define <4 x i1> @icmp_eq_vec(
+; CHECK-SAME: <4 x i1> [[X:%.*]], <4 x i1> [[Y:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = xor <4 x i1> [[Y]], splat (i1 true)
+; CHECK-NEXT:    [[RESULT:%.*]] = and <4 x i1> [[X]], [[TMP1]]
+; CHECK-NEXT:    ret <4 x i1> [[RESULT]]
 ;
-  %x.ext = zext i1 %x to i32
-  %y.ext = zext i1 %y to i32
-  %shl = shl i32 %x.ext, %y.ext
-  %result = icmp eq i32 1, %shl
-  ret i1 %result
+  %x.ext = zext <4 x i1> %x to <4 x i32>
+  %y.ext = zext <4 x i1> %y to <4 x i32>
+  %shl = shl <4 x i32> %x.ext, %y.ext
+  %result = icmp eq <4 x i32> %shl, <i32 1, i32 1, i32 1, i32 1>
+  ret <4 x i1> %result
 }
 
-define i1 @icmp_ne_comm(i1 %x, i1 %y) {
-; CHECK-LABEL: define i1 @icmp_ne_comm(
-; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]]) {
-; CHECK-NEXT:    [[TMP1:%.*]] = xor i1 [[X]], true
-; CHECK-NEXT:    [[RESULT:%.*]] = or i1 [[Y]], [[TMP1]]
-; CHECK-NEXT:    ret i1 [[RESULT]]
+define <4 x i1> @icmp_ne_vec(<4 x i1> %x, <4 x i1> %y) {
+; CHECK-LABEL: define <4 x i1> @icmp_ne_vec(
+; CHECK-SAME: <4 x i1> [[X:%.*]], <4 x i1> [[Y:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = xor <4 x i1> [[X]], splat (i1 true)
+; CHECK-NEXT:    [[RESULT:%.*]] = or <4 x i1> [[Y]], [[TMP1]]
+; CHECK-NEXT:    ret <4 x i1> [[RESULT]]
 ;
-  %x.ext = zext i1 %x to i32
-  %y.ext = zext i1 %y to i32
-  %shl = shl i32 %x.ext, %y.ext
-  %result = icmp ne i32 1, %shl
-  ret i1 %result
+  %x.ext = zext <4 x i1> %x to <4 x i32>
+  %y.ext = zext <4 x i1> %y to <4 x i32>
+  %shl = shl <4 x i32> %x.ext, %y.ext
+  %result = icmp ne <4 x i32> %shl, <i32 1, i32 1, i32 1, i32 1>
+  ret <4 x i1> %result
 }
 
 ; Different zext target bit width
@@ -87,9 +88,37 @@ define i1 @icmp_ne_64(i1 %x, i1 %y) {
   ret i1 %result
 }
 
+define <4 x i1> @icmp_eq_vec_64(<4 x i1> %x, <4 x i1> %y) {
+; CHECK-LABEL: define <4 x i1> @icmp_eq_vec_64(
+; CHECK-SAME: <4 x i1> [[X:%.*]], <4 x i1> [[Y:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = xor <4 x i1> [[Y]], splat (i1 true)
+; CHECK-NEXT:    [[RESULT:%.*]] = and <4 x i1> [[X]], [[TMP1]]
+; CHECK-NEXT:    ret <4 x i1> [[RESULT]]
+;
+  %x.ext = zext <4 x i1> %x to <4 x i64>
+  %y.ext = zext <4 x i1> %y to <4 x i64>
+  %shl = shl <4 x i64> %x.ext, %y.ext
+  %result = icmp eq <4 x i64> %shl, <i64 1, i64 1, i64 1, i64 1>
+  ret <4 x i1> %result
+}
+
+define <4 x i1> @icmp_ne_vec_64(<4 x i1> %x, <4 x i1> %y) {
+; CHECK-LABEL: define <4 x i1> @icmp_ne_vec_64(
+; CHECK-SAME: <4 x i1> [[X:%.*]], <4 x i1> [[Y:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = xor <4 x i1> [[X]], splat (i1 true)
+; CHECK-NEXT:    [[RESULT:%.*]] = or <4 x i1> [[Y]], [[TMP1]]
+; CHECK-NEXT:    ret <4 x i1> [[RESULT]]
+;
+  %x.ext = zext <4 x i1> %x to <4 x i64>
+  %y.ext = zext <4 x i1> %y to <4 x i64>
+  %shl = shl <4 x i64> %x.ext, %y.ext
+  %result = icmp ne <4 x i64> %shl, <i64 1, i64 1, i64 1, i64 1>
+  ret <4 x i1> %result
+}
+
 ; Negative test: Comparing against 3, not 1
-define i1 @icmp_eq_zero_negative(i1 %x, i1 %y) {
-; CHECK-LABEL: define i1 @icmp_eq_zero_negative(
+define i1 @icmp_eq_non_one_negative(i1 %x, i1 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_non_one_negative(
 ; CHECK-SAME: i1 [[X:%.*]], i1 [[Y:%.*]]) {
 ; CHECK-NEXT:    [[X_EXT:%.*]] = zext i1 [[X]] to i32
 ; CHECK-NEXT:    [[Y_EXT:%.*]] = zext i1 [[Y]] to i32
@@ -104,6 +133,22 @@ define i1 @icmp_eq_zero_negative(i1 %x, i1 %y) {
   ret i1 %result
 }
 
+define <4 x i1> @icmp_eq_non_one_negative_vec(<4 x i1> %x, <4 x i1> %y) {
+; CHECK-LABEL: define <4 x i1> @icmp_eq_non_one_negative_vec(
+; CHECK-SAME: <4 x i1> [[X:%.*]], <4 x i1> [[Y:%.*]]) {
+; CHECK-NEXT:    [[X_EXT:%.*]] = zext <4 x i1> [[X]] to <4 x i64>
+; CHECK-NEXT:    [[Y_EXT:%.*]] = zext <4 x i1> [[Y]] to <4 x i64>
+; CHECK-NEXT:    [[SHL:%.*]] = shl nuw nsw <4 x i64> [[X_EXT]], [[Y_EXT]]
+; CHECK-NEXT:    [[RESULT:%.*]] = icmp eq <4 x i64> [[SHL]], <i64 3, i64 3, i64 1, i64 1>
+; CHECK-NEXT:    ret <4 x i1> [[RESULT]]
+;
+  %x.ext = zext <4 x i1> %x to <4 x i64>
+  %y.ext = zext <4 x i1> %y to <4 x i64>
+  %shl = shl <4 x i64> %x.ext, %y.ext
+  %result = icmp eq <4 x i64> %shl, <i64 3, i64 3, i64 1, i64 1>
+  ret <4 x i1> %result
+}
+
 ; Negative test: Non-i1 source type
 define i1 @icmp_eq_non_i1_negative(i8 %x, i8 %y) {
 ; CHECK-LABEL: define i1 @icmp_eq_non_i1_negative(
@@ -120,3 +165,19 @@ define i1 @icmp_eq_non_i1_negative(i8 %x, i8 %y) {
   %result = icmp eq i32 %shl, 1
   ret i1 %result
 }
+
+define <4 x i1> @icmp_eq_non_i1_negative_vec(<4 x i8> %x, <4 x i8> %y) {
+; CHECK-LABEL: define <4 x i1> @icmp_eq_non_i1_negative_vec(
+; CHECK-SAME: <4 x i8> [[X:%.*]], <4 x i8> [[Y:%.*]]) {
+; CHECK-NEXT:    [[X_EXT:%.*]] = zext <4 x i8> [[X]] to <4 x i64>
+; CHECK-NEXT:    [[Y_EXT:%.*]] = zext nneg <4 x i8> [[Y]] to <4 x i64>
+; CHECK-NEXT:    [[SHL:%.*]] = shl <4 x i64> [[X_EXT]], [[Y_EXT]]
+; CHECK-NEXT:    [[RESULT:%.*]] = icmp eq <4 x i64> [[SHL]], splat (i64 1)
+; CHECK-NEXT:    ret <4 x i1> [[RESULT]]
+;
+  %x.ext = zext <4 x i8> %x to <4 x i64>
+  %y.ext = zext <4 x i8> %y to <4 x i64>
+  %shl = shl <4 x i64> %x.ext, %y.ext
+  %result = icmp eq <4 x i64> %shl, <i64 1, i64 1, i64 1, i64 1>
+  ret <4 x i1> %result
+}



More information about the llvm-commits mailing list