Skip to content

Commit 43ee867

Browse files
fhahngithub-actions[bot]
authored andcommitted
Automerge: [LAA] Check if Ptr can be freed between Assume and CtxI. (#161725)
When using information from dereferenceable assumptions, we need to make sure that the memory is not freed between the assume and the specified context instruction. Instead of just checking canBeFreed, check if there any calls that may free between the assume and the context instruction. This patch introduces a willNotFreeBetween to check for calls that may free between an assume and a context instructions, to also be used in llvm/llvm-project#161255. PR: llvm/llvm-project#161725
2 parents 7eef27b + 7ceef76 commit 43ee867

File tree

5 files changed

+55
-17
lines changed

5 files changed

+55
-17
lines changed

llvm/include/llvm/Analysis/ValueTracking.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,12 @@ LLVM_ABI bool isValidAssumeForContext(const Instruction *I,
613613
const DominatorTree *DT = nullptr,
614614
bool AllowEphemerals = false);
615615

616+
/// Returns true, if no instruction between \p Assume and \p CtxI may free
617+
/// memory and the function is marked as NoSync. The latter ensures the current
618+
/// function cannot arrange for another thread to free on its behalf.
619+
LLVM_ABI bool willNotFreeBetween(const Instruction *Assume,
620+
const Instruction *CtxI);
621+
616622
enum class OverflowResult {
617623
/// Always overflows in the direction of signed/unsigned min value.
618624
AlwaysOverflowsLow,

llvm/lib/Analysis/LoopAccessAnalysis.cpp

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -233,19 +233,25 @@ static bool evaluatePtrAddRecAtMaxBTCWillNotWrap(
233233
const SCEV *DerefBytesSCEV = SE.getConstant(WiderTy, DerefBytes);
234234

235235
// Check if we have a suitable dereferencable assumption we can use.
236-
if (!StartPtrV->canBeFreed()) {
237-
Instruction *CtxI = &*L->getHeader()->getFirstNonPHIIt();
238-
if (BasicBlock *LoopPred = L->getLoopPredecessor()) {
239-
if (isa<BranchInst>(LoopPred->getTerminator()))
240-
CtxI = LoopPred->getTerminator();
241-
}
242-
243-
RetainedKnowledge DerefRK = getKnowledgeValidInContext(
244-
StartPtrV, {Attribute::Dereferenceable}, *AC, CtxI, DT);
245-
if (DerefRK) {
246-
DerefBytesSCEV =
247-
SE.getUMaxExpr(DerefBytesSCEV, SE.getSCEV(DerefRK.IRArgValue));
248-
}
236+
Instruction *CtxI = &*L->getHeader()->getFirstNonPHIIt();
237+
if (BasicBlock *LoopPred = L->getLoopPredecessor()) {
238+
if (isa<BranchInst>(LoopPred->getTerminator()))
239+
CtxI = LoopPred->getTerminator();
240+
}
241+
RetainedKnowledge DerefRK;
242+
getKnowledgeForValue(StartPtrV, {Attribute::Dereferenceable}, *AC,
243+
[&](RetainedKnowledge RK, Instruction *Assume, auto) {
244+
if (!isValidAssumeForContext(Assume, CtxI, DT))
245+
return false;
246+
if (StartPtrV->canBeFreed() &&
247+
!willNotFreeBetween(Assume, CtxI))
248+
return false;
249+
DerefRK = std::max(DerefRK, RK);
250+
return true;
251+
});
252+
if (DerefRK) {
253+
DerefBytesSCEV =
254+
SE.getUMaxExpr(DerefBytesSCEV, SE.getSCEV(DerefRK.IRArgValue));
249255
}
250256

251257
if (DerefBytesSCEV->isZero())

llvm/lib/Analysis/ValueTracking.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ using namespace llvm::PatternMatch;
8989
static cl::opt<unsigned> DomConditionsMaxUses("dom-conditions-max-uses",
9090
cl::Hidden, cl::init(20));
9191

92+
/// Maximum number of instructions to check between assume and context
93+
/// instruction.
94+
static constexpr unsigned MaxInstrsToCheckForFree = 16;
9295

9396
/// Returns the bitwidth of the given scalar or pointer type. For vector types,
9497
/// returns the element type's bitwidth.
@@ -561,6 +564,29 @@ bool llvm::isValidAssumeForContext(const Instruction *Inv,
561564
return false;
562565
}
563566

567+
bool llvm::willNotFreeBetween(const Instruction *Assume,
568+
const Instruction *CtxI) {
569+
if (CtxI->getParent() != Assume->getParent() || !Assume->comesBefore(CtxI))
570+
return false;
571+
// Make sure the current function cannot arrange for another thread to free on
572+
// its behalf.
573+
if (!CtxI->getFunction()->hasNoSync())
574+
return false;
575+
576+
// Check if there are any calls between the assume and CtxI that may
577+
// free memory.
578+
for (const auto &[Idx, I] :
579+
enumerate(make_range(Assume->getIterator(), CtxI->getIterator()))) {
580+
// Limit number of instructions to walk.
581+
if (Idx > MaxInstrsToCheckForFree)
582+
return false;
583+
if (const auto *CB = dyn_cast<CallBase>(&I))
584+
if (!CB->hasFnAttr(Attribute::NoFree))
585+
return false;
586+
}
587+
return true;
588+
}
589+
564590
// TODO: cmpExcludesZero misses many cases where `RHS` is non-constant but
565591
// we still have enough information about `RHS` to conclude non-zero. For
566592
// example Pred=EQ, RHS=isKnownNonZero. cmpExcludesZero is called in loops

llvm/test/Analysis/LoopAccessAnalysis/early-exit-runtime-checks.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,10 +770,10 @@ define void @all_exits_dominate_latch_countable_exits_at_most_500_iterations_kno
770770
; CHECK-NEXT: %gep.A = getelementptr inbounds i32, ptr %A, i64 %iv
771771
; CHECK-NEXT: Grouped accesses:
772772
; CHECK-NEXT: Group GRP0:
773-
; CHECK-NEXT: (Low: %B High: inttoptr (i64 -1 to ptr))
773+
; CHECK-NEXT: (Low: %B High: (2000 + %B))
774774
; CHECK-NEXT: Member: {%B,+,4}<nuw><%loop.header>
775775
; CHECK-NEXT: Group GRP1:
776-
; CHECK-NEXT: (Low: %A High: inttoptr (i64 -1 to ptr))
776+
; CHECK-NEXT: (Low: %A High: (2000 + %A))
777777
; CHECK-NEXT: Member: {%A,+,4}<nuw><%loop.header>
778778
; CHECK-EMPTY:
779779
; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop.

llvm/test/Transforms/LoopVectorize/single-early-exit-deref-assumptions.ll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,8 +512,8 @@ define i64 @early_exit_alignment_and_deref_known_via_assumption_with_constant_si
512512
; CHECK-NEXT: [[INDEX1:%.*]] = phi i64 [ [[INDEX_NEXT:%.*]], %[[LOOP_INC:.*]] ], [ 0, %[[ENTRY]] ]
513513
; CHECK-NEXT: [[ARRAYIDX2:%.*]] = getelementptr inbounds i8, ptr [[P1]], i64 [[INDEX1]]
514514
; CHECK-NEXT: [[LD1:%.*]] = load i8, ptr [[ARRAYIDX2]], align 1
515-
; CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds i8, ptr [[P2]], i64 [[INDEX1]]
516-
; CHECK-NEXT: [[LD2:%.*]] = load i8, ptr [[ARRAYIDX1]], align 1
515+
; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P2]], i64 [[INDEX1]]
516+
; CHECK-NEXT: [[LD2:%.*]] = load i8, ptr [[TMP1]], align 1
517517
; CHECK-NEXT: [[CMP3:%.*]] = icmp eq i8 [[LD1]], [[LD2]]
518518
; CHECK-NEXT: br i1 [[CMP3]], label %[[LOOP_INC]], label %[[LOOP_END:.*]]
519519
; CHECK: [[LOOP_INC]]:

0 commit comments

Comments
 (0)