[llvm] [InstCombine] Optimise the expression `(C && A) || (!C && B)` with `FoldOrOfLogicalAnds` (PR #178438)

Rajveer Singh Bharadwaj via llvm-commits llvm-commits at lists.llvm.org
Sun Feb 15 02:33:25 PST 2026


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

>From 208ad33b759c88913dd7063041b156bc18527325 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) || (!C && B)`
 with `FoldOrOfLogicalAnds`

Resolves #174937

This simplification will help instcombine optimise the expression (and similar cases) to `select C, A, B`.
---
 .../InstCombine/InstCombineAndOrXor.cpp       |  80 ++++++++++++
 .../InstCombine/InstCombineInternal.h         |   1 +
 .../InstCombine/InstCombineSelect.cpp         |  64 +---------
 .../Transforms/InstCombine/select-and-or.ll   | 116 +++++++++++++++++-
 4 files changed, 199 insertions(+), 62 deletions(-)

diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index b4961105c72c2..5188f98e5e98a 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -4023,6 +4023,75 @@ static Value *FoldOrOfSelectSmaxToAbs(BinaryOperator &I,
   return nullptr;
 }
 
+Instruction *InstCombinerImpl::FoldOrOfLogicalAnds(Value *Op0, Value *Op1) {
+  Value *C, *A, *B;
+  // (C && A) || (!C && B)
+  // (C && A) || (B && !C)
+  // (A && C) || (!C && B)
+  // (A && C) || (B && !C) (may require freeze)
+  //
+  // => select C, A, B
+  if (match(Op1, m_c_LogicalAnd(m_Not(m_Value(C)), m_Value(B))) &&
+      match(Op0, m_c_LogicalAnd(m_Specific(C), m_Value(A)))) {
+    auto *SelOp0 = dyn_cast<SelectInst>(Op0);
+    auto *SelOp1 = dyn_cast<SelectInst>(Op1);
+
+    bool MayNeedFreeze = SelOp0 && SelOp1 &&
+                         match(SelOp1->getTrueValue(),
+                               m_Not(m_Specific(SelOp0->getTrueValue())));
+    if (MayNeedFreeze)
+      C = Builder.CreateFreeze(C);
+    if (!ProfcheckDisableMetadataFixes) {
+      Value *C2 = nullptr, *A2 = nullptr, *B2 = nullptr;
+      if (match(Op0, m_LogicalAnd(m_Specific(C), m_Value(A2))) && SelOp0) {
+        return SelectInst::Create(C, A, B, "", nullptr, SelOp0);
+      } else if (match(Op1, m_LogicalAnd(m_Not(m_Value(C2)), m_Value(B2))) &&
+                 SelOp1) {
+        SelectInst *NewSI = SelectInst::Create(C, A, B, "", nullptr, SelOp1);
+        NewSI->swapProfMetadata();
+        return NewSI;
+      } else {
+        return createSelectInstWithUnknownProfile(C, A, B);
+      }
+    }
+    return SelectInst::Create(C, A, B);
+  }
+
+  // (!C && A) || (C && B)
+  // (A && !C) || (C && B)
+  // (!C && A) || (B && C)
+  // (A && !C) || (B && C) (may require freeze)
+  //
+  // => select C, B, A
+  if (match(Op0, m_c_LogicalAnd(m_Not(m_Value(C)), m_Value(A))) &&
+      match(Op1, m_c_LogicalAnd(m_Specific(C), m_Value(B)))) {
+    auto *SelCond = dyn_cast<SelectInst>(Op0);
+    auto *SelFVal = dyn_cast<SelectInst>(Op1);
+    bool MayNeedFreeze = SelCond && SelFVal &&
+                         match(SelCond->getTrueValue(),
+                               m_Not(m_Specific(SelFVal->getTrueValue())));
+    if (MayNeedFreeze)
+      C = Builder.CreateFreeze(C);
+    if (!ProfcheckDisableMetadataFixes) {
+      Value *C2 = nullptr, *A2 = nullptr, *B2 = nullptr;
+      if (match(Op0, m_LogicalAnd(m_Not(m_Value(C2)), m_Value(A2))) &&
+          SelCond) {
+        SelectInst *NewSI = SelectInst::Create(C, B, A, "", nullptr, SelCond);
+        NewSI->swapProfMetadata();
+        return NewSI;
+      } else if (match(Op1, m_LogicalAnd(m_Specific(C), m_Value(B2))) &&
+                 SelFVal) {
+        return SelectInst::Create(C, B, A, "", nullptr, SelFVal);
+      } else {
+        return createSelectInstWithUnknownProfile(C, B, A);
+      }
+    }
+    return SelectInst::Create(C, B, A);
+  }
+
+  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 +4205,17 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
     return BinaryOperator::CreateMul(X, IncrementY);
   }
 
+  // (C && A) || (C && B) => select C, A, B (and similar cases)
+  //
+  // Note: This is the same transformation used in `foldSelectOfBools`,
+  // except that it's an `or` instead of `select`.
+  if (I.getType()->isIntOrIntVectorTy(1) &&
+      (Op0->hasOneUse() || Op1->hasOneUse())) {
+    if (Instruction *V = FoldOrOfLogicalAnds(Op0, Op1)) {
+      return 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/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
index 8c8b300bb2002..c9e4c3818dcc4 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -111,6 +111,7 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
   Instruction *visitSDiv(BinaryOperator &I);
   Instruction *visitFDiv(BinaryOperator &I);
   Value *simplifyRangeCheck(ICmpInst *Cmp0, ICmpInst *Cmp1, bool Inverted);
+  Instruction *FoldOrOfLogicalAnds(Value *Op0, Value *Op1);
   Instruction *visitAnd(BinaryOperator &I);
   Instruction *visitOr(BinaryOperator &I);
   bool sinkNotIntoLogicalOp(Instruction &I);
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 1f415ffa8eed2..2bf0ef41a2363 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -3678,67 +3678,9 @@ Instruction *InstCombinerImpl::foldSelectOfBools(SelectInst &SI) {
   }
 
   if (match(TrueVal, m_One())) {
-    Value *C;
-
-    // (C && A) || (!C && B) --> sel C, A, B
-    // (A && C) || (!C && B) --> sel C, A, B
-    // (C && A) || (B && !C) --> sel C, A, B
-    // (A && C) || (B && !C) --> sel C, A, B (may require freeze)
-    if (match(FalseVal, m_c_LogicalAnd(m_Not(m_Value(C)), m_Value(B))) &&
-        match(CondVal, m_c_LogicalAnd(m_Specific(C), m_Value(A)))) {
-      auto *SelCond = dyn_cast<SelectInst>(CondVal);
-      auto *SelFVal = dyn_cast<SelectInst>(FalseVal);
-      bool MayNeedFreeze = SelCond && SelFVal &&
-                           match(SelFVal->getTrueValue(),
-                                 m_Not(m_Specific(SelCond->getTrueValue())));
-      if (MayNeedFreeze)
-        C = Builder.CreateFreeze(C);
-      if (!ProfcheckDisableMetadataFixes) {
-        Value *C2 = nullptr, *A2 = nullptr, *B2 = nullptr;
-        if (match(CondVal, m_LogicalAnd(m_Specific(C), m_Value(A2))) &&
-            SelCond) {
-          return SelectInst::Create(C, A, B, "", nullptr, SelCond);
-        } else if (match(FalseVal,
-                         m_LogicalAnd(m_Not(m_Value(C2)), m_Value(B2))) &&
-                   SelFVal) {
-          SelectInst *NewSI = SelectInst::Create(C, A, B, "", nullptr, SelFVal);
-          NewSI->swapProfMetadata();
-          return NewSI;
-        } else {
-          return createSelectInstWithUnknownProfile(C, A, B);
-        }
-      }
-      return SelectInst::Create(C, A, B);
-    }
-
-    // (!C && A) || (C && B) --> sel C, B, A
-    // (A && !C) || (C && B) --> sel C, B, A
-    // (!C && A) || (B && C) --> sel C, B, A
-    // (A && !C) || (B && C) --> sel C, B, A (may require freeze)
-    if (match(CondVal, m_c_LogicalAnd(m_Not(m_Value(C)), m_Value(A))) &&
-        match(FalseVal, m_c_LogicalAnd(m_Specific(C), m_Value(B)))) {
-      auto *SelCond = dyn_cast<SelectInst>(CondVal);
-      auto *SelFVal = dyn_cast<SelectInst>(FalseVal);
-      bool MayNeedFreeze = SelCond && SelFVal &&
-                           match(SelCond->getTrueValue(),
-                                 m_Not(m_Specific(SelFVal->getTrueValue())));
-      if (MayNeedFreeze)
-        C = Builder.CreateFreeze(C);
-      if (!ProfcheckDisableMetadataFixes) {
-        Value *C2 = nullptr, *A2 = nullptr, *B2 = nullptr;
-        if (match(CondVal, m_LogicalAnd(m_Not(m_Value(C2)), m_Value(A2))) &&
-            SelCond) {
-          SelectInst *NewSI = SelectInst::Create(C, B, A, "", nullptr, SelCond);
-          NewSI->swapProfMetadata();
-          return NewSI;
-        } else if (match(FalseVal, m_LogicalAnd(m_Specific(C), m_Value(B2))) &&
-                   SelFVal) {
-          return SelectInst::Create(C, B, A, "", nullptr, SelFVal);
-        } else {
-          return createSelectInstWithUnknownProfile(C, B, A);
-        }
-      }
-      return SelectInst::Create(C, B, A);
+    // (C && A) || (!C && B) --> select C, A, B (and similar cases)
+    if (auto *V = FoldOrOfLogicalAnds(CondVal, FalseVal)) {
+      return V;
     }
   }
 
diff --git a/llvm/test/Transforms/InstCombine/select-and-or.ll b/llvm/test/Transforms/InstCombine/select-and-or.ll
index 0b8eda43beb18..7ae250f04f29e 100644
--- a/llvm/test/Transforms/InstCombine/select-and-or.ll
+++ b/llvm/test/Transforms/InstCombine/select-and-or.ll
@@ -789,6 +789,118 @@ 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) !prof !0 {
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical1(
+; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[C:%.*]], i1 [[A:%.*]], i1 [[B:%.*]], !prof [[PROF1]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = and i1 %c, %a
+  %and2 = select i1 %not, i1 %b, i1 false, !prof !1
+  %or1 = or i1 %and1, %and2
+  ret i1 %or1
+}
+
+define i1 @fold_or_of_ands_with_select_to_logical2(i1 %a, i1 %b, i1 %c) !prof !0 {
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical2(
+; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[C:%.*]], i1 [[A:%.*]], i1 [[B:%.*]], !prof [[PROF2:![0-9]+]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = and i1 %c, %a
+  %and2 = select i1 %b, i1 %not, i1 false, !prof !1
+  %or1 = or i1 %and1, %and2
+  ret i1 %or1
+}
+
+define i1 @fold_or_of_ands_with_select_to_logical3(i1 %a, i1 %b, i1 %c) !prof !0 {
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical3(
+; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[C:%.*]], i1 [[A:%.*]], i1 [[B:%.*]], !prof [[PROF1]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = and i1 %a, %c
+  %and2 = select i1 %not, i1 %b, i1 false, !prof !1
+  %or1 = or i1 %and1, %and2
+  ret i1 %or1
+}
+
+define i1 @fold_or_of_ands_with_select_to_logical4(i1 %a, i1 %b, i1 %c) !prof !0 {
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical4(
+; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[C:%.*]], i1 [[A:%.*]], i1 [[B:%.*]], !prof [[PROF2]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = and i1 %a, %c
+  %and2 = select i1 %b, i1 %not, i1 false, !prof !1
+  %or1 = or i1 %and1, %and2
+  ret i1 %or1
+}
+
+define i1 @fold_or_of_ands_with_select_to_logical5(i1 %a, i1 %b, i1 %c) !prof !0 {
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical5(
+; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[C:%.*]], i1 [[A:%.*]], i1 [[B:%.*]], !prof [[PROF2]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = select i1 %a, i1 %c, i1 false, !prof !1
+  %and2 = and i1 %not, %b
+  %or1 = or i1 %and1, %and2
+  ret i1 %or1
+}
+
+define i1 @fold_or_of_ands_with_select_to_logical6(i1 %a, i1 %b, i1 %c) !prof !0 {
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical6(
+; CHECK-NEXT:    [[TMP1:%.*]] = freeze i1 [[C:%.*]]
+; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[TMP1]], i1 [[A:%.*]], i1 [[B:%.*]], !prof [[PROF2]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = select i1 %a, i1 %c, i1 false, !prof !1
+  %and2 = select i1 %b, i1 %not, i1 false, !prof !1
+  %or1 = or i1 %and1, %and2
+  ret i1 %or1
+}
+
+define i1 @fold_or_of_ands_with_select_to_logical7(i1 %a, i1 %b, i1 %c) !prof !0 {
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical7(
+; CHECK-NEXT:    [[OR1:%.*]] = select i1 [[C:%.*]], i1 [[A:%.*]], i1 [[B:%.*]], !prof [[PROF1]]
+; CHECK-NEXT:    ret i1 [[OR1]]
+;
+  %not = xor i1 %c, true
+  %and1 = and i1 %c, %a
+  %and2 = select i1 %not, i1 %b, i1 false, !prof !1
+  %or1 = or i1 %and2, %and1
+  ret i1 %or1
+}
+
+define i8 @fold_or_of_ands_with_select_to_logical_neg(i8 %a, i8 %b, i8 %c) !prof !0 {
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical_neg(
+; CHECK-NEXT:    [[NOT:%.*]] = xor i8 [[C:%.*]], 1
+; CHECK-NEXT:    [[AND1:%.*]] = and i8 [[C]], [[A:%.*]]
+; CHECK-NEXT:    [[AND2:%.*]] = and i8 [[NOT]], [[B:%.*]]
+; CHECK-NEXT:    [[OR1:%.*]] = or i8 [[AND1]], [[AND2]]
+; CHECK-NEXT:    ret i8 [[OR1]]
+;
+  %not = xor i8 %c, 1
+  %and1 = and i8 %c, %a
+  %and2 = and i8 %not, %b
+  %or1 = or i8 %and1, %and2
+  ret i8 %or1
+}
+
+define <4 x i1> @fold_or_of_ands_with_select_to_logical9(<4 x i1> %a, <4 x i1> %b, <4 x i1> %c) !prof !0 {
+; CHECK-LABEL: @fold_or_of_ands_with_select_to_logical9(
+; CHECK-NEXT:    [[OR1:%.*]] = select <4 x i1> [[C:%.*]], <4 x i1> [[A:%.*]], <4 x i1> [[B:%.*]], !prof [[PROF1]]
+; CHECK-NEXT:    ret <4 x i1> [[OR1]]
+;
+  %not = xor <4 x i1> %c, splat (i1 true)
+  %and1 = and <4 x i1> %c, %a
+  %and2 = select <4 x i1> %not, <4 x i1> %b, <4 x i1> splat (i1 false), !prof !1
+  %or1 = or <4 x i1> %and1, %and2
+  ret <4 x 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,8 +1468,10 @@ 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}
+; CHECK: [[PROF2]] = !{!"unknown", !"instcombine"}
 ;.



More information about the llvm-commits mailing list