[llvm] [InstCombine] Fold select of and/or subset identity (PR #207925)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 7 01:28:48 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Jaeuk Lee (skku970412)
<details>
<summary>Changes</summary>
## Summary
Fold a select pattern where the condition proves that one `or` operand is
already contained in the other selected value:
```llvm
select (icmp ne (and A, B), B), (or A, B), A
select (icmp eq (and A, B), B), A, (or A, B)
```
Both forms are equivalent to:
```llvm
or A, B
```
When `(A & B) == B`, every set bit in `B` is already present in `A`, so
`A | B == A`. Otherwise the select already chooses `A | B`.
The implementation is intentionally narrow: it matches the direct `and`/`or`
shape, handles commuted `and`/`or` and swapped compare operands, and only runs
for integer scalar/vector selects.
Fixes #<!-- -->207909.
## Tests
Added InstCombine coverage for:
- the original `icmp ne` spelling
- the inverted `icmp eq` spelling
- commuted `and` / `or`
- swapped `icmp` operands
- the equivalent form comparing against the other `and` operand
- vector integer selects
- negative cases where the compare operand or select arm does not prove the
identity
Local verification:
```bash
ninja -C ../build -j20 opt
llvm/utils/update_test_checks.py --opt-binary ../build/bin/opt llvm/test/Transforms/InstCombine/select.ll
../build/bin/llvm-lit -sv llvm/test/Transforms/InstCombine/select.ll
../build/bin/llvm-lit -sv llvm/test/Transforms/InstCombine
git diff --check
```
## AI Tool Disclosure
Assisted-by: OpenAI Codex
Codex was used for code navigation, testcase exploration, implementation
drafting, and local verification command execution. I manually reviewed the
implementation, tests, and generated code before submission.
---
Full diff: https://github.com/llvm/llvm-project/pull/207925.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp (+46)
- (modified) llvm/test/Transforms/InstCombine/select.ll (+110)
``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index f107c15304d9f..1f7cbfd695985 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -141,6 +141,48 @@ static Instruction *foldSelectBinOpIdentity(SelectInst &Sel,
return IC.replaceOperand(Sel, IsEq ? 1 : 2, FoldedVal);
}
+/// select (icmp ne (and A, B), B), (or A, B), A -> or A, B
+/// select (icmp eq (and A, B), B), A, (or A, B) -> or A, B
+static Value *foldSelectAndOrSubset(SelectInst &Sel) {
+ auto *Cmp = dyn_cast<ICmpInst>(Sel.getCondition());
+ if (!Cmp || (Cmp->getPredicate() != ICmpInst::ICMP_EQ &&
+ Cmp->getPredicate() != ICmpInst::ICMP_NE))
+ return nullptr;
+
+ Value *A = nullptr;
+ Value *B = nullptr;
+ auto MatchAndSubset = [&](Value *AndVal, Value *Subset) {
+ Value *X, *Y;
+ if (!match(AndVal, m_And(m_Value(X), m_Value(Y))))
+ return false;
+ if (X == Subset)
+ A = Y;
+ else if (Y == Subset)
+ A = X;
+ else
+ return false;
+ B = Subset;
+ return true;
+ };
+
+ if (!MatchAndSubset(Cmp->getOperand(0), Cmp->getOperand(1)) &&
+ !MatchAndSubset(Cmp->getOperand(1), Cmp->getOperand(0)))
+ return nullptr;
+
+ Value *OrVal = Cmp->getPredicate() == ICmpInst::ICMP_NE ? Sel.getTrueValue()
+ : Sel.getFalseValue();
+ Value *SubsetVal = Cmp->getPredicate() == ICmpInst::ICMP_NE
+ ? Sel.getFalseValue()
+ : Sel.getTrueValue();
+ if (SubsetVal != A)
+ return nullptr;
+
+ if (!match(OrVal, m_c_Or(m_Specific(A), m_Specific(B))))
+ return nullptr;
+
+ return OrVal;
+}
+
/// This folds:
/// select (icmp eq (and X, C1)), TC, FC
/// iff C1 is a power 2 and the difference between TC and FC is a power-of-2.
@@ -4894,6 +4936,10 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
if (auto *NewGep = SelectGepWithBase(FalseGep, TrueVal, true))
return NewGep;
+ if (SelType->isIntOrIntVectorTy())
+ if (Value *V = foldSelectAndOrSubset(SI))
+ return replaceInstUsesWith(SI, V);
+
// See if we can fold the select into one of our operands.
if (SelType->isIntOrIntVectorTy() || SelType->isFPOrFPVectorTy()) {
if (Instruction *FoldI = foldSelectIntoOp(SI, TrueVal, FalseVal))
diff --git a/llvm/test/Transforms/InstCombine/select.ll b/llvm/test/Transforms/InstCombine/select.ll
index a1e84fad9a827..3abf8feda8a94 100644
--- a/llvm/test/Transforms/InstCombine/select.ll
+++ b/llvm/test/Transforms/InstCombine/select.ll
@@ -5800,3 +5800,113 @@ entry:
call void @use_v2i64(<2 x i64> %neg)
ret <2 x i64> %r
}
+
+define i32 @and_ne_rhs_select_or_lhs(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @and_ne_rhs_select_or_lhs(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: [[SEL:%.*]] = or i32 [[A]], [[B]]
+; CHECK-NEXT: ret i32 [[SEL]]
+;
+ %and = and i32 %a, %b
+ %cmp = icmp ne i32 %and, %b
+ %or = or i32 %a, %b
+ %sel = select i1 %cmp, i32 %or, i32 %a
+ ret i32 %sel
+}
+
+define i32 @and_eq_rhs_select_lhs_or(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @and_eq_rhs_select_lhs_or(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: [[SEL:%.*]] = or i32 [[A]], [[B]]
+; CHECK-NEXT: ret i32 [[SEL]]
+;
+ %and = and i32 %a, %b
+ %cmp = icmp eq i32 %and, %b
+ %or = or i32 %a, %b
+ %sel = select i1 %cmp, i32 %a, i32 %or
+ ret i32 %sel
+}
+
+define i32 @commuted_and_or_ne(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @commuted_and_or_ne(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: [[SEL:%.*]] = or i32 [[B]], [[A]]
+; CHECK-NEXT: ret i32 [[SEL]]
+;
+ %and = and i32 %b, %a
+ %cmp = icmp ne i32 %and, %b
+ %or = or i32 %b, %a
+ %sel = select i1 %cmp, i32 %or, i32 %a
+ ret i32 %sel
+}
+
+define i32 @swapped_icmp_and_ne(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @swapped_icmp_and_ne(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[A]], [[B]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %and = and i32 %a, %b
+ %cmp = icmp ne i32 %b, %and
+ %or = or i32 %a, %b
+ %sel = select i1 %cmp, i32 %or, i32 %a
+ ret i32 %sel
+}
+
+define i32 @and_ne_lhs_select_or_rhs(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @and_ne_lhs_select_or_rhs(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[A]], [[B]]
+; CHECK-NEXT: ret i32 [[OR]]
+;
+ %and = and i32 %a, %b
+ %cmp = icmp ne i32 %and, %a
+ %or = or i32 %a, %b
+ %sel = select i1 %cmp, i32 %or, i32 %b
+ ret i32 %sel
+}
+
+define <2 x i32> @and_ne_rhs_select_or_lhs_vec(<2 x i32> %a, <2 x i32> %b) {
+; CHECK-LABEL: define <2 x i32> @and_ne_rhs_select_or_lhs_vec(
+; CHECK-SAME: <2 x i32> [[A:%.*]], <2 x i32> [[B:%.*]]) {
+; CHECK-NEXT: [[SEL:%.*]] = or <2 x i32> [[A]], [[B]]
+; CHECK-NEXT: ret <2 x i32> [[SEL]]
+;
+ %and = and <2 x i32> %a, %b
+ %cmp = icmp ne <2 x i32> %and, %b
+ %or = or <2 x i32> %a, %b
+ %sel = select <2 x i1> %cmp, <2 x i32> %or, <2 x i32> %a
+ ret <2 x i32> %sel
+}
+
+define i32 @no_fold_and_ne_different_rhs(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: define i32 @no_fold_and_ne_different_rhs(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]]) {
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[A]], [[B]]
+; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i32 [[AND]], [[C]]
+; CHECK-NEXT: [[OR:%.*]] = select i1 [[CMP_NOT]], i32 0, i32 [[B]]
+; CHECK-NEXT: [[SEL:%.*]] = or i32 [[A]], [[OR]]
+; CHECK-NEXT: ret i32 [[SEL]]
+;
+ %and = and i32 %a, %b
+ %cmp = icmp ne i32 %and, %c
+ %or = or i32 %a, %b
+ %sel = select i1 %cmp, i32 %or, i32 %a
+ ret i32 %sel
+}
+
+define i32 @no_fold_and_ne_different_falseval(i32 %a, i32 %b, i32 %c) {
+; CHECK-LABEL: define i32 @no_fold_and_ne_different_falseval(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[B:%.*]], i32 [[C:%.*]]) {
+; CHECK-NEXT: [[AND:%.*]] = and i32 [[A]], [[B]]
+; CHECK-NEXT: [[CMP_NOT:%.*]] = icmp eq i32 [[AND]], [[B]]
+; CHECK-NEXT: [[OR:%.*]] = or i32 [[A]], [[B]]
+; CHECK-NEXT: [[SEL:%.*]] = select i1 [[CMP_NOT]], i32 [[C]], i32 [[OR]]
+; CHECK-NEXT: ret i32 [[SEL]]
+;
+ %and = and i32 %a, %b
+ %cmp = icmp ne i32 %and, %b
+ %or = or i32 %a, %b
+ %sel = select i1 %cmp, i32 %or, i32 %c
+ ret i32 %sel
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/207925
More information about the llvm-commits
mailing list