[llvm] 375fa6f - [Loads] Use willNotFreeBetween() for dereferenceable-at-point (#201353)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 4 00:21:15 PDT 2026
Author: Nikita Popov
Date: 2026-06-04T09:21:11+02:00
New Revision: 375fa6fb02035afc8719e8484f0c914a96987fd8
URL: https://github.com/llvm/llvm-project/commit/375fa6fb02035afc8719e8484f0c914a96987fd8
DIFF: https://github.com/llvm/llvm-project/commit/375fa6fb02035afc8719e8484f0c914a96987fd8.diff
LOG: [Loads] Use willNotFreeBetween() for dereferenceable-at-point (#201353)
If dereferenceable-at-point semantics are enabled, use
willNotFreeBetween() to check whether frees are known to not occur
between the definition point of the value and the context instruction.
We already use this logic for dereferenceable assumptions, this enables
it for other dereferenceability fact (under deref-at-point).
Added:
Modified:
llvm/lib/Analysis/Loads.cpp
llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
Removed:
################################################################################
diff --git a/llvm/lib/Analysis/Loads.cpp b/llvm/lib/Analysis/Loads.cpp
index 797d18325c336..1aed126079829 100644
--- a/llvm/lib/Analysis/Loads.cpp
+++ b/llvm/lib/Analysis/Loads.cpp
@@ -130,12 +130,34 @@ static bool isDereferenceableAndAlignedPointer(
auto IsKnownDeref = [&]() {
bool CheckForNonNull, CheckForFreed;
if (!Size.ule(V->getPointerDereferenceableBytes(DL, CheckForNonNull,
- CheckForFreed)) ||
- CheckForFreed)
+ CheckForFreed)))
return false;
if (CheckForNonNull &&
!isKnownNonZero(V, SimplifyQuery(DL, DT, AC, CtxI)))
return false;
+
+ auto *I = dyn_cast<Instruction>(V);
+ if (CheckForFreed) {
+ const Instruction *DefI;
+ if (I) {
+ // We don't want to consider frees by the instruction producing the
+ // pointer, so skip it if we can.
+ if (auto *II = dyn_cast<InvokeInst>(V)) {
+ DefI = &II->getNormalDest()->front();
+ } else if (!I->isTerminator()) {
+ DefI = I->getNextNode();
+ } else {
+ DefI = I;
+ }
+ } else {
+ // For arguments, check frees from the start of the entry block.
+ DefI = &cast<Argument>(V)->getParent()->getEntryBlock().front();
+ }
+
+ if (!willNotFreeBetween(DefI, CtxI))
+ return false;
+ }
+
// When using something like !dereferenceable on a load, the
// dereferenceability may only be valid on a specific control-flow path.
// If the instruction doesn't dominate the context instruction, we're
@@ -144,7 +166,6 @@ static bool isDereferenceableAndAlignedPointer(
// in which case we don't know if the dereferenceability info still holds.
// We don't bother handling allocas here, as they aren't speculatable
// anyway.
- auto *I = dyn_cast<Instruction>(V);
if (I && !isa<AllocaInst>(I))
return CtxI && isValidAssumeForContext(I, CtxI, DT);
return true;
diff --git a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
index a03a76373f803..f7e69b0c1f04f 100644
--- a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
+++ b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll
@@ -67,6 +67,7 @@ entry:
; GLOBAL: %d4_load{{.*}}(unaligned)
; POINT-NOT: %d4_load{{.*}}(unaligned)
%d4_load = load ptr, ptr @globali32ptr, !dereferenceable !0
+ call void @mayfree()
%load7 = load i32, ptr %d4_load
; Load from an offset not covered by the dereferenceable portion
@@ -83,6 +84,7 @@ entry:
; GLOBAL: %d_or_null_non_null_load{{.*}}(unaligned)
; POINT-NOT: %d_or_null_non_null_load{{.*}}(unaligned)
%d_or_null_non_null_load = load ptr, ptr @globali32ptr, !nonnull !2, !dereferenceable_or_null !0
+ call void @mayfree()
%load10 = load i32, ptr %d_or_null_non_null_load
; Loads from aligned arguments
@@ -119,6 +121,7 @@ entry:
%no_deref_return = call ptr @foo()
%deref_return = call dereferenceable(32) ptr @foo()
%deref_and_aligned_return = call dereferenceable(32) align 16 ptr @foo()
+ call void @mayfree()
%load23 = load i32, ptr %no_deref_return
%load24 = load i32, ptr %deref_return, align 16
%load25 = load i32, ptr %deref_and_aligned_return, align 16
@@ -130,8 +133,10 @@ entry:
; POINT-NOT: %d4_aligned_load{{.*}}(aligned)
%d4_unaligned_load = load ptr, ptr @globali32ptr, !dereferenceable !0
%d4_aligned_load = load ptr, ptr @globali32ptr, !dereferenceable !0, !align !{i64 16}
+ call void @mayfree()
%load26 = load i32, ptr %d4_unaligned_load, align 16
%load27 = load i32, ptr %d4_aligned_load, align 16
+
ret void
}
@@ -311,6 +316,76 @@ if.end:
ret void
}
+; CHECK-LABEL: 'dereferenceable_arg_freed_between'
+; GLOBAL: %a
+; POINT-NOT: %a
+define void @dereferenceable_arg_freed_between(ptr dereferenceable(16) %a) {
+ call void @mayfree()
+ %v = load i32, ptr %a
+ ret void
+}
+
+; CHECK-LABEL: 'dereferenceable_arg_freed_between2'
+; GLOBAL: %a
+; POINT-NOT: %a
+define void @dereferenceable_arg_freed_between2(ptr dereferenceable(16) %a, ptr %p) {
+ load atomic i32, ptr %p acquire, align 4
+ %v = load i32, ptr %a
+ ret void
+}
+
+; CHECK-LABEL: 'dereferenceable_arg_not_freed_between'
+; CHECK: %a
+define void @dereferenceable_arg_not_freed_between(ptr dereferenceable(16) %a) {
+ call void @mayfree() nofree
+ %v = load i32, ptr %a
+ ret void
+}
+
+; CHECK-LABEL: 'dereferenceable_ret_freed_between'
+; GLOBAL: %a
+; POINT-NOT: %a
+define void @dereferenceable_ret_freed_between() {
+ %a = call dereferenceable(16) ptr @foo()
+ call void @mayfree()
+ %v = load i32, ptr %a
+ ret void
+}
+
+; CHECK-LABEL: 'dereferenceable_ret_freed_between2'
+; GLOBAL: %a
+; POINT-NOT: %a
+define void @dereferenceable_ret_freed_between2(ptr %p) {
+ %a = call dereferenceable(16) ptr @foo()
+ load atomic i32, ptr %p acquire, align 4
+ %v = load i32, ptr %a
+ ret void
+}
+
+; CHECK-LABEL: 'dereferenceable_ret_not_freed_between'
+; CHECK: %a
+define void @dereferenceable_ret_not_freed_between() {
+ %a = call dereferenceable(16) ptr @foo()
+ call void @mayfree() nofree
+ %v = load i32, ptr %a
+ ret void
+}
+
+; CHECK-LABEL: 'dereferenceable_ret_not_freed_between_invoke'
+; CHECK: %a
+define void @dereferenceable_ret_not_freed_between_invoke() personality ptr null {
+ %a = invoke dereferenceable(16) ptr @foo() to label %cont unwind label %lpad
+
+cont:
+ call void @mayfree() nofree
+ %v = load i32, ptr %a
+ ret void
+
+lpad:
+ %l = landingpad { ptr, i32 } cleanup
+ resume { ptr, i32} %l
+}
+
declare token @llvm.experimental.gc.statepoint.p0(i64, i32, ptr, i32, i32, ...)
declare ptr addrspace(1) @llvm.experimental.gc.relocate.p1(token, i32, i32)
More information about the llvm-commits
mailing list