Skip to content

[ValueTracking] Infer NonEqual from dominating conditions/assumptions #117442

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 12, 2025

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Nov 23, 2024

This patch adds context-sensitive analysis support for isKnownNonEqual. It is required for #117436.

@dtcxzyw dtcxzyw requested a review from nikic as a code owner November 23, 2024 16:31
@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Nov 23, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 23, 2024

@llvm/pr-subscribers-llvm-analysis

Author: Yingwei Zheng (dtcxzyw)

Changes

This patch adds context-sensitive analysis support for isKnownNonEqual. It is required for #117436.


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

2 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+47-2)
  • (modified) llvm/test/Transforms/InstCombine/icmp-dom.ll (+200)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index c48068afc04816..b45bfa2fd3d964 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -3748,6 +3748,50 @@ static bool isKnownNonEqual(const Value *V1, const Value *V2,
       match(V2, m_PtrToIntSameSize(Q.DL, m_Value(B))))
     return isKnownNonEqual(A, B, DemandedElts, Depth + 1, Q);
 
+  if (!Q.CxtI)
+    return false;
+
+  // Try to infer NonEqual based on information from dominating conditions.
+  if (Q.DC && Q.DT) {
+    for (BranchInst *BI : Q.DC->conditionsFor(V1)) {
+      Value *Cond = BI->getCondition();
+      BasicBlockEdge Edge0(BI->getParent(), BI->getSuccessor(0));
+      if (Q.DT->dominates(Edge0, Q.CxtI->getParent()) &&
+          isImpliedCondition(Cond, ICmpInst::ICMP_NE, V1, V2, Q.DL,
+                             /*LHSIsTrue=*/true, Depth)
+              .value_or(false))
+        return true;
+
+      BasicBlockEdge Edge1(BI->getParent(), BI->getSuccessor(1));
+      if (Q.DT->dominates(Edge1, Q.CxtI->getParent()) &&
+          isImpliedCondition(Cond, ICmpInst::ICMP_NE, V1, V2, Q.DL,
+                             /*LHSIsTrue=*/false, Depth)
+              .value_or(false))
+        return true;
+    }
+  }
+
+  if (!Q.AC)
+    return false;
+
+  // Try to infer NonEqual based on information from assumptions.
+  for (auto &AssumeVH : Q.AC->assumptionsFor(V1)) {
+    if (!AssumeVH)
+      continue;
+    CallInst *I = cast<CallInst>(AssumeVH);
+
+    assert(I->getFunction() == Q.CxtI->getFunction() &&
+           "Got assumption for the wrong function!");
+    assert(I->getIntrinsicID() == Intrinsic::assume &&
+           "must be an assume intrinsic");
+
+    if (isImpliedCondition(I->getArgOperand(0), ICmpInst::ICMP_NE, V1, V2, Q.DL,
+                           /*LHSIsTrue=*/true, Depth)
+            .value_or(false) &&
+        isValidAssumeForContext(I, Q.CxtI, Q.DT))
+      return true;
+  }
+
   return false;
 }
 
@@ -10037,10 +10081,10 @@ void llvm::findValuesAffectedByCondition(
         Worklist.push_back(B);
       }
     } else if (match(V, m_ICmp(Pred, m_Value(A), m_Value(B)))) {
-      AddCmpOperands(A, B);
-
       bool HasRHSC = match(B, m_ConstantInt());
       if (ICmpInst::isEquality(Pred)) {
+        AddAffected(A);
+        AddAffected(B);
         if (HasRHSC) {
           Value *Y;
           // (X & C) or (X | C) or (X ^ C).
@@ -10055,6 +10099,7 @@ void llvm::findValuesAffectedByCondition(
           }
         }
       } else {
+        AddCmpOperands(A, B);
         if (HasRHSC) {
           // Handle (A + C1) u< C2, which is the canonical form of
           // A > C3 && A < C4.
diff --git a/llvm/test/Transforms/InstCombine/icmp-dom.ll b/llvm/test/Transforms/InstCombine/icmp-dom.ll
index 3cf3a7af77041c..07793e8951de59 100644
--- a/llvm/test/Transforms/InstCombine/icmp-dom.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-dom.ll
@@ -534,3 +534,203 @@ else:
   %cmp1 = icmp eq i32 %and1, 0
   ret i1 %cmp1
 }
+
+; TODO: X != Y implies X | Y != 0
+define i1 @or_nonzero_from_nonequal(i8 %x, i8 %y) {
+; CHECK-LABEL: @or_nonzero_from_nonequal(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK:       if.else:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cond = icmp eq i8 %x, %y
+  br i1 %cond, label %if.else, label %if.then
+
+if.then:
+  %or = or i8 %x, %y
+  %cmp = icmp eq i8 %or, 0
+  ret i1 %cmp
+
+if.else:
+  ret i1 false
+}
+
+define i1 @test_nonequal_domcond1(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_domcond1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[COND2:%.*]] = icmp eq i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[COND1]], i1 true, i1 [[COND2]]
+; CHECK-NEXT:    br i1 [[OR_COND]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    ret i1 false
+; CHECK:       if.end:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cond1 = icmp eq i64 %y, %x
+  %cond2 = icmp eq i64 %w, %z
+  %or.cond = select i1 %cond1, i1 true, i1 %cond2
+  br i1 %or.cond, label %if.end, label %if.then
+
+if.then:
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+  ret i1 %cmp
+
+if.end:
+  ret i1 false
+}
+
+define i1 @test_nonequal_domcond2(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_domcond2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND1:%.*]] = icmp ne i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[COND1]], i1 [[COND2]], i1 false
+; CHECK-NEXT:    br i1 [[OR_COND]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    ret i1 false
+; CHECK:       if.end:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cond1 = icmp ne i64 %y, %x
+  %cond2 = icmp ne i64 %w, %z
+  %or.cond = select i1 %cond1, i1 %cond2, i1 false
+  br i1 %or.cond, label %if.then, label %if.end
+
+if.then:
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+  ret i1 %cmp
+
+if.end:
+  ret i1 false
+}
+
+define i1 @test_nonequal_assume(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_assume(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND1:%.*]] = icmp ne i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cond1 = icmp ne i64 %y, %x
+  call void @llvm.assume(i1 %cond1)
+  %cond2 = icmp ne i64 %w, %z
+  call void @llvm.assume(i1 %cond2)
+
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+  ret i1 %cmp
+}
+
+; Negative tests
+
+define i1 @test_nonequal_invalid_domcond1(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_invalid_domcond1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND1:%.*]] = icmp ne i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[COND2:%.*]] = icmp eq i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[COND1]], i1 true, i1 [[COND2]]
+; CHECK-NEXT:    br i1 [[OR_COND]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    ret i1 true
+; CHECK:       if.end:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cond1 = icmp ne i64 %y, %x
+  %cond2 = icmp eq i64 %w, %z
+  %or.cond = select i1 %cond1, i1 true, i1 %cond2
+  br i1 %or.cond, label %if.end, label %if.then
+
+if.then:
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+  ret i1 %cmp
+
+if.end:
+  ret i1 false
+}
+
+define i1 @test_nonequal_invalid_domcond2(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_invalid_domcond2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[COND2:%.*]] = icmp eq i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[COND1]], i1 true, i1 [[COND2]]
+; CHECK-NEXT:    br i1 [[OR_COND]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    br label [[IF_END]]
+; CHECK:       if.end:
+; CHECK-NEXT:    [[SUB1:%.*]] = sub i64 [[W]], [[Z]]
+; CHECK-NEXT:    [[SUB2:%.*]] = sub i64 [[Y]], [[X]]
+; CHECK-NEXT:    [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[SUB1]], i64 [[SUB2]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[UMIN]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+entry:
+  %cond1 = icmp eq i64 %y, %x
+  %cond2 = icmp eq i64 %w, %z
+  %or.cond = select i1 %cond1, i1 true, i1 %cond2
+  br i1 %or.cond, label %if.then, label %if.end
+
+if.then:
+  br label %if.end
+
+if.end:
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+  ret i1 %cmp
+}
+
+define i1 @test_nonequal_invalid_assume(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_invalid_assume(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB1:%.*]] = sub i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[SUB2:%.*]] = sub i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[SUB1]], i64 [[SUB2]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[UMIN]], 0
+; CHECK-NEXT:    call void @side_effect()
+; CHECK-NEXT:    [[COND1:%.*]] = icmp ne i64 [[Y]], [[X]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[W]], [[Z]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+entry:
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+
+  call void @side_effect()
+  %cond1 = icmp ne i64 %y, %x
+  call void @llvm.assume(i1 %cond1)
+  %cond2 = icmp ne i64 %w, %z
+  call void @llvm.assume(i1 %cond2)
+  ret i1 %cmp
+}
+
+declare void @side_effect()

@llvmbot
Copy link
Member

llvmbot commented Nov 23, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Yingwei Zheng (dtcxzyw)

Changes

This patch adds context-sensitive analysis support for isKnownNonEqual. It is required for #117436.


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

2 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+47-2)
  • (modified) llvm/test/Transforms/InstCombine/icmp-dom.ll (+200)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index c48068afc04816..b45bfa2fd3d964 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -3748,6 +3748,50 @@ static bool isKnownNonEqual(const Value *V1, const Value *V2,
       match(V2, m_PtrToIntSameSize(Q.DL, m_Value(B))))
     return isKnownNonEqual(A, B, DemandedElts, Depth + 1, Q);
 
+  if (!Q.CxtI)
+    return false;
+
+  // Try to infer NonEqual based on information from dominating conditions.
+  if (Q.DC && Q.DT) {
+    for (BranchInst *BI : Q.DC->conditionsFor(V1)) {
+      Value *Cond = BI->getCondition();
+      BasicBlockEdge Edge0(BI->getParent(), BI->getSuccessor(0));
+      if (Q.DT->dominates(Edge0, Q.CxtI->getParent()) &&
+          isImpliedCondition(Cond, ICmpInst::ICMP_NE, V1, V2, Q.DL,
+                             /*LHSIsTrue=*/true, Depth)
+              .value_or(false))
+        return true;
+
+      BasicBlockEdge Edge1(BI->getParent(), BI->getSuccessor(1));
+      if (Q.DT->dominates(Edge1, Q.CxtI->getParent()) &&
+          isImpliedCondition(Cond, ICmpInst::ICMP_NE, V1, V2, Q.DL,
+                             /*LHSIsTrue=*/false, Depth)
+              .value_or(false))
+        return true;
+    }
+  }
+
+  if (!Q.AC)
+    return false;
+
+  // Try to infer NonEqual based on information from assumptions.
+  for (auto &AssumeVH : Q.AC->assumptionsFor(V1)) {
+    if (!AssumeVH)
+      continue;
+    CallInst *I = cast<CallInst>(AssumeVH);
+
+    assert(I->getFunction() == Q.CxtI->getFunction() &&
+           "Got assumption for the wrong function!");
+    assert(I->getIntrinsicID() == Intrinsic::assume &&
+           "must be an assume intrinsic");
+
+    if (isImpliedCondition(I->getArgOperand(0), ICmpInst::ICMP_NE, V1, V2, Q.DL,
+                           /*LHSIsTrue=*/true, Depth)
+            .value_or(false) &&
+        isValidAssumeForContext(I, Q.CxtI, Q.DT))
+      return true;
+  }
+
   return false;
 }
 
@@ -10037,10 +10081,10 @@ void llvm::findValuesAffectedByCondition(
         Worklist.push_back(B);
       }
     } else if (match(V, m_ICmp(Pred, m_Value(A), m_Value(B)))) {
-      AddCmpOperands(A, B);
-
       bool HasRHSC = match(B, m_ConstantInt());
       if (ICmpInst::isEquality(Pred)) {
+        AddAffected(A);
+        AddAffected(B);
         if (HasRHSC) {
           Value *Y;
           // (X & C) or (X | C) or (X ^ C).
@@ -10055,6 +10099,7 @@ void llvm::findValuesAffectedByCondition(
           }
         }
       } else {
+        AddCmpOperands(A, B);
         if (HasRHSC) {
           // Handle (A + C1) u< C2, which is the canonical form of
           // A > C3 && A < C4.
diff --git a/llvm/test/Transforms/InstCombine/icmp-dom.ll b/llvm/test/Transforms/InstCombine/icmp-dom.ll
index 3cf3a7af77041c..07793e8951de59 100644
--- a/llvm/test/Transforms/InstCombine/icmp-dom.ll
+++ b/llvm/test/Transforms/InstCombine/icmp-dom.ll
@@ -534,3 +534,203 @@ else:
   %cmp1 = icmp eq i32 %and1, 0
   ret i1 %cmp1
 }
+
+; TODO: X != Y implies X | Y != 0
+define i1 @or_nonzero_from_nonequal(i8 %x, i8 %y) {
+; CHECK-LABEL: @or_nonzero_from_nonequal(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i8 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[IF_ELSE:%.*]], label [[IF_THEN:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    [[OR:%.*]] = or i8 [[X]], [[Y]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i8 [[OR]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK:       if.else:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cond = icmp eq i8 %x, %y
+  br i1 %cond, label %if.else, label %if.then
+
+if.then:
+  %or = or i8 %x, %y
+  %cmp = icmp eq i8 %or, 0
+  ret i1 %cmp
+
+if.else:
+  ret i1 false
+}
+
+define i1 @test_nonequal_domcond1(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_domcond1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[COND2:%.*]] = icmp eq i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[COND1]], i1 true, i1 [[COND2]]
+; CHECK-NEXT:    br i1 [[OR_COND]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    ret i1 false
+; CHECK:       if.end:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cond1 = icmp eq i64 %y, %x
+  %cond2 = icmp eq i64 %w, %z
+  %or.cond = select i1 %cond1, i1 true, i1 %cond2
+  br i1 %or.cond, label %if.end, label %if.then
+
+if.then:
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+  ret i1 %cmp
+
+if.end:
+  ret i1 false
+}
+
+define i1 @test_nonequal_domcond2(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_domcond2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND1:%.*]] = icmp ne i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[COND1]], i1 [[COND2]], i1 false
+; CHECK-NEXT:    br i1 [[OR_COND]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    ret i1 false
+; CHECK:       if.end:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cond1 = icmp ne i64 %y, %x
+  %cond2 = icmp ne i64 %w, %z
+  %or.cond = select i1 %cond1, i1 %cond2, i1 false
+  br i1 %or.cond, label %if.then, label %if.end
+
+if.then:
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+  ret i1 %cmp
+
+if.end:
+  ret i1 false
+}
+
+define i1 @test_nonequal_assume(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_assume(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND1:%.*]] = icmp ne i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cond1 = icmp ne i64 %y, %x
+  call void @llvm.assume(i1 %cond1)
+  %cond2 = icmp ne i64 %w, %z
+  call void @llvm.assume(i1 %cond2)
+
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+  ret i1 %cmp
+}
+
+; Negative tests
+
+define i1 @test_nonequal_invalid_domcond1(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_invalid_domcond1(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND1:%.*]] = icmp ne i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[COND2:%.*]] = icmp eq i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[COND1]], i1 true, i1 [[COND2]]
+; CHECK-NEXT:    br i1 [[OR_COND]], label [[IF_END:%.*]], label [[IF_THEN:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    ret i1 true
+; CHECK:       if.end:
+; CHECK-NEXT:    ret i1 false
+;
+entry:
+  %cond1 = icmp ne i64 %y, %x
+  %cond2 = icmp eq i64 %w, %z
+  %or.cond = select i1 %cond1, i1 true, i1 %cond2
+  br i1 %or.cond, label %if.end, label %if.then
+
+if.then:
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+  ret i1 %cmp
+
+if.end:
+  ret i1 false
+}
+
+define i1 @test_nonequal_invalid_domcond2(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_invalid_domcond2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[COND1:%.*]] = icmp eq i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[COND2:%.*]] = icmp eq i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[OR_COND:%.*]] = select i1 [[COND1]], i1 true, i1 [[COND2]]
+; CHECK-NEXT:    br i1 [[OR_COND]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+; CHECK:       if.then:
+; CHECK-NEXT:    br label [[IF_END]]
+; CHECK:       if.end:
+; CHECK-NEXT:    [[SUB1:%.*]] = sub i64 [[W]], [[Z]]
+; CHECK-NEXT:    [[SUB2:%.*]] = sub i64 [[Y]], [[X]]
+; CHECK-NEXT:    [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[SUB1]], i64 [[SUB2]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[UMIN]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+entry:
+  %cond1 = icmp eq i64 %y, %x
+  %cond2 = icmp eq i64 %w, %z
+  %or.cond = select i1 %cond1, i1 true, i1 %cond2
+  br i1 %or.cond, label %if.then, label %if.end
+
+if.then:
+  br label %if.end
+
+if.end:
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+  ret i1 %cmp
+}
+
+define i1 @test_nonequal_invalid_assume(i64 %x, i64 %y, i64 %z, i64 %w) {
+; CHECK-LABEL: @test_nonequal_invalid_assume(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[SUB1:%.*]] = sub i64 [[W:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[SUB2:%.*]] = sub i64 [[Y:%.*]], [[X:%.*]]
+; CHECK-NEXT:    [[UMIN:%.*]] = call i64 @llvm.umin.i64(i64 [[SUB1]], i64 [[SUB2]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i64 [[UMIN]], 0
+; CHECK-NEXT:    call void @side_effect()
+; CHECK-NEXT:    [[COND1:%.*]] = icmp ne i64 [[Y]], [[X]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[COND1]])
+; CHECK-NEXT:    [[COND2:%.*]] = icmp ne i64 [[W]], [[Z]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[COND2]])
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+entry:
+  %sub1 = sub i64 %w, %z
+  %sub2 = sub i64 %y, %x
+  %umin = call i64 @llvm.umin.i64(i64 %sub1, i64 %sub2)
+  %cmp = icmp eq i64 %umin, 0
+
+  call void @side_effect()
+  %cond1 = icmp ne i64 %y, %x
+  call void @llvm.assume(i1 %cond1)
+  %cond2 = icmp ne i64 %w, %z
+  call void @llvm.assume(i1 %cond2)
+  ret i1 %cmp
+}
+
+declare void @side_effect()

bool HasRHSC = match(B, m_ConstantInt());
if (ICmpInst::isEquality(Pred)) {
AddAffected(A);
AddAffected(B);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the impact here mainly from adding more affected values or from using them in isKnownNonEqual?

Something we could do is add a bitfield for what the affected value is for (e.g. knownbits, etc), so we can quickly skip ones that are not relevant.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is mainly from adding more affected values.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something we could do is add a bitfield for what the affected value is for (e.g. knownbits, etc), so we can quickly skip ones that are not relevant.

Are you working on this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I'm not working on this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@artagnon
Copy link
Contributor

Just a thought, but can we do this in ConstraintElimination instead?

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Nov 26, 2024

Just a thought, but can we do this in ConstraintElimination instead?

It seems that ConstraintElimination is weak at handling the non-equality relations, right?

@artagnon
Copy link
Contributor

Just a thought, but can we do this in ConstraintElimination instead?

It seems that ConstraintElimination is weak at handling the non-equality relations, right?

I think so too, but am unsure if we want to make it stronger for handling non-equality relations. Otherwise, you can probably squash the regression by implementing @nikic's suggestion, and this patch should be good to go.

@nikic
Copy link
Contributor

nikic commented Dec 3, 2024

Compile-time impact has increased with the SQ migration (presumably because DC is available more now): http://llvm-compile-time-tracker.com/compare.php?from=22417ec6cca0ed8ccecb0c2b77011e591378fd2a&to=8f3cf204bb91ce09fb3651b8a7e770de7e603b2f&stat=instructions%3Au But also more optimization impact now.

Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please land the migration of isKnownNonEqual to SimplifyQuery separately?

dtcxzyw added a commit that referenced this pull request Feb 1, 2025
github-actions bot pushed a commit to arm/arm-toolchain that referenced this pull request Feb 1, 2025
if (isImpliedCondition(I->getArgOperand(0), ICmpInst::ICMP_NE, V1, V2, Q.DL,
/*LHSIsTrue=*/true, Depth)
.value_or(false) &&
isValidAssumeForContext(I, Q.CxtI, Q.DT))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that assume is valid before recursive function?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.value_or(false) &&
isValidAssumeForContext(I, Q.CxtI, Q.DT))
return true;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe nicer to have a helper isKnownNonEqualFromContext?

return false;

// Try to infer NonEqual based on information from assumptions.
for (auto &AssumeVH : Q.AC->assumptionsFor(V1)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and above, any value in iterating through V2 as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We add both V1 and V2 in findValuesAffectedByCondition. So it is ok to iterate on conditions related to V1.

@nikic
Copy link
Contributor

nikic commented Feb 6, 2025

Could you please rebase now that the SimplifyQuery change landed? I'm generally inclined to just go ahead with this change, the compile-time impact is minor and this adds useful functionality. Only thing I'd check is if not using isImpliedCondition would help mitigate the impact (and only handle the trivial icmp eq/ne with matching operands case).

@nikic
Copy link
Contributor

nikic commented Feb 6, 2025

Ah, I see this is stuck in "Processing updates". You'll have to rebase and push again to get GitHub unstuck...

@artagnon
Copy link
Contributor

artagnon commented Feb 6, 2025

Ah, I see this is stuck in "Processing updates". You'll have to rebase and push again to get GitHub unstuck...

That doesn't help either. If it's stuck for over 24h, just close it and open a duplicate: I think there was a recent bug introduced in the GitHub codebase, because status.github.com reports all green.

@dtcxzyw dtcxzyw force-pushed the perf/nonequal-domcond branch from 0c92658 to 4e8b027 Compare February 7, 2025 06:38
Copy link
Contributor

@nikic nikic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Only thing I'd check is if not using isImpliedCondition would help mitigate the impact (and only handle the trivial icmp eq/ne with matching operands case).

I confirmed this makes no difference.

@dtcxzyw dtcxzyw force-pushed the perf/nonequal-domcond branch from 4e8b027 to 8b5145c Compare February 12, 2025 10:21
@dtcxzyw dtcxzyw merged commit 324e27e into llvm:main Feb 12, 2025
8 checks passed
@dtcxzyw dtcxzyw deleted the perf/nonequal-domcond branch February 12, 2025 12:15
flovent pushed a commit to flovent/llvm-project that referenced this pull request Feb 13, 2025
…llvm#117442)

This patch adds context-sensitive analysis support for
`isKnownNonEqual`. It is required for
llvm#117436.
joaosaffran pushed a commit to joaosaffran/llvm-project that referenced this pull request Feb 14, 2025
…llvm#117442)

This patch adds context-sensitive analysis support for
`isKnownNonEqual`. It is required for
llvm#117436.
sivan-shani pushed a commit to sivan-shani/llvm-project that referenced this pull request Feb 24, 2025
…llvm#117442)

This patch adds context-sensitive analysis support for
`isKnownNonEqual`. It is required for
llvm#117436.
dtcxzyw added a commit that referenced this pull request Apr 18, 2025
This patch avoids adding RHS for comparisons with two variable operands
(#118493 (comment)).
Instead, we iterate over related dominating conditions of both V1 and V2
in `isKnownNonEqualFromContext`, as suggested by goldsteinn
(#117442 (comment)).

Compile-time improvement:
https://llvm-compile-time-tracker.com/compare.php?from=c6d95c441a29a45782ff72d6cb82839b86fd0e4a&to=88464baedd7b1731281eaa0ce4438122b4d218a7&stat=instructions:u
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Apr 18, 2025
…7388)

This patch avoids adding RHS for comparisons with two variable operands
(llvm/llvm-project#118493 (comment)).
Instead, we iterate over related dominating conditions of both V1 and V2
in `isKnownNonEqualFromContext`, as suggested by goldsteinn
(llvm/llvm-project#117442 (comment)).

Compile-time improvement:
https://llvm-compile-time-tracker.com/compare.php?from=c6d95c441a29a45782ff72d6cb82839b86fd0e4a&to=88464baedd7b1731281eaa0ce4438122b4d218a7&stat=instructions:u
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
This patch avoids adding RHS for comparisons with two variable operands
(llvm#118493 (comment)).
Instead, we iterate over related dominating conditions of both V1 and V2
in `isKnownNonEqualFromContext`, as suggested by goldsteinn
(llvm#117442 (comment)).

Compile-time improvement:
https://llvm-compile-time-tracker.com/compare.php?from=c6d95c441a29a45782ff72d6cb82839b86fd0e4a&to=88464baedd7b1731281eaa0ce4438122b4d218a7&stat=instructions:u
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
This patch avoids adding RHS for comparisons with two variable operands
(llvm#118493 (comment)).
Instead, we iterate over related dominating conditions of both V1 and V2
in `isKnownNonEqualFromContext`, as suggested by goldsteinn
(llvm#117442 (comment)).

Compile-time improvement:
https://llvm-compile-time-tracker.com/compare.php?from=c6d95c441a29a45782ff72d6cb82839b86fd0e4a&to=88464baedd7b1731281eaa0ce4438122b4d218a7&stat=instructions:u
IanWood1 pushed a commit to IanWood1/llvm-project that referenced this pull request May 6, 2025
This patch avoids adding RHS for comparisons with two variable operands
(llvm#118493 (comment)).
Instead, we iterate over related dominating conditions of both V1 and V2
in `isKnownNonEqualFromContext`, as suggested by goldsteinn
(llvm#117442 (comment)).

Compile-time improvement:
https://llvm-compile-time-tracker.com/compare.php?from=c6d95c441a29a45782ff72d6cb82839b86fd0e4a&to=88464baedd7b1731281eaa0ce4438122b4d218a7&stat=instructions:u
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants