[ValueTracking] Allow dereferenceable(0) to be applied to a null pointer#175913
[ValueTracking] Allow dereferenceable(0) to be applied to a null pointer#175913philnik777 merged 4 commits intollvm:mainfrom
Conversation
|
@llvm/pr-subscribers-llvm-ir @llvm/pr-subscribers-llvm-analysis Author: Nikolas Klauser (philnik777) Changes
Full diff: https://github.com/llvm/llvm-project/pull/175913.diff 3 Files Affected:
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index be752b42ea412..58dcb7d42a3d1 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -1572,8 +1572,8 @@ Currently, only the following parameter attributes are defined:
dereferenceability (consider a pointer to one element past the end of an
array), however ``dereferenceable(<n>)`` does imply ``nonnull`` in
``addrspace(0)`` (which is the default address space), except if the
- ``null_pointer_is_valid`` function attribute is present.
- ``n`` should be a positive number. The pointer should be well defined,
+ ``null_pointer_is_valid`` function attribute is present or ``n == 0``.
+ ``n`` should be a non-negative number. The pointer should be well defined,
otherwise it is undefined behavior. This means ``dereferenceable(<n>)``
implies ``noundef``. When used in an assume operand bundle, more restricted
semantics apply. See :ref:`assume operand bundles <assume_opbundles>` for
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 3dc321b28a060..be346a9fcdd77 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -834,12 +834,29 @@ static bool isKnownNonZeroFromAssume(const Value *V, const SimplifyQuery &Q) {
continue;
if (RetainedKnowledge RK = getKnowledgeFromBundle(
*I, I->bundle_op_info_begin()[Elem.Index])) {
- if (RK.WasOn == V &&
- (RK.AttrKind == Attribute::NonNull ||
- (RK.AttrKind == Attribute::Dereferenceable &&
- !NullPointerIsDefined(Q.CxtI->getFunction(),
- V->getType()->getPointerAddressSpace()))) &&
- isValidAssumeForContext(I, Q.CxtI, Q.DT))
+ if (RK.WasOn != V)
+ continue;
+ bool AssumeImpliesNonNull = [&]() {
+ if (RK.AttrKind == Attribute::NonNull)
+ return true;
+
+ if (RK.AttrKind == Attribute::Dereferenceable) {
+ if (NullPointerIsDefined(Q.CxtI->getFunction(),
+ V->getType()->getPointerAddressSpace()))
+ return false;
+
+ if (!RK.IRArgValue)
+ return true;
+
+ if(auto* CI = dyn_cast<ConstantInt>(RK.IRArgValue))
+ return !CI->isZero();
+
+ return false;
+ }
+
+ return false;
+ }();
+ if (AssumeImpliesNonNull && isValidAssumeForContext(I, Q.CxtI, Q.DT))
return true;
}
continue;
diff --git a/llvm/test/Analysis/ValueTracking/assume.ll b/llvm/test/Analysis/ValueTracking/assume.ll
index 298facfa3aa9d..e5f5cdbdd9cfe 100644
--- a/llvm/test/Analysis/ValueTracking/assume.ll
+++ b/llvm/test/Analysis/ValueTracking/assume.ll
@@ -159,3 +159,73 @@ A:
%6 = phi i32 [ %4, %3 ], [ 0, %A ]
ret i32 %6
}
+
+define dso_local i32 @test5(ptr readonly %0, i1 %cond, i32 %bytes) nofree nosync {
+; CHECK-LABEL: @test5(
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 [[BYTES:%.*]]) ]
+; CHECK-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
+; CHECK: B:
+; CHECK-NEXT: br label [[A]]
+; CHECK: A:
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
+; CHECK-NEXT: br i1 [[TMP2]], label [[TMP4:%.*]], label [[TMP6:%.*]]
+; CHECK: 3:
+; CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4
+; CHECK-NEXT: br label [[TMP4]]
+; CHECK: 5:
+; CHECK-NEXT: [[TMP5:%.*]] = phi i32 [ [[TMP3]], [[TMP6]] ], [ 0, [[A]] ]
+; CHECK-NEXT: ret i32 [[TMP5]]
+;
+ call void @llvm.assume(i1 true) ["dereferenceable"(ptr %0, i32 %bytes)]
+ br i1 %cond, label %A, label %B
+
+B:
+ br label %A
+
+A:
+ %2 = icmp eq ptr %0, null
+ br i1 %2, label %5, label %3
+
+3: ; preds = %1
+ %4 = load i32, ptr %0, align 4
+ br label %5
+
+5: ; preds = %1, %3
+ %6 = phi i32 [ %4, %3 ], [ 0, %A ]
+ ret i32 %6
+}
+
+define dso_local i32 @test6(ptr readonly %0, i1 %cond) nofree nosync {
+; CHECK-LABEL: @test6(
+; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 0) ]
+; CHECK-NEXT: br i1 [[COND:%.*]], label [[A:%.*]], label [[B:%.*]]
+; CHECK: B:
+; CHECK-NEXT: br label [[A]]
+; CHECK: A:
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null
+; CHECK-NEXT: br i1 [[TMP2]], label [[TMP4:%.*]], label [[TMP6:%.*]]
+; CHECK: 3:
+; CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[TMP0]], align 4
+; CHECK-NEXT: br label [[TMP4]]
+; CHECK: 5:
+; CHECK-NEXT: [[TMP5:%.*]] = phi i32 [ [[TMP3]], [[TMP6]] ], [ 0, [[A]] ]
+; CHECK-NEXT: ret i32 [[TMP5]]
+;
+ call void @llvm.assume(i1 true) ["dereferenceable"(ptr %0, i32 0)]
+ br i1 %cond, label %A, label %B
+
+B:
+ br label %A
+
+A:
+ %2 = icmp eq ptr %0, null
+ br i1 %2, label %5, label %3
+
+3: ; preds = %1
+ %4 = load i32, ptr %0, align 4
+ br label %5
+
+5: ; preds = %1, %3
+ %6 = phi i32 [ %4, %3 ], [ 0, %A ]
+ ret i32 %6
+}
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
nikic
left a comment
There was a problem hiding this comment.
This shouldn't change the spec for the dereferenceable attribute, but add an exception to the assume operand bundle docs in https://llvm.org/docs/LangRef.html#assume-operand-bundles.
dereferenceable(0) is rejected by the IR parser, and I don't see a reason to allow it. Operand bundles are generally more liberal in what they accept, specifically because of non-constant values.
fhahn
left a comment
There was a problem hiding this comment.
LGTM, thanks for tracking this down + the fix
…ter (llvm#175913) `dereferenceable(<n>)` with n being potentially zero can come up when using an operand bundle with a variable size. Currently this implies that the pointer is non-null, even though `[nullptr, nullptr)` is a valid range in any programming language I'm aware of. This patch removes this implication and updates the language reference to reflect that `dereferenceable` with a zero argument is valid.
llvm#175913 removed that `__builtin_assume_dereferenceable(ptr, 0)` implies `ptr != nullptr`, which should allow us to use the builtin with LLVM 23. This reverts commit 776c09c.
…ter (llvm#175913) `dereferenceable(<n>)` with n being potentially zero can come up when using an operand bundle with a variable size. Currently this implies that the pointer is non-null, even though `[nullptr, nullptr)` is a valid range in any programming language I'm aware of. This patch removes this implication and updates the language reference to reflect that `dereferenceable` with a zero argument is valid. (cherry picked from commit d2afc3e)
…ter (llvm#175913) `dereferenceable(<n>)` with n being potentially zero can come up when using an operand bundle with a variable size. Currently this implies that the pointer is non-null, even though `[nullptr, nullptr)` is a valid range in any programming language I'm aware of. This patch removes this implication and updates the language reference to reflect that `dereferenceable` with a zero argument is valid.
llvm#175913 removed that `__builtin_assume_dereferenceable(ptr, 0)` implies `ptr != nullptr`, which should allow us to use the builtin with LLVM 23. This reverts commit 776c09c.
dereferenceable(<n>)with n being potentially zero can come up when using an operand bundle with a variable size. Currently this implies that the pointer is non-null, even though[nullptr, nullptr)is a valid range in any programming language I'm aware of. This patch removes this implication and updates the language reference to reflect thatdereferenceablewith a zero argument is valid.