[llvm] [ValueTracking] Support multiple predecessors in willNotFreeBetween (PR #223580)
Vy Nguyen via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 30 06:03:08 PDT 2026
https://github.com/oontvoo updated https://github.com/llvm/llvm-project/pull/223580
>From bdb114f0ad42bf2d373242a3961b8735dbded96a Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Mon, 14 Sep 2026 22:14:37 -0400
Subject: [PATCH 01/12] [ValueTracking] Support multiple predecessors in
willNotFreeBetween
Previously, `willNotFreeBetween` only walked backward along a linear chain of single-predecessor blocks, bailing out whenever control flow branched and merged (eg., conditional `if-else` blocks before a loop preheader).
This PR generalises the backward walk to use a worklist over all predecessors from `CtxBB` to `AssumeBB` (bounded by `MaxInstrsToCheckForFree`).
This enables LICM( and possibly other passes) to prove dereferenceability and hoist invariant loads across merging control flow when no path contains a freeing instruction.
---
llvm/lib/Analysis/ValueTracking.cpp | 38 +++++++++++--------
llvm/unittests/Analysis/ValueTrackingTest.cpp | 34 +++++++++++++++++
2 files changed, 57 insertions(+), 15 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index ac49dbc8d76d3..472582f9b383d 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -761,22 +761,30 @@ bool llvm::willNotFreeBetween(const Instruction *Assume,
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()));
-
- const BasicBlock *PredBB = CurBB->getSinglePredecessor();
- if (!PredBB)
- return false;
-
- if (!hasNoFreeInRange(make_range(CurBB->begin(),
- CurBB == CtxBB ? CtxIter : CurBB->end())))
- return false;
- CurBB = PredBB;
+ // Multi-predecessor worklist.
+ SmallVector<const BasicBlock *, 8> Worklist;
+ SmallPtrSet<const BasicBlock *, 8> Visited;
+ Worklist.push_back(CtxBB);
+ Visited.insert(CtxBB);
+ while (!Worklist.empty()) {
+ const BasicBlock *CurBB = Worklist.pop_back_val();
+ if (CurBB == AssumeBB) {
+ if (!hasNoFreeInRange(
+ make_range(Assume->getIterator(), AssumeBB->end())))
+ return false;
+ continue;
+ }
+ if (!hasNoFreeInRange(make_range(
+ CurBB->begin(), CurBB == CtxBB ? CtxIter : CurBB->end())))
+ return false;
+ if (pred_empty(CurBB))
+ return false;
+ for (const BasicBlock *Pred : predecessors(CurBB)) {
+ if (Visited.insert(Pred).second)
+ Worklist.push_back(Pred);
+ }
}
+ return true;
}
// TODO: cmpExcludesZero misses many cases where `RHS` is non-constant but
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 70731e34185de..4aa9beca62fe0 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -3412,6 +3412,40 @@ TEST_F(ValueTrackingTest, HaveNoCommonBitsSet) {
}
}
+TEST_F(ValueTrackingTest, willNotFreeBetweenMultiPredecessor) {
+ StringRef Assembly = R"(
+ declare void @llvm.assume(i1)
+ declare void @may_free()
+ declare void @no_free() nofree
+ define void @test(i1 %cond, ptr %p) {
+ entry:
+ %A = call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %p, i64 16) ]
+ br i1 %cond, label %if.then, label %if.else
+ if.then:
+ call void @no_free()
+ br label %merge
+ if.else:
+ br label %merge
+ merge:
+ %CxtI = load i32, ptr %p
+ br i1 %cond, label %if.then2, label %if.else2
+ if.then2:
+ call void @may_free()
+ br label %merge2
+ if.else2:
+ br label %merge2
+ merge2:
+ %CxtI2 = load i32, ptr %p
+ ret void
+ }
+ )";
+ parseAssembly(Assembly);
+ // Both paths from %A to %CxtI only call nofree functions -> should return true.
+ EXPECT_TRUE(willNotFreeBetween(A, CxtI));
+ // One path from %A to %CxtI2 calls @may_free() -> should return false.
+ EXPECT_FALSE(willNotFreeBetween(A, CxtI2));
+}
+
class IsBytewiseValueTest : public ValueTrackingTest,
public ::testing::WithParamInterface<
std::pair<const char *, const char *>> {
>From 5a20056044c6af328951ae74aae73cd22e3add21 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Mon, 14 Sep 2026 22:35:01 -0400
Subject: [PATCH 02/12] formatting
---
llvm/lib/Analysis/ValueTracking.cpp | 29 +++++++++----------
llvm/unittests/Analysis/ValueTrackingTest.cpp | 3 +-
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 472582f9b383d..ae7a29a34cd1e 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -767,22 +767,21 @@ bool llvm::willNotFreeBetween(const Instruction *Assume,
Worklist.push_back(CtxBB);
Visited.insert(CtxBB);
while (!Worklist.empty()) {
- const BasicBlock *CurBB = Worklist.pop_back_val();
- if (CurBB == AssumeBB) {
- if (!hasNoFreeInRange(
- make_range(Assume->getIterator(), AssumeBB->end())))
- return false;
- continue;
- }
- if (!hasNoFreeInRange(make_range(
- CurBB->begin(), CurBB == CtxBB ? CtxIter : CurBB->end())))
+ const BasicBlock *CurBB = Worklist.pop_back_val();
+ if (CurBB == AssumeBB) {
+ if (!hasNoFreeInRange(make_range(Assume->getIterator(), AssumeBB->end())))
return false;
- if (pred_empty(CurBB))
- return false;
- for (const BasicBlock *Pred : predecessors(CurBB)) {
- if (Visited.insert(Pred).second)
- Worklist.push_back(Pred);
- }
+ continue;
+ }
+ if (!hasNoFreeInRange(make_range(CurBB->begin(),
+ CurBB == CtxBB ? CtxIter : CurBB->end())))
+ return false;
+ if (pred_empty(CurBB))
+ return false;
+ for (const BasicBlock *Pred : predecessors(CurBB)) {
+ if (Visited.insert(Pred).second)
+ Worklist.push_back(Pred);
+ }
}
return true;
}
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 4aa9beca62fe0..35135d12b2226 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -3440,7 +3440,8 @@ TEST_F(ValueTrackingTest, willNotFreeBetweenMultiPredecessor) {
}
)";
parseAssembly(Assembly);
- // Both paths from %A to %CxtI only call nofree functions -> should return true.
+ // Both paths from %A to %CxtI only call nofree functions -> should return
+ // true.
EXPECT_TRUE(willNotFreeBetween(A, CxtI));
// One path from %A to %CxtI2 calls @may_free() -> should return false.
EXPECT_FALSE(willNotFreeBetween(A, CxtI2));
>From 641281794fd766b386a4f25b1927e6b8ad84a673 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Tue, 15 Sep 2026 17:51:46 -0400
Subject: [PATCH 03/12] address review comments: - handle loop back edges -
replace unit test with lit test - update other tests
---
llvm/lib/Analysis/ValueTracking.cpp | 54 ++++++++++---------
.../ValueTracking/memory-dereferenceable.ll | 43 +++++++++++++++
.../LICM/hoist-speculatable-load.ll | 2 +-
...able-info-from-assumption-constant-size.ll | 6 +--
llvm/unittests/Analysis/ValueTrackingTest.cpp | 35 ------------
5 files changed, 77 insertions(+), 63 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index ae7a29a34cd1e..2d4ac35d8bc90 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -733,49 +733,55 @@ bool llvm::isValidAssumeForContext(const Instruction *Inv,
return false;
}
+static bool hasNoFreeInRange(BasicBlock::const_iterator Begin,
+ BasicBlock::const_iterator End,
+ unsigned &NumChecked) {
+ for (const Instruction &I : make_range(Begin, End)) {
+ if (NumChecked++ > MaxInstrsToCheckForFree)
+ return false;
+ if (auto *CB = dyn_cast<CallBase>(&I)) {
+ if (!CB->hasFnAttr(Attribute::NoFree))
+ return false;
+ } else if (I.maySynchronize())
+ return false;
+ }
+ return true;
+}
+
bool llvm::willNotFreeBetween(const Instruction *Assume,
const Instruction *CtxI) {
- // Helper to check if there are any calls in the range that may free memory.
unsigned NumChecked = 0;
- auto hasNoFreeInRange = [&NumChecked](auto Range) {
- for (const Instruction &I : Range) {
- if (NumChecked++ > MaxInstrsToCheckForFree)
- return false;
-
- if (auto *CB = dyn_cast<CallBase>(&I)) {
- if (!CB->hasFnAttr(Attribute::NoFree))
- return false;
- } else if (I.maySynchronize())
- return false;
- }
- return true;
- };
-
const BasicBlock *CtxBB = CtxI->getParent();
const BasicBlock *AssumeBB = Assume->getParent();
BasicBlock::const_iterator CtxIter = CtxI->getIterator();
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));
+ return hasNoFreeInRange(Assume->getIterator(), CtxIter, NumChecked);
}
-
- // Multi-predecessor worklist.
+ // Check instructions before CtxI in CtxBB.
+ if (!hasNoFreeInRange(CtxBB->begin(), CtxIter, NumChecked))
+ return false;
+ if (pred_empty(CtxBB))
+ return false;
SmallVector<const BasicBlock *, 8> Worklist;
SmallPtrSet<const BasicBlock *, 8> Visited;
- Worklist.push_back(CtxBB);
- Visited.insert(CtxBB);
+ for (const BasicBlock *Pred : predecessors(CtxBB)) {
+ if (Visited.insert(Pred).second)
+ Worklist.push_back(Pred);
+ }
while (!Worklist.empty()) {
const BasicBlock *CurBB = Worklist.pop_back_val();
if (CurBB == AssumeBB) {
- if (!hasNoFreeInRange(make_range(Assume->getIterator(), AssumeBB->end())))
+ if (!hasNoFreeInRange(Assume->getIterator(), AssumeBB->end(), NumChecked))
return false;
continue;
}
- if (!hasNoFreeInRange(make_range(CurBB->begin(),
- CurBB == CtxBB ? CtxIter : CurBB->end())))
+ if (!hasNoFreeInRange(CurBB == CtxBB ? CtxIter : CurBB->begin(),
+ CurBB->end(), NumChecked))
return false;
+ if (CurBB == CtxBB)
+ continue;
if (pred_empty(CurBB))
return false;
for (const BasicBlock *Pred : predecessors(CurBB)) {
diff --git a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
index 9fbf27c3d7e72..299e9d5e4496f 100644
--- a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
+++ b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
@@ -411,6 +411,49 @@ declare ptr addrspace(1) @func1(ptr addrspace(1) returned) nounwind argmemonly
; Can free any object accessible in memory
declare void @mayfree()
+; CHECK-LABEL: 'dereferenceable_arg_multi_pred_nofree'
+; CHECK: %a
+define void @dereferenceable_arg_multi_pred_nofree(ptr dereferenceable(16) %a, i1 %cond) {
+entry:
+ br i1 %cond, label %if.then, label %if.else
+if.then:
+ call void @mayfree() nofree
+ br label %merge
+if.else:
+ br label %merge
+merge:
+ %v = load i32, ptr %a
+ ret void
+}
+; CHECK-LABEL: 'dereferenceable_arg_multi_pred_freed'
+; GLOBAL: %a
+; POINT-NOT: %a
+define void @dereferenceable_arg_multi_pred_freed(ptr dereferenceable(16) %a, i1 %cond) {
+entry:
+ br i1 %cond, label %if.then, label %if.else
+if.then:
+ call void @mayfree()
+ br label %merge
+if.else:
+ br label %merge
+merge:
+ %v = load i32, ptr %a
+ ret void
+}
+; CHECK-LABEL: 'dereferenceable_arg_loop_backedge_freed'
+; GLOBAL: %a
+; POINT-NOT: %a
+define void @dereferenceable_arg_loop_backedge_freed(ptr dereferenceable(16) %a, i1 %again) {
+entry:
+ br label %loop
+loop:
+ %v = load i32, ptr %a
+ call void @mayfree()
+ br i1 %again, label %loop, label %exit
+exit:
+ ret void
+}
+
!0 = !{i64 4}
!1 = !{i64 2}
!2 = !{}
diff --git a/llvm/test/Transforms/LICM/hoist-speculatable-load.ll b/llvm/test/Transforms/LICM/hoist-speculatable-load.ll
index ab754a1f9a9d6..05c3982e080e6 100644
--- a/llvm/test/Transforms/LICM/hoist-speculatable-load.ll
+++ b/llvm/test/Transforms/LICM/hoist-speculatable-load.ll
@@ -66,12 +66,12 @@ define void @f_nofree_nosync(i32 %ptr_i, ptr %ptr2, i1 %cond) nofree nosync {
; CHECK-NEXT: store i32 0, ptr [[PTR2:%.*]], align 4
; CHECK-NEXT: br label [[FOR_BODY_LR_PH]]
; CHECK: for.body.lr.ph:
+; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 4
; CHECK-NEXT: br label [[FOR_BODY:%.*]]
; CHECK: for.body:
; CHECK-NEXT: [[I_08:%.*]] = phi i32 [ 0, [[FOR_BODY_LR_PH]] ], [ [[INC:%.*]], [[IF_END:%.*]] ]
; CHECK-NEXT: br i1 [[COND]], label [[IF_END]], label [[IF:%.*]]
; CHECK: if:
-; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[PTR]], align 4, !invariant.load [[META0]]
; CHECK-NEXT: store i32 [[TMP0]], ptr [[PTR2]], align 4
; CHECK-NEXT: br label [[IF_END]]
; CHECK: if.end:
diff --git a/llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-constant-size.ll b/llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-constant-size.ll
index 21a2dd94542b8..20e49af5e4b7e 100644
--- a/llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-constant-size.ll
+++ b/llvm/test/Transforms/LoopVectorize/dereferenceable-info-from-assumption-constant-size.ll
@@ -1182,9 +1182,9 @@ define void @deref_assumption_in_header_constant_trip_count_multiple_loop_predec
; CHECK-NEXT: [[TMP0:%.*]] = getelementptr i32, ptr [[A]], i64 [[INDEX]]
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[B]], i64 [[INDEX]]
; CHECK-NEXT: [[WIDE_LOAD:%.*]] = load <2 x i32>, ptr [[TMP1]], align 4
-; CHECK-NEXT: [[TMP2:%.*]] = icmp slt <2 x i32> [[WIDE_LOAD]], zeroinitializer
-; CHECK-NEXT: [[WIDE_MASKED_LOAD:%.*]] = call <2 x i32> @llvm.masked.load.v2i32.p0(ptr align 4 [[TMP0]], <2 x i1> [[TMP2]], <2 x i32> poison)
-; CHECK-NEXT: [[PREDPHI:%.*]] = select <2 x i1> [[TMP2]], <2 x i32> [[WIDE_MASKED_LOAD]], <2 x i32> [[WIDE_LOAD]]
+; CHECK-NEXT: [[TMP2:%.*]] = icmp sge <2 x i32> [[WIDE_LOAD]], zeroinitializer
+; CHECK-NEXT: [[WIDE_LOAD1:%.*]] = load <2 x i32>, ptr [[TMP0]], align 4
+; CHECK-NEXT: [[PREDPHI:%.*]] = select <2 x i1> [[TMP2]], <2 x i32> [[WIDE_LOAD]], <2 x i32> [[WIDE_LOAD1]]
; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[C]], i64 [[INDEX]]
; CHECK-NEXT: store <2 x i32> [[PREDPHI]], ptr [[TMP3]], align 4
; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 35135d12b2226..70731e34185de 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -3412,41 +3412,6 @@ TEST_F(ValueTrackingTest, HaveNoCommonBitsSet) {
}
}
-TEST_F(ValueTrackingTest, willNotFreeBetweenMultiPredecessor) {
- StringRef Assembly = R"(
- declare void @llvm.assume(i1)
- declare void @may_free()
- declare void @no_free() nofree
- define void @test(i1 %cond, ptr %p) {
- entry:
- %A = call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %p, i64 16) ]
- br i1 %cond, label %if.then, label %if.else
- if.then:
- call void @no_free()
- br label %merge
- if.else:
- br label %merge
- merge:
- %CxtI = load i32, ptr %p
- br i1 %cond, label %if.then2, label %if.else2
- if.then2:
- call void @may_free()
- br label %merge2
- if.else2:
- br label %merge2
- merge2:
- %CxtI2 = load i32, ptr %p
- ret void
- }
- )";
- parseAssembly(Assembly);
- // Both paths from %A to %CxtI only call nofree functions -> should return
- // true.
- EXPECT_TRUE(willNotFreeBetween(A, CxtI));
- // One path from %A to %CxtI2 calls @may_free() -> should return false.
- EXPECT_FALSE(willNotFreeBetween(A, CxtI2));
-}
-
class IsBytewiseValueTest : public ValueTrackingTest,
public ::testing::WithParamInterface<
std::pair<const char *, const char *>> {
>From 4638c094595722c7d4eec41ace094fd919789575 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Thu, 17 Sep 2026 09:05:59 -0400
Subject: [PATCH 04/12] formatting
---
llvm/lib/Analysis/ValueTracking.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 2d4ac35d8bc90..bbeb73946d7cb 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -742,8 +742,9 @@ static bool hasNoFreeInRange(BasicBlock::const_iterator Begin,
if (auto *CB = dyn_cast<CallBase>(&I)) {
if (!CB->hasFnAttr(Attribute::NoFree))
return false;
- } else if (I.maySynchronize())
+ } else if (I.maySynchronize()) {
return false;
+ }
}
return true;
}
>From b4dfeb4736c3c90ba85b8dced13adf7cfccd8013 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Thu, 17 Sep 2026 10:30:52 -0400
Subject: [PATCH 05/12] also pass the DT to willNotFreeBetween(): helps prune
backwards search, fixes loop backedge handling
---
llvm/include/llvm/Analysis/ValueTracking.h | 3 +-
llvm/lib/Analysis/Loads.cpp | 4 +--
llvm/lib/Analysis/ValueTracking.cpp | 39 +++++++++++++++++-----
3 files changed, 34 insertions(+), 12 deletions(-)
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index 9a1fdd0ca05a9..99e0121adb6d7 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -660,7 +660,8 @@ inline bool isValidAssumeForContext(const Instruction *I,
/// Returns true, if no instruction between \p Assume and \p CtxI may free
/// (including through synchronization).
LLVM_ABI bool willNotFreeBetween(const Instruction *Assume,
- const Instruction *CtxI);
+ const Instruction *CtxI,
+ const DominatorTree *DT = nullptr);
enum class OverflowResult {
/// Always overflows in the direction of signed/unsigned min value.
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index de9022c540d42..4a75a7841fcb9 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -54,7 +54,7 @@ static bool isDereferenceableAndAlignedPointerViaAssumption(
// Dereferenceable information from assumptions is only valid if the
// value cannot be freed between the assumption and use.
if (!IsDerefable &&
- (!PtrCanBeFreed || willNotFreeBetween(Assume, SQ.CxtI)) &&
+ (!PtrCanBeFreed || willNotFreeBetween(Assume, SQ.CxtI, SQ.DT)) &&
CheckSize(RK))
IsDerefable = true;
}
@@ -149,7 +149,7 @@ static bool isDereferenceableAndAlignedPointer(
DefI = &cast<Argument>(V)->getParent()->getEntryBlock().front();
}
- if (!SQ.CxtI || !willNotFreeBetween(DefI, SQ.CxtI))
+ if (!SQ.CxtI || !willNotFreeBetween(DefI, SQ.CxtI, SQ.DT))
return false;
}
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index bbeb73946d7cb..d27f25c191128 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -750,41 +750,62 @@ static bool hasNoFreeInRange(BasicBlock::const_iterator Begin,
}
bool llvm::willNotFreeBetween(const Instruction *Assume,
- const Instruction *CtxI) {
- unsigned NumChecked = 0;
+ const Instruction *CtxI,
+ const DominatorTree *DT) {
+
const BasicBlock *CtxBB = CtxI->getParent();
const BasicBlock *AssumeBB = Assume->getParent();
+ unsigned NumChecked = 0;
BasicBlock::const_iterator CtxIter = CtxI->getIterator();
+
if (CtxBB == AssumeBB) {
if (Assume != CtxI && !Assume->comesBefore(CtxI))
return false;
return hasNoFreeInRange(Assume->getIterator(), CtxIter, NumChecked);
}
- // Check instructions before CtxI in CtxBB.
+
+ if (DT && !DT->dominates(Assume, CtxI))
+ return false;
+
if (!hasNoFreeInRange(CtxBB->begin(), CtxIter, NumChecked))
return false;
if (pred_empty(CtxBB))
return false;
- SmallVector<const BasicBlock *, 8> Worklist;
- SmallPtrSet<const BasicBlock *, 8> Visited;
+
+ SmallVector<const BasicBlock *, 16> Worklist;
+ SmallPtrSet<const BasicBlock *, 16> Visited;
+ // Enqueue all predecessors of CtxBB.
+ // Note: CtxBB is NOT pre-inserted to ensure that loop
+ // backedges returning to CtxBB are enqueued and checked correclty.
for (const BasicBlock *Pred : predecessors(CtxBB)) {
if (Visited.insert(Pred).second)
Worklist.push_back(Pred);
}
while (!Worklist.empty()) {
const BasicBlock *CurBB = Worklist.pop_back_val();
+
if (CurBB == AssumeBB) {
if (!hasNoFreeInRange(Assume->getIterator(), AssumeBB->end(), NumChecked))
return false;
continue;
}
- if (!hasNoFreeInRange(CurBB == CtxBB ? CtxIter : CurBB->begin(),
- CurBB->end(), NumChecked))
+
+ if (DT && !DT->dominates(AssumeBB, CurBB))
return false;
- if (CurBB == CtxBB)
- continue;
+
if (pred_empty(CurBB))
return false;
+ // If CurBB == CtxBB (due to a loop backedge targeting CtxBB), check
+ // instructions from CtxIter to the end of CtxBB (instructions before
+ // CtxIter were checked above). Otherwise, check the entire block.
+ BasicBlock::const_iterator Start =
+ (CurBB == CtxBB) ? CtxIter : CurBB->begin();
+ if (!hasNoFreeInRange(Start, CurBB->end(), NumChecked))
+ return false;
+ // If we reached CtxBB via a backedge, do not re-expand its predecessors
+ // as they were already enqueued at initialization.
+ if (CurBB == CtxBB)
+ continue;
for (const BasicBlock *Pred : predecessors(CurBB)) {
if (Visited.insert(Pred).second)
Worklist.push_back(Pred);
>From 2f889af4f2e6ed8f86e410d583b596647c12f0fb Mon Sep 17 00:00:00 2001
From: Vy Nguyen <1762963+oontvoo at users.noreply.github.com>
Date: Thu, 17 Sep 2026 12:18:19 -0400
Subject: [PATCH 06/12] added extra space
Co-authored-by: Florian Hahn <flo at fhahn.com>
---
llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll | 1 +
1 file changed, 1 insertion(+)
diff --git a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
index 299e9d5e4496f..b632de12f8abf 100644
--- a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
+++ b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
@@ -416,6 +416,7 @@ declare void @mayfree()
define void @dereferenceable_arg_multi_pred_nofree(ptr dereferenceable(16) %a, i1 %cond) {
entry:
br i1 %cond, label %if.then, label %if.else
+
if.then:
call void @mayfree() nofree
br label %merge
>From 970f58c6ea95d306fa61b6adbfe02b4989e3fcb1 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <1762963+oontvoo at users.noreply.github.com>
Date: Fri, 18 Sep 2026 09:29:55 -0400
Subject: [PATCH 07/12] Update llvm/lib/Analysis/ValueTracking.cpp
Co-authored-by: Antonio Frighetto <me at antoniofrighetto.com>
---
llvm/lib/Analysis/ValueTracking.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index d27f25c191128..b4bd342a293c7 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -776,7 +776,7 @@ bool llvm::willNotFreeBetween(const Instruction *Assume,
SmallPtrSet<const BasicBlock *, 16> Visited;
// Enqueue all predecessors of CtxBB.
// Note: CtxBB is NOT pre-inserted to ensure that loop
- // backedges returning to CtxBB are enqueued and checked correclty.
+ // backedges returning to CtxBB are enqueued and checked correctly.
for (const BasicBlock *Pred : predecessors(CtxBB)) {
if (Visited.insert(Pred).second)
Worklist.push_back(Pred);
>From 03b9697339092aafed7ebc183a7ad8f886645e97 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Fri, 18 Sep 2026 12:39:58 -0400
Subject: [PATCH 08/12] remove redundant dominate check
---
llvm/lib/Analysis/ValueTracking.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index b4bd342a293c7..1251c40c9ece6 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -790,8 +790,8 @@ bool llvm::willNotFreeBetween(const Instruction *Assume,
continue;
}
- if (DT && !DT->dominates(AssumeBB, CurBB))
- return false;
+ assert((!DT || DT->dominates(AssumeBB, CurBB)) &&
+ "Blocks between Assume and CtxI must be dominated by AssumeBB");
if (pred_empty(CurBB))
return false;
>From 4027c71b1a33b80851c7cf3415039b478236eb42 Mon Sep 17 00:00:00 2001
From: Vy Nguyen <1762963+oontvoo at users.noreply.github.com>
Date: Fri, 18 Sep 2026 22:44:26 -0400
Subject: [PATCH 09/12] Update llvm/lib/Analysis/ValueTracking.cpp
Co-authored-by: Antonio Frighetto <me at antoniofrighetto.com>
---
llvm/lib/Analysis/ValueTracking.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 1251c40c9ece6..7990b6d97863f 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -798,8 +798,7 @@ bool llvm::willNotFreeBetween(const Instruction *Assume,
// If CurBB == CtxBB (due to a loop backedge targeting CtxBB), check
// instructions from CtxIter to the end of CtxBB (instructions before
// CtxIter were checked above). Otherwise, check the entire block.
- BasicBlock::const_iterator Start =
- (CurBB == CtxBB) ? CtxIter : CurBB->begin();
+ auto StartIt = (CurBB == CtxBB) ? CtxIter : CurBB->begin();
if (!hasNoFreeInRange(Start, CurBB->end(), NumChecked))
return false;
// If we reached CtxBB via a backedge, do not re-expand its predecessors
>From 1dbfe2a52abf1df6865144c002adea758199ddda Mon Sep 17 00:00:00 2001
From: Vy Nguyen <1762963+oontvoo at users.noreply.github.com>
Date: Fri, 18 Sep 2026 23:20:38 -0400
Subject: [PATCH 10/12] s/StartIt/Start
---
llvm/lib/Analysis/ValueTracking.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 7990b6d97863f..27c0dee9eb0fa 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -798,7 +798,7 @@ bool llvm::willNotFreeBetween(const Instruction *Assume,
// If CurBB == CtxBB (due to a loop backedge targeting CtxBB), check
// instructions from CtxIter to the end of CtxBB (instructions before
// CtxIter were checked above). Otherwise, check the entire block.
- auto StartIt = (CurBB == CtxBB) ? CtxIter : CurBB->begin();
+ auto Start = (CurBB == CtxBB) ? CtxIter : CurBB->begin();
if (!hasNoFreeInRange(Start, CurBB->end(), NumChecked))
return false;
// If we reached CtxBB via a backedge, do not re-expand its predecessors
>From a6550a5d8e8f6eaa00e1e9d4685bf6c976de07de Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Mon, 21 Sep 2026 12:52:36 -0400
Subject: [PATCH 11/12] address review comment, simplify worklist
---
llvm/lib/Analysis/ValueTracking.cpp | 34 +++++++----------------------
1 file changed, 8 insertions(+), 26 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 930af829f5ae1..d18a95610d20c 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -756,63 +756,45 @@ static bool hasNoFreeInRange(BasicBlock::const_iterator Begin,
bool llvm::willNotFreeBetween(const Instruction *Assume,
const Instruction *CtxI,
const DominatorTree *DT) {
-
const BasicBlock *CtxBB = CtxI->getParent();
const BasicBlock *AssumeBB = Assume->getParent();
unsigned NumChecked = 0;
BasicBlock::const_iterator CtxIter = CtxI->getIterator();
-
if (CtxBB == AssumeBB) {
if (Assume != CtxI && !Assume->comesBefore(CtxI))
return false;
return hasNoFreeInRange(Assume->getIterator(), CtxIter, NumChecked);
}
-
if (DT && !DT->dominates(Assume, CtxI))
return false;
-
if (!hasNoFreeInRange(CtxBB->begin(), CtxIter, NumChecked))
return false;
if (pred_empty(CtxBB))
return false;
-
- SmallVector<const BasicBlock *, 16> Worklist;
- SmallPtrSet<const BasicBlock *, 16> Visited;
- // Enqueue all predecessors of CtxBB.
- // Note: CtxBB is NOT pre-inserted to ensure that loop
+ // Note: CtxBB is NOT pre-inserted into Visited to ensure that loop
// backedges returning to CtxBB are enqueued and checked correctly.
- for (const BasicBlock *Pred : predecessors(CtxBB)) {
- if (Visited.insert(Pred).second)
- Worklist.push_back(Pred);
- }
+ SmallVector<const BasicBlock *, 16> Worklist(predecessors(CtxBB));
+ SmallPtrSet<const BasicBlock *, 16> Visited;
while (!Worklist.empty()) {
const BasicBlock *CurBB = Worklist.pop_back_val();
-
+ if (!Visited.insert(CurBB).second)
+ continue;
if (CurBB == AssumeBB) {
if (!hasNoFreeInRange(Assume->getIterator(), AssumeBB->end(), NumChecked))
return false;
continue;
}
-
assert((!DT || DT->dominates(AssumeBB, CurBB)) &&
"Blocks between Assume and CtxI must be dominated by AssumeBB");
-
if (pred_empty(CurBB))
return false;
// If CurBB == CtxBB (due to a loop backedge targeting CtxBB), check
// instructions from CtxIter to the end of CtxBB (instructions before
// CtxIter were checked above). Otherwise, check the entire block.
- auto Start = (CurBB == CtxBB) ? CtxIter : CurBB->begin();
- if (!hasNoFreeInRange(Start, CurBB->end(), NumChecked))
+ auto StartIt = (CurBB == CtxBB) ? CtxIter : CurBB->begin();
+ if (!hasNoFreeInRange(StartIt, CurBB->end(), NumChecked))
return false;
- // If we reached CtxBB via a backedge, do not re-expand its predecessors
- // as they were already enqueued at initialization.
- if (CurBB == CtxBB)
- continue;
- for (const BasicBlock *Pred : predecessors(CurBB)) {
- if (Visited.insert(Pred).second)
- Worklist.push_back(Pred);
- }
+ append_range(Worklist, predecessors(CurBB));
}
return true;
}
>From 2946489729ca7489653e07b5c6c4422d6e07892b Mon Sep 17 00:00:00 2001
From: Vy Nguyen <vyng at google.com>
Date: Mon, 21 Sep 2026 12:57:18 -0400
Subject: [PATCH 12/12] add newlines separating lbocks
---
llvm/lib/Analysis/ValueTracking.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index d18a95610d20c..99b7e27ead0b0 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -771,6 +771,7 @@ bool llvm::willNotFreeBetween(const Instruction *Assume,
return false;
if (pred_empty(CtxBB))
return false;
+
// Note: CtxBB is NOT pre-inserted into Visited to ensure that loop
// backedges returning to CtxBB are enqueued and checked correctly.
SmallVector<const BasicBlock *, 16> Worklist(predecessors(CtxBB));
@@ -779,6 +780,7 @@ bool llvm::willNotFreeBetween(const Instruction *Assume,
const BasicBlock *CurBB = Worklist.pop_back_val();
if (!Visited.insert(CurBB).second)
continue;
+
if (CurBB == AssumeBB) {
if (!hasNoFreeInRange(Assume->getIterator(), AssumeBB->end(), NumChecked))
return false;
@@ -786,8 +788,10 @@ bool llvm::willNotFreeBetween(const Instruction *Assume,
}
assert((!DT || DT->dominates(AssumeBB, CurBB)) &&
"Blocks between Assume and CtxI must be dominated by AssumeBB");
+
if (pred_empty(CurBB))
return false;
+
// If CurBB == CtxBB (due to a loop backedge targeting CtxBB), check
// instructions from CtxIter to the end of CtxBB (instructions before
// CtxIter were checked above). Otherwise, check the entire block.
More information about the llvm-commits
mailing list