[llvm] 2706780 - [InstCombine] support trunc condition in foldSelectICmpAndAnd (#223132)

via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 12 03:46:39 PDT 2026


Author: Andreas Jonson
Date: 2026-09-12T12:46:34+02:00
New Revision: 2706780356665d560cc5a38dee91e7377a6d6002

URL: https://github.com/llvm/llvm-project/commit/2706780356665d560cc5a38dee91e7377a6d6002
DIFF: https://github.com/llvm/llvm-project/commit/2706780356665d560cc5a38dee91e7377a6d6002.diff

LOG: [InstCombine] support trunc condition in foldSelectICmpAndAnd (#223132)

Reland https://github.com/llvm/llvm-project/pull/220690 with the fix for
asan failure see fixup commit

Added: 
    

Modified: 
    llvm/include/llvm/IR/PatternMatch.h
    llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
    llvm/test/Transforms/InstCombine/select-of-bittest.ll

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/IR/PatternMatch.h b/llvm/include/llvm/IR/PatternMatch.h
index 88f3989352cf6..32d5e45320463 100644
--- a/llvm/include/llvm/IR/PatternMatch.h
+++ b/llvm/include/llvm/IR/PatternMatch.h
@@ -688,6 +688,17 @@ m_SpecificInt_ICMP(ICmpInst::Predicate Predicate, const APInt &Threshold) {
   return P;
 }
 
+/// Match an integer or vector with every element comparing 'pred' (eg/ne/...)
+/// to Threshold. For vectors, this includes constants with undefined elements.
+inline cst_pred_ty<icmp_pred_with_threshold, false>
+m_SpecificInt_ICMP_ForbidPoison(ICmpInst::Predicate Predicate,
+                                const APInt &Threshold) {
+  cst_pred_ty<icmp_pred_with_threshold, false> P;
+  P.Pred = Predicate;
+  P.Thr = &Threshold;
+  return P;
+}
+
 struct is_nan {
   bool isValue(const APFloat &C) const { return C.isNaN(); }
 };

diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
index 4f0c21831cb4c..a4061bbd60770 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineSelect.cpp
@@ -749,37 +749,51 @@ static Value *foldSelectICmpMinMax(const ICmpInst *Cmp, Value *TVal,
 ///   Z may be 0 if lshr is missing.
 /// Worst-case scenario is that we will replace 5 instructions with 5 
diff erent
 /// instructions, but we got rid of select.
-static Instruction *foldSelectICmpAndAnd(Type *SelType, const ICmpInst *Cmp,
+static Instruction *foldSelectICmpAndAnd(Type *SelType, const Value *Cond,
                                          Value *TVal, Value *FVal,
                                          InstCombiner::BuilderTy &Builder) {
-  if (!(Cmp->hasOneUse() && Cmp->getOperand(0)->hasOneUse() &&
-        Cmp->getPredicate() == ICmpInst::ICMP_EQ &&
-        match(Cmp->getOperand(1), m_Zero()) && match(FVal, m_One())))
+  Value *A, *X, *Y, *Z;
+  CmpPredicate Pred;
+  unsigned NumReplaced = 1 + Cond->hasOneUse();
+  if (match(Cond, m_Trunc(m_Value(X)))) {
+    Y = ConstantInt::get(X->getType(), 1);
+    Pred = ICmpInst::ICMP_NE;
+  } else if (match(Cond,
+                   m_ICmp(Pred, m_And(m_Value(X), m_Value(Y)), m_Zero())) &&
+             ICmpInst::isEquality(Pred)) {
+    NumReplaced +=
+        Cond->hasOneUse() && cast<ICmpInst>(Cond)->getOperand(0)->hasOneUse();
+  } else
+    return nullptr;
+
+  if (Pred == ICmpInst::ICMP_NE)
+    std::swap(TVal, FVal);
+
+  if (!match(FVal, m_One()))
     return nullptr;
 
   // The TrueVal has general form of:  and %B, 1
-  Value *B;
-  if (!match(TVal, m_OneUse(m_And(m_Value(B), m_One()))))
+  if (!match(TVal, m_And(m_Value(A), m_One())))
     return nullptr;
 
-  // Where %B may be optionally shifted:  lshr %X, %Z.
-  Value *X, *Z;
-  const bool HasShift = match(B, m_OneUse(m_LShr(m_Value(X), m_Value(Z))));
+  APInt BitWidth(SelType->getScalarSizeInBits(),
+                 SelType->getScalarSizeInBits());
+  auto TValPattern = m_CombineOr(
+      m_Deferred(X),
+      m_LShr(m_Deferred(X), m_Value(Z, m_SpecificInt_ICMP_ForbidPoison(
+                                           CmpInst::ICMP_ULT, BitWidth))));
 
-  // The shift must be valid.
-  // TODO: This restricts the fold to constant shift amounts. Is there a way to
-  //       handle variable shifts safely? PR47012
-  if (HasShift &&
-      !match(Z, m_SpecificInt_ICMP(CmpInst::ICMP_ULT,
-                                   APInt(SelType->getScalarSizeInBits(),
-                                         SelType->getScalarSizeInBits()))))
-    return nullptr;
+  if (!match(A, TValPattern)) {
+    std::swap(X, Y);
+    if (!match(A, TValPattern))
+      return nullptr;
+  }
 
-  if (!HasShift)
-    X = B;
+  bool HasShift = A != X;
+  if (TVal->hasOneUse())
+    NumReplaced += 1 + (HasShift && A->hasOneUse());
 
-  Value *Y;
-  if (!match(Cmp->getOperand(0), m_c_And(m_Specific(X), m_Value(Y))))
+  if (NumReplaced < (4u - isa<Constant>(Y)))
     return nullptr;
 
   // ((X & Y) == 0) ? ((X >> Z) & 1) : 1 --> (X & (Y | (1 << Z))) != 0
@@ -2451,10 +2465,6 @@ Instruction *InstCombinerImpl::foldSelectInstWithICmp(SelectInst &SI,
   if (Value *V = foldSelectICmpMinMax(ICI, TrueVal, FalseVal, Builder, SQ))
     return replaceInstUsesWith(SI, V);
 
-  if (Instruction *V =
-          foldSelectICmpAndAnd(SI.getType(), ICI, TrueVal, FalseVal, Builder))
-    return V;
-
   if (Value *V = foldSelectICmpAndZeroShl(ICI, TrueVal, FalseVal, Builder))
     return replaceInstUsesWith(SI, V);
 
@@ -4912,6 +4922,10 @@ Instruction *InstCombinerImpl::visitSelectInst(SelectInst &SI) {
     if (Instruction *Result = foldSelectInstWithICmp(SI, ICI))
       return Result;
 
+  if (Instruction *V =
+          foldSelectICmpAndAnd(SelType, CondVal, TrueVal, FalseVal, Builder))
+    return V;
+
   if (Value *V = foldSelectBitTest(SI, CondVal, TrueVal, FalseVal, Builder, SQ))
     return replaceInstUsesWith(SI, V);
 

diff  --git a/llvm/test/Transforms/InstCombine/select-of-bittest.ll b/llvm/test/Transforms/InstCombine/select-of-bittest.ll
index 4495e7f6ed067..3715038d7fd01 100644
--- a/llvm/test/Transforms/InstCombine/select-of-bittest.ll
+++ b/llvm/test/Transforms/InstCombine/select-of-bittest.ll
@@ -3,6 +3,10 @@
 
 ; https://bugs.llvm.org/show_bug.cgi?id=36950
 
+declare void @use32(i32)
+declare void @use1(i1)
+declare void @use_vec1(<2 x i1>)
+
 ; These all should be just and+icmp, there should be no select.
 
 define i32 @and_lshr_and(i32 %arg) {
@@ -20,6 +24,39 @@ define i32 @and_lshr_and(i32 %arg) {
   ret i32 %t4
 }
 
+define i32 @and_lshr_and_ne_cond(i32 %arg) {
+; CHECK-LABEL: @and_lshr_and_ne_cond(
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[ARG:%.*]], 4
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0
+; CHECK-NEXT:    call void @use1(i1 [[TMP2]])
+; CHECK-NEXT:    [[TMP3:%.*]] = and i32 [[ARG]], 6
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp ne i32 [[TMP3]], 0
+; CHECK-NEXT:    [[T4:%.*]] = zext i1 [[TMP4]] to i32
+; CHECK-NEXT:    ret i32 [[T4]]
+;
+  %t = and i32 %arg, 4
+  %t1 = icmp ne i32 %t, 0
+  call void @use1(i1 %t1)
+  %t2 = lshr i32 %arg, 1
+  %t3 = and i32 %t2, 1
+  %t4 = select i1 %t1, i32 1, i32 %t3
+  ret i32 %t4
+}
+
+define i32 @and_lshr_and_trunc_cond(i32 %arg) {
+; CHECK-LABEL: @and_lshr_and_trunc_cond(
+; CHECK-NEXT:    [[TMP1:%.*]] = and i32 [[ARG:%.*]], 3
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i32 [[TMP1]], 0
+; CHECK-NEXT:    [[T4:%.*]] = zext i1 [[TMP2]] to i32
+; CHECK-NEXT:    ret i32 [[T4]]
+;
+  %t1 = trunc i32 %arg to i1
+  %t2 = lshr i32 %arg, 1
+  %t3 = and i32 %t2, 1
+  %t4 = select i1 %t1, i32 1, i32 %t3
+  ret i32 %t4
+}
+
 define <2 x i32> @and_lshr_and_splatvec(<2 x i32> %arg) {
 ; CHECK-LABEL: @and_lshr_and_splatvec(
 ; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i32> [[ARG:%.*]], splat (i32 3)
@@ -35,6 +72,39 @@ define <2 x i32> @and_lshr_and_splatvec(<2 x i32> %arg) {
   ret <2 x i32> %t4
 }
 
+define <2 x i32> @and_lshr_and_ne_cond_splatvec(<2 x i32> %arg) {
+; CHECK-LABEL: @and_lshr_and_ne_cond_splatvec(
+; CHECK-NEXT:    [[T:%.*]] = and <2 x i32> [[ARG:%.*]], splat (i32 4)
+; CHECK-NEXT:    [[T1:%.*]] = icmp ne <2 x i32> [[T]], zeroinitializer
+; CHECK-NEXT:    call void @use_vec1(<2 x i1> [[T1]])
+; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i32> [[ARG]], splat (i32 6)
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne <2 x i32> [[TMP1]], zeroinitializer
+; CHECK-NEXT:    [[T4:%.*]] = zext <2 x i1> [[TMP2]] to <2 x i32>
+; CHECK-NEXT:    ret <2 x i32> [[T4]]
+;
+  %t = and <2 x i32> %arg, <i32 4, i32 4>
+  %t1 = icmp ne <2 x i32> %t, zeroinitializer
+  call void @use_vec1(<2 x i1> %t1)
+  %t2 = lshr <2 x i32> %arg, <i32 1, i32 1>
+  %t3 = and <2 x i32> %t2, <i32 1, i32 1>
+  %t4 = select <2 x i1> %t1, <2 x i32> <i32 1, i32 1>, <2 x i32> %t3
+  ret <2 x i32> %t4
+}
+
+define <2 x i32> @and_lshr_and_trunc_cond_splatvec(<2 x i32> %arg) {
+; CHECK-LABEL: @and_lshr_and_trunc_cond_splatvec(
+; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i32> [[ARG:%.*]], splat (i32 3)
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne <2 x i32> [[TMP1]], zeroinitializer
+; CHECK-NEXT:    [[T4:%.*]] = zext <2 x i1> [[TMP2]] to <2 x i32>
+; CHECK-NEXT:    ret <2 x i32> [[T4]]
+;
+  %t1 = trunc <2 x i32> %arg to <2 x i1>
+  %t2 = lshr <2 x i32> %arg, <i32 1, i32 1>
+  %t3 = and <2 x i32> %t2, <i32 1, i32 1>
+  %t4 = select <2 x i1> %t1, <2 x i32> <i32 1, i32 1>, <2 x i32> %t3
+  ret <2 x i32> %t4
+}
+
 define <2 x i32> @and_lshr_and_vec_v0(<2 x i32> %arg) {
 ; CHECK-LABEL: @and_lshr_and_vec_v0(
 ; CHECK-NEXT:    [[TMP1:%.*]] = and <2 x i32> [[ARG:%.*]], <i32 3, i32 6>
@@ -82,9 +152,11 @@ define <2 x i32> @and_lshr_and_vec_v2(<2 x i32> %arg) {
 
 define <3 x i32> @and_lshr_and_vec_poison(<3 x i32> %arg) {
 ; CHECK-LABEL: @and_lshr_and_vec_poison(
-; CHECK-NEXT:    [[TMP1:%.*]] = and <3 x i32> [[ARG:%.*]], <i32 3, i32 poison, i32 3>
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne <3 x i32> [[TMP1]], zeroinitializer
-; CHECK-NEXT:    [[T4:%.*]] = zext <3 x i1> [[TMP2]] to <3 x i32>
+; CHECK-NEXT:    [[T:%.*]] = and <3 x i32> [[ARG:%.*]], <i32 1, i32 poison, i32 1>
+; CHECK-NEXT:    [[T1:%.*]] = icmp eq <3 x i32> [[T]], <i32 0, i32 poison, i32 0>
+; CHECK-NEXT:    [[T2:%.*]] = lshr <3 x i32> [[ARG]], <i32 1, i32 poison, i32 1>
+; CHECK-NEXT:    [[T3:%.*]] = and <3 x i32> [[T2]], <i32 1, i32 poison, i32 1>
+; CHECK-NEXT:    [[T4:%.*]] = select <3 x i1> [[T1]], <3 x i32> [[T3]], <3 x i32> <i32 1, i32 poison, i32 1>
 ; CHECK-NEXT:    ret <3 x i32> [[T4]]
 ;
   %t = and <3 x i32> %arg, <i32 1, i32 poison, i32 1>
@@ -222,10 +294,11 @@ define <2 x i32> @f_var0_vec(<2 x i32> %arg, <2 x i32> %arg1) {
 
 define <3 x i32> @f_var0_vec_poison(<3 x i32> %arg, <3 x i32> %arg1) {
 ; CHECK-LABEL: @f_var0_vec_poison(
-; CHECK-NEXT:    [[TMP1:%.*]] = or <3 x i32> [[ARG1:%.*]], <i32 2, i32 poison, i32 2>
-; CHECK-NEXT:    [[TMP2:%.*]] = and <3 x i32> [[ARG:%.*]], [[TMP1]]
-; CHECK-NEXT:    [[TMP3:%.*]] = icmp ne <3 x i32> [[TMP2]], zeroinitializer
-; CHECK-NEXT:    [[T5:%.*]] = zext <3 x i1> [[TMP3]] to <3 x i32>
+; CHECK-NEXT:    [[TMP2:%.*]] = and <3 x i32> [[ARG:%.*]], [[TMP1:%.*]]
+; CHECK-NEXT:    [[T2:%.*]] = icmp eq <3 x i32> [[TMP2]], <i32 0, i32 poison, i32 0>
+; CHECK-NEXT:    [[T3:%.*]] = lshr <3 x i32> [[ARG]], <i32 1, i32 poison, i32 1>
+; CHECK-NEXT:    [[T4:%.*]] = and <3 x i32> [[T3]], <i32 1, i32 poison, i32 1>
+; CHECK-NEXT:    [[T5:%.*]] = select <3 x i1> [[T2]], <3 x i32> [[T4]], <3 x i32> <i32 1, i32 poison, i32 1>
 ; CHECK-NEXT:    ret <3 x i32> [[T5]]
 ;
   %t = and <3 x i32> %arg, %arg1
@@ -449,10 +522,6 @@ define <3 x i32> @f_var3_vec_poison(<3 x i32> %arg, <3 x i32> %arg1, <3 x i32> %
 
 ; One use only.
 
-declare void @use32(i32)
-
-declare void @use1(i1)
-
 define i32 @n_var0_oneuse(i32 %arg, i32 %arg1, i32 %arg2) {
 ; CHECK-LABEL: @n_var0_oneuse(
 ; CHECK-NEXT:    [[T:%.*]] = and i32 [[ARG:%.*]], [[ARG1:%.*]]
@@ -499,6 +568,27 @@ define i32 @n_var1_oneuse(i32 %arg, i32 %arg1) {
   ret i32 %t4
 }
 
+define i32 @lshr_oneuse_and_multiuse(i32 %arg) {
+; CHECK-LABEL: @lshr_oneuse_and_multiuse(
+; CHECK-NEXT:    [[T:%.*]] = and i32 [[ARG:%.*]], 5
+; CHECK-NEXT:    [[T3:%.*]] = icmp eq i32 [[T]], 0
+; CHECK-NEXT:    [[TMP1:%.*]] = lshr i32 [[ARG]], 1
+; CHECK-NEXT:    [[DOTLOBIT:%.*]] = and i32 [[TMP1]], 1
+; CHECK-NEXT:    [[T_LOBIT:%.*]] = select i1 [[T3]], i32 [[DOTLOBIT]], i32 1
+; CHECK-NEXT:    call void @use32(i32 [[T]])
+; CHECK-NEXT:    call void @use32(i32 [[DOTLOBIT]])
+; CHECK-NEXT:    ret i32 [[T_LOBIT]]
+;
+  %t = and i32 %arg, 5
+  %t3 = icmp eq i32 %t, 0
+  %t4 = lshr i32 %arg, 1
+  %t5 = and i32 %t4, 1
+  %t6 = select i1 %t3, i32 %t5, i32 1
+  call void @use32(i32 %t)
+  call void @use32(i32 %t5)
+  ret i32 %t6
+}
+
 ; Different variables are used
 
 define i32 @n0(i32 %arg, i32 %arg1) {


        


More information about the llvm-commits mailing list