[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