[llvm] [ValueTracking] Support trunc nuw condition in isImpliedCondition (PR #141528)

Andreas Jonson via llvm-commits llvm-commits at lists.llvm.org
Mon May 26 13:39:48 PDT 2025


https://github.com/andjo403 created https://github.com/llvm/llvm-project/pull/141528

Many trunc nuw conditions is implied/implies icmp instructions.

Proof: https://alive2.llvm.org/ce/z/oqQyxC

cc https://github.com/llvm/llvm-project/issues/134093

>From 6774f1623fd778b1fad98bf9842ac3359286555a Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Mon, 26 May 2025 21:45:24 +0200
Subject: [PATCH 1/4] [ValueTracking] Test trunc nuw condition in
 isImpliedCondition (NFC)

---
 .../select-safe-impliedcond-transforms.ll     | 90 +++++++++++++++++++
 1 file changed, 90 insertions(+)

diff --git a/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll b/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll
index 4b66b71773683..d8eda76cf649a 100644
--- a/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll
+++ b/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll
@@ -176,3 +176,93 @@ define i1 @a_false_implies_b_false2_comm(i8 %z, i1 %X, i1 %Y) {
   %res = or i1 %sel, %a
   ret i1 %res
 }
+
+define i1 @trunc_nuw_implies_icmp_eq(i8 %x, i1 %c) {
+; CHECK-LABEL: @trunc_nuw_implies_icmp_eq(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw i8 [[X:%.*]] to i1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], 0
+; CHECK-NEXT:    [[C:%.*]] = select i1 [[CMP]], i1 [[C1:%.*]], i1 false
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[TRUNC]], i1 true, i1 [[C]]
+; CHECK-NEXT:    ret i1 [[SEL2]]
+;
+  %trunc = trunc nuw i8 %x to i1
+  %cmp = icmp eq i8 %x, 0
+  %sel1 = select i1 %cmp, i1 %c, i1 false
+  %sel2 = select i1 %trunc, i1 true, i1 %sel1
+  ret i1 %sel2
+}
+
+define i1 @icmp_eq_implies_trunc_nuw(i8 %x, i1 %c) {
+; CHECK-LABEL: @icmp_eq_implies_trunc_nuw(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw i8 [[X:%.*]] to i1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], 0
+; CHECK-NEXT:    [[C:%.*]] = select i1 [[TRUNC]], i1 [[C1:%.*]], i1 false
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[CMP]], i1 true, i1 [[C]]
+; CHECK-NEXT:    ret i1 [[SEL2]]
+;
+  %trunc = trunc nuw i8 %x to i1
+  %cmp = icmp eq i8 %x, 0
+  %sel1 = select i1 %trunc, i1 %c, i1 false
+  %sel2 = select i1 %cmp, i1 true, i1 %sel1
+  ret i1 %sel2
+}
+
+define <2 x i1>  @trunc_nuw_implies_icmp_eq_vec(<2 x i8>  %x, <2 x i1> %c) {
+; CHECK-LABEL: @trunc_nuw_implies_icmp_eq_vec(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw <2 x i8> [[X:%.*]] to <2 x i1>
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[X]], zeroinitializer
+; CHECK-NEXT:    [[C:%.*]] = select <2 x i1> [[CMP]], <2 x i1> [[C1:%.*]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    [[SEL2:%.*]] = select <2 x i1> [[TRUNC]], <2 x i1> splat (i1 true), <2 x i1> [[C]]
+; CHECK-NEXT:    ret <2 x i1> [[SEL2]]
+;
+  %trunc = trunc nuw <2 x i8>  %x to <2 x i1>
+  %cmp = icmp eq <2 x i8>  %x, splat (i8 0)
+  %sel1 = select <2 x i1> %cmp, <2 x i1> %c, <2 x i1> splat (i1 false)
+  %sel2 = select <2 x i1> %trunc, <2 x i1> splat (i1 true), <2 x i1> %sel1
+  ret <2 x i1> %sel2
+}
+
+define <2 x i1>  @icmp_eq_implies_trunc_nuw_vec(<2 x i8>  %x, <2 x i1> %c) {
+; CHECK-LABEL: @icmp_eq_implies_trunc_nuw_vec(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw <2 x i8> [[X:%.*]] to <2 x i1>
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[X]], zeroinitializer
+; CHECK-NEXT:    [[C:%.*]] = select <2 x i1> [[TRUNC]], <2 x i1> [[C1:%.*]], <2 x i1> zeroinitializer
+; CHECK-NEXT:    [[SEL2:%.*]] = select <2 x i1> [[CMP]], <2 x i1> splat (i1 true), <2 x i1> [[C]]
+; CHECK-NEXT:    ret <2 x i1> [[SEL2]]
+;
+  %trunc = trunc nuw <2 x i8>  %x to <2 x i1>
+  %cmp = icmp eq <2 x i8>  %x, splat (i8 0)
+  %sel1 = select <2 x i1> %trunc, <2 x i1> %c, <2 x i1> splat (i1 false)
+  %sel2 = select <2 x i1> %cmp, <2 x i1> splat (i1 true), <2 x i1> %sel1
+  ret <2 x i1> %sel2
+}
+
+define i1 @neg_trunc_implies_icmp_eq(i8 %x, i1 %c) {
+; CHECK-LABEL: @neg_trunc_implies_icmp_eq(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[X:%.*]] to i1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], 0
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[CMP]], i1 [[C:%.*]], i1 false
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[TRUNC]], i1 true, i1 [[SEL1]]
+; CHECK-NEXT:    ret i1 [[SEL2]]
+;
+  %trunc = trunc i8 %x to i1
+  %cmp = icmp eq i8 %x, 0
+  %sel1 = select i1 %cmp, i1 %c, i1 false
+  %sel2 = select i1 %trunc, i1 true, i1 %sel1
+  ret i1 %sel2
+}
+
+define i1 @neg_icmp_eq_implies_trunc(i8 %x, i1 %c) {
+; CHECK-LABEL: @neg_icmp_eq_implies_trunc(
+; CHECK-NEXT:    [[TRUNC:%.*]] = trunc i8 [[X:%.*]] to i1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], 0
+; CHECK-NEXT:    [[SEL1:%.*]] = select i1 [[TRUNC]], i1 [[C:%.*]], i1 false
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[CMP]], i1 true, i1 [[SEL1]]
+; CHECK-NEXT:    ret i1 [[SEL2]]
+;
+  %trunc = trunc i8 %x to i1
+  %cmp = icmp eq i8 %x, 0
+  %sel1 = select i1 %trunc, i1 %c, i1 false
+  %sel2 = select i1 %cmp, i1 true, i1 %sel1
+  ret i1 %sel2
+}

>From bd6627cfc74a0db56be3f0626a05cee78cd84f65 Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Mon, 26 May 2025 21:49:04 +0200
Subject: [PATCH 2/4] [ValueTracking] Decompose ICmpInst argument in
 isImpliedCondICmps (NFC)

---
 llvm/lib/Analysis/ValueTracking.cpp | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 5b9338b500ecc..942b5f5dc8c98 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9165,15 +9165,13 @@ isImpliedCondCommonOperandWithCR(CmpPredicate LPred, const ConstantRange &LCR,
 /// is true.  Return false if LHS implies RHS is false. Otherwise, return
 /// std::nullopt if we can't infer anything.
 static std::optional<bool>
-isImpliedCondICmps(const ICmpInst *LHS, CmpPredicate RPred, const Value *R0,
-                   const Value *R1, const DataLayout &DL, bool LHSIsTrue) {
-  Value *L0 = LHS->getOperand(0);
-  Value *L1 = LHS->getOperand(1);
-
+isImpliedCondICmps(CmpPredicate LPred, const Value *L0, const Value *L1,
+                   CmpPredicate RPred, const Value *R0, const Value *R1,
+                   const DataLayout &DL, bool LHSIsTrue) {
   // The rest of the logic assumes the LHS condition is true.  If that's not the
   // case, invert the predicate to make it so.
-  CmpPredicate LPred =
-      LHSIsTrue ? LHS->getCmpPredicate() : LHS->getInverseCmpPredicate();
+  if (!LHSIsTrue)
+    LPred = ICmpInst::getInverseCmpPredicate(LPred);
 
   // We can have non-canonical operands, so try to normalize any common operand
   // to L0/R0.
@@ -9314,9 +9312,10 @@ llvm::isImpliedCondition(const Value *LHS, CmpPredicate RHSPred,
     LHSIsTrue = !LHSIsTrue;
 
   // Both LHS and RHS are icmps.
-  const ICmpInst *LHSCmp = dyn_cast<ICmpInst>(LHS);
-  if (LHSCmp)
-    return isImpliedCondICmps(LHSCmp, RHSPred, RHSOp0, RHSOp1, DL, LHSIsTrue);
+  if (const auto *LHSCmp = dyn_cast<ICmpInst>(LHS))
+    return isImpliedCondICmps(LHSCmp->getCmpPredicate(), LHSCmp->getOperand(0),
+                              LHSCmp->getOperand(1), RHSPred, RHSOp0, RHSOp1,
+                              DL, LHSIsTrue);
 
   /// The LHS should be an 'or', 'and', or a 'select' instruction.  We expect
   /// the RHS to be an icmp.

>From 701603f6d755b54cf559fc1184c7037b397ec70f Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Mon, 26 May 2025 22:00:57 +0200
Subject: [PATCH 3/4] [ValueTracking] Support trunc nuw condition in
 isImpliedCondition LHS argument.

---
 llvm/lib/Analysis/ValueTracking.cpp                       | 5 +++++
 .../InstCombine/select-safe-impliedcond-transforms.ll     | 8 ++------
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 942b5f5dc8c98..d585b55f02401 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9316,6 +9316,11 @@ llvm::isImpliedCondition(const Value *LHS, CmpPredicate RHSPred,
     return isImpliedCondICmps(LHSCmp->getCmpPredicate(), LHSCmp->getOperand(0),
                               LHSCmp->getOperand(1), RHSPred, RHSOp0, RHSOp1,
                               DL, LHSIsTrue);
+  const Value *V;
+  if (match(LHS, m_NUWTrunc(m_Value(V))))
+    return isImpliedCondICmps(CmpInst::ICMP_NE, V,
+                              ConstantInt::get(V->getType(), 0), RHSPred,
+                              RHSOp0, RHSOp1, DL, LHSIsTrue);
 
   /// The LHS should be an 'or', 'and', or a 'select' instruction.  We expect
   /// the RHS to be an icmp.
diff --git a/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll b/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll
index d8eda76cf649a..517bbad90dc30 100644
--- a/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll
+++ b/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll
@@ -180,9 +180,7 @@ define i1 @a_false_implies_b_false2_comm(i8 %z, i1 %X, i1 %Y) {
 define i1 @trunc_nuw_implies_icmp_eq(i8 %x, i1 %c) {
 ; CHECK-LABEL: @trunc_nuw_implies_icmp_eq(
 ; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw i8 [[X:%.*]] to i1
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], 0
-; CHECK-NEXT:    [[C:%.*]] = select i1 [[CMP]], i1 [[C1:%.*]], i1 false
-; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[TRUNC]], i1 true, i1 [[C]]
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[TRUNC]], i1 true, i1 [[C:%.*]]
 ; CHECK-NEXT:    ret i1 [[SEL2]]
 ;
   %trunc = trunc nuw i8 %x to i1
@@ -210,9 +208,7 @@ define i1 @icmp_eq_implies_trunc_nuw(i8 %x, i1 %c) {
 define <2 x i1>  @trunc_nuw_implies_icmp_eq_vec(<2 x i8>  %x, <2 x i1> %c) {
 ; CHECK-LABEL: @trunc_nuw_implies_icmp_eq_vec(
 ; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw <2 x i8> [[X:%.*]] to <2 x i1>
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[X]], zeroinitializer
-; CHECK-NEXT:    [[C:%.*]] = select <2 x i1> [[CMP]], <2 x i1> [[C1:%.*]], <2 x i1> zeroinitializer
-; CHECK-NEXT:    [[SEL2:%.*]] = select <2 x i1> [[TRUNC]], <2 x i1> splat (i1 true), <2 x i1> [[C]]
+; CHECK-NEXT:    [[SEL2:%.*]] = select <2 x i1> [[TRUNC]], <2 x i1> splat (i1 true), <2 x i1> [[C:%.*]]
 ; CHECK-NEXT:    ret <2 x i1> [[SEL2]]
 ;
   %trunc = trunc nuw <2 x i8>  %x to <2 x i1>

>From e35a12f95c5b860dfac8d7013f96100089a9255e Mon Sep 17 00:00:00 2001
From: Andreas Jonson <andjo403 at hotmail.com>
Date: Mon, 26 May 2025 22:10:52 +0200
Subject: [PATCH 4/4] [ValueTracking] Support trunc nuw condition in
 isImpliedCondition RHS argument.

---
 llvm/lib/Analysis/ValueTracking.cpp                  |  9 +++++++++
 .../select-safe-impliedcond-transforms.ll            | 12 ++++--------
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index d585b55f02401..b7084cf3619f3 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9358,6 +9358,15 @@ std::optional<bool> llvm::isImpliedCondition(const Value *LHS, const Value *RHS,
     return std::nullopt;
   }
 
+  const Value *V;
+  if (match(RHS, m_NUWTrunc(m_Value(V)))) {
+    if (auto Implied = isImpliedCondition(LHS, CmpInst::ICMP_NE, V,
+                                          ConstantInt::get(V->getType(), 0), DL,
+                                          LHSIsTrue, Depth))
+      return InvertRHS ? !*Implied : *Implied;
+    return std::nullopt;
+  }
+
   if (Depth == MaxAnalysisRecursionDepth)
     return std::nullopt;
 
diff --git a/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll b/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll
index 517bbad90dc30..ba34930dc14c1 100644
--- a/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll
+++ b/llvm/test/Transforms/InstCombine/select-safe-impliedcond-transforms.ll
@@ -192,10 +192,8 @@ define i1 @trunc_nuw_implies_icmp_eq(i8 %x, i1 %c) {
 
 define i1 @icmp_eq_implies_trunc_nuw(i8 %x, i1 %c) {
 ; CHECK-LABEL: @icmp_eq_implies_trunc_nuw(
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw i8 [[X:%.*]] to i1
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X]], 0
-; CHECK-NEXT:    [[C:%.*]] = select i1 [[TRUNC]], i1 [[C1:%.*]], i1 false
-; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[CMP]], i1 true, i1 [[C]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[X:%.*]], 0
+; CHECK-NEXT:    [[SEL2:%.*]] = select i1 [[CMP]], i1 true, i1 [[C:%.*]]
 ; CHECK-NEXT:    ret i1 [[SEL2]]
 ;
   %trunc = trunc nuw i8 %x to i1
@@ -220,10 +218,8 @@ define <2 x i1>  @trunc_nuw_implies_icmp_eq_vec(<2 x i8>  %x, <2 x i1> %c) {
 
 define <2 x i1>  @icmp_eq_implies_trunc_nuw_vec(<2 x i8>  %x, <2 x i1> %c) {
 ; CHECK-LABEL: @icmp_eq_implies_trunc_nuw_vec(
-; CHECK-NEXT:    [[TRUNC:%.*]] = trunc nuw <2 x i8> [[X:%.*]] to <2 x i1>
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[X]], zeroinitializer
-; CHECK-NEXT:    [[C:%.*]] = select <2 x i1> [[TRUNC]], <2 x i1> [[C1:%.*]], <2 x i1> zeroinitializer
-; CHECK-NEXT:    [[SEL2:%.*]] = select <2 x i1> [[CMP]], <2 x i1> splat (i1 true), <2 x i1> [[C]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i8> [[X:%.*]], zeroinitializer
+; CHECK-NEXT:    [[SEL2:%.*]] = select <2 x i1> [[CMP]], <2 x i1> splat (i1 true), <2 x i1> [[C:%.*]]
 ; CHECK-NEXT:    ret <2 x i1> [[SEL2]]
 ;
   %trunc = trunc nuw <2 x i8>  %x to <2 x i1>



More information about the llvm-commits mailing list