Index: llvm/include/llvm/IR/InstrTypes.h =================================================================== --- llvm/include/llvm/IR/InstrTypes.h +++ llvm/include/llvm/IR/InstrTypes.h @@ -1755,6 +1755,11 @@ /// one byte is dereferenceable and the pointer is in addrspace(0). bool isReturnNonNull() const; + /// Return true if this parameter is implied nonnull by attributes. If + /// AllowUndefOrPoison is true, then a null parameter is merely poison. If it + /// is false, then a null parameter is undefined behavior. + bool paramIsNonNull(unsigned ArgNo, bool AllowUndefOrPoison) const; + /// Determine if the return value is marked with NoAlias attribute. bool returnDoesNotAlias() const { return Attrs.hasAttribute(AttributeList::ReturnIndex, Attribute::NoAlias); Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -2151,12 +2151,12 @@ // If the value is used as an argument to a call or invoke, then argument // attributes may provide an answer about null-ness. if (const auto *CB = dyn_cast(U)) - if (auto *CalledFunc = CB->getCalledFunction()) - for (const Argument &Arg : CalledFunc->args()) - if (CB->getArgOperand(Arg.getArgNo()) == V && - Arg.hasNonNullAttr(/* AllowUndefOrPoison */ false) && - DT->dominates(CB, CtxI)) - return true; + for (const Use &Arg : CB->args()) + if (Arg.get() == V && + CB->paramIsNonNull(CB->getArgOperandNo(&Arg), + /* AllowUndefOrPoison */ false) && + DT->dominates(CB, CtxI)) + return true; // If the value is used as a load/store, then the pointer must be non null. if (V == getLoadStorePointerOperand(U)) { Index: llvm/lib/IR/Instructions.cpp =================================================================== --- llvm/lib/IR/Instructions.cpp +++ llvm/lib/IR/Instructions.cpp @@ -322,6 +322,18 @@ return false; } +bool CallBase::paramIsNonNull(unsigned ArgNo, bool AllowUndefOrPoison) const { + Type *ArgType = getArgOperand(ArgNo)->getType(); + if (!ArgType->isPointerTy()) return false; + if (paramHasAttr(ArgNo, Attribute::NonNull) && + (AllowUndefOrPoison || paramHasAttr(ArgNo, Attribute::NoUndef))) + return true; + if (getDereferenceableBytes(ArgNo) > 0 && + !NullPointerIsDefined(getFunction(), ArgType->getPointerAddressSpace())) + return true; + return false; +} + Value *CallBase::getReturnedArgOperand() const { unsigned Index; Index: llvm/test/Transforms/InstCombine/align-addr.ll =================================================================== --- llvm/test/Transforms/InstCombine/align-addr.ll +++ llvm/test/Transforms/InstCombine/align-addr.ll @@ -115,7 +115,7 @@ ; CHECK-LABEL: @test3( ; CHECK-NEXT: [[A4_CAST:%.*]] = bitcast %struct.s* [[A4:%.*]] to i8* ; CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* noundef nonnull align 4 dereferenceable(16) [[A4_CAST]], i8 0, i64 16, i1 false) -; CHECK-NEXT: call void @use(i8* [[A4_CAST]]) +; CHECK-NEXT: call void @use(i8* nonnull [[A4_CAST]]) ; CHECK-NEXT: ret void ; %a4.cast = bitcast %struct.s* %a4 to i8* Index: llvm/test/Transforms/InstCombine/memset_chk-1.ll =================================================================== --- llvm/test/Transforms/InstCombine/memset_chk-1.ll +++ llvm/test/Transforms/InstCombine/memset_chk-1.ll @@ -76,7 +76,7 @@ ; CHECK-NEXT: [[CALL49:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[A:%.*]]) ; CHECK-NEXT: [[ADD180:%.*]] = add i64 [[CALL49]], 1 ; CHECK-NEXT: [[YO107:%.*]] = call i64 @llvm.objectsize.i64.p0i8(i8* [[B:%.*]], i1 false, i1 false, i1 false) -; CHECK-NEXT: [[CALL50:%.*]] = call i8* @__memmove_chk(i8* [[B]], i8* [[A]], i64 [[ADD180]], i64 [[YO107]]) +; CHECK-NEXT: [[CALL50:%.*]] = call i8* @__memmove_chk(i8* [[B]], i8* nonnull [[A]], i64 [[ADD180]], i64 [[YO107]]) ; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[B]]) ; CHECK-NEXT: [[STRCHR1:%.*]] = getelementptr i8, i8* [[B]], i64 [[STRLEN]] ; CHECK-NEXT: [[D:%.*]] = load i8*, i8** [[C:%.*]], align 8 @@ -114,13 +114,13 @@ define float* @pr25892(i64 %size) #0 { ; CHECK-LABEL: @pr25892( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @malloc(i64 [[SIZE:%.*]]) [[ATTR3:#.*]] +; CHECK-NEXT: [[CALL:%.*]] = tail call i8* @malloc(i64 [[SIZE:%.*]]) #[[ATTR3:[0-9]+]] ; CHECK-NEXT: [[CMP:%.*]] = icmp eq i8* [[CALL]], null ; CHECK-NEXT: br i1 [[CMP]], label [[CLEANUP:%.*]], label [[IF_END:%.*]] ; CHECK: if.end: ; CHECK-NEXT: [[BC:%.*]] = bitcast i8* [[CALL]] to float* ; CHECK-NEXT: [[CALL2:%.*]] = tail call i64 @llvm.objectsize.i64.p0i8(i8* nonnull [[CALL]], i1 false, i1 false, i1 false) -; CHECK-NEXT: [[CALL3:%.*]] = tail call i8* @__memset_chk(i8* nonnull [[CALL]], i32 0, i64 [[SIZE]], i64 [[CALL2]]) [[ATTR3]] +; CHECK-NEXT: [[CALL3:%.*]] = tail call i8* @__memset_chk(i8* nonnull [[CALL]], i32 0, i64 [[SIZE]], i64 [[CALL2]]) #[[ATTR3]] ; CHECK-NEXT: br label [[CLEANUP]] ; CHECK: cleanup: ; CHECK-NEXT: [[RETVAL_0:%.*]] = phi float* [ [[BC]], [[IF_END]] ], [ null, [[ENTRY:%.*]] ] Index: llvm/test/Transforms/InstCombine/sprintf-1.ll =================================================================== --- llvm/test/Transforms/InstCombine/sprintf-1.ll +++ llvm/test/Transforms/InstCombine/sprintf-1.ll @@ -106,7 +106,7 @@ ; WIN-LABEL: @test_simplify7( ; WIN-NEXT: [[STRLEN:%.*]] = call i32 @strlen(i8* noundef nonnull dereferenceable(1) [[STR:%.*]]) ; WIN-NEXT: [[LENINC:%.*]] = add i32 [[STRLEN]], 1 -; WIN-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 [[DST:%.*]], i8* align 1 [[STR]], i32 [[LENINC]], i1 false) +; WIN-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 [[DST:%.*]], i8* nonnull align 1 [[STR]], i32 [[LENINC]], i1 false) ; WIN-NEXT: ret i32 [[STRLEN]] ; %fmt = getelementptr [3 x i8], [3 x i8]* @percent_s, i32 0, i32 0 @@ -139,7 +139,7 @@ ; WIN-LABEL: @test_simplify9( ; WIN-NEXT: [[STRLEN:%.*]] = call i32 @strlen(i8* noundef nonnull dereferenceable(1) [[STR:%.*]]) ; WIN-NEXT: [[LENINC:%.*]] = add i32 [[STRLEN]], 1 -; WIN-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 [[DST:%.*]], i8* align 1 [[STR]], i32 [[LENINC]], i1 false) +; WIN-NEXT: call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 1 [[DST:%.*]], i8* nonnull align 1 [[STR]], i32 [[LENINC]], i1 false) ; WIN-NEXT: ret i32 [[STRLEN]] ; %fmt = getelementptr [3 x i8], [3 x i8]* @percent_s, i32 0, i32 0 Index: llvm/test/Transforms/InstCombine/strstr-1.ll =================================================================== --- llvm/test/Transforms/InstCombine/strstr-1.ll +++ llvm/test/Transforms/InstCombine/strstr-1.ll @@ -62,7 +62,7 @@ define i1 @test_simplify5(i8* %str, i8* %pat) { ; CHECK-LABEL: @test_simplify5( ; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(i8* noundef nonnull dereferenceable(1) [[PAT:%.*]]) -; CHECK-NEXT: [[STRNCMP:%.*]] = call i32 @strncmp(i8* [[STR:%.*]], i8* [[PAT]], i64 [[STRLEN]]) +; CHECK-NEXT: [[STRNCMP:%.*]] = call i32 @strncmp(i8* [[STR:%.*]], i8* nonnull [[PAT]], i64 [[STRLEN]]) ; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[STRNCMP]], 0 ; CHECK-NEXT: ret i1 [[CMP1]] ; Index: llvm/test/Transforms/InstSimplify/known-non-zero-opaque-ptrs.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstSimplify/known-non-zero-opaque-ptrs.ll @@ -0,0 +1,47 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -S -instsimplify -force-opaque-pointers < %s | FileCheck %s + +declare void @zero_args() +declare void @two_args(ptr, ptr) + +define i1 @test_zero_args_nonnull(ptr %p) { +; CHECK-LABEL: @test_zero_args_nonnull( +; CHECK-NEXT: call void @zero_args(ptr noundef nonnull [[P:%.*]]) +; CHECK-NEXT: ret i1 true +; + call void @zero_args(ptr nonnull noundef %p) + %c = icmp ne ptr %p, null + ret i1 %c +} + +define i1 @test_zero_args_maybe_null(ptr %p) { +; CHECK-LABEL: @test_zero_args_maybe_null( +; CHECK-NEXT: call void @zero_args(ptr [[P:%.*]]) +; CHECK-NEXT: [[C:%.*]] = icmp ne ptr [[P]], null +; CHECK-NEXT: ret i1 [[C]] +; + call void @zero_args(ptr %p) + %c = icmp ne ptr %p, null + ret i1 %c +} + +define i1 @test_two_args_nonnull(ptr %p) { +; CHECK-LABEL: @test_two_args_nonnull( +; CHECK-NEXT: call void @two_args(ptr noundef nonnull [[P:%.*]]) +; CHECK-NEXT: ret i1 true +; + call void @two_args(ptr nonnull noundef %p) + %c = icmp ne ptr %p, null + ret i1 %c +} + +define i1 @test_two_args_maybe_null(ptr %p) { +; CHECK-LABEL: @test_two_args_maybe_null( +; CHECK-NEXT: call void @two_args(ptr [[P:%.*]]) +; CHECK-NEXT: [[C:%.*]] = icmp ne ptr [[P]], null +; CHECK-NEXT: ret i1 [[C]] +; + call void @two_args(ptr %p) + %c = icmp ne ptr %p, null + ret i1 %c +}