diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h index 47ddc7555594c57..6ff90e1d095aace 100644 --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1591,6 +1591,14 @@ class CallBase : public Instruction { /// Determine whether the argument or parameter has the given attribute. bool paramHasAttr(unsigned ArgNo, Attribute::AttrKind Kind) const; + /// Return true if this argument has the nonnull attribute on either the + /// CallBase instruction or the called function. Also returns true if at least + /// one byte is known to be dereferenceable and the pointer is in + /// addrspace(0). If \p AllowUndefOrPoison is true, respect the semantics of + /// nonnull attribute and return true even if the argument can be undef or + /// poison. + bool paramHasNonNullAttr(unsigned ArgNo, bool AllowUndefOrPoison) const; + /// Get the attribute of a given kind at a position. Attribute getAttributeAtIndex(unsigned i, Attribute::AttrKind Kind) const { return getAttributes().getAttributeAtIndex(i, Kind); diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index b63a0a07f7de292..45c3b85ea39fb46 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -2652,40 +2652,42 @@ static bool isKnownNonNullFromDominatingCondition(const Value *V, return false; unsigned NumUsesExplored = 0; - for (const auto *U : V->users()) { + for (auto &U : V->uses()) { // Avoid massive lists if (NumUsesExplored >= DomConditionsMaxUses) break; NumUsesExplored++; + const Instruction *UI = cast(U.getUser()); // 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; + if (V->getType()->isPointerTy()) { + if (const auto *CB = dyn_cast(UI)) { + if (CB->isArgOperand(&U) && + CB->paramHasNonNullAttr(CB->getArgOperandNo(&U), + /*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)) { - const Instruction *I = cast(U); - if (!NullPointerIsDefined(I->getFunction(), + if (V == getLoadStorePointerOperand(UI)) { + if (!NullPointerIsDefined(UI->getFunction(), V->getType()->getPointerAddressSpace()) && - DT->dominates(I, CtxI)) + DT->dominates(UI, CtxI)) return true; } - if ((match(U, m_IDiv(m_Value(), m_Specific(V))) || - match(U, m_IRem(m_Value(), m_Specific(V)))) && - isValidAssumeForContext(cast(U), CtxI, DT)) + if ((match(UI, m_IDiv(m_Value(), m_Specific(V))) || + match(UI, m_IRem(m_Value(), m_Specific(V)))) && + isValidAssumeForContext(UI, CtxI, DT)) return true; // Consider only compare instructions uniquely controlling a branch Value *RHS; CmpPredicate Pred; - if (!match(U, m_c_ICmp(Pred, m_Specific(V), m_Value(RHS)))) + if (!match(UI, m_c_ICmp(Pred, m_Specific(V), m_Value(RHS)))) continue; bool NonNullIfTrue; @@ -2698,7 +2700,7 @@ static bool isKnownNonNullFromDominatingCondition(const Value *V, SmallVector WorkList; SmallPtrSet Visited; - for (const auto *CmpU : U->users()) { + for (const auto *CmpU : UI->users()) { assert(WorkList.empty() && "Should be!"); if (Visited.insert(CmpU).second) WorkList.push_back(CmpU); diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp index c9f5807765e400f..dd3b79ed6d7d273 100644 --- a/llvm/lib/IR/Instructions.cpp +++ b/llvm/lib/IR/Instructions.cpp @@ -432,6 +432,23 @@ bool CallBase::paramHasAttr(unsigned ArgNo, Attribute::AttrKind Kind) const { } } +bool CallBase::paramHasNonNullAttr(unsigned ArgNo, + bool AllowUndefOrPoison) const { + assert(getArgOperand(ArgNo)->getType()->isPointerTy() && + "Argument must be a pointer"); + if (paramHasAttr(ArgNo, Attribute::NonNull) && + (AllowUndefOrPoison || paramHasAttr(ArgNo, Attribute::NoUndef))) + return true; + + if (getParamDereferenceableBytes(ArgNo) > 0 && + !NullPointerIsDefined( + getCaller(), + getArgOperand(ArgNo)->getType()->getPointerAddressSpace())) + return true; + + return false; +} + bool CallBase::hasFnAttrOnCalledFunction(Attribute::AttrKind Kind) const { if (auto *F = dyn_cast(getCalledOperand())) return F->getAttributes().hasFnAttr(Kind); diff --git a/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll b/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll index 79d2653a3a14666..dff7e13b8a2e773 100644 --- a/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll +++ b/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll @@ -220,3 +220,70 @@ return: %retval.0 = phi ptr [ %1, %if.end ], [ null, %entry ] ret ptr %retval.0 } + +define i1 @test_known_nonnull_at_callsite(ptr %src) { +; CHECK-LABEL: @test_known_nonnull_at_callsite( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @callee(ptr noundef nonnull [[SRC:%.*]]) +; CHECK-NEXT: ret i1 false +; +entry: + call void @callee(ptr noundef nonnull %src) + %nonnull = icmp eq ptr %src, null + ret i1 %nonnull +} + +define i1 @test_known_nonnull_mixed(ptr %src) { +; CHECK-LABEL: @test_known_nonnull_mixed( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @callee2(ptr nonnull [[SRC:%.*]]) +; CHECK-NEXT: ret i1 false +; +entry: + call void @callee2(ptr nonnull %src) + %nonnull = icmp eq ptr %src, null + ret i1 %nonnull +} + +define i1 @test_known_nonnull_at_callsite_dereferenceable(ptr %src) { +; CHECK-LABEL: @test_known_nonnull_at_callsite_dereferenceable( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @callee(ptr dereferenceable(1) [[SRC:%.*]]) +; CHECK-NEXT: ret i1 false +; +entry: + call void @callee(ptr dereferenceable(1) %src) + %nonnull = icmp eq ptr %src, null + ret i1 %nonnull +} + +; Negative tests + +define i1 @test_known_nonnull_at_callsite_without_noundef(ptr %src) { +; CHECK-LABEL: @test_known_nonnull_at_callsite_without_noundef( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @callee(ptr nonnull [[SRC:%.*]]) +; CHECK-NEXT: [[NONNULL:%.*]] = icmp eq ptr [[SRC]], null +; CHECK-NEXT: ret i1 [[NONNULL]] +; +entry: + call void @callee(ptr nonnull %src) + %nonnull = icmp eq ptr %src, null + ret i1 %nonnull +} + +define i1 @test_known_nonnull_at_callsite_dereferenceable_null_is_defined(ptr %src) null_pointer_is_valid { +; CHECK-LABEL: @test_known_nonnull_at_callsite_dereferenceable_null_is_defined( +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @callee(ptr dereferenceable(1) [[SRC:%.*]]) +; CHECK-NEXT: [[NONNULL:%.*]] = icmp eq ptr [[SRC]], null +; CHECK-NEXT: ret i1 [[NONNULL]] +; +entry: + call void @callee(ptr dereferenceable(1) %src) + %nonnull = icmp eq ptr %src, null + ret i1 %nonnull +} + +declare void @callee(ptr) +declare void @callee2(ptr noundef) diff --git a/llvm/test/Transforms/InstCombine/align-addr.ll b/llvm/test/Transforms/InstCombine/align-addr.ll index 6ef4d85fe4e4121..b77037e592b54b1 100644 --- a/llvm/test/Transforms/InstCombine/align-addr.ll +++ b/llvm/test/Transforms/InstCombine/align-addr.ll @@ -112,7 +112,7 @@ define void @test3(ptr sret(%struct.s) %a4) { ; Check that the alignment is bumped up the alignment of the sret type. ; CHECK-LABEL: @test3( ; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(16) [[A4:%.*]], i8 0, i64 16, i1 false) -; CHECK-NEXT: call void @use(ptr [[A4]]) +; CHECK-NEXT: call void @use(ptr nonnull [[A4]]) ; CHECK-NEXT: ret void ; call void @llvm.memset.p0.i64(ptr %a4, i8 0, i64 16, i1 false) diff --git a/llvm/test/Transforms/InstCombine/memset_chk-1.ll b/llvm/test/Transforms/InstCombine/memset_chk-1.ll index 44b549e400dd857..9020f174fb5b7d7 100644 --- a/llvm/test/Transforms/InstCombine/memset_chk-1.ll +++ b/llvm/test/Transforms/InstCombine/memset_chk-1.ll @@ -92,7 +92,7 @@ define i32 @test_rauw(ptr %a, ptr %b, ptr %c) { ; CHECK-NEXT: [[CALL49:%.*]] = call i64 @strlen(ptr noundef nonnull dereferenceable(1) [[A:%.*]]) ; CHECK-NEXT: [[ADD180:%.*]] = add i64 [[CALL49]], 1 ; CHECK-NEXT: [[YO107:%.*]] = call i64 @llvm.objectsize.i64.p0(ptr [[B:%.*]], i1 false, i1 false, i1 false) -; CHECK-NEXT: [[CALL50:%.*]] = call ptr @__memmove_chk(ptr [[B]], ptr [[A]], i64 [[ADD180]], i64 [[YO107]]) +; CHECK-NEXT: [[CALL50:%.*]] = call ptr @__memmove_chk(ptr [[B]], ptr nonnull [[A]], i64 [[ADD180]], i64 [[YO107]]) ; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr noundef nonnull dereferenceable(1) [[B]]) ; CHECK-NEXT: [[STRCHR1:%.*]] = getelementptr inbounds i8, ptr [[B]], i64 [[STRLEN]] ; CHECK-NEXT: [[D:%.*]] = load ptr, ptr [[C:%.*]], align 8 @@ -100,7 +100,7 @@ define i32 @test_rauw(ptr %a, ptr %b, ptr %c) { ; CHECK-NEXT: [[SUB183:%.*]] = ptrtoint ptr [[B]] to i64 ; CHECK-NEXT: [[SUB184:%.*]] = sub i64 [[SUB182]], [[SUB183]] ; CHECK-NEXT: [[ADD52_I_I:%.*]] = add nsw i64 [[SUB184]], 1 -; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[STRCHR1]], i8 0, i64 [[ADD52_I_I]], i1 false) +; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr nonnull align 1 [[STRCHR1]], i8 0, i64 [[ADD52_I_I]], i1 false) ; CHECK-NEXT: ret i32 4 ; entry: diff --git a/llvm/test/Transforms/InstCombine/sprintf-1.ll b/llvm/test/Transforms/InstCombine/sprintf-1.ll index 0749015059415ca..1d87758340f710e 100644 --- a/llvm/test/Transforms/InstCombine/sprintf-1.ll +++ b/llvm/test/Transforms/InstCombine/sprintf-1.ll @@ -103,7 +103,7 @@ define i32 @test_simplify7(ptr %dst, ptr %str) { ; NOSTPCPY-LABEL: @test_simplify7( ; NOSTPCPY-NEXT: [[STRLEN:%.*]] = call i32 @strlen(ptr noundef nonnull dereferenceable(1) [[STR:%.*]]) ; NOSTPCPY-NEXT: [[LENINC:%.*]] = add i32 [[STRLEN]], 1 -; NOSTPCPY-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[DST:%.*]], ptr align 1 [[STR]], i32 [[LENINC]], i1 false) +; NOSTPCPY-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[DST:%.*]], ptr nonnull align 1 [[STR]], i32 [[LENINC]], i1 false) ; NOSTPCPY-NEXT: ret i32 [[STRLEN]] ; %r = call i32 (ptr, ptr, ...) @sprintf(ptr %dst, ptr @percent_s, ptr %str) @@ -133,7 +133,7 @@ define i32 @test_simplify9(ptr %dst, ptr %str) { ; NOSTPCPY-LABEL: @test_simplify9( ; NOSTPCPY-NEXT: [[STRLEN:%.*]] = call i32 @strlen(ptr noundef nonnull dereferenceable(1) [[STR:%.*]]) ; NOSTPCPY-NEXT: [[LENINC:%.*]] = add i32 [[STRLEN]], 1 -; NOSTPCPY-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[DST:%.*]], ptr align 1 [[STR]], i32 [[LENINC]], i1 false) +; NOSTPCPY-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[DST:%.*]], ptr nonnull align 1 [[STR]], i32 [[LENINC]], i1 false) ; NOSTPCPY-NEXT: ret i32 [[STRLEN]] ; %r = call i32 (ptr, ptr, ...) @sprintf(ptr %dst, ptr @percent_s, ptr %str) diff --git a/llvm/test/Transforms/InstCombine/stpncpy-1.ll b/llvm/test/Transforms/InstCombine/stpncpy-1.ll index 87f54918b7d25ed..6ef9b425ae9d937 100644 --- a/llvm/test/Transforms/InstCombine/stpncpy-1.ll +++ b/llvm/test/Transforms/InstCombine/stpncpy-1.ll @@ -70,11 +70,11 @@ define void @fold_stpncpy_overlap(ptr %dst, i64 %n) { define void @call_stpncpy_overlap(ptr %dst, i64 %n) { ; ANY-LABEL: @call_stpncpy_overlap( ; ANY-NEXT: [[ES_2:%.*]] = call ptr @stpncpy(ptr noundef nonnull dereferenceable(1) [[DST:%.*]], ptr noundef nonnull dereferenceable(1) [[DST]], i64 2) -; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES_2]]) +; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_2]]) ; ANY-NEXT: [[ES_3:%.*]] = call ptr @stpncpy(ptr noundef nonnull dereferenceable(1) [[DST]], ptr noundef nonnull dereferenceable(1) [[DST]], i64 3) -; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES_3]]) -; ANY-NEXT: [[ES_N:%.*]] = call ptr @stpncpy(ptr [[DST]], ptr [[DST]], i64 [[N:%.*]]) -; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES_N]]) +; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_3]]) +; ANY-NEXT: [[ES_N:%.*]] = call ptr @stpncpy(ptr nonnull [[DST]], ptr nonnull [[DST]], i64 [[N:%.*]]) +; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_N]]) ; ANY-NEXT: ret void ; ; Do not transform stpncpy(D, D, 2). @@ -428,9 +428,9 @@ define void @fold_stpncpy_s(ptr %dst, ptr %src) { define void @call_stpncpy_s(ptr %dst, ptr %src, i64 %n) { ; ANY-LABEL: @call_stpncpy_s( ; ANY-NEXT: [[ES_2:%.*]] = call ptr @stpncpy(ptr noundef nonnull dereferenceable(1) [[DST:%.*]], ptr noundef nonnull dereferenceable(1) [[SRC:%.*]], i64 2) -; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES_2]]) -; ANY-NEXT: [[ES_N:%.*]] = call ptr @stpncpy(ptr [[DST]], ptr [[SRC]], i64 [[N:%.*]]) -; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES_N]]) +; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_2]]) +; ANY-NEXT: [[ES_N:%.*]] = call ptr @stpncpy(ptr nonnull [[DST]], ptr nonnull [[SRC]], i64 [[N:%.*]]) +; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_N]]) ; ANY-NEXT: ret void ; ; Do not transform stpncpy(D, S, 2). Both *D and *S must be derefernceable diff --git a/llvm/test/Transforms/InstCombine/strlcpy-1.ll b/llvm/test/Transforms/InstCombine/strlcpy-1.ll index fd9d0580426f0a4..ad538259ae96255 100644 --- a/llvm/test/Transforms/InstCombine/strlcpy-1.ll +++ b/llvm/test/Transforms/InstCombine/strlcpy-1.ll @@ -229,18 +229,18 @@ define void @fold_strlcpy_s_0(ptr %dst, ptr %s, i64 %n) { define void @call_strlcpy_s0_n(ptr %dst, ptr %s, i64 %n) { ; ANY-LABEL: @call_strlcpy_s0_n( ; ANY-NEXT: [[NS_2:%.*]] = call i64 @strlcpy(ptr noundef nonnull dereferenceable(1) [[DST:%.*]], ptr noundef nonnull dereferenceable(1) [[S:%.*]], i64 2) -; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS_2]]) -; ANY-NEXT: [[NS_N:%.*]] = call i64 @strlcpy(ptr [[DST]], ptr noundef nonnull dereferenceable(1) [[S]], i64 [[N:%.*]]) -; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS_N]]) +; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS_2]]) +; ANY-NEXT: [[NS_N:%.*]] = call i64 @strlcpy(ptr nonnull [[DST]], ptr noundef nonnull dereferenceable(1) [[S]], i64 [[N:%.*]]) +; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS_N]]) ; ANY-NEXT: [[NZ:%.*]] = or i64 [[N]], 1 ; ANY-NEXT: [[NS_NZ:%.*]] = call i64 @strlcpy(ptr noundef nonnull dereferenceable(1) [[DST]], ptr noundef nonnull dereferenceable(1) [[S]], i64 [[NZ]]) -; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS_NZ]]) -; ANY-NEXT: [[NS0_N:%.*]] = call i64 @strlcpy(ptr [[DST]], ptr noundef nonnull dereferenceable(1) getelementptr inbounds nuw (i8, ptr @s4, i64 4), i64 [[N]]) -; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS0_N]]) -; ANY-NEXT: [[NS1_N:%.*]] = call i64 @strlcpy(ptr [[DST]], ptr noundef nonnull dereferenceable(1) getelementptr inbounds nuw (i8, ptr @s4, i64 3), i64 [[N]]) -; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS1_N]]) -; ANY-NEXT: [[NS4_N:%.*]] = call i64 @strlcpy(ptr [[DST]], ptr noundef nonnull dereferenceable(1) @s4, i64 [[N]]) -; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS4_N]]) +; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS_NZ]]) +; ANY-NEXT: [[NS0_N:%.*]] = call i64 @strlcpy(ptr nonnull [[DST]], ptr noundef nonnull dereferenceable(1) getelementptr inbounds nuw (i8, ptr @s4, i64 4), i64 [[N]]) +; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS0_N]]) +; ANY-NEXT: [[NS1_N:%.*]] = call i64 @strlcpy(ptr nonnull [[DST]], ptr noundef nonnull dereferenceable(1) getelementptr inbounds nuw (i8, ptr @s4, i64 3), i64 [[N]]) +; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS1_N]]) +; ANY-NEXT: [[NS4_N:%.*]] = call i64 @strlcpy(ptr nonnull [[DST]], ptr noundef nonnull dereferenceable(1) @s4, i64 [[N]]) +; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS4_N]]) ; ANY-NEXT: ret void ; %ns_2 = call i64 @strlcpy(ptr %dst, ptr %s, i64 2) diff --git a/llvm/test/Transforms/InstCombine/strncpy-4.ll b/llvm/test/Transforms/InstCombine/strncpy-4.ll index aa70e76e20ed909..0e1f800a9a35724 100644 --- a/llvm/test/Transforms/InstCombine/strncpy-4.ll +++ b/llvm/test/Transforms/InstCombine/strncpy-4.ll @@ -45,11 +45,11 @@ define void @fold_strncpy_overlap(ptr %dst, i64 %n) { define void @call_strncpy_overlap(ptr %dst, i64 %n) { ; CHECK-LABEL: @call_strncpy_overlap( ; CHECK-NEXT: [[ED_2:%.*]] = call ptr @strncpy(ptr noundef nonnull dereferenceable(1) [[DST:%.*]], ptr noundef nonnull dereferenceable(1) [[DST]], i64 2) -; CHECK-NEXT: call void @sink(ptr [[DST]], ptr [[ED_2]]) +; CHECK-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ED_2]]) ; CHECK-NEXT: [[ED_3:%.*]] = call ptr @strncpy(ptr noundef nonnull dereferenceable(1) [[DST]], ptr noundef nonnull dereferenceable(1) [[DST]], i64 3) -; CHECK-NEXT: call void @sink(ptr [[DST]], ptr [[ED_3]]) -; CHECK-NEXT: [[ED_N:%.*]] = call ptr @strncpy(ptr [[DST]], ptr [[DST]], i64 [[N:%.*]]) -; CHECK-NEXT: call void @sink(ptr [[DST]], ptr [[ED_N]]) +; CHECK-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ED_3]]) +; CHECK-NEXT: [[ED_N:%.*]] = call ptr @strncpy(ptr nonnull [[DST]], ptr nonnull [[DST]], i64 [[N:%.*]]) +; CHECK-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ED_N]]) ; CHECK-NEXT: ret void ; @@ -141,11 +141,11 @@ define void @fold_strncpy_s(ptr %dst, ptr %src, i64 %n) { define void @call_strncpy_s(ptr %dst, ptr %src, i64 %n) { ; CHECK-LABEL: @call_strncpy_s( ; CHECK-NEXT: [[ED_2:%.*]] = call ptr @strncpy(ptr noundef nonnull dereferenceable(1) [[DST:%.*]], ptr noundef nonnull dereferenceable(1) [[SRC:%.*]], i64 2) -; CHECK-NEXT: call void @sink(ptr [[DST]], ptr [[ED_2]]) +; CHECK-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ED_2]]) ; CHECK-NEXT: [[ED_9:%.*]] = call ptr @strncpy(ptr noundef nonnull dereferenceable(1) [[DST]], ptr noundef nonnull dereferenceable(1) [[SRC]], i64 9) -; CHECK-NEXT: call void @sink(ptr [[DST]], ptr [[ED_9]]) -; CHECK-NEXT: [[ED_N:%.*]] = call ptr @strncpy(ptr [[DST]], ptr [[SRC]], i64 [[N:%.*]]) -; CHECK-NEXT: call void @sink(ptr [[DST]], ptr [[ED_N]]) +; CHECK-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ED_9]]) +; CHECK-NEXT: [[ED_N:%.*]] = call ptr @strncpy(ptr nonnull [[DST]], ptr nonnull [[SRC]], i64 [[N:%.*]]) +; CHECK-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ED_N]]) ; CHECK-NEXT: ret void ; ; Do not transform strncpy(D, S, 2) when S is unknown. Both *D and *S must diff --git a/llvm/test/Transforms/InstCombine/strstr-1.ll b/llvm/test/Transforms/InstCombine/strstr-1.ll index 68de7614aad2baa..083ee47bb47d1f8 100644 --- a/llvm/test/Transforms/InstCombine/strstr-1.ll +++ b/llvm/test/Transforms/InstCombine/strstr-1.ll @@ -58,7 +58,7 @@ define ptr @test_simplify4(ptr %str) { define i1 @test_simplify5(ptr %str, ptr %pat) { ; CHECK-LABEL: @test_simplify5( ; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr noundef nonnull dereferenceable(1) [[PAT:%.*]]) -; CHECK-NEXT: [[STRNCMP:%.*]] = call i32 @strncmp(ptr [[STR:%.*]], ptr [[PAT]], i64 [[STRLEN]]) +; CHECK-NEXT: [[STRNCMP:%.*]] = call i32 @strncmp(ptr [[STR:%.*]], ptr nonnull [[PAT]], i64 [[STRLEN]]) ; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[STRNCMP]], 0 ; CHECK-NEXT: ret i1 [[CMP1]] ; diff --git a/llvm/test/Transforms/InstSimplify/known-non-zero-opaque-ptrs.ll b/llvm/test/Transforms/InstSimplify/known-non-zero-opaque-ptrs.ll index 92084196d955326..941b31fd1198cd0 100644 --- a/llvm/test/Transforms/InstSimplify/known-non-zero-opaque-ptrs.ll +++ b/llvm/test/Transforms/InstSimplify/known-non-zero-opaque-ptrs.ll @@ -4,12 +4,10 @@ declare void @zero_args() declare void @two_args(ptr, ptr) -; TODO: Could be non-null based on call-site attributes. 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: [[C:%.*]] = icmp ne ptr [[P]], null -; CHECK-NEXT: ret i1 [[C]] +; CHECK-NEXT: ret i1 true ; call void @zero_args(ptr nonnull noundef %p) %c = icmp ne ptr %p, null @@ -27,12 +25,10 @@ define i1 @test_zero_args_maybe_null(ptr %p) { ret i1 %c } -; TODO: Could be non-null based on call-site attributes. 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: [[C:%.*]] = icmp ne ptr [[P]], null -; CHECK-NEXT: ret i1 [[C]] +; CHECK-NEXT: ret i1 true ; call void @two_args(ptr nonnull noundef %p) %c = icmp ne ptr %p, null