Skip to content

[ValueTracking] Handle chain of single-pred blocks in willNotFreeBetween()#202308

Merged
nikic merged 2 commits into
llvm:mainfrom
nikic:willnotfree-chain
Jun 9, 2026
Merged

[ValueTracking] Handle chain of single-pred blocks in willNotFreeBetween()#202308
nikic merged 2 commits into
llvm:mainfrom
nikic:willnotfree-chain

Conversation

@nikic

@nikic nikic commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

willNotFreeBetween() currently handles the case where both instructions are in the same block, or one is in the single predecessor of the other. This patch extends this to handle a chain of single predecessor blocks.

The budget now applies to all checked instructions, rather than per block. Also increase the budget by a factor of two (which means that new budget interpretation should never regress relative to the previous).

…een()

willNotFreeBetween() currently handles the case where both
instructions are in the same block, or one is in the single
predecessor of the other. This patch extends this to handle a
chain of single predecessor blocks.

The budget now applies to all checked instructions, rather than
per block. Also increase the budget by a factor of two (which
means that new budget interpretation should never regress relative
to the previous).
@llvmorg-github-actions llvmorg-github-actions Bot added llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Jun 8, 2026
@llvmorg-github-actions

llvmorg-github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown

@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-analysis

Author: Nikita Popov (nikic)

Changes

willNotFreeBetween() currently handles the case where both instructions are in the same block, or one is in the single predecessor of the other. This patch extends this to handle a chain of single predecessor blocks.

The budget now applies to all checked instructions, rather than per block. Also increase the budget by a factor of two (which means that new budget interpretation should never regress relative to the previous).


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

3 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+21-15)
  • (modified) llvm/test/Transforms/LICM/hoist-deref-load.ll (+1)
  • (modified) llvm/test/Transforms/SLPVectorizer/AArch64/masked-loads-side-effects-after-vec.ll (+1)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1261664c5b986..3efb7c64c78e7 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -93,7 +93,7 @@ static cl::opt<unsigned> DomConditionsMaxUses("dom-conditions-max-uses",
 
 /// Maximum number of instructions to check between assume and context
 /// instruction.
-static constexpr unsigned MaxInstrsToCheckForFree = 16;
+static constexpr unsigned MaxInstrsToCheckForFree = 32;
 
 /// Returns the bitwidth of the given scalar or pointer type. For vector types,
 /// returns the element type's bitwidth.
@@ -704,9 +704,10 @@ bool llvm::isValidAssumeForContext(const Instruction *Inv,
 bool llvm::willNotFreeBetween(const Instruction *Assume,
                               const Instruction *CtxI) {
   // Helper to check if there are any calls in the range that may free memory.
-  auto hasNoFreeInRange = [](auto Range) {
+  unsigned NumChecked = 0;
+  auto hasNoFreeInRange = [&NumChecked](auto Range) {
     for (const auto &[Idx, I] : enumerate(Range)) {
-      if (Idx > MaxInstrsToCheckForFree)
+      if (NumChecked++ > MaxInstrsToCheckForFree)
         return false;
 
       if (auto *CB = dyn_cast<CallBase>(&I)) {
@@ -718,27 +719,32 @@ bool llvm::willNotFreeBetween(const Instruction *Assume,
     return true;
   };
 
-  // Handle cross-block case: CtxI in a successor of Assume's block.
   const BasicBlock *CtxBB = CtxI->getParent();
   const BasicBlock *AssumeBB = Assume->getParent();
   BasicBlock::const_iterator CtxIter = CtxI->getIterator();
-  if (CtxBB != AssumeBB) {
-    if (CtxBB->getSinglePredecessor() != AssumeBB)
+  if (CtxBB == AssumeBB) {
+    // Same block case: check that Assume comes before CtxI.
+    if (Assume != CtxI && !Assume->comesBefore(CtxI))
       return false;
+    return hasNoFreeInRange(make_range(Assume->getIterator(), CtxIter));
+  }
+
+  // Handle chain of single-predecessor blocks.
+  const BasicBlock *CurBB = CtxBB;
+  while (true) {
+    if (CurBB == AssumeBB)
+      return hasNoFreeInRange(
+          make_range(Assume->getIterator(), AssumeBB->end()));
 
-    if (!hasNoFreeInRange(make_range(CtxBB->begin(), CtxIter)))
+    const BasicBlock *PredBB = CurBB->getSinglePredecessor();
+    if (!PredBB)
       return false;
 
-    CtxIter = AssumeBB->end();
-  } else {
-    // Same block case: check that Assume comes before CtxI.
-    if (Assume != CtxI && !Assume->comesBefore(CtxI))
+    if (!hasNoFreeInRange(make_range(CurBB->begin(),
+                                     CurBB == CtxBB ? CtxIter : CurBB->end())))
       return false;
+    CurBB = PredBB;
   }
-
-  // Check if there are any calls between Assume and CtxIter that may free
-  // memory.
-  return hasNoFreeInRange(make_range(Assume->getIterator(), CtxIter));
 }
 
 // TODO: cmpExcludesZero misses many cases where `RHS` is non-constant but
diff --git a/llvm/test/Transforms/LICM/hoist-deref-load.ll b/llvm/test/Transforms/LICM/hoist-deref-load.ll
index c498e85ddd6c2..4ff21a34afa3b 100644
--- a/llvm/test/Transforms/LICM/hoist-deref-load.ll
+++ b/llvm/test/Transforms/LICM/hoist-deref-load.ll
@@ -1,5 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
 ; RUN: opt -S -passes=licm -verify-memoryssa < %s | FileCheck %s
+; RUN: opt -S -passes=licm -verify-memoryssa -use-dereferenceable-at-point-semantics < %s | FileCheck %s
 ; RUN: opt -aa-pipeline=basic-aa -passes='require<opt-remark-emit>,loop-mssa(loop-simplifycfg,licm)' -verify-memoryssa -S < %s | FileCheck %s
 
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
diff --git a/llvm/test/Transforms/SLPVectorizer/AArch64/masked-loads-side-effects-after-vec.ll b/llvm/test/Transforms/SLPVectorizer/AArch64/masked-loads-side-effects-after-vec.ll
index ca3c8bbac6366..df4c527aa216a 100644
--- a/llvm/test/Transforms/SLPVectorizer/AArch64/masked-loads-side-effects-after-vec.ll
+++ b/llvm/test/Transforms/SLPVectorizer/AArch64/masked-loads-side-effects-after-vec.ll
@@ -1,5 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
 ; RUN: opt -S --passes=slp-vectorizer -mtriple=aarch64-unknown-linux-gnu < %s | FileCheck %s
+; RUN: opt -S --passes=slp-vectorizer -mtriple=aarch64-unknown-linux-gnu -use-dereferenceable-at-point-semantics < %s | FileCheck %s
 
 declare noalias ptr @malloc()
 

@dtcxzyw dtcxzyw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@antoniofrighetto antoniofrighetto left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

Comment thread llvm/lib/Analysis/ValueTracking.cpp Outdated

@fhahn fhahn left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, thanks.

Might be good to add an explicit test to something like llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-constant-size.ll

@nikic nikic enabled auto-merge (squash) June 9, 2026 07:41
@nikic nikic merged commit c04c945 into llvm:main Jun 9, 2026
9 of 10 checks passed
@nikic nikic deleted the willnotfree-chain branch June 9, 2026 07:50
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:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants