[llvm] [InstCombine] Optimise the expression `(C & A) | (select (C ^ true), B, false)` with `FoldOrOfAndsWithSelectToLogical` (PR #178438)
Rajveer Singh Bharadwaj via llvm-commits
llvm-commits at lists.llvm.org
Mon Feb 9 04:29:42 PST 2026
https://github.com/Rajveer100 updated https://github.com/llvm/llvm-project/pull/178438
>From c655f7a48f6e113b418d130e7e1b175af45a234a 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)` with `FoldOrOfAndsWithSelectToLogical`
Resolves #174937
This simplification will help instcombine optimise the expression (and similar cases) to `select C, A, B`.
---
.../InstCombine/InstCombineAndOrXor.cpp | 56 ++++++++++++
.../InstCombine/InstCombineInternal.h | 2 +
.../Transforms/InstCombine/select-and-or.ll | 88 ++++++++++++++++++-
3 files changed, 145 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index b4961105c72c2..9b563ea1d737b 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -4023,6 +4023,47 @@ static Value *FoldOrOfSelectSmaxToAbs(BinaryOperator &I,
return nullptr;
}
+Instruction *
+InstCombinerImpl::FoldOrOfAndsWithSelectToLogical(BinaryOperator &I, Value *Op0,
+ Value *Op1) {
+ Value *C, *A, *B;
+ // (C && A) | (select (C ^ true), B, false) => (C && A) | (!C && B)
+ // (C && A) | (select B, (C ^ true), false) => (C && A) | (B && !C)
+ // (A && C) | (select (C ^ true), B, false) => (A && C) | (!C && B)
+ // (A && C) | (select B, (C ^ true), false) => (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 *SelInstOp0 = dyn_cast<SelectInst>(Op0);
+ auto *SelInstOp1 = dyn_cast<SelectInst>(Op1);
+
+ bool MayNeedFreeze = SelInstOp0 && SelInstOp1 &&
+ match(SelInstOp0->getTrueValue(),
+ m_Not(m_Specific(SelInstOp1->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))) && SelInstOp0) {
+ return SelectInst::Create(C, A, B, "", nullptr, SelInstOp0);
+ } else if (match(Op1, m_LogicalAnd(m_Not(m_Value(C2)), m_Value(B2))) &&
+ SelInstOp1) {
+ SelectInst *NewSI =
+ SelectInst::Create(C, A, B, "", nullptr, SelInstOp1);
+ NewSI->swapProfMetadata();
+ return NewSI;
+ } else {
+ return createSelectInstWithUnknownProfile(C, A, B);
+ }
+ }
+ return SelectInst::Create(C, A, B);
+ }
+
+ 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 +4177,21 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
return BinaryOperator::CreateMul(X, IncrementY);
}
+ // (C && A) | (select (C ^ true), B, false) => (C && A) | (!C && B)
+ // This can be further optimised to => select C, A, B
+ //
+ // Note: This is the logical version of a similar transformation handled
+ // in `foldSelectOfBools` except that it's a `select` instead of `or`.
+ if (Op0->hasOneUse() || Op1->hasOneUse()) {
+ if (Instruction *V = FoldOrOfAndsWithSelectToLogical(I, Op0, Op1)) {
+ return V;
+ }
+
+ if (Instruction *V = FoldOrOfAndsWithSelectToLogical(I, Op1, Op0)) {
+ 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..e9ee4a69bc916 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
+++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h
@@ -111,6 +111,8 @@ class LLVM_LIBRARY_VISIBILITY InstCombinerImpl final
Instruction *visitSDiv(BinaryOperator &I);
Instruction *visitFDiv(BinaryOperator &I);
Value *simplifyRangeCheck(ICmpInst *Cmp0, ICmpInst *Cmp1, bool Inverted);
+ Instruction *FoldOrOfAndsWithSelectToLogical(BinaryOperator &I, Value *Op0,
+ Value *Op1);
Instruction *visitAnd(BinaryOperator &I);
Instruction *visitOr(BinaryOperator &I);
bool sinkNotIntoLogicalOp(Instruction &I);
diff --git a/llvm/test/Transforms/InstCombine/select-and-or.ll b/llvm/test/Transforms/InstCombine/select-and-or.ll
index 0b8eda43beb18..0251e19be6d12 100644
--- a/llvm/test/Transforms/InstCombine/select-and-or.ll
+++ b/llvm/test/Transforms/InstCombine/select-and-or.ll
@@ -789,6 +789,90 @@ 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: [[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 = 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 i1 @or_and1_multiuse(i1 %a, i1 %b, i1 %c) {
; CHECK-LABEL: @or_and1_multiuse(
; CHECK-NEXT: [[NOTB:%.*]] = xor i1 [[B:%.*]], true
@@ -1356,8 +1440,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