diff --git a/llvm/lib/IR/Value.cpp b/llvm/lib/IR/Value.cpp --- a/llvm/lib/IR/Value.cpp +++ b/llvm/lib/IR/Value.cpp @@ -613,9 +613,14 @@ bool &CanBeNull) const { assert(getType()->isPointerTy() && "must be pointer"); + const Function *F = nullptr; + if (auto *I = dyn_cast(this)) + F = I->getFunction(); + uint64_t DerefBytes = 0; CanBeNull = false; if (const Argument *A = dyn_cast(this)) { + F = A->getParent(); DerefBytes = A->getDereferenceableBytes(); if (DerefBytes == 0 && (A->hasByValAttr() || A->hasStructRetAttr())) { Type *PT = cast(A->getType())->getElementType(); @@ -672,6 +677,12 @@ CanBeNull = false; } } + + // Even if we know it is "dereferenceable", it can still be null if null is a + // valid pointer. This is a problem as "can be null" is overloaded to mean, + // "equals NULL" and "or is not dereferenceable". + // TODO: Add a separate flag to communicate "IsKnownDeref". + CanBeNull |= NullPointerIsDefined(F, getType()->getPointerAddressSpace()); return DerefBytes; } diff --git a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll --- a/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll +++ b/llvm/test/Analysis/ValueTracking/memory-dereferenceable.ll @@ -51,10 +51,16 @@ %sret_gep_outside = getelementptr %struct.A, %struct.A* %result, i64 0, i32 1, i64 7 load i8, i8* %sret_gep_outside -; CHECK: %dparam{{.*}}(aligned) +; FIXME: This can be null but it is also known dereferenceable. However, right +; now we cannot return both information from +; Value::getPointerDereferenceableBytes(...). +; CHECK-NOT: %dparam{{.*}}(aligned) %load3 = load i32, i32 addrspace(1)* %dparam -; CHECK: %relocate{{.*}}(aligned) +; FIXME: This can be null but it is also known dereferenceable. However, right +; now we cannot return both information from +; Value::getPointerDereferenceableBytes(...). +; CHECK-NOT: %relocate{{.*}}(aligned) %tok = tail call token (i64, i32, i1 ()*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_i1f(i64 0, i32 0, i1 ()* @return_i1, i32 0, i32 0, i32 0, i32 0, i32 addrspace(1)* %dparam) %relocate = call i32 addrspace(1)* @llvm.experimental.gc.relocate.p1i32(token %tok, i32 7, i32 7) %load4 = load i32, i32 addrspace(1)* %relocate @@ -106,8 +112,11 @@ %load14 = load i8, i8* @globalptr.align16, align 16 ; Loads from aligned arguments -; CHECK: %dparam.align1{{.*}}(unaligned) -; CHECK: %dparam.align16{{.*}}(aligned) +; FIXME: This can be null but it is also known dereferenceable. However, right +; now we cannot return both information from +; Value::getPointerDereferenceableBytes(...). +; CHECK-NOT: %dparam.align1{{.*}}(unaligned) +; CHECK-NOT: %dparam.align16{{.*}}(aligned) %load15 = load i8, i8 addrspace(1)* %dparam.align1, align 16 %load16 = load i8, i8 addrspace(1)* %dparam.align16, align 16 @@ -132,10 +141,13 @@ %load18 = load i1, i1* %alloca.align16, align 16 ; Loads from GEPs -; CHECK: %gep.align1.offset1{{.*}}(unaligned) -; CHECK: %gep.align16.offset1{{.*}}(unaligned) -; CHECK: %gep.align1.offset16{{.*}}(unaligned) -; CHECK: %gep.align16.offset16{{.*}}(aligned) +; FIXME: This can be null but it is also known dereferenceable. However, right +; now we cannot return both information from +; Value::getPointerDereferenceableBytes(...). +; CHECK-NOT: %gep.align1.offset1{{.*}}(unaligned) +; CHECK-NOT: %gep.align16.offset1{{.*}}(unaligned) +; CHECK-NOT: %gep.align1.offset16{{.*}}(unaligned) +; CHECK-NOT: %gep.align16.offset16{{.*}}(aligned) %gep.align1.offset1 = getelementptr inbounds i8, i8 addrspace(1)* %dparam.align1, i32 1 %gep.align16.offset1 = getelementptr inbounds i8, i8 addrspace(1)* %dparam.align16, i32 1 %gep.align1.offset16 = getelementptr inbounds i8, i8 addrspace(1)* %dparam.align1, i32 16