Skip to content

Commit 315507d

Browse files
committed
[ValueTracking] Check across single predecessors in willNotFreeBetween.
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
1 parent 6429549 commit 315507d

File tree

2 files changed

+353
-16
lines changed

2 files changed

+353
-16
lines changed

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -704,25 +704,43 @@ bool llvm::isValidAssumeForContext(const Instruction *Inv,
704704

705705
bool llvm::willNotFreeBetween(const Instruction *Assume,
706706
const Instruction *CtxI) {
707-
if (CtxI->getParent() != Assume->getParent() || !Assume->comesBefore(CtxI))
708-
return false;
707+
// Helper to check if there are any calls in the range that may free memory.
708+
auto hasNoFreeCalls = [](auto Range) {
709+
for (const auto &[Idx, I] : enumerate(Range)) {
710+
if (Idx > MaxInstrsToCheckForFree)
711+
return false;
712+
if (const auto *CB = dyn_cast<CallBase>(&I))
713+
if (!CB->hasFnAttr(Attribute::NoFree))
714+
return false;
715+
}
716+
return true;
717+
};
718+
709719
// Make sure the current function cannot arrange for another thread to free on
710720
// its behalf.
711721
if (!CtxI->getFunction()->hasNoSync())
712722
return false;
713723

714-
// Check if there are any calls between the assume and CtxI that may
715-
// free memory.
716-
for (const auto &[Idx, I] :
717-
enumerate(make_range(Assume->getIterator(), CtxI->getIterator()))) {
718-
// Limit number of instructions to walk.
719-
if (Idx > MaxInstrsToCheckForFree)
724+
// Handle cross-block case: CtxI in a successor of Assume's block.
725+
const BasicBlock *CtxBB = CtxI->getParent();
726+
const BasicBlock *AssumeBB = Assume->getParent();
727+
if (CtxBB != AssumeBB) {
728+
if (CtxBB->getSinglePredecessor() != AssumeBB)
720729
return false;
721-
if (const auto *CB = dyn_cast<CallBase>(&I))
722-
if (!CB->hasFnAttr(Attribute::NoFree))
723-
return false;
730+
731+
if (!hasNoFreeCalls(make_range(CtxBB->begin(), CtxBB->end())))
732+
return false;
733+
734+
CtxI = AssumeBB->getTerminator();
724735
}
725-
return true;
736+
737+
// Same block case: check that Assume comes before CtxI.
738+
if (!Assume->comesBefore(CtxI))
739+
return false;
740+
741+
// Check if there are any calls between Assume and CtxI that may free memory.
742+
return hasNoFreeCalls(
743+
make_range(Assume->getIterator(), std::next(CtxI->getIterator())));
726744
}
727745

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

0 commit comments

Comments
 (0)