; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 3 ; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s --check-prefixes=COMMON,FNATTRS ; RUN: opt -S -passes=attributor-light %s | FileCheck %s --check-prefixes=COMMON,ATTRIBUTOR target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" declare nonnull ptr @ret_nonnull() ; Return a pointer trivially nonnull (call return attribute) define ptr @test1() { ; COMMON-LABEL: define nonnull ptr @test1() { ; COMMON-NEXT: [[RET:%.*]] = call ptr @ret_nonnull() ; COMMON-NEXT: ret ptr [[RET]] ; %ret = call ptr @ret_nonnull() ret ptr %ret } ; Return a pointer trivially nonnull (argument attribute) define ptr @test2(ptr nonnull %p) { ; FNATTRS-LABEL: define nonnull ptr @test2( ; FNATTRS-SAME: ptr nonnull readnone returned [[P:%.*]]) #[[ATTR0:[0-9]+]] { ; FNATTRS-NEXT: ret ptr [[P]] ; ; ATTRIBUTOR-LABEL: define nonnull ptr @test2( ; ATTRIBUTOR-SAME: ptr nofree nonnull readnone [[P:%.*]]) #[[ATTR0:[0-9]+]] { ; ATTRIBUTOR-NEXT: ret ptr [[P]] ; ret ptr %p } ; Given an SCC where one of the functions can not be marked nonnull, ; can we still mark the other one which is trivially nonnull define ptr @scc_binder(i1 %c) { ; FNATTRS-LABEL: define noundef ptr @scc_binder( ; FNATTRS-SAME: i1 [[C:%.*]]) { ; FNATTRS-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] ; FNATTRS: rec: ; FNATTRS-NEXT: [[TMP1:%.*]] = call ptr @test3(i1 [[C]]) ; FNATTRS-NEXT: br label [[END]] ; FNATTRS: end: ; FNATTRS-NEXT: ret ptr null ; ; ATTRIBUTOR-LABEL: define ptr @scc_binder( ; ATTRIBUTOR-SAME: i1 [[C:%.*]]) { ; ATTRIBUTOR-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] ; ATTRIBUTOR: rec: ; ATTRIBUTOR-NEXT: [[TMP1:%.*]] = call ptr @test3(i1 [[C]]) ; ATTRIBUTOR-NEXT: br label [[END]] ; ATTRIBUTOR: end: ; ATTRIBUTOR-NEXT: ret ptr null ; br i1 %c, label %rec, label %end rec: call ptr @test3(i1 %c) br label %end end: ret ptr null } define ptr @test3(i1 %c) { ; COMMON-LABEL: define nonnull ptr @test3( ; COMMON-SAME: i1 [[C:%.*]]) { ; COMMON-NEXT: [[TMP1:%.*]] = call ptr @scc_binder(i1 [[C]]) ; COMMON-NEXT: [[RET:%.*]] = call ptr @ret_nonnull() ; COMMON-NEXT: ret ptr [[RET]] ; call ptr @scc_binder(i1 %c) %ret = call ptr @ret_nonnull() ret ptr %ret } ; Given a mutual recursive set of functions, we can mark them ; nonnull if neither can ever return null. (In this case, they ; just never return period.) define ptr @test4_helper() { ; FNATTRS-LABEL: define noalias nonnull ptr @test4_helper( ; FNATTRS-SAME: ) #[[ATTR1:[0-9]+]] { ; FNATTRS-NEXT: [[RET:%.*]] = call ptr @test4() ; FNATTRS-NEXT: ret ptr [[RET]] ; ; ATTRIBUTOR-LABEL: define ptr @test4_helper( ; ATTRIBUTOR-SAME: ) #[[ATTR1:[0-9]+]] { ; ATTRIBUTOR-NEXT: [[RET:%.*]] = call ptr @test4() #[[ATTR1]] ; ATTRIBUTOR-NEXT: ret ptr [[RET]] ; %ret = call ptr @test4() ret ptr %ret } define ptr @test4() { ; FNATTRS-LABEL: define noalias nonnull ptr @test4( ; FNATTRS-SAME: ) #[[ATTR1]] { ; FNATTRS-NEXT: [[RET:%.*]] = call ptr @test4_helper() ; FNATTRS-NEXT: ret ptr [[RET]] ; ; ATTRIBUTOR-LABEL: define ptr @test4( ; ATTRIBUTOR-SAME: ) #[[ATTR1]] { ; ATTRIBUTOR-NEXT: [[RET:%.*]] = call ptr @test4_helper() #[[ATTR1]] ; ATTRIBUTOR-NEXT: ret ptr [[RET]] ; %ret = call ptr @test4_helper() ret ptr %ret } ; Given a mutual recursive set of functions which *can* return null ; make sure we haven't marked them as nonnull. define ptr @test5_helper(i1 %c) { ; FNATTRS-LABEL: define noalias noundef ptr @test5_helper( ; FNATTRS-SAME: i1 [[C:%.*]]) #[[ATTR1]] { ; FNATTRS-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] ; FNATTRS: rec: ; FNATTRS-NEXT: [[RET:%.*]] = call ptr @test5(i1 [[C]]) ; FNATTRS-NEXT: br label [[END]] ; FNATTRS: end: ; FNATTRS-NEXT: ret ptr null ; ; ATTRIBUTOR-LABEL: define ptr @test5_helper( ; ATTRIBUTOR-SAME: i1 [[C:%.*]]) #[[ATTR1]] { ; ATTRIBUTOR-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] ; ATTRIBUTOR: rec: ; ATTRIBUTOR-NEXT: [[RET:%.*]] = call ptr @test5(i1 [[C]]) #[[ATTR1]] ; ATTRIBUTOR-NEXT: br label [[END]] ; ATTRIBUTOR: end: ; ATTRIBUTOR-NEXT: ret ptr null ; br i1 %c, label %rec, label %end rec: %ret = call ptr @test5(i1 %c) br label %end end: ret ptr null } define ptr @test5(i1 %c) { ; FNATTRS-LABEL: define noalias noundef ptr @test5( ; FNATTRS-SAME: i1 [[C:%.*]]) #[[ATTR1]] { ; FNATTRS-NEXT: [[RET:%.*]] = call ptr @test5_helper(i1 [[C]]) ; FNATTRS-NEXT: ret ptr [[RET]] ; ; ATTRIBUTOR-LABEL: define ptr @test5( ; ATTRIBUTOR-SAME: i1 [[C:%.*]]) #[[ATTR1]] { ; ATTRIBUTOR-NEXT: [[RET:%.*]] = call ptr @test5_helper(i1 [[C]]) #[[ATTR1]] ; ATTRIBUTOR-NEXT: ret ptr [[RET]] ; %ret = call ptr @test5_helper(i1 %c) ret ptr %ret } ; Local analysis, but going through a self recursive phi define ptr @test6a() { ; COMMON-LABEL: define nonnull ptr @test6a() { ; COMMON-NEXT: entry: ; COMMON-NEXT: [[RET:%.*]] = call ptr @ret_nonnull() ; COMMON-NEXT: br label [[LOOP:%.*]] ; COMMON: loop: ; COMMON-NEXT: [[PHI:%.*]] = phi ptr [ [[RET]], [[ENTRY:%.*]] ], [ [[PHI]], [[LOOP]] ] ; COMMON-NEXT: br i1 undef, label [[LOOP]], label [[EXIT:%.*]] ; COMMON: exit: ; COMMON-NEXT: ret ptr [[PHI]] ; entry: %ret = call ptr @ret_nonnull() br label %loop loop: %phi = phi ptr [%ret, %entry], [%phi, %loop] br i1 undef, label %loop, label %exit exit: ret ptr %phi } define ptr @test6b(i1 %c) { ; COMMON-LABEL: define nonnull ptr @test6b( ; COMMON-SAME: i1 [[C:%.*]]) { ; COMMON-NEXT: entry: ; COMMON-NEXT: [[RET:%.*]] = call ptr @ret_nonnull() ; COMMON-NEXT: br label [[LOOP:%.*]] ; COMMON: loop: ; COMMON-NEXT: [[PHI:%.*]] = phi ptr [ [[RET]], [[ENTRY:%.*]] ], [ [[PHI]], [[LOOP]] ] ; COMMON-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]] ; COMMON: exit: ; COMMON-NEXT: ret ptr [[PHI]] ; entry: %ret = call ptr @ret_nonnull() br label %loop loop: %phi = phi ptr [%ret, %entry], [%phi, %loop] br i1 %c, label %loop, label %exit exit: ret ptr %phi } define ptr @test7(ptr %a) { ; FNATTRS-LABEL: define ptr @test7( ; FNATTRS-SAME: ptr readnone returned [[A:%.*]]) #[[ATTR0]] { ; FNATTRS-NEXT: ret ptr [[A]] ; ; ATTRIBUTOR-LABEL: define ptr @test7( ; ATTRIBUTOR-SAME: ptr nofree readnone [[A:%.*]]) #[[ATTR0]] { ; ATTRIBUTOR-NEXT: ret ptr [[A]] ; ret ptr %a } define ptr @test8(ptr %a) { ; FNATTRS-LABEL: define nonnull ptr @test8( ; FNATTRS-SAME: ptr readnone [[A:%.*]]) #[[ATTR0]] { ; FNATTRS-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 1 ; FNATTRS-NEXT: ret ptr [[B]] ; ; ATTRIBUTOR-LABEL: define nonnull ptr @test8( ; ATTRIBUTOR-SAME: ptr nofree readnone [[A:%.*]]) #[[ATTR0]] { ; ATTRIBUTOR-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 1 ; ATTRIBUTOR-NEXT: ret ptr [[B]] ; %b = getelementptr inbounds i8, ptr %a, i64 1 ret ptr %b } define ptr @test9(ptr %a, i64 %n) { ; FNATTRS-LABEL: define ptr @test9( ; FNATTRS-SAME: ptr readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR0]] { ; FNATTRS-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]] ; FNATTRS-NEXT: ret ptr [[B]] ; ; ATTRIBUTOR-LABEL: define ptr @test9( ; ATTRIBUTOR-SAME: ptr nofree readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR0]] { ; ATTRIBUTOR-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]] ; ATTRIBUTOR-NEXT: ret ptr [[B]] ; %b = getelementptr inbounds i8, ptr %a, i64 %n ret ptr %b } declare void @llvm.assume(i1) ; FIXME: missing nonnull define ptr @test10(ptr %a, i64 %n) { ; FNATTRS-LABEL: define ptr @test10( ; FNATTRS-SAME: ptr readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR3:[0-9]+]] { ; FNATTRS-NEXT: [[CMP:%.*]] = icmp ne i64 [[N]], 0 ; FNATTRS-NEXT: call void @llvm.assume(i1 [[CMP]]) ; FNATTRS-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]] ; FNATTRS-NEXT: ret ptr [[B]] ; ; ATTRIBUTOR-LABEL: define ptr @test10( ; ATTRIBUTOR-SAME: ptr nofree readnone [[A:%.*]], i64 [[N:%.*]]) #[[ATTR3:[0-9]+]] { ; ATTRIBUTOR-NEXT: [[CMP:%.*]] = icmp ne i64 [[N]], 0 ; ATTRIBUTOR-NEXT: call void @llvm.assume(i1 [[CMP]]) #[[ATTR14:[0-9]+]] ; ATTRIBUTOR-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]] ; ATTRIBUTOR-NEXT: ret ptr [[B]] ; %cmp = icmp ne i64 %n, 0 call void @llvm.assume(i1 %cmp) %b = getelementptr inbounds i8, ptr %a, i64 %n ret ptr %b } ; TEST 11 ; char* test11(char *p) { ; return p? p: nonnull(); ; } define ptr @test11(ptr) local_unnamed_addr { ; FNATTRS-LABEL: define nonnull ptr @test11( ; FNATTRS-SAME: ptr readnone [[TMP0:%.*]]) local_unnamed_addr { ; FNATTRS-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null ; FNATTRS-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]] ; FNATTRS: 3: ; FNATTRS-NEXT: [[TMP4:%.*]] = tail call ptr @ret_nonnull() ; FNATTRS-NEXT: br label [[TMP5]] ; FNATTRS: 5: ; FNATTRS-NEXT: [[TMP6:%.*]] = phi ptr [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ] ; FNATTRS-NEXT: ret ptr [[TMP6]] ; ; ATTRIBUTOR-LABEL: define nonnull ptr @test11( ; ATTRIBUTOR-SAME: ptr [[TMP0:%.*]]) local_unnamed_addr { ; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null ; ATTRIBUTOR-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]] ; ATTRIBUTOR: 3: ; ATTRIBUTOR-NEXT: [[TMP4:%.*]] = tail call ptr @ret_nonnull() ; ATTRIBUTOR-NEXT: br label [[TMP5]] ; ATTRIBUTOR: 5: ; ATTRIBUTOR-NEXT: [[TMP6:%.*]] = phi ptr [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ] ; ATTRIBUTOR-NEXT: ret ptr [[TMP6]] ; %2 = icmp eq ptr %0, null br i1 %2, label %3, label %5 ;