[llvm] [ValueTracking] Handle chain of single-pred blocks in willNotFreeBetween() (PR #202308)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 8 03:07:46 PDT 2026
https://github.com/nikic created https://github.com/llvm/llvm-project/pull/202308
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).
>From b8b8f20fc22d396637d78360611328dbe89013fd Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Mon, 8 Jun 2026 12:02:09 +0200
Subject: [PATCH] [ValueTracking] Handle chain of single-pred blocks in
willNotFreeBetween()
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).
---
llvm/lib/Analysis/ValueTracking.cpp | 36 +++++++++++--------
llvm/test/Transforms/LICM/hoist-deref-load.ll | 1 +
.../masked-loads-side-effects-after-vec.ll | 1 +
3 files changed, 23 insertions(+), 15 deletions(-)
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()
More information about the llvm-commits
mailing list