[llvm] [InstCombine] select (trunc Y), 0, (X & 1) -> (X & 1) & ~Y (PR #222110)

via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 9 16:52:23 PDT 2026


https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/222110

>From da2484a89f03665d27f2a5e074a505e3fea17bbc Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Tue, 8 Sep 2026 14:29:03 -0400
Subject: [PATCH 1/2] Pre-commit test (NFC)

---
 .../Transforms/InstCombine/and-not-select.ll  | 76 +++++++++++++++++++
 1 file changed, 76 insertions(+)
 create mode 100644 llvm/test/Transforms/InstCombine/and-not-select.ll

diff --git a/llvm/test/Transforms/InstCombine/and-not-select.ll b/llvm/test/Transforms/InstCombine/and-not-select.ll
new file mode 100644
index 0000000000000..5c92a3e035133
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/and-not-select.ll
@@ -0,0 +1,76 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; select (trunc b), 0, (a & 1) -> (a & 1) & ~b
+define i32 @test_select_and_not(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_select_and_not(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i32 [[B:%.*]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[A:%.*]], 1
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TRUNC]], i32 0, i32 [[AND]]
+; CHECK-NEXT:    ret i32 [[SEL]]
+;
+  %trunc = trunc i32 %b to i1
+  %and = and i32 %a, 1
+  %sel = select i1 %trunc, i32 0, i32 %and
+  ret i32 %sel
+}
+
+; Should not transform if mask is not 1 (for now)
+define i32 @test_select_and_not_mask2(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_select_and_not_mask2(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i32 [[B:%.*]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[A:%.*]], 2
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TRUNC]], i32 0, i32 [[AND]]
+; CHECK-NEXT:    ret i32 [[SEL]]
+;
+  %trunc = trunc i32 %b to i1
+  %and = and i32 %a, 2
+  %sel = select i1 %trunc, i32 0, i32 %and
+  ret i32 %sel
+}
+
+; Should not transform if trunc has multiple uses (would increase instruction count)
+define i32 @test_select_and_not_multiuse_trunc(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_select_and_not_multiuse_trunc(
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[A:%.*]], 1
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i32 [[B:%.*]], -1
+; CHECK-NEXT:    [[SEL:%.*]] = and i32 [[AND]], [[TMP1]]
+; CHECK-NEXT:    [[ZEXT:%.*]] = and i32 [[B]], 1
+; CHECK-NEXT:    [[ADD:%.*]] = or i32 [[SEL]], [[ZEXT]]
+; CHECK-NEXT:    ret i32 [[ADD]]
+;
+  %trunc = trunc i32 %b to i1
+  %and = and i32 %a, 1
+  %sel = select i1 %trunc, i32 0, i32 %and
+  %zext = zext i1 %trunc to i32
+  %add = add i32 %sel, %zext
+  ret i32 %add
+}
+
+; Inverse: select (trunc b), (a & 1), 0 -> (a & 1) & b
+define i32 @test_select_and(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_select_and(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i32 [[B:%.*]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[A:%.*]], 1
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TRUNC]], i32 [[AND]], i32 0
+; CHECK-NEXT:    ret i32 [[SEL]]
+;
+  %trunc = trunc i32 %b to i1
+  %and = and i32 %a, 1
+  %sel = select i1 %trunc, i32 %and, i32 0
+  ret i32 %sel
+}
+
+; Inverse: should not transform if mask is not 1
+define i32 @test_select_and_mask2(i32 %a, i32 %b) {
+; CHECK-LABEL: @test_select_and_mask2(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i32 [[B:%.*]] to i1
+; CHECK-NEXT:    [[AND:%.*]] = and i32 [[A:%.*]], 2
+; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TRUNC]], i32 [[AND]], i32 0
+; CHECK-NEXT:    ret i32 [[SEL]]
+;
+  %trunc = trunc i32 %b to i1
+  %and = and i32 %a, 2
+  %sel = select i1 %trunc, i32 %and, i32 0
+  ret i32 %sel
+}

>From 93f234d36e6dfd363eba6d2be230a940ebadd4e4 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Wed, 9 Sep 2026 19:40:21 -0400
Subject: [PATCH 2/2] [InstCombine] select (trunc Y), 0, (X & 1) -> (X & 1) &
 ~Y

Fixes https://github.com/llvm/llvm-project/issues/144282
---
 .../Transforms/InstCombine/InstCombineSelect.cpp  | 15 +++++++++++++++
 .../test/Transforms/InstCombine/and-not-select.ll |  7 +++----
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 4f0c21831cb4c..6e05de8823d77 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -4605,6 +4605,21 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
   if (Instruction *I = canonicalizeScalarSelectOfVecs(SI, *this))
     return I;
 
+  Value *X, *Y;
+  // select (trunc Y), (X & 1), 0 -> (X & 1) & Y
+  if (match(CondVal, m_Trunc(m_Value(Y))) && match(FalseVal, m_Zero()) &&
+      match(TrueVal, m_And(m_Value(X), m_One())) && Y->getType() == SelType) {
+    return BinaryOperator::CreateAnd(TrueVal, Y);
+  }
+
+  // select (trunc Y), 0, (X & 1) -> (X & 1) & ~Y
+  if (match(CondVal, m_OneUse(m_Trunc(m_Value(Y)))) &&
+      match(TrueVal, m_Zero()) && match(FalseVal, m_And(m_Value(X), m_One())) &&
+      Y->getType() == SelType) {
+    Value *NotY = Builder.CreateNot(Y);
+    return BinaryOperator::CreateAnd(FalseVal, NotY);
+  }
+
   // Fold: select (icmp ult X, 2), X, ctpop(X)  -->  ctpop(X)
   // ctpop(0)==0 and ctpop(1)==1, so the guard is always redundant.
   if (match(FalseVal, m_Ctpop(m_Specific(TrueVal))) &&
diff --git a/llvm/test/Transforms/InstCombine/and-not-select.ll b/llvm/test/Transforms/InstCombine/and-not-select.ll
index 5c92a3e035133..2330e23bf5c6d 100644
--- a/llvm/test/Transforms/InstCombine/and-not-select.ll
+++ b/llvm/test/Transforms/InstCombine/and-not-select.ll
@@ -4,9 +4,9 @@
 ; select (trunc b), 0, (a & 1) -> (a & 1) & ~b
 define i32 @test_select_and_not(i32 %a, i32 %b) {
 ; CHECK-LABEL: @test_select_and_not(
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i32 [[B:%.*]] to i1
 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TRUNC]], i32 0, i32 [[AND]]
+; CHECK-NEXT:    [[TMP1:%.*]] = xor i32 [[B:%.*]], -1
+; CHECK-NEXT:    [[SEL:%.*]] = and i32 [[AND]], [[TMP1]]
 ; CHECK-NEXT:    ret i32 [[SEL]]
 ;
   %trunc = trunc i32 %b to i1
@@ -50,9 +50,8 @@ define i32 @test_select_and_not_multiuse_trunc(i32 %a, i32 %b) {
 ; Inverse: select (trunc b), (a & 1), 0 -> (a & 1) & b
 define i32 @test_select_and(i32 %a, i32 %b) {
 ; CHECK-LABEL: @test_select_and(
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i32 [[B:%.*]] to i1
 ; CHECK-NEXT:    [[AND:%.*]] = and i32 [[A:%.*]], 1
-; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[TRUNC]], i32 [[AND]], i32 0
+; CHECK-NEXT:    [[SEL:%.*]] = and i32 [[AND]], [[B:%.*]]
 ; CHECK-NEXT:    ret i32 [[SEL]]
 ;
   %trunc = trunc i32 %b to i1



More information about the llvm-commits mailing list