[llvm] [InstCombine] Fold shift of boolean zext to logic sequence (PR #180596)

Gergo Stomfai via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 10 07:12:23 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/5] 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/5] 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/5] 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
+}

>From a89a8d5c13ee5b649897243fc2b5bc4c85393b5f Mon Sep 17 00:00:00 2001
From: stomfaig <stomfaig at gmail.com>
Date: Tue, 10 Feb 2026 15:03:15 +0000
Subject: [PATCH 4/5] add one-use check

---
 .../InstCombine/InstCombineCompares.cpp       |  9 +++++--
 .../InstCombine/icmp-shl-zext-zext-1.ll       | 24 +++++++++++++++++++
 2 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index c509d5f894fab..721ab005efe19 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -7900,8 +7900,13 @@ Instruction *InstCombinerImpl::visitICmpInst(ICmpInst &I) {
     Value *A, *B;
     CmpPredicate CmpPred;
 
-    if (match(&I, m_ICmp(CmpPred, m_Shl(m_ZExt(m_Value(A)), m_ZExt(m_Value(B))),
-                         m_One())) &&
+    // If shl is one-use, we eliminate shl and icmp at least,
+    // and introduce a xor and an and, so we expect neutral
+    // to positive benefit.
+    if (match(&I,
+              m_ICmp(CmpPred,
+                     m_OneUse(m_Shl(m_ZExt(m_Value(A)), m_ZExt(m_Value(B)))),
+                     m_One())) &&
         A->getType()->isIntOrIntVectorTy(1) &&
         B->getType()->isIntOrIntVectorTy(1)) {
 
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 0f3e8b42014d8..9eb039cae5f99 100644
--- a/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
@@ -181,3 +181,27 @@ define <4 x i1> @icmp_eq_non_i1_negative_vec(<4 x i8> %x, <4 x i8> %y) {
   %result = icmp eq <4 x i64> %shl, <i64 1, i64 1, i64 1, i64 1>
   ret <4 x i1> %result
 }
+
+; Negative test: should only rewrite pattern if shl
+; has a single use.
+define <2 x i1> @icmp_eq_not_one_use_shl_neg(i1 %x, i1 %y) {
+; CHECK-LABEL: define <2 x i1> @icmp_eq_not_one_use_shl_neg(
+; 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]], 1
+; CHECK-NEXT:    [[SECOND_USE:%.*]] = icmp eq i32 [[SHL]], 3
+; CHECK-NEXT:    [[VEC:%.*]] = insertelement <2 x i1> poison, i1 [[RESULT]], i64 0
+; CHECK-NEXT:    [[VEC2:%.*]] = insertelement <2 x i1> [[VEC]], i1 [[SECOND_USE]], i64 1
+; CHECK-NEXT:    ret <2 x i1> [[VEC2]]
+;
+  %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
+  %second_use = icmp eq i32 %shl, 3
+  %vec = insertelement <2 x i1> undef, i1 %result, i32 0
+  %vec2 = insertelement <2 x i1> %vec, i1 %second_use, i32 1
+  ret <2 x i1> %vec2
+}

>From 90f8341dce11ae1b5e0f477deaf2d34af30b6376 Mon Sep 17 00:00:00 2001
From: stomfaig <stomfaig at gmail.com>
Date: Tue, 10 Feb 2026 15:11:15 +0000
Subject: [PATCH 5/5] remove undef

---
 .../InstCombine/icmp-shl-zext-zext-1.ll        | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 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 9eb039cae5f99..49ea17f352c5a 100644
--- a/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-shl-zext-zext-1.ll
@@ -182,26 +182,24 @@ define <4 x i1> @icmp_eq_non_i1_negative_vec(<4 x i8> %x, <4 x i8> %y) {
   ret <4 x i1> %result
 }
 
+declare void @use(i32)
+
 ; Negative test: should only rewrite pattern if shl
 ; has a single use.
-define <2 x i1> @icmp_eq_not_one_use_shl_neg(i1 %x, i1 %y) {
-; CHECK-LABEL: define <2 x i1> @icmp_eq_not_one_use_shl_neg(
+define i1 @icmp_eq_not_one_use_shl_neg(i1 %x, i1 %y) {
+; CHECK-LABEL: define i1 @icmp_eq_not_one_use_shl_neg(
 ; 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]], 1
-; CHECK-NEXT:    [[SECOND_USE:%.*]] = icmp eq i32 [[SHL]], 3
-; CHECK-NEXT:    [[VEC:%.*]] = insertelement <2 x i1> poison, i1 [[RESULT]], i64 0
-; CHECK-NEXT:    [[VEC2:%.*]] = insertelement <2 x i1> [[VEC]], i1 [[SECOND_USE]], i64 1
-; CHECK-NEXT:    ret <2 x i1> [[VEC2]]
+; CHECK-NEXT:    call void @use(i32 [[SHL]])
+; 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
-  %second_use = icmp eq i32 %shl, 3
-  %vec = insertelement <2 x i1> undef, i1 %result, i32 0
-  %vec2 = insertelement <2 x i1> %vec, i1 %second_use, i32 1
-  ret <2 x i1> %vec2
+  call void @use(i32 %shl) ; second use for %shl
+  ret i1 %result
 }



More information about the llvm-commits mailing list