Skip to content

Commit 1baee84

Browse files
committed
ValueTracking] Check across single predecessors in willNotFreeBetween. (llvm#167965)
Extend willNotFreeBetween to perform simple checking across blocks to support the case where CtxI is in a successor of the block that contains the assume, but the assume's parent is the single predecessor of CtxI's block. This enables using _builtin_assume_dereferenceable to vectorize std::find_if and co in practice. End-to-end reproducer: https://godbolt.org/z/6jbsd4EjT PR: llvm#167965 (cherry picked from commit eb98b65)
1 parent 167c4fd commit 1baee84

File tree

2 files changed

+453
-12
lines changed

2 files changed

+453
-12
lines changed

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -557,25 +557,44 @@ bool llvm::isValidAssumeForContext(const Instruction *Inv,
557557

558558
bool llvm::willNotFreeBetween(const Instruction *Assume,
559559
const Instruction *CtxI) {
560-
if (CtxI->getParent() != Assume->getParent() || !Assume->comesBefore(CtxI))
561-
return false;
560+
// Helper to check if there are any calls in the range that may free memory.
561+
auto hasNoFreeCalls = [](auto Range) {
562+
for (const auto &[Idx, I] : enumerate(Range)) {
563+
if (Idx > MaxInstrsToCheckForFree)
564+
return false;
565+
if (const auto *CB = dyn_cast<CallBase>(&I))
566+
if (!CB->hasFnAttr(Attribute::NoFree))
567+
return false;
568+
}
569+
return true;
570+
};
571+
562572
// Make sure the current function cannot arrange for another thread to free on
563573
// its behalf.
564574
if (!CtxI->getFunction()->hasNoSync())
565575
return false;
566576

567-
// Check if there are any calls between the assume and CtxI that may
568-
// free memory.
569-
for (const auto &[Idx, I] :
570-
enumerate(make_range(Assume->getIterator(), CtxI->getIterator()))) {
571-
// Limit number of instructions to walk.
572-
if (Idx > MaxInstrsToCheckForFree)
577+
// Handle cross-block case: CtxI in a successor of Assume's block.
578+
const BasicBlock *CtxBB = CtxI->getParent();
579+
const BasicBlock *AssumeBB = Assume->getParent();
580+
BasicBlock::const_iterator CtxIter = CtxI->getIterator();
581+
if (CtxBB != AssumeBB) {
582+
if (CtxBB->getSinglePredecessor() != AssumeBB)
583+
return false;
584+
585+
if (!hasNoFreeCalls(make_range(CtxBB->begin(), CtxBB->end())))
586+
return false;
587+
588+
CtxIter = AssumeBB->end();
589+
} else {
590+
// Same block case: check that Assume comes before CtxI.
591+
if (!Assume->comesBefore(CtxI))
573592
return false;
574-
if (const auto *CB = dyn_cast<CallBase>(&I))
575-
if (!CB->hasFnAttr(Attribute::NoFree))
576-
return false;
577593
}
578-
return true;
594+
595+
// Check if there are any calls between Assume and CtxIter that may free
596+
// memory.
597+
return hasNoFreeCalls(make_range(Assume->getIterator(), CtxIter));
579598
}
580599

581600
// TODO: cmpExcludesZero misses many cases where `RHS` is non-constant but

0 commit comments

Comments
 (0)