; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes ; RUN: opt -attributor -enable-new-pm=0 -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=15 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_NPM,NOT_CGSCC_OPM,NOT_TUNIT_NPM,IS__TUNIT____,IS________OPM,IS__TUNIT_OPM ; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=17 -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CGSCC_OPM,NOT_CGSCC_NPM,NOT_TUNIT_OPM,IS__TUNIT____,IS________NPM,IS__TUNIT_NPM ; RUN: opt -attributor-cgscc -enable-new-pm=0 -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_NPM,IS__CGSCC____,IS________OPM,IS__CGSCC_OPM ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_TUNIT_NPM,NOT_TUNIT_OPM,NOT_CGSCC_OPM,IS__CGSCC____,IS________NPM,IS__CGSCC_NPM target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" declare nonnull i8* @ret_nonnull() declare void @llvm.assume(i1) ; Return a pointer trivially nonnull (call return attribute) define i8* @test1() { ; CHECK-LABEL: define {{[^@]+}}@test1() { ; CHECK-NEXT: [[RET:%.*]] = call nonnull i8* @ret_nonnull() ; CHECK-NEXT: ret i8* [[RET]] ; %ret = call i8* @ret_nonnull() ret i8* %ret } ; Return a pointer trivially nonnull (argument attribute) define i8* @test2(i8* nonnull %p) { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn ; IS__TUNIT____-LABEL: define {{[^@]+}}@test2 ; IS__TUNIT____-SAME: (i8* nofree nonnull readnone returned "no-capture-maybe-returned" [[P:%.*]]) [[ATTR1:#.*]] { ; IS__TUNIT____-NEXT: ret i8* [[P]] ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@test2 ; IS__CGSCC____-SAME: (i8* nofree nonnull readnone returned "no-capture-maybe-returned" [[P:%.*]]) [[ATTR1:#.*]] { ; IS__CGSCC____-NEXT: ret i8* [[P]] ; ret i8* %p } define i8* @test2A(i1 %c, i8* %ret) { ; ATTRIBUTOR: define nonnull i8* @test2A(i1 %c, i8* nofree nonnull readnone returned %ret) ; NOT_CGSCC_OPM: Function Attrs: nofree nosync nounwind willreturn ; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@test2A ; NOT_CGSCC_OPM-SAME: (i1 [[C:%.*]], i8* nofree nonnull readnone returned "no-capture-maybe-returned" [[RET:%.*]]) [[ATTR0:#.*]] { ; NOT_CGSCC_OPM-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] ; NOT_CGSCC_OPM: A: ; NOT_CGSCC_OPM-NEXT: call void @llvm.assume(i1 noundef true) [[ATTR12:#.*]] [ "nonnull"(i8* [[RET]]) ] ; NOT_CGSCC_OPM-NEXT: ret i8* [[RET]] ; NOT_CGSCC_OPM: B: ; NOT_CGSCC_OPM-NEXT: call void @llvm.assume(i1 noundef true) [[ATTR12]] [ "nonnull"(i8* [[RET]]) ] ; NOT_CGSCC_OPM-NEXT: ret i8* [[RET]] ; ; IS__CGSCC_OPM: Function Attrs: nofree nosync nounwind willreturn ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@test2A ; IS__CGSCC_OPM-SAME: (i1 [[C:%.*]], i8* nofree nonnull readnone returned "no-capture-maybe-returned" [[RET:%.*]]) [[ATTR0:#.*]] { ; IS__CGSCC_OPM-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] ; IS__CGSCC_OPM: A: ; IS__CGSCC_OPM-NEXT: call void @llvm.assume(i1 noundef true) [[ATTR13:#.*]] [ "nonnull"(i8* [[RET]]) ] ; IS__CGSCC_OPM-NEXT: ret i8* [[RET]] ; IS__CGSCC_OPM: B: ; IS__CGSCC_OPM-NEXT: call void @llvm.assume(i1 noundef true) [[ATTR13]] [ "nonnull"(i8* [[RET]]) ] ; IS__CGSCC_OPM-NEXT: ret i8* [[RET]] ; br i1 %c, label %A, label %B A: call void @llvm.assume(i1 true) [ "nonnull"(i8* %ret) ] ret i8* %ret B: call void @llvm.assume(i1 true) [ "nonnull"(i8* %ret) ] ret i8* %ret } define i8* @test2B(i1 %c, i8* %ret) { ; ATTRIBUTOR: define nonnull dereferenceable(4) i8* @test2B(i1 %c, i8* nofree nonnull readnone returned dereferenceable(4) %ret) ; NOT_CGSCC_OPM: Function Attrs: nofree nosync nounwind willreturn ; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@test2B ; NOT_CGSCC_OPM-SAME: (i1 [[C:%.*]], i8* nofree nonnull readnone returned dereferenceable(4) "no-capture-maybe-returned" [[RET:%.*]]) [[ATTR0]] { ; NOT_CGSCC_OPM-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] ; NOT_CGSCC_OPM: A: ; NOT_CGSCC_OPM-NEXT: call void @llvm.assume(i1 noundef true) [[ATTR12]] [ "dereferenceable"(i8* [[RET]], i32 4) ] ; NOT_CGSCC_OPM-NEXT: ret i8* [[RET]] ; NOT_CGSCC_OPM: B: ; NOT_CGSCC_OPM-NEXT: call void @llvm.assume(i1 noundef true) [[ATTR12]] [ "dereferenceable"(i8* [[RET]], i32 4) ] ; NOT_CGSCC_OPM-NEXT: ret i8* [[RET]] ; ; IS__CGSCC_OPM: Function Attrs: nofree nosync nounwind willreturn ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@test2B ; IS__CGSCC_OPM-SAME: (i1 [[C:%.*]], i8* nofree nonnull readnone returned dereferenceable(4) "no-capture-maybe-returned" [[RET:%.*]]) [[ATTR0]] { ; IS__CGSCC_OPM-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] ; IS__CGSCC_OPM: A: ; IS__CGSCC_OPM-NEXT: call void @llvm.assume(i1 noundef true) [[ATTR13]] [ "dereferenceable"(i8* [[RET]], i32 4) ] ; IS__CGSCC_OPM-NEXT: ret i8* [[RET]] ; IS__CGSCC_OPM: B: ; IS__CGSCC_OPM-NEXT: call void @llvm.assume(i1 noundef true) [[ATTR13]] [ "dereferenceable"(i8* [[RET]], i32 4) ] ; IS__CGSCC_OPM-NEXT: ret i8* [[RET]] ; br i1 %c, label %A, label %B A: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* %ret, i32 4) ] ret i8* %ret B: call void @llvm.assume(i1 true) [ "dereferenceable"(i8* %ret, i32 4) ] ret i8* %ret } ; 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 i8* @scc_binder(i1 %c) { ; IS________OPM-LABEL: define {{[^@]+}}@scc_binder ; IS________OPM-SAME: (i1 [[C:%.*]]) { ; IS________OPM-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] ; IS________OPM: rec: ; IS________OPM-NEXT: [[TMP1:%.*]] = call i8* @test3(i1 [[C]]) ; IS________OPM-NEXT: br label [[END]] ; IS________OPM: end: ; IS________OPM-NEXT: ret i8* null ; ; IS________NPM-LABEL: define {{[^@]+}}@scc_binder ; IS________NPM-SAME: (i1 [[C:%.*]]) { ; IS________NPM-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] ; IS________NPM: rec: ; IS________NPM-NEXT: [[TMP1:%.*]] = call i8* @test3(i1 noundef [[C]]) ; IS________NPM-NEXT: br label [[END]] ; IS________NPM: end: ; IS________NPM-NEXT: ret i8* null ; br i1 %c, label %rec, label %end rec: call i8* @test3(i1 %c) br label %end end: ret i8* null } define i8* @test3(i1 %c) { ; CHECK-LABEL: define {{[^@]+}}@test3 ; CHECK-SAME: (i1 [[C:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call i8* @scc_binder(i1 [[C]]) ; CHECK-NEXT: [[RET:%.*]] = call nonnull i8* @ret_nonnull() ; CHECK-NEXT: ret i8* [[RET]] ; call i8* @scc_binder(i1 %c) %ret = call i8* @ret_nonnull() ret i8* %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 i8* @test4_helper() { ; NOT_CGSCC_NPM: Function Attrs: nofree noreturn nosync nounwind readnone ; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@test4_helper ; NOT_CGSCC_NPM-SAME: () [[ATTR2:#.*]] { ; NOT_CGSCC_NPM-NEXT: unreachable ; ; IS__CGSCC_NPM: Function Attrs: nofree norecurse noreturn nosync nounwind readnone willreturn ; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@test4_helper ; IS__CGSCC_NPM-SAME: () [[ATTR2:#.*]] { ; IS__CGSCC_NPM-NEXT: unreachable ; %ret = call i8* @test4() ret i8* %ret } define i8* @test4() { ; NOT_CGSCC_NPM: Function Attrs: nofree noreturn nosync nounwind readnone ; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@test4 ; NOT_CGSCC_NPM-SAME: () [[ATTR2]] { ; NOT_CGSCC_NPM-NEXT: unreachable ; ; IS__CGSCC_NPM: Function Attrs: nofree norecurse noreturn nosync nounwind readnone willreturn ; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@test4 ; IS__CGSCC_NPM-SAME: () [[ATTR2]] { ; IS__CGSCC_NPM-NEXT: unreachable ; %ret = call i8* @test4_helper() ret i8* %ret } ; Given a mutual recursive set of functions which *can* return null ; make sure we haven't marked them as nonnull. define i8* @test5_helper(i1 %c) { ; NOT_CGSCC_NPM: Function Attrs: nofree nosync nounwind readnone willreturn ; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@test5_helper ; NOT_CGSCC_NPM-SAME: (i1 [[C:%.*]]) [[ATTR3:#.*]] { ; NOT_CGSCC_NPM-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] ; NOT_CGSCC_NPM: rec: ; NOT_CGSCC_NPM-NEXT: br label [[END]] ; NOT_CGSCC_NPM: end: ; NOT_CGSCC_NPM-NEXT: ret i8* null ; ; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@test5_helper ; IS__CGSCC_NPM-SAME: (i1 [[C:%.*]]) [[ATTR1:#.*]] { ; IS__CGSCC_NPM-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] ; IS__CGSCC_NPM: rec: ; IS__CGSCC_NPM-NEXT: br label [[END]] ; IS__CGSCC_NPM: end: ; IS__CGSCC_NPM-NEXT: ret i8* null ; br i1 %c, label %rec, label %end rec: %ret = call i8* @test5(i1 %c) br label %end end: ret i8* null } define i8* @test5(i1 %c) { ; NOT_CGSCC_NPM: Function Attrs: nofree nosync nounwind readnone willreturn ; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@test5 ; NOT_CGSCC_NPM-SAME: (i1 [[C:%.*]]) [[ATTR3]] { ; NOT_CGSCC_NPM-NEXT: ret i8* null ; ; IS__CGSCC_NPM: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@test5 ; IS__CGSCC_NPM-SAME: (i1 [[C:%.*]]) [[ATTR1]] { ; IS__CGSCC_NPM-NEXT: ret i8* null ; %ret = call i8* @test5_helper(i1 %c) ret i8* %ret } ; Local analysis, but going through a self recursive phi define i8* @test6a() { ; CHECK: Function Attrs: noreturn ; CHECK-LABEL: define {{[^@]+}}@test6a ; CHECK-SAME: () [[ATTR3:#.*]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[RET:%.*]] = call i8* @ret_nonnull() ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: unreachable ; CHECK: exit: ; CHECK-NEXT: unreachable ; entry: %ret = call i8* @ret_nonnull() br label %loop loop: %phi = phi i8* [%ret, %entry], [%phi, %loop] br i1 undef, label %loop, label %exit exit: ret i8* %phi } define i8* @test6b(i1 %c) { ; CHECK-LABEL: define {{[^@]+}}@test6b ; CHECK-SAME: (i1 [[C:%.*]]) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[RET:%.*]] = call nonnull i8* @ret_nonnull() ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[PHI:%.*]] = phi i8* [ [[RET]], [[ENTRY:%.*]] ], [ [[PHI]], [[LOOP]] ] ; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: ret i8* [[PHI]] ; entry: %ret = call i8* @ret_nonnull() br label %loop loop: %phi = phi i8* [%ret, %entry], [%phi, %loop] br i1 %c, label %loop, label %exit exit: ret i8* %phi } define i8* @test7(i8* %a) { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn ; IS__TUNIT____-LABEL: define {{[^@]+}}@test7 ; IS__TUNIT____-SAME: (i8* nofree readnone returned "no-capture-maybe-returned" [[A:%.*]]) [[ATTR1]] { ; IS__TUNIT____-NEXT: [[B:%.*]] = getelementptr inbounds i8, i8* [[A]], i64 0 ; IS__TUNIT____-NEXT: ret i8* [[B]] ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@test7 ; IS__CGSCC____-SAME: (i8* nofree readnone returned "no-capture-maybe-returned" [[A:%.*]]) [[ATTR1]] { ; IS__CGSCC____-NEXT: [[B:%.*]] = getelementptr inbounds i8, i8* [[A]], i64 0 ; IS__CGSCC____-NEXT: ret i8* [[B]] ; %b = getelementptr inbounds i8, i8* %a, i64 0 ret i8* %b } define i8* @test8(i8* %a) { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn ; IS__TUNIT____-LABEL: define {{[^@]+}}@test8 ; IS__TUNIT____-SAME: (i8* nofree readnone "no-capture-maybe-returned" [[A:%.*]]) [[ATTR1]] { ; IS__TUNIT____-NEXT: [[B:%.*]] = getelementptr inbounds i8, i8* [[A]], i64 1 ; IS__TUNIT____-NEXT: ret i8* [[B]] ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@test8 ; IS__CGSCC____-SAME: (i8* nofree readnone "no-capture-maybe-returned" [[A:%.*]]) [[ATTR1]] { ; IS__CGSCC____-NEXT: [[B:%.*]] = getelementptr inbounds i8, i8* [[A]], i64 1 ; IS__CGSCC____-NEXT: ret i8* [[B]] ; %b = getelementptr inbounds i8, i8* %a, i64 1 ret i8* %b } define i8* @test9(i8* %a, i64 %n) { ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readnone willreturn ; IS__TUNIT____-LABEL: define {{[^@]+}}@test9 ; IS__TUNIT____-SAME: (i8* nofree readnone "no-capture-maybe-returned" [[A:%.*]], i64 [[N:%.*]]) [[ATTR1]] { ; IS__TUNIT____-NEXT: [[B:%.*]] = getelementptr inbounds i8, i8* [[A]], i64 [[N]] ; IS__TUNIT____-NEXT: ret i8* [[B]] ; ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readnone willreturn ; IS__CGSCC____-LABEL: define {{[^@]+}}@test9 ; IS__CGSCC____-SAME: (i8* nofree readnone "no-capture-maybe-returned" [[A:%.*]], i64 [[N:%.*]]) [[ATTR1]] { ; IS__CGSCC____-NEXT: [[B:%.*]] = getelementptr inbounds i8, i8* [[A]], i64 [[N]] ; IS__CGSCC____-NEXT: ret i8* [[B]] ; %b = getelementptr inbounds i8, i8* %a, i64 %n ret i8* %b } ; ATTRIBUTOR_OPM: define i8* @test10 ; ATTRIBUTOR_NPM: define nonnull i8* @test10 define i8* @test10(i8* %a, i64 %n) { ; NOT_CGSCC_OPM: Function Attrs: nofree nosync nounwind willreturn ; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@test10 ; NOT_CGSCC_OPM-SAME: (i8* nofree readnone "no-capture-maybe-returned" [[A:%.*]], i64 [[N:%.*]]) [[ATTR0]] { ; NOT_CGSCC_OPM-NEXT: call void @llvm.assume(i1 noundef true) [[ATTR12]] ; NOT_CGSCC_OPM-NEXT: [[B:%.*]] = getelementptr inbounds i8, i8* [[A]], i64 [[N]] ; NOT_CGSCC_OPM-NEXT: ret i8* [[B]] ; ; IS__CGSCC_OPM: Function Attrs: nofree nosync nounwind willreturn ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@test10 ; IS__CGSCC_OPM-SAME: (i8* nofree readnone "no-capture-maybe-returned" [[A:%.*]], i64 [[N:%.*]]) [[ATTR0]] { ; IS__CGSCC_OPM-NEXT: call void @llvm.assume(i1 noundef true) [[ATTR13]] ; IS__CGSCC_OPM-NEXT: [[B:%.*]] = getelementptr inbounds i8, i8* [[A]], i64 [[N]] ; IS__CGSCC_OPM-NEXT: ret i8* [[B]] ; %cmp = icmp ne i64 %n, 0 call void @llvm.assume(i1 %cmp) %b = getelementptr inbounds i8, i8* %a, i64 %n ret i8* %b } ; TEST 11 ; char* test11(char *p) { ; return p? p: nonnull(); ; } ; FIXME: missing nonnull define i8* @test11(i8*) local_unnamed_addr { ; CHECK-LABEL: define {{[^@]+}}@test11 ; CHECK-SAME: (i8* [[TMP0:%.*]]) local_unnamed_addr { ; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null ; CHECK-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]] ; CHECK: 3: ; CHECK-NEXT: [[TMP4:%.*]] = tail call i8* @ret_nonnull() ; CHECK-NEXT: br label [[TMP5]] ; CHECK: 5: ; CHECK-NEXT: [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ] ; CHECK-NEXT: ret i8* [[TMP6]] ; %2 = icmp eq i8* %0, null br i1 %2, label %3, label %5 ;