[llvm] [InstCombine] Fold select of and/or subset identity to `or` (PR #209072)

Joel Walker via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 13 17:08:33 PDT 2026


================
@@ -141,6 +141,57 @@ static Instruction *foldSelectBinOpIdentity(SelectInst &Sel,
   return IC.replaceOperand(Sel, IsEq ? 1 : 2, FoldedVal);
 }
 
+/// Fold a select whose condition proves that one 'or' operand is redundant:
+///   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
+/// When (A & B) == B, every set bit of B is already set in A, so A | B == A and
+/// the select yields A; otherwise the select already yields A | B. Either way
+/// the result is A | B.
+///
+/// The matched 'or' may be 'disjoint', a flag the select did not carry, so a
+/// fresh flag-free 'or' is built to avoid introducing poison on the path where
+/// the 'A' arm was selected.
+static Value *foldSelectAndOrSubset(SelectInst &SI,
+                                    InstCombiner::BuilderTy &Builder) {
+  auto *Cmp = dyn_cast<ICmpInst>(SI.getCondition());
+  if (!Cmp)
+    return nullptr;
+  ICmpInst::Predicate Pred = Cmp->getPredicate();
+  if (!ICmpInst::isEquality(Pred))
+    return nullptr;
+
+  // Match '(and A, B)' compared against 'B' (a test that B's bits are a subset
+  // of A's). The 'and' may be on either side of the compare, and its operands
+  // may be commuted.
+  Value *A = nullptr, *B = nullptr;
+  auto MatchSubsetTest = [&](Value *MaybeAnd, Value *Other) {
+    Value *X, *Y;
+    if (!match(MaybeAnd, m_And(m_Value(X), m_Value(Y))))
+      return false;
+    if (X == Other)
+      A = Y;
+    else if (Y == Other)
+      A = X;
+    else
+      return false;
+    B = Other;
+    return true;
+  };
+  if (!MatchSubsetTest(Cmp->getOperand(0), Cmp->getOperand(1)) &&
----------------
Joel-Wwalker wrote:

Applied, with the operand order swapped: `m_c_ICmp(Pred, m_Value(B), m_c_And(m_Value(A), m_Deferred(B)))`.

With the literal form, `m_Deferred(B)` sits outside the commutative `m_c_And`. When the deferred operand fails to match, `m_c_ICmp` retries its own operand order but `m_c_And` is not re-entered with the other binding, so `icmp eq/ne (and B, A), B` is missed. Two existing tests in select.ll stop folding with it: `and_or_subset_commuted` (`and i32 %b, %a` compared with `%b`) and `and_or_subset_other_operand` (`and i32 %a, %b` compared with `%a`). Binding B from the non-and side first lets `m_c_And` find it in either position, and all four operand orders fold.

Also rebased onto current main.

https://github.com/llvm/llvm-project/pull/209072


More information about the llvm-commits mailing list