[llvm] [ValueTracking] Improve `isImpliedCondCommonOperandWithConstants` to handle truncated LHS (PR #69829)

via llvm-commits llvm-commits at lists.llvm.org
Sat Oct 21 02:52:24 PDT 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-analysis

Author: Yingwei Zheng (dtcxzyw)

<details>
<summary>Changes</summary>

This patch improves `isImpliedCondCommonOperandWithConstants` to handle patterns like `L0 == trunc(R0)` or `R0 == trunc(L0)`. I am sure that we don't need to handle `sext/zext` cases since they have been handled by `InstCombinerImpl::foldAndOrOfICmpsUsingRanges`.

Alive2: https://alive2.llvm.org/ce/z/FLcm2h

Fixes #<!-- -->68514.


---
Full diff: https://github.com/llvm/llvm-project/pull/69829.diff


2 Files Affected:

- (modified) llvm/lib/Analysis/ValueTracking.cpp (+34-7) 
- (added) llvm/test/Analysis/ValueTracking/pr68514.ll (+70) 


``````````diff
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 8824a05e3aa6ccd..188faf00329ece2 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -8231,14 +8231,30 @@ isImpliedCondMatchingOperands(CmpInst::Predicate LPred,
   return std::nullopt;
 }
 
-/// Return true if "icmp LPred X, LC" implies "icmp RPred X, RC" is true.
-/// Return false if "icmp LPred X, LC" implies "icmp RPred X, RC" is false.
-/// Otherwise, return std::nullopt if we can't infer anything.
+/// Return true if "icmp LPred X, LC" implies "icmp RPred cast(X), RC" is true.
+/// Return false if "icmp LPred X, LC" implies "icmp RPred cast(X), RC" is
+/// false. Otherwise, return std::nullopt if we can't infer anything.
 static std::optional<bool> isImpliedCondCommonOperandWithConstants(
-    CmpInst::Predicate LPred, const APInt &LC, CmpInst::Predicate RPred,
-    const APInt &RC) {
+    const Value *L0, CmpInst::Predicate LPred, const APInt &LC, const Value *R0,
+    CmpInst::Predicate RPred, const APInt &RC) {
   ConstantRange DomCR = ConstantRange::makeExactICmpRegion(LPred, LC);
   ConstantRange CR = ConstantRange::makeExactICmpRegion(RPred, RC);
+
+  if (L0 == R0)
+    ; // noop
+  // Example: icmp eq X, 3 --> icmp sgt trunc(X), 2
+  else if (match(R0, m_Trunc(m_Specific(L0))))
+    DomCR = DomCR.truncate(RC.getBitWidth());
+  // Example: icmp slt trunc(X), 3 --> icmp ne X, 3
+  else if (match(L0, m_Trunc(m_Specific(R0)))) {
+    // Try to prove by negation
+    DomCR = DomCR.inverse();
+    CR = CR.inverse();
+    std::swap(DomCR, CR);
+    DomCR = DomCR.truncate(LC.getBitWidth());
+  } else
+    return std::nullopt;
+
   ConstantRange Intersection = DomCR.intersectWith(CR);
   ConstantRange Difference = DomCR.difference(CR);
   if (Intersection.isEmptySet())
@@ -8272,8 +8288,19 @@ static std::optional<bool> isImpliedCondICmps(const ICmpInst *LHS,
   // Can we infer anything when the 0-operands match and the 1-operands are
   // constants (not necessarily matching)?
   const APInt *LC, *RC;
-  if (L0 == R0 && match(L1, m_APInt(LC)) && match(R1, m_APInt(RC)))
-    return isImpliedCondCommonOperandWithConstants(LPred, *LC, RPred, *RC);
+  if (match(L1, m_APInt(LC)) && match(R1, m_APInt(RC))) {
+    if (auto Res = isImpliedCondCommonOperandWithConstants(L0, LPred, *LC, R0,
+                                                           RPred, *RC))
+      return Res;
+
+    if (match(L0, m_Trunc(m_Specific(R0)))) {
+      // When L0 == trunc(R0), we use the law of excluded middle to cover some
+      // missing cases.
+      if (auto Res = isImpliedCondCommonOperandWithConstants(
+              L0, LPred, *LC, R0, ICmpInst::getInversePredicate(RPred), *RC))
+        return !*Res;
+    }
+  }
 
   // L0 = R0 = L1 + R1, L0 >=u L1 implies R0 >=u R1, L0 <u L1 implies R0 <u R1
   if (ICmpInst::isUnsigned(LPred) && ICmpInst::isUnsigned(RPred)) {
diff --git a/llvm/test/Analysis/ValueTracking/pr68514.ll b/llvm/test/Analysis/ValueTracking/pr68514.ll
new file mode 100644
index 000000000000000..482cad942624aaa
--- /dev/null
+++ b/llvm/test/Analysis/ValueTracking/pr68514.ll
@@ -0,0 +1,70 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; Tests from PR68514
+define i1 @f(i32 %a) {
+; CHECK-LABEL: define i1 @f(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cmp = icmp eq i32 %a, 0
+  %b = trunc i32 %a to i16
+  %cmp2 = icmp sgt i16 %b, 2
+  %and9 = and i1 %cmp, %cmp2
+  ret i1 %and9
+}
+
+define i1 @g(i32 %a) {
+; CHECK-LABEL: define i1 @g(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[B:%.*]] = trunc i32 [[A]] to i16
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp sgt i16 [[B]], 2
+; CHECK-NEXT:    ret i1 [[CMP2]]
+;
+entry:
+  %cmp = icmp eq i32 %a, 3
+  %b = trunc i32 %a to i16
+  %cmp2 = icmp sgt i16 %b, 2
+  %or9 = or i1 %cmp, %cmp2
+  ret i1 %or9
+}
+
+; b = trunc(a)
+
+define i1 @fold_trunc(i32 %a) {
+; CHECK-LABEL: define i1 @fold_trunc(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[A]], 2
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+entry:
+  %cmp = icmp ugt i32 %a, 2
+  %b = trunc i32 %a to i16
+  %cmp2 = icmp ugt i16 %b, 2
+  %and9 = or i1 %cmp, %cmp2
+  ret i1 %and9
+}
+
+; Negative tests
+
+define i1 @nofold_trunc(i32 %a) {
+; CHECK-LABEL: define i1 @nofold_trunc(
+; CHECK-SAME: i32 [[A:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i32 [[A]], 3
+; CHECK-NEXT:    [[B:%.*]] = trunc i32 [[A]] to i16
+; CHECK-NEXT:    [[CMP2:%.*]] = icmp ugt i16 [[B]], 2
+; CHECK-NEXT:    [[AND9:%.*]] = or i1 [[CMP]], [[CMP2]]
+; CHECK-NEXT:    ret i1 [[AND9]]
+;
+entry:
+  %cmp = icmp ugt i32 %a, 3
+  %b = trunc i32 %a to i16
+  %cmp2 = icmp ugt i16 %b, 2
+  %and9 = or i1 %cmp, %cmp2
+  ret i1 %and9
+}

``````````

</details>


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


More information about the llvm-commits mailing list