diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index be752b42ea412..39c74128de6c8 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -3115,6 +3115,7 @@ the behavior is undefined, unless one of the following exceptions applies: * ``dereferenceable()`` operand bundles only guarantee the pointer is dereferenceable at the point of the assumption. The pointer may not be dereferenceable at later pointers, e.g., because it could have been freed. + Only ``n > 0`` implies that the pointer is dereferenceable. In addition to allowing operand bundles encoding function and parameter attributes, an assume operand bundle may also encode a ``separate_storage`` diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 3dc321b28a060..ece8425aef698 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -834,12 +834,26 @@ 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; + assert(RK.IRArgValue && + "Dereferenceable attribute without IR argument?"); + + auto *CI = dyn_cast(RK.IRArgValue); + return CI && !CI->isZero(); + } + + 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..dafc15c2b2d23 100644 --- a/llvm/test/Analysis/ValueTracking/assume.ll +++ b/llvm/test/Analysis/ValueTracking/assume.ll @@ -159,3 +159,35 @@ A: %6 = phi i32 [ %4, %3 ], [ 0, %A ] ret i32 %6 } + +define i1 @test_dereferenceable_unknown_size_not_nonnull(ptr %ptr, i32 %bytes) { +; CHECK-LABEL: @test_dereferenceable_unknown_size_not_nonnull( +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 [[BYTES:%.*]]) ] +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null +; CHECK-NEXT: ret i1 [[TMP2]] +; + call void @llvm.assume(i1 true) ["dereferenceable"(ptr %ptr, i32 %bytes)] + %2 = icmp eq ptr %ptr, null + ret i1 %2 +} + +define i1 @test_dereferenceable_zero_size_not_nonnull(ptr %ptr) { +; CHECK-LABEL: @test_dereferenceable_zero_size_not_nonnull( +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 0) ] +; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null +; CHECK-NEXT: ret i1 [[TMP2]] +; + call void @llvm.assume(i1 true) ["dereferenceable"(ptr %ptr, i32 0)] + %2 = icmp eq ptr %ptr, null + ret i1 %2 +} + +define i1 @test_dereferenceable_non_zero_size_is_nonnull(ptr %ptr) { +; CHECK-LABEL: @test_dereferenceable_non_zero_size_is_nonnull( +; CHECK-NEXT: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr [[TMP0:%.*]], i32 1) ] +; CHECK-NEXT: ret i1 false +; + call void @llvm.assume(i1 true) ["dereferenceable"(ptr %ptr, i32 1)] + %2 = icmp eq ptr %ptr, null + ret i1 %2 +}