[llvm] [InstCombine] Optimise the expression `(C & A) | (select (C ^ true), B, false) => (C & A) | (!C & B)` with `FoldOrOfAndsWithSelectToLogical` (PR #178438)

Rajveer Singh Bharadwaj via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 28 06:48:22 PST 2026


https://github.com/Rajveer100 created https://github.com/llvm/llvm-project/pull/178438

Resolves #174937

This simplification will help instcombine further optimise to `select C, A, B` in the next iteration.

>From 9113f870e5bb42ba0bb70cfc982d93688d9c8f5a Mon Sep 17 00:00:00 2001
From: Rajveer <rajveer.developer at icloud.com>
Date: Wed, 28 Jan 2026 19:53:19 +0530
Subject: [PATCH] [InstCombine] Optimise the expression `(C & A) | (select (C ^
 true), B, false) => (C & A) | (!C & B)` with
 `FoldOrOfAndsWithSelectToLogical`

Resolves #174937

This simplification will help instcombine further optimise to `select C, A, B` in the next iteration.
---
 .../InstCombine/InstCombineAndOrXor.cpp       | 80 +++++++++++++++++++
 .../Transforms/InstCombine/select-and-or.ll   | 65 ++++++++++++++-
 2 files changed, 144 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index b4961105c72c2..a3ce846f102fb 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -4023,6 +4023,72 @@ static Value *FoldOrOfSelectSmaxToAbs(BinaryOperator &I,
   return nullptr;
 }
 
+static Value *FoldOrOfAndsWithSelectToLogical(BinaryOperator &I,
+                                              InstCombiner::BuilderTy &Builder,
+                                              Value *Op0, Value *Op1) {
+  Value *C, *A;
+  if (match(Op0, m_c_LogicalAnd(m_Value(C), m_Value(A)))) {
+    Value *Cond, *B, *False;
+    // (C & A) | (select (C ^ true), B, false) => (C & A) | (!C & B)
+    if (match(Op1, m_Select(m_Value(Cond), m_Value(B), m_Value(False))) &&
+        match(Cond, m_Xor(m_Specific(C), m_One())) && B != A) {
+      if (match(False, m_Zero())) {
+        Value *NotC = Builder.CreateNot(C);
+        Value *SelectToAnd = Builder.CreateAnd(NotC, B);
+        return SelectToAnd;
+      }
+    }
+
+    // (A & C) | (select (A ^ true), B, false) => (A & C) | (!A & B)
+    if (match(Op1, m_Select(m_Value(Cond), m_Value(B), m_Value(False))) &&
+        match(Cond, m_Xor(m_Specific(A), m_One())) && B != C) {
+      if (match(False, m_Zero())) {
+        Value *NotA = Builder.CreateNot(A);
+        Value *SelectToAnd = Builder.CreateAnd(NotA, B);
+        return SelectToAnd;
+      }
+    }
+
+    // (C & A) | (select (C ^ true), false, B) => (C & A) | (C & B)
+    if (match(Op1, m_Select(m_Value(Cond), m_Value(False), m_Value(B))) &&
+        match(Cond, m_Xor(m_Specific(C), m_One())) && B != A) {
+      if (match(False, m_Zero())) {
+        Value *SelectToAnd = Builder.CreateAnd(C, B);
+        return SelectToAnd;
+      }
+    }
+
+    // (C & A) | (select C, B, false) => (C & A) | (C & B)
+    if (match(Op1, m_Select(m_Value(Cond), m_Value(B), m_Value(False))) &&
+        match(Cond, m_Specific(C)) && B != A) {
+      if (match(False, m_Zero())) {
+        Value *SelectToAnd = Builder.CreateAnd(C, B);
+        return SelectToAnd;
+      }
+    }
+
+    // (A & C) | (select (A ^ true), false, B) => (A & C) | (A & B)
+    if (match(Op1, m_Select(m_Value(Cond), m_Value(False), m_Value(B))) &&
+        match(Cond, m_Xor(m_Specific(A), m_One())) && B != C) {
+      if (match(False, m_Zero())) {
+        Value *SelectToAnd = Builder.CreateAnd(A, B);
+        return SelectToAnd;
+      }
+    }
+
+    // (A & C) | (select A, B, false) => (A & C) | (A & B)
+    if (match(Op1, m_Select(m_Value(Cond), m_Value(B), m_Value(False))) &&
+        match(Cond, m_Specific(A)) && B != C) {
+      if (match(False, m_Zero())) {
+        Value *SelectToAnd = Builder.CreateAnd(A, B);
+        return SelectToAnd;
+      }
+    }
+  }
+
+  return nullptr;
+}
+
 // FIXME: We use commutative matchers (m_c_*) for some, but not all, matches
 // here. We should standardize that construct where it is needed or choose some
 // other way to ensure that commutated variants of patterns are not missed.
@@ -4136,6 +4202,20 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
     return BinaryOperator::CreateMul(X, IncrementY);
   }
 
+  // (C & A) | (select (C ^ true), B, false) => (C & A) | (!C & B)
+  // This later gets further optimised to => select C, A, B
+  //
+  // Similarly:
+  // (C & A) | (select (C ^ true), false, B) => (C & A) | (C & B)
+  //   => select C, (A & B), false
+  if (auto *V = FoldOrOfAndsWithSelectToLogical(I, Builder, Op0, Op1)) {
+    return replaceOperand(I, 1, V);
+  }
+
+  if (auto *V = FoldOrOfAndsWithSelectToLogical(I, Builder, Op1, Op0)) {
+    return replaceOperand(I, 0, V);
+  }
+
   // (A & C) | (B & D)
   Value *A, *B, *C, *D;
   if (match(Op0, m_And(m_Value(A), m_Value(C))) &&
diff --git a/llvm/test/Transforms/InstCombine/select-and-or.ll b/llvm/test/Transforms/InstCombine/select-and-or.ll
index 0b8eda43beb18..0cd07cfde2c61 100644
--- a/llvm/test/Transforms/InstCombine/select-and-or.ll
+++ b/llvm/test/Transforms/InstCombine/select-and-or.ll
@@ -789,6 +789,68 @@ define i1 @or_and2_commuted(i1 %a, i1 %b, i1 %c) {
   ret i1 %r
 }
 
+define i1 @fold_or_of_ands_with_select_to_logical1(i1 %a, i1 %b, i1 %c){
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical1(
+; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[C:%.*]], i1 [[A:%.*]], i1 [[B:%.*]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = and i1 %c, %a
+  %and2 = select i1 %not, i1 %b, i1 false
+  %or1 = or i1 %and1, %and2
+  ret i1 %or1
+}
+
+define i1 @fold_or_of_ands_with_select_to_logical2(i1 %a, i1 %b, i1 %c){
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical2(
+; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[C:%.*]], i1 [[A:%.*]], i1 [[B:%.*]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = and i1 %a, %c
+  %and2 = select i1 %not, i1 %b, i1 false
+  %or1 = or i1 %and1, %and2
+  ret i1 %or1
+}
+
+define i1 @fold_or_of_ands_with_select_to_logical3(i1 %a, i1 %b, i1 %c){
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical3(
+; CHECK-NEXT:    [[TMP1:%.*]] = or i1 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[OR1:%.*]] = and i1 [[C:%.*]], [[TMP1]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = and i1 %c, %a
+  %and2 = select i1 %not, i1 false, i1 %b
+  %or1 = or i1 %and1, %and2
+  ret i1 %or1
+}
+
+define i1 @fold_or_of_ands_with_select_to_logical4(i1 %a, i1 %b, i1 %c){
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical4(
+; CHECK-NEXT:    [[AND11:%.*]] = or i1 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[OR1:%.*]] = and i1 [[AND11]], [[C:%.*]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = and i1 %a, %c
+  %and2 = select i1 %not, i1 false, i1 %b
+  %or1 = or i1 %and1, %and2
+  ret i1 %or1
+}
+
+define i1 @fold_or_of_ands_with_select_to_logical5(i1 %a, i1 %b, i1 %c){
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical5(
+; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[C:%.*]], i1 [[A:%.*]], i1 [[B:%.*]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = and i1 %c, %a
+  %and2 = select i1 %not, i1 %b, i1 false
+  %or1 = or i1 %and2, %and1
+  ret i1 %or1
+}
+
 define i1 @or_and1_multiuse(i1 %a, i1 %b, i1 %c) {
 ; CHECK-LABEL: @or_and1_multiuse(
 ; CHECK-NEXT:    [[NOTB:%.*]] = xor i1 [[B:%.*]], true
@@ -1356,7 +1418,8 @@ define i8 @test_logical_commuted_and_ne_a_b(i1 %other_cond, i8 %a, i8 %b)  {
 !0 = !{!"function_entry_count", i64 1000}
 !1 = !{!"branch_weights", i32 2, i32 3}
 ;.
-; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
+; CHECK: attributes #[[ATTR1:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
 ;.
 ; CHECK: [[META0:![0-9]+]] = !{!"function_entry_count", i64 1000}
 ; CHECK: [[PROF1]] = !{!"branch_weights", i32 3, i32 2}



More information about the llvm-commits mailing list