Skip to content

[ValueTracking] Refactor isKnownNonEqualFromContext #127388

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 2 commits into from
Apr 18, 2025

Conversation

dtcxzyw
Copy link
Member

@dtcxzyw dtcxzyw commented Feb 16, 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

@dtcxzyw dtcxzyw requested a review from nikic as a code owner February 16, 2025 11:10
@llvmbot llvmbot added the llvm:analysis Includes value tracking, cost tables and constant folding label Feb 16, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 16, 2025

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-analysis

Author: Yingwei Zheng (dtcxzyw)

Changes

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


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

1 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+61-44)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 2a49a10447e0b..c1d3e9fd7e010 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -3786,6 +3786,63 @@ static bool isNonEqualPointersWithRecursiveGEP(const Value *A, const Value *B,
           (StartOffset.sle(OffsetB) && StepOffset.isNegative()));
 }
 
+static bool isKnownNonEqualFromContext(const Value *V1, const Value *V2,
+                                       unsigned Depth, const SimplifyQuery &Q) {
+  if (!Q.CxtI)
+    return false;
+
+  // Try to infer NonEqual based on information from dominating conditions.
+  if (Q.DC && Q.DT) {
+    auto IsKnownNonEqualFromDominatingCondition = [&](const Value *V) {
+      for (BranchInst *BI : Q.DC->conditionsFor(V)) {
+        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;
+      }
+
+      return false;
+    };
+
+    if (IsKnownNonEqualFromDominatingCondition(V1) ||
+        IsKnownNonEqualFromDominatingCondition(V2))
+      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;
+}
+
 /// Return true if it is known that V1 != V2.
 static bool isKnownNonEqual(const Value *V1, const Value *V2,
                             const APInt &DemandedElts, unsigned Depth,
@@ -3857,49 +3914,8 @@ 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;
-  }
+  if (isKnownNonEqualFromContext(V1, V2, Depth, Q))
+    return true;
 
   return false;
 }
@@ -10278,7 +10294,8 @@ void llvm::findValuesAffectedByCondition(
       bool HasRHSC = match(B, m_ConstantInt());
       if (ICmpInst::isEquality(Pred)) {
         AddAffected(A);
-        AddAffected(B);
+        if (IsAssume)
+          AddAffected(B);
         if (HasRHSC) {
           Value *Y;
           // (X & C) or (X | C).

Copy link
Contributor

@andjo403 andjo403 left a comment

Choose a reason for hiding this comment

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

Looks good to me

Copy link
Contributor

@artagnon artagnon left a comment

Choose a reason for hiding this comment

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

Could you kindly add the NFC tag to the title? It's not obvious that this patch is an NFC, although the patch summary has an explanation.

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.

What's the reason for the additional diffs on llvm-opt-benchmark?

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Feb 16, 2025

Could you kindly add the NFC tag to the title? It's not obvious that this patch is an NFC, although the patch summary has an explanation.

It is not NFC. See dtcxzyw/llvm-opt-benchmark#2121. The net effect is positive.

@artagnon
Copy link
Contributor

Could you kindly add the NFC tag to the title? It's not obvious that this patch is an NFC, although the patch summary has an explanation.

It is not NFC. See dtcxzyw/llvm-opt-benchmark#2121. The net effect is positive.

Could add some tests extracted from the diffs?

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Feb 17, 2025

Case 1 (regression):

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

define void @Dau_DecFindSets_int(i32 %0, i1 %1) {
  br label %3

3:                                                ; preds = %Dau_DecCheckSetTop.exit, %2
  %.081 = phi i32 [ 0, %2 ], [ %0, %Dau_DecCheckSetTop.exit ]
  br label %Dau_DecCheckSetTop.exit

Dau_DecCheckSetTop.exit:                          ; preds = %._crit_edge.i.i, %4, %3
  br i1 %1, label %4, label %3

4:                                                ; preds = %Dau_DecCheckSetTop.exit
  %5 = and i32 %0, 1
  %6 = icmp eq i32 %5, %.081
  br i1 %6, label %7, label %Dau_DecCheckSetTop.exit

7:                                                ; preds = %4
  br label %._crit_edge.i.i

._crit_edge.i.i:                                  ; preds = %7
  %.not.i.i = icmp eq i32 %.081, 31
  br i1 %.not.i.i, label %.preheader.i.i, label %Dau_DecCheckSetTop.exit

.preheader.i.i:                                   ; preds = %._crit_edge.i.i
  ret void
}

With this patch, we do not add %.081 for the condition %6 = icmp eq i32 %5, %.081. Thus %.not.i.i = icmp eq i32 %.081, 31 cannot be folded into false.

@dtcxzyw
Copy link
Member Author

dtcxzyw commented Feb 17, 2025

Case 2 (improvement):

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

define void @_ZN3net17HpackHuffmanTable17BuildDecodeTablesERKSt6vectorINS_18HpackHuffmanSymbolESaIS2_EE(ptr %0) {
entry:
  %1 = load i8, ptr %0, align 1
  br label %while.cond

while.cond:                                       ; preds = %for.body125, %while.cond, %entry
  %2 = load i8, ptr %0, align 1
  %cmp97.not = icmp eq i8 %2, 0
  %cmp101 = icmp uge i8 %2, %1
  %or.cond.not1 = select i1 %cmp97.not, i1 true, i1 %cmp101
  br i1 %or.cond.not1, label %while.cond, label %for.body125

for.body125:                                      ; preds = %for.body125, %while.cond
  %cmp124.not.old = icmp eq i8 %1, %2
  call void @use(i1 %cmp124.not.old)
  br i1 %cmp124.not.old, label %while.cond, label %for.body125
}

declare void @use(i1)

With this patch, we iterate over related dominating conditions of %2 (%or.cond.not1) and fold %cmp124.not.old into false.

@llvmbot llvmbot added llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:transforms labels Feb 17, 2025
Copy link
Contributor

@artagnon artagnon left a comment

Choose a reason for hiding this comment

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

LGTM, but I'm not sure if @nikic would assess the regression differently.

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.

Okay, so the differences here are because isImpliedCondition can also use things other than equality conditions, and we may pick up different "unrelated" conditions depending on how we query things.

I don't really have a strong opinion on how this query is done, so this approach is fine by me.

@dtcxzyw dtcxzyw merged commit b1b065f into llvm:main Apr 18, 2025
10 checks passed
@dtcxzyw dtcxzyw deleted the perf/non-equal-one-side branch April 18, 2025 14:14
@llvm-ci
Copy link
Collaborator

llvm-ci commented Apr 18, 2025

LLVM Buildbot has detected a new failure on builder arc-builder running on arc-worker while building llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/3/builds/14720

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'lit :: googletest-timeout.py' FAILED ********************
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 9
not env -u FILECHECK_OPTS "/usr/local/bin/python3.9" /buildbot/worker/arc-folder/llvm-project/llvm/utils/lit/lit.py -j1 --order=lexical -v Inputs/googletest-timeout    --param gtest_filter=InfiniteLoopSubTest --timeout=1 > /buildbot/worker/arc-folder/build/utils/lit/tests/Output/googletest-timeout.py.tmp.cmd.out
# executed command: not env -u FILECHECK_OPTS /usr/local/bin/python3.9 /buildbot/worker/arc-folder/llvm-project/llvm/utils/lit/lit.py -j1 --order=lexical -v Inputs/googletest-timeout --param gtest_filter=InfiniteLoopSubTest --timeout=1
# .---command stderr------------
# | lit.py: /buildbot/worker/arc-folder/llvm-project/llvm/utils/lit/lit/main.py:72: note: The test suite configuration requested an individual test timeout of 0 seconds but a timeout of 1 seconds was requested on the command line. Forcing timeout to be 1 seconds.
# `-----------------------------
# RUN: at line 11
FileCheck --check-prefix=CHECK-INF < /buildbot/worker/arc-folder/build/utils/lit/tests/Output/googletest-timeout.py.tmp.cmd.out /buildbot/worker/arc-folder/build/utils/lit/tests/googletest-timeout.py
# executed command: FileCheck --check-prefix=CHECK-INF /buildbot/worker/arc-folder/build/utils/lit/tests/googletest-timeout.py
# RUN: at line 16
not env -u FILECHECK_OPTS "/usr/local/bin/python3.9" /buildbot/worker/arc-folder/llvm-project/llvm/utils/lit/lit.py -j1 --order=lexical -v Inputs/googletest-timeout   --param gtest_filter=InfiniteLoopSubTest  --param set_timeout=1   > /buildbot/worker/arc-folder/build/utils/lit/tests/Output/googletest-timeout.py.tmp.cfgset.out
# executed command: not env -u FILECHECK_OPTS /usr/local/bin/python3.9 /buildbot/worker/arc-folder/llvm-project/llvm/utils/lit/lit.py -j1 --order=lexical -v Inputs/googletest-timeout --param gtest_filter=InfiniteLoopSubTest --param set_timeout=1
# .---command stderr------------
# | Traceback (most recent call last):
# |   File "/buildbot/worker/arc-folder/llvm-project/llvm/utils/lit/lit/formats/googletest.py", line 304, in post_process_shard_results
# |     testsuites = json.load(f)["testsuites"]
# |   File "/usr/local/lib/python3.9/json/__init__.py", line 293, in load
# |     return loads(fp.read(),
# |   File "/usr/local/lib/python3.9/json/__init__.py", line 346, in loads
# |     return _default_decoder.decode(s)
# |   File "/usr/local/lib/python3.9/json/decoder.py", line 337, in decode
# |     obj, end = self.raw_decode(s, idx=_w(s, 0).end())
# |   File "/usr/local/lib/python3.9/json/decoder.py", line 355, in raw_decode
# |     raise JSONDecodeError("Expecting value", s, err.value) from None
# | json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
# | 
# | During handling of the above exception, another exception occurred:
# | 
# | Traceback (most recent call last):
# |   File "/buildbot/worker/arc-folder/llvm-project/llvm/utils/lit/lit.py", line 6, in <module>
# |     main()
# |   File "/buildbot/worker/arc-folder/llvm-project/llvm/utils/lit/lit/main.py", line 130, in main
# |     selected_tests, discovered_tests = GoogleTest.post_process_shard_results(
# |   File "/buildbot/worker/arc-folder/llvm-project/llvm/utils/lit/lit/formats/googletest.py", line 306, in post_process_shard_results
# |     raise RuntimeError(
# | RuntimeError: Failed to parse json file: /buildbot/worker/arc-folder/build/utils/lit/tests/Inputs/googletest-timeout/DummySubDir/OneTest.py-googletest-timeout-23548-1-2.json
# | 
# `-----------------------------
# RUN: at line 19
FileCheck --check-prefix=CHECK-INF < /buildbot/worker/arc-folder/build/utils/lit/tests/Output/googletest-timeout.py.tmp.cfgset.out /buildbot/worker/arc-folder/build/utils/lit/tests/googletest-timeout.py
# executed command: FileCheck --check-prefix=CHECK-INF /buildbot/worker/arc-folder/build/utils/lit/tests/googletest-timeout.py
# .---command stderr------------
# | /buildbot/worker/arc-folder/build/utils/lit/tests/googletest-timeout.py:34:14: error: CHECK-INF: expected string not found in input
# | # CHECK-INF: Timed Out: 1
# |              ^
...

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.

6 participants