[llvm] [InstSimplify] Discard unnecessary `select` when the conditions are `ICmps` with EQ (PR #179183)

Gábor Spaits via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 6 05:06:03 PST 2026


https://github.com/spaits updated https://github.com/llvm/llvm-project/pull/179183

>From ab65b67bd437c4ed319945489bc8aaf2db509aa4 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Wed, 25 Feb 2026 12:54:16 +0100
Subject: [PATCH 1/3] [InstSimplify] Implement `Cond ? V : V` -> `V` when no
 Refinment allowed

Related to #179183 and #177410 .

I am not sure how profitable would this be. But I may be somewhat optimistic, since there is use for this in #177410 and optimizations can already improve code without the cases in #177410. See the tests.

When the select wasn't a poison barrier and returning an arm also isn't a refinment some optimizations can be done with selects in `simplifyWithOpsReplaced`.

The new recursive function was needed because `impliesPoison` can't really give any useful information, when a select is assumed poison and the right side of the implication isn't a user of that select. (Because of select poison semantics.) Also the behaviour of this recursive function could be added to `impliesPoison`, but then it would need extra parameters.

I have some test cases but I see that there is more to add.
Alove2 for the current cases: https://alive2.llvm.org/ce/z/pKwffE
---
 llvm/lib/Analysis/InstructionSimplify.cpp     | 82 ++++++++++++++++
 .../Transforms/InstSimplify/select-icmp.ll    | 95 +++++++++++++++++++
 2 files changed, 177 insertions(+)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 3d5ee74c0e2e8..ceadee5a7705b 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -48,6 +48,7 @@
 #include "llvm/Support/KnownBits.h"
 #include "llvm/Support/KnownFPClass.h"
 #include <algorithm>
+#include <iterator>
 #include <optional>
 using namespace llvm;
 using namespace llvm::PatternMatch;
@@ -4395,6 +4396,50 @@ Value *llvm::simplifyFCmpInst(CmpPredicate Predicate, Value *LHS, Value *RHS,
   return ::simplifyFCmpInst(Predicate, LHS, RHS, FMF, Q, RecursionLimit);
 }
 
+// The value is only poison, if any of these values are poison.
+// It can be useful to decide if replacing a value with another would be
+// refining or introduce more poison.
+//
+// It is especially useful for select instructions. When it comes to selects and
+// impliesPoison, if select is the ValueAssumedToBePoison it is rare that it
+// yields true for any Value, that isn't a user of the select, due to the fact
+// that a select has more complex poison propagation than other instruction.
+static bool areTheseAllTheValuesThatMayCausePoisonInValue(
+    const SmallVector<const Value *, 2> ValuesAssumedPoison, const Value *V,
+    unsigned Depth) {
+  if (any_of(ValuesAssumedPoison, [V](const auto *P) { return P == V; }))
+    return true;
+
+  if (const Instruction *I = dyn_cast<Instruction>(V)) {
+    if (I->hasPoisonGeneratingAnnotations())
+      return false;
+
+    SmallVector<const Value *, 2> ValuesImplyingPoison;
+    copy_if(ValuesAssumedPoison, std::back_inserter(ValuesImplyingPoison),
+            [V](const auto *P) { return impliesPoison(P, V); });
+
+    if (ValuesImplyingPoison.empty())
+      return false;
+
+    const unsigned MaxDepth = 2;
+    if (Depth > MaxDepth)
+      return false;
+
+    return all_of(I->operand_values(),
+                  [&ValuesImplyingPoison, Depth](const auto *Op) {
+                    return areTheseAllTheValuesThatMayCausePoisonInValue(
+                        ValuesImplyingPoison, Op, Depth + 1);
+                  });
+  }
+  return false;
+}
+
+static bool areTheseAllTheValuesThatMayCausePoisonInValue(
+    const SmallVector<const Value *, 2> ValuesAssumedPoison, const Value *V) {
+  return areTheseAllTheValuesThatMayCausePoisonInValue(ValuesAssumedPoison, V,
+                                                       0);
+}
+
 static Value *simplifyWithOpsReplaced(Value *V,
                                       ArrayRef<std::pair<Value *, Value *>> Ops,
                                       const SimplifyQuery &Q,
@@ -4538,6 +4583,43 @@ static Value *simplifyWithOpsReplaced(Value *V,
       if (NewOps.size() == 2 && match(NewOps[1], m_Zero()))
         return NewOps[0];
     }
+
+    if (SelectInst *SI = dyn_cast<SelectInst>(I)) {
+      Value *SimplifiedValue = nullptr;
+      Value *Cond = SI->getCondition();
+
+      // The select couldn't act as a poison barrirer for its old operands and
+      // the new condition can't be more poisonus than the old one.
+      if (impliesPoison(NewOps[0], Cond) &&
+          impliesPoison(SI->getTrueValue(), Cond) &&
+          impliesPoison(SI->getFalseValue(), Cond)) {
+
+        // Cond ? V : V -> V
+        // We know that after replacement both of the operands will be the same.
+        // We have to make sure that no refinment happens. This is done by
+        // checking if the select condition can only be poison if any of the
+        // arms are poison. If the condition could be poison and neither of the
+        // arms would be poison, returning the less poisonus value would be a
+        // refinment.
+        if (NewOps[1] == NewOps[2] &&
+            areTheseAllTheValuesThatMayCausePoisonInValue(
+                {SI->getFalseValue(), SI->getTrueValue()}, Cond))
+          SimplifiedValue = NewOps[1];
+
+        // TODO: Implement more non refining select optimizations here!
+
+        // If we could do any simplification check if it has any poison
+        // generating flags and handle them.
+        if (SimplifiedValue) {
+          if (SI->hasPoisonGeneratingAnnotations()) {
+            if (!DropFlags)
+              return nullptr;
+            DropFlags->push_back(SI);
+          }
+          return SimplifiedValue;
+        }
+      }
+    }
   } else {
     // The simplification queries below may return the original value. Consider:
     //   %div = udiv i32 %arg, %arg2
diff --git a/llvm/test/Transforms/InstSimplify/select-icmp.ll b/llvm/test/Transforms/InstSimplify/select-icmp.ll
index 5ee10bb954766..efe676d9fcd26 100755
--- a/llvm/test/Transforms/InstSimplify/select-icmp.ll
+++ b/llvm/test/Transforms/InstSimplify/select-icmp.ll
@@ -309,3 +309,98 @@ define <2 x ptr> @ptr_eq_replace_vector_constant(<2 x ptr> %a) {
   %sel = select <2 x i1> %cmp, <2 x ptr> %a, <2 x ptr> <ptr inttoptr (i64 42 to ptr), ptr inttoptr (i64 88 to ptr)>
   ret <2 x ptr> %sel
 }
+
+define i32 @selectICmpSelectInArmNotSimplifiedDueToMorePoison(i32 %1, i32 %2, i1 %scond) {
+; CHECK-LABEL: @selectICmpSelectInArmNotSimplifiedDueToMorePoison(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP0:%.*]], [[TMP1:%.*]]
+; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[SCOND:%.*]], i32 [[TMP1]], i32 [[TMP0]]
+; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[TMP3]], i32 [[TMP0]], i32 [[TMP4]]
+; CHECK-NEXT:    ret i32 [[TMP5]]
+;
+  %4 = icmp eq i32 %1, %2
+  %5 = select i1 %scond, i32 %2, i32 %1
+  %6 = select i1 %4, i32 %1, i32 %5
+  ret i32 %6
+}
+
+define i32 @selectICmpSelectInArmSimplifiedDueToSameValues(i32 %1, i32 %2, i1 %scond) {
+; CHECK-LABEL: @selectICmpSelectInArmSimplifiedDueToSameValues(
+; CHECK-NEXT:    [[SAMEVALUES:%.*]] = icmp ule i32 [[TMP0:%.*]], [[TMP1:%.*]]
+; CHECK-NEXT:    [[TMP6:%.*]] = select i1 [[SAMEVALUES]], i32 [[TMP1]], i32 [[TMP0]]
+; CHECK-NEXT:    ret i32 [[TMP6]]
+;
+  %4 = icmp eq i32 %1, %2
+  %sameValues = icmp ule i32 %1, %2
+  %5 = select i1 %sameValues, i32 %2, i32 %1
+  %6 = select i1 %4, i32 %1, i32 %5
+  ret i32 %6
+}
+
+define i32 @selectICmpSelectInArmSimplifiedNoPoisonFlagOnCondUseDefChain(i32 %1, i32 %2, i1 %scond) {
+; CHECK-LABEL: @selectICmpSelectInArmSimplifiedNoPoisonFlagOnCondUseDefChain(
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i32 [[TMP0:%.*]], [[TMP1:%.*]]
+; CHECK-NEXT:    [[WITHPOISONFLAG:%.*]] = or i32 [[TMP0]], 42
+; CHECK-NEXT:    [[SAMEVALUES:%.*]] = icmp ule i32 [[WITHPOISONFLAG]], [[TMP1]]
+; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[SAMEVALUES]], i32 [[TMP1]], i32 [[TMP0]]
+; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[TMP4]], i32 [[TMP0]], i32 [[TMP3]]
+; CHECK-NEXT:    ret i32 [[TMP5]]
+;
+  %4 = icmp eq i32 %1, %2
+  %withPoisonFlag = or i32 %1, 42
+  %sameValues = icmp ule i32 %withPoisonFlag, %2
+  %5 = select i1 %sameValues, i32 %2, i32 %1
+  %6 = select i1 %4, i32 %1, i32 %5
+  ret i32 %6
+}
+
+define i32 @selectICmpSelectInArmNotSimplifiedPoisonFlagOnCondUseDefChain(i32 %1, i32 %2, i1 %scond) {
+; CHECK-LABEL: @selectICmpSelectInArmNotSimplifiedPoisonFlagOnCondUseDefChain(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP0:%.*]], [[TMP1:%.*]]
+; CHECK-NEXT:    [[WITHPOISONFLAG:%.*]] = or disjoint i32 [[TMP0]], 42
+; CHECK-NEXT:    [[SAMEVALUES:%.*]] = icmp ule i32 [[WITHPOISONFLAG]], [[TMP1]]
+; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[SAMEVALUES]], i32 [[TMP1]], i32 [[TMP0]]
+; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[TMP3]], i32 [[TMP0]], i32 [[TMP4]]
+; CHECK-NEXT:    ret i32 [[TMP5]]
+;
+  %4 = icmp eq i32 %1, %2
+  %withPoisonFlag = or disjoint i32 %1, 42
+  %sameValues = icmp ule i32 %withPoisonFlag, %2
+  %5 = select i1 %sameValues, i32 %2, i32 %1
+  %6 = select i1 %4, i32 %1, i32 %5
+  ret i32 %6
+}
+
+define i32 @selectICmpSelectInArmNotSimplifiedSecondSelectPoisonBarrier(i32 %1, i32 %2, i1 %scond) {
+; CHECK-LABEL: @selectICmpSelectInArmNotSimplifiedSecondSelectPoisonBarrier(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP0:%.*]], [[TMP1:%.*]]
+; CHECK-NEXT:    [[SAMEVALUES:%.*]] = icmp ule i32 [[TMP0]], [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[SAMEVALUES]], [[SCOND:%.*]]
+; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[OR]], i32 [[TMP1]], i32 [[TMP0]]
+; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[TMP3]], i32 [[TMP0]], i32 [[TMP4]]
+; CHECK-NEXT:    ret i32 [[TMP5]]
+;
+  %4 = icmp eq i32 %1, %2
+  %sameValues = icmp ule i32 %1, %2
+  %or = or i1 %sameValues, %scond
+  %5 = select i1 %or, i32 %2, i32 %1
+  %6 = select i1 %4, i32 %1, i32 %5
+  ret i32 %6
+}
+
+define i32 @selectICmpSelectInArmNotSimplifiedSecondSelectPoisonBarrierPoisonFlagInCond(i32 %1, i32 %2, i1 %scond) {
+; CHECK-LABEL: @selectICmpSelectInArmNotSimplifiedSecondSelectPoisonBarrierPoisonFlagInCond(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP0:%.*]], [[TMP1:%.*]]
+; CHECK-NEXT:    [[SAMEVALUES:%.*]] = icmp ule i32 [[TMP0]], [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = or disjoint i1 [[SAMEVALUES]], [[SCOND:%.*]]
+; CHECK-NEXT:    [[TMP4:%.*]] = select i1 [[OR]], i32 [[TMP1]], i32 [[TMP0]]
+; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[TMP3]], i32 [[TMP0]], i32 [[TMP4]]
+; CHECK-NEXT:    ret i32 [[TMP5]]
+;
+  %4 = icmp eq i32 %1, %2
+  %sameValues = icmp ule i32 %1, %2
+  %or = or disjoint i1 %sameValues, %scond
+  %5 = select i1 %or, i32 %2, i32 %1
+  %6 = select i1 %4, i32 %1, i32 %5
+  ret i32 %6
+}
+

>From eedae8781e75110f0f3bf4e3f2b7b60df4f8f957 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Wed, 4 Mar 2026 13:42:07 +0100
Subject: [PATCH 2/3] [InstSimplify] Decompose and/or trees to simplify selects

---
 llvm/lib/Analysis/InstructionSimplify.cpp     |  23 ++-
 llvm/lib/Analysis/ValueTracking.cpp           |   2 +-
 .../Transforms/InstSimplify/select-select.ll  | 174 ++++++++++++++++++
 3 files changed, 197 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/Transforms/InstSimplify/select-select.ll

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index ceadee5a7705b..97aa01304424e 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -35,6 +35,7 @@
 #include "llvm/Analysis/TargetLibraryInfo.h"
 #include "llvm/Analysis/ValueTracking.h"
 #include "llvm/Analysis/VectorUtils.h"
+#include "llvm/IR/Constant.h"
 #include "llvm/IR/ConstantFPRange.h"
 #include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/DataLayout.h"
@@ -45,6 +46,7 @@
 #include "llvm/IR/Operator.h"
 #include "llvm/IR/PatternMatch.h"
 #include "llvm/IR/Statepoint.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/KnownBits.h"
 #include "llvm/Support/KnownFPClass.h"
 #include <algorithm>
@@ -4411,9 +4413,13 @@ static bool areTheseAllTheValuesThatMayCausePoisonInValue(
     return true;
 
   if (const Instruction *I = dyn_cast<Instruction>(V)) {
+    // Having a poison generating annotation means that it can be poison
+    // without any of the given values being poison.
     if (I->hasPoisonGeneratingAnnotations())
       return false;
 
+    // Filtering out the assumed poison values, so only the relevant ones are
+    // brought forward to the next recursive call.
     SmallVector<const Value *, 2> ValuesImplyingPoison;
     copy_if(ValuesAssumedPoison, std::back_inserter(ValuesImplyingPoison),
             [V](const auto *P) { return impliesPoison(P, V); });
@@ -4431,6 +4437,11 @@ static bool areTheseAllTheValuesThatMayCausePoisonInValue(
                         ValuesImplyingPoison, Op, Depth + 1);
                   });
   }
+
+  // A constant can't cause poison, unless it is itself a poison.
+  if (const Constant *C = dyn_cast<Constant>(V))
+    return !C->containsUndefOrPoisonElement();
+
   return false;
 }
 
@@ -4603,7 +4614,7 @@ static Value *simplifyWithOpsReplaced(Value *V,
         // refinment.
         if (NewOps[1] == NewOps[2] &&
             areTheseAllTheValuesThatMayCausePoisonInValue(
-                {SI->getFalseValue(), SI->getTrueValue()}, Cond))
+                {SI->getFalseValue(), SI->getTrueValue(), NewOps[1]}, Cond))
           SimplifiedValue = NewOps[1];
 
         // TODO: Implement more non refining select optimizations here!
@@ -5280,6 +5291,16 @@ static Value *simplifySelectInst(Value *Cond, Value *TrueVal, Value *FalseVal,
       return ConstantVector::get(NewC);
   }
 
+  CmpPredicate Pred1, Pred2;
+  Value *V1, *V2, *EQV;
+  if (match(Cond,
+            m_And(m_SpecificICmp(ICmpInst::ICMP_EQ, m_Value(V1), m_Value(EQV)),
+                  m_SpecificICmp(ICmpInst::ICMP_EQ, m_Value(V2),
+                                 m_Deferred(EQV)))))
+    if (Value *V = simplifySelectWithEquivalence(
+            {{V2, EQV}, {V1, EQV}}, TrueVal, FalseVal, Q, MaxRecurse))
+      return V;
+
   if (Value *V =
           simplifySelectWithICmpCond(Cond, TrueVal, FalseVal, Q, MaxRecurse))
     return V;
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index c8f53f903428d..4c542b667a05b 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -7611,7 +7611,7 @@ static bool directlyImpliesPoison(const Value *ValAssumedPoison, const Value *V,
   if (ValAssumedPoison == V)
     return true;
 
-  const unsigned MaxDepth = 2;
+  const unsigned MaxDepth = 3;
   if (Depth >= MaxDepth)
     return false;
 
diff --git a/llvm/test/Transforms/InstSimplify/select-select.ll b/llvm/test/Transforms/InstSimplify/select-select.ll
new file mode 100644
index 0000000000000..393d6bb6f0253
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/select-select.ll
@@ -0,0 +1,174 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=instsimplify -S | FileCheck %s
+
+define i32 @AndICmpConstant(i32 %6, i32 %44) {
+; CHECK-LABEL: @AndICmpConstant(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP1:%.*]], 4
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i32 [[TMP0:%.*]], 4
+; CHECK-NEXT:    [[TMP5:%.*]] = xor i1 [[TMP4]], true
+; CHECK-NEXT:    [[TMP6:%.*]] = or i1 [[TMP3]], [[TMP5]]
+; CHECK-NEXT:    [[SPEC_SELECT_I:%.*]] = select i1 [[TMP6]], i32 [[TMP0]], i32 [[TMP1]]
+; CHECK-NEXT:    ret i32 [[SPEC_SELECT_I]]
+;
+  %46 = icmp eq i32 %44, 4
+  %47 = icmp eq i32 %6, 4
+  %or.cond.i = and i1 %47, %46
+  %48 = xor i1 %47, true
+  %49 = or i1 %46, %48
+  %spec.select.i = select i1 %49, i32 %6, i32 %44
+  %.0.i8 = select i1 %or.cond.i, i32 4, i32 %spec.select.i
+  ret i32 %.0.i8
+}
+
+
+define i32 @AndICmp(i32 %6, i32 %44, i32 %val) {
+; CHECK-LABEL: @AndICmp(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP1:%.*]], [[VAL:%.*]]
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i32 [[TMP0:%.*]], [[VAL]]
+; CHECK-NEXT:    [[TMP5:%.*]] = xor i1 [[TMP4]], true
+; CHECK-NEXT:    [[TMP6:%.*]] = or i1 [[TMP3]], [[TMP5]]
+; CHECK-NEXT:    [[DOT0_I8:%.*]] = select i1 [[TMP6]], i32 [[TMP0]], i32 [[TMP1]]
+; CHECK-NEXT:    ret i32 [[DOT0_I8]]
+;
+  %46 = icmp eq i32 %44, %val
+  %47 = icmp eq i32 %6, %val
+  %or.cond.i = and i1 %47, %46
+  %48 = xor i1 %47, true
+  %49 = or i1 %46, %48
+  %spec.select.i = select i1 %49, i32 %6, i32 %44
+  %.0.i8 = select i1 %or.cond.i, i32 %val, i32 %spec.select.i
+  ret i32 %.0.i8
+}
+
+define i32 @AndICmpRemovedNonConst(i32 %6, i32 %44, i32 %val) {
+; CHECK-LABEL: @AndICmpRemovedNonConst(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP1:%.*]], [[VAL:%.*]]
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i32 [[TMP0:%.*]], [[VAL]]
+; CHECK-NEXT:    [[OR_COND_I:%.*]] = and i1 [[TMP4]], [[TMP3]]
+; CHECK-NEXT:    [[TMP5:%.*]] = xor i1 [[TMP4]], true
+; CHECK-NEXT:    [[SPEC_SELECT_I:%.*]] = select i1 [[TMP5]], i32 [[TMP0]], i32 [[TMP1]]
+; CHECK-NEXT:    [[DOT0_I8:%.*]] = select i1 [[OR_COND_I]], i32 [[VAL]], i32 [[SPEC_SELECT_I]]
+; CHECK-NEXT:    ret i32 [[DOT0_I8]]
+;
+  %46 = icmp eq i32 %44, %val
+  %47 = icmp eq i32 %6, %val
+  %or.cond.i = and i1 %47, %46
+  %48 = xor i1 %47, true
+  %spec.select.i = select i1 %48, i32 %6, i32 %44
+  %.0.i8 = select i1 %or.cond.i, i32 %val, i32 %spec.select.i
+  ret i32 %.0.i8
+}
+
+define i32 @AndICmpAnd(i32 %6, i32 %44) {
+; CHECK-LABEL: @AndICmpAnd(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP1:%.*]], 4
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i32 [[TMP0:%.*]], 4
+; CHECK-NEXT:    [[TMP5:%.*]] = xor i1 [[TMP4]], true
+; CHECK-NEXT:    [[TMP6:%.*]] = and i1 [[TMP3]], [[TMP5]]
+; CHECK-NEXT:    [[SPEC_SELECT_I:%.*]] = select i1 [[TMP6]], i32 [[TMP0]], i32 [[TMP1]]
+; CHECK-NEXT:    ret i32 [[SPEC_SELECT_I]]
+;
+  %46 = icmp eq i32 %44, 4
+  %47 = icmp eq i32 %6, 4
+  %or.cond.i = and i1 %47, %46
+  %48 = xor i1 %47, true
+  %49 = and i1 %46, %48
+  %spec.select.i = select i1 %49, i32 %6, i32 %44
+  %.0.i8 = select i1 %or.cond.i, i32 4, i32 %spec.select.i
+  ret i32 %.0.i8
+}
+
+define i32 @AndICmpAnd2(i32 %6, i32 %44) {
+; CHECK-LABEL: @AndICmpAnd2(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP1:%.*]], 4
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i32 [[TMP0:%.*]], 4
+; CHECK-NEXT:    [[TMP5:%.*]] = or i1 [[TMP3]], [[TMP4]]
+; CHECK-NEXT:    [[SPEC_SELECT_I:%.*]] = select i1 [[TMP5]], i32 [[TMP0]], i32 [[TMP1]]
+; CHECK-NEXT:    ret i32 [[SPEC_SELECT_I]]
+;
+  %46 = icmp eq i32 %44, 4
+  %47 = icmp eq i32 %6, 4
+  %or.cond.i = and i1 %47, %46
+  %48 = and i1 %47, true
+  %49 = or i1 %46, %48
+  %spec.select.i = select i1 %49, i32 %6, i32 %44
+  %.0.i8 = select i1 %or.cond.i, i32 4, i32 %spec.select.i
+  ret i32 %.0.i8
+}
+
+define i32 @negative_firstSelectPoisonBarrier(i32 %6, i32 %44, i1 %cond) {
+; CHECK-LABEL: @negative_firstSelectPoisonBarrier(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP1:%.*]], 4
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i32 [[TMP0:%.*]], 4
+; CHECK-NEXT:    [[OR_COND_I:%.*]] = and i1 [[TMP4]], [[TMP3]]
+; CHECK-NEXT:    [[SPEC_SELECT_I:%.*]] = select i1 [[COND:%.*]], i32 [[TMP0]], i32 [[TMP1]]
+; CHECK-NEXT:    [[DOT0_I8:%.*]] = select i1 [[OR_COND_I]], i32 4, i32 [[SPEC_SELECT_I]]
+; CHECK-NEXT:    ret i32 [[DOT0_I8]]
+;
+  %46 = icmp eq i32 %44, 4
+  %47 = icmp eq i32 %6, 4
+  %or.cond.i = and i1 %47, %46
+  %spec.select.i = select i1 %cond, i32 %6, i32 %44
+  %.0.i8 = select i1 %or.cond.i, i32 4, i32 %spec.select.i
+  ret i32 %.0.i8
+}
+
+define i32 @negative_selectCondOnlyMutualOneArm(i32 %6, i32 %44) {
+; CHECK-LABEL: @negative_selectCondOnlyMutualOneArm(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP0:%.*]], 4
+; CHECK-NEXT:    [[TMP4:%.*]] = xor i1 [[TMP3]], true
+; CHECK-NEXT:    [[SPEC_SELECT_I:%.*]] = select i1 [[TMP4]], i32 [[TMP0]], i32 [[TMP1:%.*]]
+; CHECK-NEXT:    ret i32 [[SPEC_SELECT_I]]
+;
+  %46 = icmp eq i32 %44, 4
+  %47 = icmp eq i32 %6, 4
+  %or.cond.i = and i1 %47, %46
+  %48 = xor i1 %47, true
+  %spec.select.i = select i1 %48, i32 %6, i32 %44
+  %.0.i8 = select i1 %or.cond.i, i32 4, i32 %spec.select.i
+  ret i32 %.0.i8
+}
+
+define i32 @negative_condMorePoisonThanArm(i32 %0, i32 %1) {
+; CHECK-LABEL: @negative_condMorePoisonThanArm(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP1:%.*]], 4
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i32 [[TMP0:%.*]], 4
+; CHECK-NEXT:    [[OR_COND_I:%.*]] = and i1 [[TMP4]], [[TMP3]]
+; CHECK-NEXT:    [[TMP5:%.*]] = or disjoint i1 [[TMP3]], [[TMP4]]
+; CHECK-NEXT:    [[SPEC_SELECT_I:%.*]] = select i1 [[TMP5]], i32 [[TMP0]], i32 [[TMP1]]
+; CHECK-NEXT:    [[DOT0_I8:%.*]] = select i1 [[OR_COND_I]], i32 4, i32 [[SPEC_SELECT_I]]
+; CHECK-NEXT:    ret i32 [[DOT0_I8]]
+;
+  %3 = icmp eq i32 %1, 4
+  %4 = icmp eq i32 %0, 4
+  %or.cond.i = and i1 %4, %3
+  %5 = and i1 %4, true
+  %6 = or disjoint i1 %3, %5
+  %spec.select.i = select i1 %6, i32 %0, i32 %1
+  %.0.i8 = select i1 %or.cond.i, i32 4, i32 %spec.select.i
+  ret i32 %.0.i8
+}
+
+define i32 @negative_CmpUsed(i32 %6, i32 %44) {
+; CHECK-LABEL: @negative_CmpUsed(
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i32 [[TMP1:%.*]], 4
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i32 [[TMP0:%.*]], 4
+; CHECK-NEXT:    [[OR_COND_I:%.*]] = and i1 [[TMP4]], [[TMP3]]
+; CHECK-NEXT:    [[TMP5:%.*]] = xor i1 [[TMP4]], true
+; CHECK-NEXT:    [[TMP6:%.*]] = or i1 [[TMP3]], [[TMP5]]
+; CHECK-NEXT:    [[SPEC_SELECT_I:%.*]] = select i1 [[TMP6]], i32 [[TMP0]], i32 [[TMP1]]
+; CHECK-NEXT:    call void @use32(i1 [[OR_COND_I]])
+; CHECK-NEXT:    ret i32 [[SPEC_SELECT_I]]
+;
+  %46 = icmp eq i32 %44, 4
+  %47 = icmp eq i32 %6, 4
+  %or.cond.i = and i1 %47, %46
+  %48 = xor i1 %47, true
+  %49 = or i1 %46, %48
+  %spec.select.i = select i1 %49, i32 %6, i32 %44
+  %.0.i8 = select i1 %or.cond.i, i32 4, i32 %spec.select.i
+  call void @use32(i1 %or.cond.i)
+  ret i32 %.0.i8
+}
+
+declare void @use32(i1)

>From 7ea9c2948e5536db795b645af9626aed31b53866 Mon Sep 17 00:00:00 2001
From: Gabor Spaits <gaborspaits1 at gmail.com>
Date: Fri, 6 Mar 2026 14:05:29 +0100
Subject: [PATCH 3/3] Regression test update

---
 llvm/test/CodeGen/Thumb2/mve-pred-and.ll      | 25 +++----------------
 .../Transforms/InstCombine/and-or-icmps.ll    | 19 ++++++--------
 .../test/Transforms/InstCombine/trunc-lshr.ll |  4 +--
 .../Transforms/InstSimplify/select-icmp.ll    |  8 +++---
 .../SimplifyCFG/branch-fold-multiple.ll       |  2 +-
 5 files changed, 18 insertions(+), 40 deletions(-)

diff --git a/llvm/test/CodeGen/Thumb2/mve-pred-and.ll b/llvm/test/CodeGen/Thumb2/mve-pred-and.ll
index 42eec5e596a96..65c82e34ef47b 100644
--- a/llvm/test/CodeGen/Thumb2/mve-pred-and.ll
+++ b/llvm/test/CodeGen/Thumb2/mve-pred-and.ll
@@ -4,9 +4,7 @@
 define arm_aapcs_vfpcc <4 x i32> @cmpeqz_v4i1(<4 x i32> %a, <4 x i32> %b) {
 ; CHECK-LABEL: cmpeqz_v4i1:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    vorr q2, q0, q1
-; CHECK-NEXT:    vcmp.i32 eq, q2, zr
-; CHECK-NEXT:    vpsel q0, q0, q1
+; CHECK-NEXT:    vmov q0, q1
 ; CHECK-NEXT:    bx lr
 entry:
   %c1 = icmp eq <4 x i32> %a, zeroinitializer
@@ -479,9 +477,7 @@ entry:
 define arm_aapcs_vfpcc <8 x i16> @cmpeqz_v8i1(<8 x i16> %a, <8 x i16> %b) {
 ; CHECK-LABEL: cmpeqz_v8i1:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    vorr q2, q0, q1
-; CHECK-NEXT:    vcmp.i16 eq, q2, zr
-; CHECK-NEXT:    vpsel q0, q0, q1
+; CHECK-NEXT:    vmov q0, q1
 ; CHECK-NEXT:    bx lr
 entry:
   %c1 = icmp eq <8 x i16> %a, zeroinitializer
@@ -527,9 +523,7 @@ entry:
 define arm_aapcs_vfpcc <16 x i8> @cmpeqz_v16i1(<16 x i8> %a, <16 x i8> %b) {
 ; CHECK-LABEL: cmpeqz_v16i1:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    vorr q2, q0, q1
-; CHECK-NEXT:    vcmp.i8 eq, q2, zr
-; CHECK-NEXT:    vpsel q0, q0, q1
+; CHECK-NEXT:    vmov q0, q1
 ; CHECK-NEXT:    bx lr
 entry:
   %c1 = icmp eq <16 x i8> %a, zeroinitializer
@@ -575,18 +569,7 @@ entry:
 define arm_aapcs_vfpcc <2 x i64> @cmpeqz_v2i1(<2 x i64> %a, <2 x i64> %b) {
 ; CHECK-LABEL: cmpeqz_v2i1:
 ; CHECK:       @ %bb.0: @ %entry
-; CHECK-NEXT:    vorr q2, q0, q1
-; CHECK-NEXT:    vmov r0, r1, d4
-; CHECK-NEXT:    orrs r0, r1
-; CHECK-NEXT:    mov.w r1, #0
-; CHECK-NEXT:    csetm r0, eq
-; CHECK-NEXT:    bfi r1, r0, #0, #8
-; CHECK-NEXT:    vmov r0, r2, d5
-; CHECK-NEXT:    orrs r0, r2
-; CHECK-NEXT:    csetm r0, eq
-; CHECK-NEXT:    bfi r1, r0, #8, #8
-; CHECK-NEXT:    vmsr p0, r1
-; CHECK-NEXT:    vpsel q0, q0, q1
+; CHECK-NEXT:    vmov q0, q1
 ; CHECK-NEXT:    bx lr
 entry:
   %c1 = icmp eq <2 x i64> %a, zeroinitializer
diff --git a/llvm/test/Transforms/InstCombine/and-or-icmps.ll b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
index 4cd089ca524c2..b1a73ba654a3e 100644
--- a/llvm/test/Transforms/InstCombine/and-or-icmps.ll
+++ b/llvm/test/Transforms/InstCombine/and-or-icmps.ll
@@ -1651,11 +1651,10 @@ define i1 @logical_and_logical_and_icmps_comm1(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: @logical_and_logical_and_icmps_comm1(
 ; CHECK-NEXT:    [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
 ; CHECK-NEXT:    [[Z_SHIFT:%.*]] = shl nuw i8 1, [[Z:%.*]]
-; CHECK-NEXT:    [[X_M2:%.*]] = and i8 [[X:%.*]], [[Z_SHIFT]]
-; CHECK-NEXT:    [[C2:%.*]] = trunc i8 [[X]] to i1
-; CHECK-NEXT:    [[C3:%.*]] = icmp ne i8 [[X_M2]], 0
-; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[C3]], i1 [[C1]], i1 false
-; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[TMP1]], i1 [[C2]], i1 false
+; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[Z_SHIFT]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = and i8 [[X:%.*]], [[TMP1]]
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp eq i8 [[TMP2]], [[TMP1]]
+; CHECK-NEXT:    [[AND2:%.*]] = select i1 [[TMP3]], i1 [[C1]], i1 false
 ; CHECK-NEXT:    ret i1 [[AND2]]
 ;
   %c1 = icmp eq i8 %y, 42
@@ -1998,13 +1997,11 @@ define i1 @logical_or_logical_or_icmps(i8 %x, i8 %y, i8 %z) {
 define i1 @logical_or_logical_or_icmps_comm1(i8 %x, i8 %y, i8 %z) {
 ; CHECK-LABEL: @logical_or_logical_or_icmps_comm1(
 ; CHECK-NEXT:    [[C1:%.*]] = icmp eq i8 [[Y:%.*]], 42
-; CHECK-NEXT:    [[X_M1:%.*]] = and i8 [[X:%.*]], 1
 ; CHECK-NEXT:    [[Z_SHIFT:%.*]] = shl nuw i8 1, [[Z:%.*]]
-; CHECK-NEXT:    [[X_M2:%.*]] = and i8 [[X]], [[Z_SHIFT]]
-; CHECK-NEXT:    [[C2:%.*]] = icmp eq i8 [[X_M1]], 0
-; CHECK-NEXT:    [[C3:%.*]] = icmp eq i8 [[X_M2]], 0
-; CHECK-NEXT:    [[TMP1:%.*]] = select i1 [[C3]], i1 true, i1 [[C1]]
-; CHECK-NEXT:    [[OR2:%.*]] = select i1 [[TMP1]], i1 true, i1 [[C2]]
+; CHECK-NEXT:    [[TMP1:%.*]] = or i8 [[Z_SHIFT]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = and i8 [[X:%.*]], [[TMP1]]
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp ne i8 [[TMP2]], [[TMP1]]
+; CHECK-NEXT:    [[OR2:%.*]] = select i1 [[TMP3]], i1 true, i1 [[C1]]
 ; CHECK-NEXT:    ret i1 [[OR2]]
 ;
   %c1 = icmp eq i8 %y, 42
diff --git a/llvm/test/Transforms/InstCombine/trunc-lshr.ll b/llvm/test/Transforms/InstCombine/trunc-lshr.ll
index 0e996e5d017fe..86a311d350d91 100644
--- a/llvm/test/Transforms/InstCombine/trunc-lshr.ll
+++ b/llvm/test/Transforms/InstCombine/trunc-lshr.ll
@@ -61,7 +61,7 @@ define i1 @test4(i32 %i, ptr %p) {
 ; CHECK-NEXT:    [[T:%.*]] = trunc nuw i32 [[DOTLOBIT]] to i1
 ; CHECK-NEXT:    [[B:%.*]] = icmp slt i32 [[I]], 0
 ; CHECK-NEXT:    [[NOT_:%.*]] = xor i1 [[T]], true
-; CHECK-NEXT:    [[COMMON_RET1_OP:%.*]] = select i1 [[NOT_]], i1 [[B]], i1 false
+; CHECK-NEXT:    [[COMMON_RET1_OP:%.*]] = and i1 [[B]], [[NOT_]]
 ; CHECK-NEXT:    store i32 [[DOTLOBIT]], ptr [[P]], align 1
 ; CHECK-NEXT:    ret i1 [[COMMON_RET1_OP]]
 ;
@@ -82,7 +82,7 @@ define i1 @test5(i32 %i, ptr %p) {
 ; CHECK-NEXT:    [[T:%.*]] = trunc nuw i32 [[DOTLOBIT]] to i1
 ; CHECK-NEXT:    [[B:%.*]] = icmp slt i32 [[I]], 0
 ; CHECK-NEXT:    [[NOT_:%.*]] = xor i1 [[T]], true
-; CHECK-NEXT:    [[COMMON_RET1_OP:%.*]] = select i1 [[NOT_]], i1 [[B]], i1 false
+; CHECK-NEXT:    [[COMMON_RET1_OP:%.*]] = and i1 [[B]], [[NOT_]]
 ; CHECK-NEXT:    store i32 [[DOTLOBIT]], ptr [[P]], align 1
 ; CHECK-NEXT:    ret i1 [[COMMON_RET1_OP]]
 ;
diff --git a/llvm/test/Transforms/InstSimplify/select-icmp.ll b/llvm/test/Transforms/InstSimplify/select-icmp.ll
index efe676d9fcd26..6f549e0e7f0d6 100755
--- a/llvm/test/Transforms/InstSimplify/select-icmp.ll
+++ b/llvm/test/Transforms/InstSimplify/select-icmp.ll
@@ -338,11 +338,9 @@ define i32 @selectICmpSelectInArmSimplifiedDueToSameValues(i32 %1, i32 %2, i1 %s
 
 define i32 @selectICmpSelectInArmSimplifiedNoPoisonFlagOnCondUseDefChain(i32 %1, i32 %2, i1 %scond) {
 ; CHECK-LABEL: @selectICmpSelectInArmSimplifiedNoPoisonFlagOnCondUseDefChain(
-; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i32 [[TMP0:%.*]], [[TMP1:%.*]]
-; CHECK-NEXT:    [[WITHPOISONFLAG:%.*]] = or i32 [[TMP0]], 42
-; CHECK-NEXT:    [[SAMEVALUES:%.*]] = icmp ule i32 [[WITHPOISONFLAG]], [[TMP1]]
-; CHECK-NEXT:    [[TMP3:%.*]] = select i1 [[SAMEVALUES]], i32 [[TMP1]], i32 [[TMP0]]
-; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[TMP4]], i32 [[TMP0]], i32 [[TMP3]]
+; CHECK-NEXT:    [[WITHPOISONFLAG:%.*]] = or i32 [[TMP0:%.*]], 42
+; CHECK-NEXT:    [[SAMEVALUES:%.*]] = icmp ule i32 [[WITHPOISONFLAG]], [[TMP1:%.*]]
+; CHECK-NEXT:    [[TMP5:%.*]] = select i1 [[SAMEVALUES]], i32 [[TMP1]], i32 [[TMP0]]
 ; CHECK-NEXT:    ret i32 [[TMP5]]
 ;
   %4 = icmp eq i32 %1, %2
diff --git a/llvm/test/Transforms/SimplifyCFG/branch-fold-multiple.ll b/llvm/test/Transforms/SimplifyCFG/branch-fold-multiple.ll
index fd400632a5916..01d60a4583b9f 100644
--- a/llvm/test/Transforms/SimplifyCFG/branch-fold-multiple.ll
+++ b/llvm/test/Transforms/SimplifyCFG/branch-fold-multiple.ll
@@ -113,7 +113,7 @@ define i1 @test3(i32 %0, i32 %1, i32 %2, i32 %3) {
 ; CHECK-NEXT:    [[CMP2_2:%.*]] = icmp sgt i32 [[TMP2]], 0
 ; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[CMP2_1]], i1 true, i1 [[CMP2_2]]
 ; CHECK-NEXT:    [[CMP2_3:%.*]] = icmp sgt i32 [[TMP3]], 0
-; CHECK-NEXT:    [[OR_COND1:%.*]] = select i1 [[OR_COND]], i1 true, i1 [[CMP2_3]]
+; CHECK-NEXT:    [[OR_COND1:%.*]] = or i1 [[OR_COND]], [[CMP2_3]]
 ; CHECK-NEXT:    [[SPEC_SELECT:%.*]] = select i1 [[OR_COND1]], i1 false, i1 true
 ; CHECK-NEXT:    br label [[CLEANUP]]
 ; CHECK:       cleanup:



More information about the llvm-commits mailing list