; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals ; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT ; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" declare nonnull ptr @ret_nonnull() declare void @llvm.assume(i1) ; Return a pointer trivially nonnull (call return attribute) define ptr @test1() { ; CHECK-LABEL: define {{[^@]+}}@test1() { ; CHECK-NEXT: [[RET:%.*]] = call nonnull ptr @ret_nonnull() ; CHECK-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) { ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CHECK-LABEL: define {{[^@]+}}@test2 ; CHECK-SAME: (ptr nofree nonnull readnone returned "no-capture-maybe-returned" [[P:%.*]]) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: ret ptr [[P]] ; ret ptr %p } define ptr @test2A(i1 %c, ptr %ret) { ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) ; CHECK-LABEL: define {{[^@]+}}@test2A ; CHECK-SAME: (i1 noundef [[C:%.*]], ptr nofree nonnull readnone returned "no-capture-maybe-returned" [[RET:%.*]]) #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] ; CHECK: A: ; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR15:[0-9]+]] [ "nonnull"(ptr [[RET]]) ] ; CHECK-NEXT: ret ptr [[RET]] ; CHECK: B: ; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR15]] [ "nonnull"(ptr [[RET]]) ] ; CHECK-NEXT: ret ptr [[RET]] ; br i1 %c, label %A, label %B A: call void @llvm.assume(i1 true) [ "nonnull"(ptr %ret) ] ret ptr %ret B: call void @llvm.assume(i1 true) [ "nonnull"(ptr %ret) ] ret ptr %ret } define ptr @test2B(i1 %c, ptr %ret) { ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) ; CHECK-LABEL: define {{[^@]+}}@test2B ; CHECK-SAME: (i1 noundef [[C:%.*]], ptr nofree nonnull readnone returned dereferenceable(4) "no-capture-maybe-returned" [[RET:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: br i1 [[C]], label [[A:%.*]], label [[B:%.*]] ; CHECK: A: ; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR15]] [ "dereferenceable"(ptr [[RET]], i32 4) ] ; CHECK-NEXT: ret ptr [[RET]] ; CHECK: B: ; CHECK-NEXT: call void @llvm.assume(i1 noundef true) #[[ATTR15]] [ "dereferenceable"(ptr [[RET]], i32 4) ] ; CHECK-NEXT: ret ptr [[RET]] ; br i1 %c, label %A, label %B A: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %ret, i32 4) ] ret ptr %ret B: call void @llvm.assume(i1 true) [ "dereferenceable"(ptr %ret, i32 4) ] ret ptr %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 ptr @scc_binder(i1 %c) { ; CHECK-LABEL: define {{[^@]+}}@scc_binder ; CHECK-SAME: (i1 noundef [[C:%.*]]) { ; CHECK-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] ; CHECK: rec: ; CHECK-NEXT: [[TMP1:%.*]] = call ptr @test3(i1 noundef [[C]]) ; CHECK-NEXT: br label [[END]] ; CHECK: end: ; CHECK-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) { ; CHECK-LABEL: define {{[^@]+}}@test3 ; CHECK-SAME: (i1 [[C:%.*]]) { ; CHECK-NEXT: [[TMP1:%.*]] = call ptr @scc_binder(i1 noundef [[C]]) ; CHECK-NEXT: [[RET:%.*]] = call nonnull ptr @ret_nonnull() ; CHECK-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() { ; TUNIT: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@test4_helper ; TUNIT-SAME: () #[[ATTR3:[0-9]+]] { ; TUNIT-NEXT: ret ptr undef ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@test4_helper ; CGSCC-SAME: () #[[ATTR1]] { ; CGSCC-NEXT: ret ptr undef ; %ret = call ptr @test4() ret ptr %ret } define ptr @test4() { ; TUNIT: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@test4 ; TUNIT-SAME: () #[[ATTR3]] { ; TUNIT-NEXT: ret ptr undef ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@test4 ; CGSCC-SAME: () #[[ATTR1]] { ; CGSCC-NEXT: ret ptr undef ; %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) { ; TUNIT: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@test5_helper ; TUNIT-SAME: (i1 noundef [[C:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] ; TUNIT: rec: ; TUNIT-NEXT: br label [[END]] ; TUNIT: end: ; TUNIT-NEXT: ret ptr null ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@test5_helper ; CGSCC-SAME: (i1 noundef [[C:%.*]]) #[[ATTR1]] { ; CGSCC-NEXT: br i1 [[C]], label [[REC:%.*]], label [[END:%.*]] ; CGSCC: rec: ; CGSCC-NEXT: br label [[END]] ; CGSCC: end: ; CGSCC-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) { ; TUNIT: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define {{[^@]+}}@test5 ; TUNIT-SAME: (i1 [[C:%.*]]) #[[ATTR3]] { ; TUNIT-NEXT: ret ptr null ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define {{[^@]+}}@test5 ; CGSCC-SAME: (i1 [[C:%.*]]) #[[ATTR1]] { ; CGSCC-NEXT: ret ptr null ; %ret = call ptr @test5_helper(i1 %c) ret ptr %ret } ; Local analysis, but going through a self recursive phi define ptr @test6a() { ; ; TUNIT: Function Attrs: noreturn ; TUNIT-LABEL: define {{[^@]+}}@test6a ; TUNIT-SAME: () #[[ATTR4:[0-9]+]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[RET:%.*]] = call ptr @ret_nonnull() ; TUNIT-NEXT: br label [[LOOP:%.*]] ; TUNIT: loop: ; TUNIT-NEXT: unreachable ; TUNIT: exit: ; TUNIT-NEXT: unreachable ; ; CGSCC: Function Attrs: noreturn ; CGSCC-LABEL: define {{[^@]+}}@test6a ; CGSCC-SAME: () #[[ATTR3:[0-9]+]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[RET:%.*]] = call ptr @ret_nonnull() ; CGSCC-NEXT: br label [[LOOP:%.*]] ; CGSCC: loop: ; CGSCC-NEXT: unreachable ; CGSCC: exit: ; CGSCC-NEXT: unreachable ; 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) { ; CHECK-LABEL: define {{[^@]+}}@test6b ; CHECK-SAME: (i1 [[C:%.*]]) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[RET:%.*]] = call nonnull ptr @ret_nonnull() ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[RET]], [[ENTRY:%.*]] ], [ [[RET]], [[LOOP]] ] ; CHECK-NEXT: br i1 [[C]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: ret ptr [[RET]] ; 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) { ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CHECK-LABEL: define {{[^@]+}}@test7 ; CHECK-SAME: (ptr nofree readnone returned "no-capture-maybe-returned" [[A:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: ret ptr [[A]] ; ret ptr %a } define ptr @test8(ptr %a) { ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CHECK-LABEL: define {{[^@]+}}@test8 ; CHECK-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[A:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 1 ; CHECK-NEXT: ret ptr [[B]] ; %b = getelementptr inbounds i8, ptr %a, i64 1 ret ptr %b } define ptr @test9(ptr %a, i64 %n) { ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CHECK-LABEL: define {{[^@]+}}@test9 ; CHECK-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[A:%.*]], i64 [[N:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]] ; CHECK-NEXT: ret ptr [[B]] ; %b = getelementptr inbounds i8, ptr %a, i64 %n ret ptr %b } ; ATTRIBUTOR_OPM: define ptr @test10 ; ATTRIBUTOR_NPM: define nonnull ptr @test10 define ptr @test10(ptr %a, i64 %n) { ; CHECK: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(inaccessiblemem: write) ; CHECK-LABEL: define {{[^@]+}}@test10 ; CHECK-SAME: (ptr nofree readnone "no-capture-maybe-returned" [[A:%.*]], i64 [[N:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: [[CMP:%.*]] = icmp ne i64 [[N]], 0 ; CHECK-NEXT: call void @llvm.assume(i1 noundef [[CMP]]) #[[ATTR15]] ; CHECK-NEXT: [[B:%.*]] = getelementptr inbounds i8, ptr [[A]], i64 [[N]] ; CHECK-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(); ; } ; FIXME: missing nonnull define ptr @test11(ptr) local_unnamed_addr { ; CHECK-LABEL: define {{[^@]+}}@test11 ; CHECK-SAME: (ptr [[TMP0:%.*]]) local_unnamed_addr { ; CHECK-NEXT: [[TMP2:%.*]] = icmp eq ptr [[TMP0]], null ; CHECK-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]] ; CHECK: 3: ; CHECK-NEXT: [[TMP4:%.*]] = tail call ptr @ret_nonnull() ; CHECK-NEXT: br label [[TMP5]] ; CHECK: 5: ; CHECK-NEXT: [[TMP6:%.*]] = phi ptr [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ] ; CHECK-NEXT: ret ptr [[TMP6]] ; %2 = icmp eq ptr %0, null br i1 %2, label %3, label %5 ;