; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals --version 2 ; 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 ; ; Test cases specifically designed for the "no-capture" argument attribute. ; We use FIXME's to indicate problems and missing attributes. ; target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" declare ptr @unknown() ; TEST comparison against NULL ; ; int is_null_return(int *p) { ; return p == 0; ; } ; ; no-capture is missing on %p because it is not dereferenceable define i32 @is_null_return(ptr %p) #0 { ; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable ; CHECK-LABEL: define i32 @is_null_return ; CHECK-SAME: (ptr nofree readnone [[P:%.*]]) #[[ATTR0:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[P]], null ; CHECK-NEXT: [[CONV:%.*]] = zext i1 [[CMP]] to i32 ; CHECK-NEXT: ret i32 [[CONV]] ; entry: %cmp = icmp eq ptr %p, null %conv = zext i1 %cmp to i32 ret i32 %conv } ; TEST comparison against NULL in control flow ; ; int is_null_control(int *p) { ; if (p == 0) ; return 1; ; if (0 == p) ; return 1; ; return 0; ; } ; ; no-capture is missing on %p because it is not dereferenceable define i32 @is_null_control(ptr %p) #0 { ; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable ; CHECK-LABEL: define i32 @is_null_control ; CHECK-SAME: (ptr nofree [[P:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4 ; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[P]], null ; CHECK-NEXT: br i1 [[CMP]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] ; CHECK: if.then: ; CHECK-NEXT: store i32 1, ptr [[RETVAL]], align 4 ; CHECK-NEXT: br label [[RETURN:%.*]] ; CHECK: if.end: ; CHECK-NEXT: br label [[IF_END3:%.*]] ; CHECK: if.then2: ; CHECK-NEXT: unreachable ; CHECK: if.end3: ; CHECK-NEXT: store i32 0, ptr [[RETVAL]], align 4 ; CHECK-NEXT: br label [[RETURN]] ; CHECK: return: ; CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[RETVAL]], align 4 ; CHECK-NEXT: ret i32 [[TMP0]] ; entry: %retval = alloca i32, align 4 %cmp = icmp eq ptr %p, null br i1 %cmp, label %if.then, label %if.end if.then: ; preds = %entry store i32 1, ptr %retval, align 4 br label %return if.end: ; preds = %entry %cmp1 = icmp eq ptr null, %p br i1 %cmp1, label %if.then2, label %if.end3 if.then2: ; preds = %if.end store i32 1, ptr %retval, align 4 br label %return if.end3: ; preds = %if.end store i32 0, ptr %retval, align 4 br label %return return: ; preds = %if.end3, %if.then2, %if.then %0 = load i32, ptr %retval, align 4 ret i32 %0 } ; TEST singleton SCC ; ; double *srec0(double *a) { ; srec0(a); ; return 0; ; } ; define ptr @srec0(ptr %a) #0 { ; CHECK: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable ; CHECK-LABEL: define noalias noundef align 4294967296 ptr @srec0 ; CHECK-SAME: (ptr nocapture nofree readnone [[A:%.*]]) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: ret ptr null ; entry: %call = call ptr @srec0(ptr %a) ret ptr null } ; TEST singleton SCC with lots of nested recursive calls ; ; int* srec16(int* a) { ; return srec16(srec16(srec16(srec16( ; srec16(srec16(srec16(srec16( ; srec16(srec16(srec16(srec16( ; srec16(srec16(srec16(srec16( ; a ; )))))))))))))))); ; } ; ; Other arguments are possible here due to the no-return behavior. ; define ptr @srec16(ptr %a) #0 { ; CHECK: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable ; CHECK-LABEL: define noalias nonnull align 4294967296 dereferenceable(4294967295) ptr @srec16 ; CHECK-SAME: (ptr nocapture nofree readnone [[A:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: ret ptr undef ; entry: %call = call ptr @srec16(ptr %a) %call1 = call ptr @srec16(ptr %call) %call2 = call ptr @srec16(ptr %call1) %call3 = call ptr @srec16(ptr %call2) %call4 = call ptr @srec16(ptr %call3) %call5 = call ptr @srec16(ptr %call4) %call6 = call ptr @srec16(ptr %call5) %call7 = call ptr @srec16(ptr %call6) %call8 = call ptr @srec16(ptr %call7) %call9 = call ptr @srec16(ptr %call8) %call10 = call ptr @srec16(ptr %call9) %call11 = call ptr @srec16(ptr %call10) %call12 = call ptr @srec16(ptr %call11) %call13 = call ptr @srec16(ptr %call12) %call14 = call ptr @srec16(ptr %call13) %call15 = call ptr @srec16(ptr %call14) ret ptr %call15 } ; TEST SCC with various calls, casts, and comparisons agains NULL ; ; float *scc_A(int *a) { ; return (float*)(a ? (int*)scc_A((int*)scc_B((double*)scc_C((short*)a))) : a); ; } ; ; long *scc_B(double *a) { ; return (long*)(a ? scc_C((short*)scc_B((double*)scc_A((int*)a))) : a); ; } ; ; void *scc_C(short *a) { ; return scc_A((int*)(scc_A(a) ? scc_B((double*)a) : scc_C(a))); ; } define ptr @scc_A(ptr dereferenceable_or_null(4) %a) { ; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define noundef dereferenceable_or_null(4) ptr @scc_A ; CHECK-SAME: (ptr nofree noundef readnone returned dereferenceable_or_null(4) "no-capture-maybe-returned" [[A:%.*]]) #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[A]], null ; CHECK-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] ; CHECK: cond.true: ; CHECK-NEXT: [[CALL:%.*]] = call dereferenceable_or_null(4) ptr @scc_C(ptr noalias nofree noundef nonnull readnone dereferenceable(4) "no-capture-maybe-returned" [[A]]) #[[ATTR2]] ; CHECK-NEXT: [[CALL1:%.*]] = call dereferenceable_or_null(8) ptr @scc_B(ptr noalias nofree noundef readnone dereferenceable_or_null(8) "no-capture-maybe-returned" [[A]]) #[[ATTR2]] ; CHECK-NEXT: [[CALL2:%.*]] = call ptr @scc_A(ptr noalias nofree noundef readnone dereferenceable_or_null(8) "no-capture-maybe-returned" [[A]]) #[[ATTR2]] ; CHECK-NEXT: br label [[COND_END:%.*]] ; CHECK: cond.false: ; CHECK-NEXT: br label [[COND_END]] ; CHECK: cond.end: ; CHECK-NEXT: [[COND:%.*]] = phi ptr [ [[A]], [[COND_TRUE]] ], [ [[A]], [[COND_FALSE]] ] ; CHECK-NEXT: ret ptr [[A]] ; entry: %tobool = icmp ne ptr %a, null br i1 %tobool, label %cond.true, label %cond.false cond.true: ; preds = %entry %call = call ptr @scc_C(ptr %a) %call1 = call ptr @scc_B(ptr %call) %call2 = call ptr @scc_A(ptr %call1) br label %cond.end cond.false: ; preds = %entry br label %cond.end cond.end: ; preds = %cond.false, %cond.true %cond = phi ptr [ %call2, %cond.true ], [ %a, %cond.false ] ret ptr %cond } ; FIXME: the call1 below to scc_B should return dereferenceable_or_null(8) (as the callee does). Something prevented that deduction and needs to be investigated. define ptr @scc_B(ptr dereferenceable_or_null(8) %a) { ; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define noundef dereferenceable_or_null(8) ptr @scc_B ; CHECK-SAME: (ptr nofree noundef readnone returned dereferenceable_or_null(8) "no-capture-maybe-returned" [[A:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[A]], null ; CHECK-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] ; CHECK: cond.true: ; CHECK-NEXT: [[CALL:%.*]] = call dereferenceable_or_null(4) ptr @scc_A(ptr noalias nofree noundef nonnull readnone dereferenceable(8) "no-capture-maybe-returned" [[A]]) #[[ATTR2]] ; CHECK-NEXT: [[CALL1:%.*]] = call dereferenceable_or_null(8) ptr @scc_B(ptr noalias nofree noundef readnone dereferenceable_or_null(8) "no-capture-maybe-returned" [[A]]) #[[ATTR2]] ; CHECK-NEXT: [[CALL2:%.*]] = call ptr @scc_C(ptr noalias nofree noundef readnone dereferenceable_or_null(8) "no-capture-maybe-returned" [[A]]) #[[ATTR2]] ; CHECK-NEXT: br label [[COND_END:%.*]] ; CHECK: cond.false: ; CHECK-NEXT: br label [[COND_END]] ; CHECK: cond.end: ; CHECK-NEXT: [[COND:%.*]] = phi ptr [ [[A]], [[COND_TRUE]] ], [ [[A]], [[COND_FALSE]] ] ; CHECK-NEXT: ret ptr [[A]] ; entry: %tobool = icmp ne ptr %a, null br i1 %tobool, label %cond.true, label %cond.false cond.true: ; preds = %entry %call = call ptr @scc_A(ptr %a) %call1 = call ptr @scc_B(ptr %call) %call2 = call ptr @scc_C(ptr %call1) br label %cond.end cond.false: ; preds = %entry br label %cond.end cond.end: ; preds = %cond.false, %cond.true %cond = phi ptr [ %call2, %cond.true ], [ %a, %cond.false ] ret ptr %cond } define ptr @scc_C(ptr dereferenceable_or_null(2) %a) { ; CHECK: Function Attrs: nofree nosync nounwind memory(none) ; CHECK-LABEL: define noundef dereferenceable_or_null(4) ptr @scc_C ; CHECK-SAME: (ptr nofree noundef readnone returned dereferenceable_or_null(4) "no-capture-maybe-returned" [[A:%.*]]) #[[ATTR2]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CALL:%.*]] = call dereferenceable_or_null(4) ptr @scc_A(ptr noalias nofree noundef readnone dereferenceable_or_null(4) "no-capture-maybe-returned" [[A]]) #[[ATTR2]] ; CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne ptr [[A]], null ; CHECK-NEXT: br i1 [[TOBOOL]], label [[COND_TRUE:%.*]], label [[COND_FALSE:%.*]] ; CHECK: cond.true: ; CHECK-NEXT: [[CALL1:%.*]] = call ptr @scc_B(ptr noalias nofree noundef readnone dereferenceable_or_null(8) "no-capture-maybe-returned" [[A]]) #[[ATTR2]] ; CHECK-NEXT: br label [[COND_END:%.*]] ; CHECK: cond.false: ; CHECK-NEXT: [[CALL2:%.*]] = call ptr @scc_C(ptr noalias nofree noundef readnone dereferenceable_or_null(4) "no-capture-maybe-returned" [[A]]) #[[ATTR2]] ; CHECK-NEXT: br label [[COND_END]] ; CHECK: cond.end: ; CHECK-NEXT: [[COND:%.*]] = phi ptr [ [[A]], [[COND_TRUE]] ], [ [[A]], [[COND_FALSE]] ] ; CHECK-NEXT: [[CALL3:%.*]] = call ptr @scc_A(ptr noalias nofree noundef readnone dereferenceable_or_null(4) "no-capture-maybe-returned" [[A]]) #[[ATTR2]] ; CHECK-NEXT: ret ptr [[A]] ; entry: %call = call ptr @scc_A(ptr %a) %tobool = icmp ne ptr %call, null br i1 %tobool, label %cond.true, label %cond.false cond.true: ; preds = %entry %call1 = call ptr @scc_B(ptr %a) br label %cond.end cond.false: ; preds = %entry %call2 = call ptr @scc_C(ptr %a) br label %cond.end cond.end: ; preds = %cond.false, %cond.true %cond = phi ptr [ %call1, %cond.true ], [ %call2, %cond.false ] %call3 = call ptr @scc_A(ptr %cond) ret ptr %call3 } ; TEST call to external function, marked no-capture ; ; void external_no_capture(int /* no-capture */ *p); ; void test_external_no_capture(int *p) { ; external_no_capture(p); ; } ; declare void @external_no_capture(ptr nocapture) define void @test_external_no_capture(ptr %p) #0 { ; CHECK: Function Attrs: noinline nounwind uwtable ; CHECK-LABEL: define void @test_external_no_capture ; CHECK-SAME: (ptr nocapture [[P:%.*]]) #[[ATTR3:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: call void @external_no_capture(ptr nocapture [[P]]) ; CHECK-NEXT: ret void ; entry: call void @external_no_capture(ptr %p) ret void } ; TEST call to external var-args function, marked no-capture ; ; void test_var_arg_call(char *p, int a) { ; printf(p, a); ; } ; define void @test_var_arg_call(ptr %p, i32 %a) #0 { ; CHECK: Function Attrs: noinline nounwind uwtable ; CHECK-LABEL: define void @test_var_arg_call ; CHECK-SAME: (ptr nocapture [[P:%.*]], i32 [[A:%.*]]) #[[ATTR3]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CALL:%.*]] = call i32 (ptr, ...) @printf(ptr nocapture [[P]], i32 [[A]]) ; CHECK-NEXT: ret void ; entry: %call = call i32 (ptr, ...) @printf(ptr %p, i32 %a) ret void } declare i32 @printf(ptr nocapture, ...) ; TEST "captured" only through return ; ; long *not_captured_but_returned_0(long *a) { ; *a1 = 0; ; return a; ; } ; ; There should *not* be a no-capture attribute on %a define ptr @not_captured_but_returned_0(ptr %a) #0 { ; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: write) uwtable ; CHECK-LABEL: define noundef nonnull align 8 dereferenceable(8) ptr @not_captured_but_returned_0 ; CHECK-SAME: (ptr nofree noundef nonnull returned writeonly align 8 dereferenceable(8) "no-capture-maybe-returned" [[A:%.*]]) #[[ATTR4:[0-9]+]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: store i64 0, ptr [[A]], align 8 ; CHECK-NEXT: ret ptr [[A]] ; entry: store i64 0, ptr %a, align 8 ret ptr %a } ; TEST "captured" only through return ; ; long *not_captured_but_returned_1(long *a) { ; *(a+1) = 1; ; return a + 1; ; } ; ; There should *not* be a no-capture attribute on %a define ptr @not_captured_but_returned_1(ptr %a) #0 { ; CHECK: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: write) uwtable ; CHECK-LABEL: define noundef nonnull align 8 dereferenceable(8) ptr @not_captured_but_returned_1 ; CHECK-SAME: (ptr nofree nonnull writeonly align 8 dereferenceable(16) "no-capture-maybe-returned" [[A:%.*]]) #[[ATTR4]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i64, ptr [[A]], i64 1 ; CHECK-NEXT: store i64 1, ptr [[ADD_PTR]], align 8 ; CHECK-NEXT: ret ptr [[ADD_PTR]] ; entry: %add.ptr = getelementptr inbounds i64, ptr %a, i64 1 store i64 1, ptr %add.ptr, align 8 ret ptr %add.ptr } ; TEST calls to "captured" only through return functions ; ; void test_not_captured_but_returned_calls(long *a) { ; not_captured_but_returned_0(a); ; not_captured_but_returned_1(a); ; } ; define void @test_not_captured_but_returned_calls(ptr %a) #0 { ; TUNIT: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: write) uwtable ; TUNIT-LABEL: define void @test_not_captured_but_returned_calls ; TUNIT-SAME: (ptr nocapture nofree writeonly align 8 [[A:%.*]]) #[[ATTR4]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[CALL:%.*]] = call ptr @not_captured_but_returned_0(ptr nofree noundef writeonly align 8 "no-capture-maybe-returned" [[A]]) #[[ATTR12:[0-9]+]] ; TUNIT-NEXT: [[CALL1:%.*]] = call ptr @not_captured_but_returned_1(ptr nofree writeonly align 8 "no-capture-maybe-returned" [[A]]) #[[ATTR12]] ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(argmem: write) uwtable ; CGSCC-LABEL: define void @test_not_captured_but_returned_calls ; CGSCC-SAME: (ptr nofree noundef nonnull writeonly align 8 dereferenceable(16) [[A:%.*]]) #[[ATTR5:[0-9]+]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CALL:%.*]] = call ptr @not_captured_but_returned_0(ptr nofree noundef nonnull writeonly align 8 dereferenceable(16) [[A]]) #[[ATTR14:[0-9]+]] ; CGSCC-NEXT: [[CALL1:%.*]] = call ptr @not_captured_but_returned_1(ptr nofree noundef nonnull writeonly align 8 dereferenceable(16) [[A]]) #[[ATTR14]] ; CGSCC-NEXT: ret void ; entry: %call = call ptr @not_captured_but_returned_0(ptr %a) %call1 = call ptr @not_captured_but_returned_1(ptr %a) ret void } ; TEST "captured" only through transitive return ; ; long* negative_test_not_captured_but_returned_call_0a(long *a) { ; return not_captured_but_returned_0(a); ; } ; ; There should *not* be a no-capture attribute on %a define ptr @negative_test_not_captured_but_returned_call_0a(ptr %a) #0 { ; TUNIT: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: write) uwtable ; TUNIT-LABEL: define align 8 ptr @negative_test_not_captured_but_returned_call_0a ; TUNIT-SAME: (ptr nofree returned writeonly align 8 "no-capture-maybe-returned" [[A:%.*]]) #[[ATTR4]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[CALL:%.*]] = call ptr @not_captured_but_returned_0(ptr nofree noundef writeonly align 8 "no-capture-maybe-returned" [[A]]) #[[ATTR12]] ; TUNIT-NEXT: ret ptr [[A]] ; ; CGSCC: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(argmem: write) uwtable ; CGSCC-LABEL: define noundef nonnull align 8 dereferenceable(8) ptr @negative_test_not_captured_but_returned_call_0a ; CGSCC-SAME: (ptr nofree noundef nonnull writeonly align 8 dereferenceable(8) [[A:%.*]]) #[[ATTR5]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CALL:%.*]] = call noundef nonnull align 8 dereferenceable(8) ptr @not_captured_but_returned_0(ptr nofree noundef nonnull writeonly align 8 dereferenceable(8) [[A]]) #[[ATTR14]] ; CGSCC-NEXT: ret ptr [[CALL]] ; entry: %call = call ptr @not_captured_but_returned_0(ptr %a) ret ptr %call } ; TEST captured through write ; ; void negative_test_not_captured_but_returned_call_0b(long *a) { ; *a = (long)not_captured_but_returned_0(a); ; } ; ; There should *not* be a no-capture attribute on %a define void @negative_test_not_captured_but_returned_call_0b(ptr %a) #0 { ; TUNIT: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: write) uwtable ; TUNIT-LABEL: define void @negative_test_not_captured_but_returned_call_0b ; TUNIT-SAME: (ptr nofree writeonly align 8 [[A:%.*]]) #[[ATTR4]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[CALL:%.*]] = call ptr @not_captured_but_returned_0(ptr nofree noundef writeonly align 8 "no-capture-maybe-returned" [[A]]) #[[ATTR12]] ; TUNIT-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[A]] to i64 ; TUNIT-NEXT: store i64 [[TMP0]], ptr [[A]], align 8 ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(argmem: write) uwtable ; CGSCC-LABEL: define void @negative_test_not_captured_but_returned_call_0b ; CGSCC-SAME: (ptr nofree noundef nonnull writeonly align 8 dereferenceable(8) [[A:%.*]]) #[[ATTR5]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CALL:%.*]] = call ptr @not_captured_but_returned_0(ptr nofree noundef nonnull writeonly align 8 dereferenceable(8) [[A]]) #[[ATTR14]] ; CGSCC-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[CALL]] to i64 ; CGSCC-NEXT: store i64 [[TMP0]], ptr [[A]], align 8 ; CGSCC-NEXT: ret void ; entry: %call = call ptr @not_captured_but_returned_0(ptr %a) %0 = ptrtoint ptr %call to i64 store i64 %0, ptr %a, align 8 ret void } ; TEST "captured" only through transitive return ; ; long* negative_test_not_captured_but_returned_call_1a(long *a) { ; return not_captured_but_returned_1(a); ; } ; ; There should *not* be a no-capture attribute on %a define ptr @negative_test_not_captured_but_returned_call_1a(ptr %a) #0 { ; TUNIT: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: write) uwtable ; TUNIT-LABEL: define noundef nonnull align 8 dereferenceable(8) ptr @negative_test_not_captured_but_returned_call_1a ; TUNIT-SAME: (ptr nofree writeonly align 8 "no-capture-maybe-returned" [[A:%.*]]) #[[ATTR4]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[CALL:%.*]] = call noundef nonnull align 8 dereferenceable(8) ptr @not_captured_but_returned_1(ptr nofree writeonly align 8 "no-capture-maybe-returned" [[A]]) #[[ATTR12]] ; TUNIT-NEXT: ret ptr [[CALL]] ; ; CGSCC: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(argmem: write) uwtable ; CGSCC-LABEL: define noundef nonnull align 8 dereferenceable(8) ptr @negative_test_not_captured_but_returned_call_1a ; CGSCC-SAME: (ptr nofree noundef nonnull writeonly align 8 dereferenceable(16) [[A:%.*]]) #[[ATTR5]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CALL:%.*]] = call noundef nonnull align 8 dereferenceable(8) ptr @not_captured_but_returned_1(ptr nofree noundef nonnull writeonly align 8 dereferenceable(16) [[A]]) #[[ATTR14]] ; CGSCC-NEXT: ret ptr [[CALL]] ; entry: %call = call ptr @not_captured_but_returned_1(ptr %a) ret ptr %call } ; TEST captured through write ; ; void negative_test_not_captured_but_returned_call_1b(long *a) { ; *a = (long)not_captured_but_returned_1(a); ; } ; ; There should *not* be a no-capture attribute on %a define void @negative_test_not_captured_but_returned_call_1b(ptr %a) #0 { ; TUNIT: Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write) uwtable ; TUNIT-LABEL: define void @negative_test_not_captured_but_returned_call_1b ; TUNIT-SAME: (ptr nofree writeonly align 8 [[A:%.*]]) #[[ATTR5:[0-9]+]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[CALL:%.*]] = call align 8 ptr @not_captured_but_returned_1(ptr nofree writeonly align 8 "no-capture-maybe-returned" [[A]]) #[[ATTR12]] ; TUNIT-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[CALL]] to i64 ; TUNIT-NEXT: store i64 [[TMP0]], ptr [[CALL]], align 8 ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree noinline nosync nounwind willreturn memory(write) uwtable ; CGSCC-LABEL: define void @negative_test_not_captured_but_returned_call_1b ; CGSCC-SAME: (ptr nofree noundef nonnull writeonly align 8 dereferenceable(16) [[A:%.*]]) #[[ATTR6:[0-9]+]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CALL:%.*]] = call align 8 ptr @not_captured_but_returned_1(ptr nofree noundef nonnull writeonly align 8 dereferenceable(16) [[A]]) #[[ATTR14]] ; CGSCC-NEXT: [[TMP0:%.*]] = ptrtoint ptr [[CALL]] to i64 ; CGSCC-NEXT: store i64 [[TMP0]], ptr [[CALL]], align 8 ; CGSCC-NEXT: ret void ; entry: %call = call ptr @not_captured_but_returned_1(ptr %a) %0 = ptrtoint ptr %call to i64 store i64 %0, ptr %call, align 8 ret void } ; TEST return argument or unknown call result ; ; int* ret_arg_or_unknown(int* b) { ; if (b == 0) ; return b; ; return unknown(); ; } ; ; Verify we do *not* assume b is returned or not captured. ; define ptr @ret_arg_or_unknown(ptr %b) #0 { ; CHECK: Function Attrs: noinline nounwind uwtable ; CHECK-LABEL: define ptr @ret_arg_or_unknown ; CHECK-SAME: (ptr [[B:%.*]]) #[[ATTR3]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[B]], null ; CHECK-NEXT: br i1 [[CMP]], label [[RET_ARG:%.*]], label [[RET_UNKNOWN:%.*]] ; CHECK: ret_arg: ; CHECK-NEXT: ret ptr [[B]] ; CHECK: ret_unknown: ; CHECK-NEXT: [[CALL:%.*]] = call ptr @unknown() ; CHECK-NEXT: ret ptr [[CALL]] ; entry: %cmp = icmp eq ptr %b, null br i1 %cmp, label %ret_arg, label %ret_unknown ret_arg: ret ptr %b ret_unknown: %call = call ptr @unknown() ret ptr %call } define ptr @ret_arg_or_unknown_through_phi(ptr %b) #0 { ; CHECK: Function Attrs: noinline nounwind uwtable ; CHECK-LABEL: define ptr @ret_arg_or_unknown_through_phi ; CHECK-SAME: (ptr [[B:%.*]]) #[[ATTR3]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CMP:%.*]] = icmp eq ptr [[B]], null ; CHECK-NEXT: br i1 [[CMP]], label [[RET_ARG:%.*]], label [[RET_UNKNOWN:%.*]] ; CHECK: ret_arg: ; CHECK-NEXT: br label [[R:%.*]] ; CHECK: ret_unknown: ; CHECK-NEXT: [[CALL:%.*]] = call ptr @unknown() ; CHECK-NEXT: br label [[R]] ; CHECK: r: ; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[B]], [[RET_ARG]] ], [ [[CALL]], [[RET_UNKNOWN]] ] ; CHECK-NEXT: ret ptr [[PHI]] ; entry: %cmp = icmp eq ptr %b, null br i1 %cmp, label %ret_arg, label %ret_unknown ret_arg: br label %r ret_unknown: %call = call ptr @unknown() br label %r r: %phi = phi ptr [ %b, %ret_arg ], [ %call, %ret_unknown ] ret ptr %phi } ; TEST not captured by readonly external function ; declare ptr @readonly_unknown(ptr, ptr) readonly define void @not_captured_by_readonly_call(ptr %b) #0 { ; TUNIT: Function Attrs: noinline nosync nounwind memory(read) uwtable ; TUNIT-LABEL: define void @not_captured_by_readonly_call ; TUNIT-SAME: (ptr nocapture readonly [[B:%.*]]) #[[ATTR7:[0-9]+]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[CALL:%.*]] = call ptr @readonly_unknown(ptr readonly [[B]], ptr readonly [[B]]) #[[ATTR13:[0-9]+]] ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: noinline nosync nounwind memory(read) uwtable ; CGSCC-LABEL: define void @not_captured_by_readonly_call ; CGSCC-SAME: (ptr nocapture readonly [[B:%.*]]) #[[ATTR8:[0-9]+]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CALL:%.*]] = call ptr @readonly_unknown(ptr readonly [[B]], ptr readonly [[B]]) #[[ATTR15:[0-9]+]] ; CGSCC-NEXT: ret void ; entry: %call = call ptr @readonly_unknown(ptr %b, ptr %b) ret void } ; TEST not captured by readonly external function if return chain is known ; ; Make sure the returned flag on %r is strong enough to justify nocapture on %b but **not** on %r. ; define ptr @not_captured_by_readonly_call_not_returned_either1(ptr %b, ptr returned %r) { ; TUNIT: Function Attrs: nosync nounwind memory(read) ; TUNIT-LABEL: define ptr @not_captured_by_readonly_call_not_returned_either1 ; TUNIT-SAME: (ptr nocapture readonly [[B:%.*]], ptr readonly returned [[R:%.*]]) #[[ATTR8:[0-9]+]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[CALL:%.*]] = call ptr @readonly_unknown(ptr readonly [[B]], ptr readonly [[R]]) #[[ATTR8]] ; TUNIT-NEXT: ret ptr [[CALL]] ; ; CGSCC: Function Attrs: nosync nounwind memory(read) ; CGSCC-LABEL: define ptr @not_captured_by_readonly_call_not_returned_either1 ; CGSCC-SAME: (ptr nocapture readonly [[B:%.*]], ptr readonly returned [[R:%.*]]) #[[ATTR9:[0-9]+]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CALL:%.*]] = call ptr @readonly_unknown(ptr readonly [[B]], ptr readonly [[R]]) #[[ATTR9]] ; CGSCC-NEXT: ret ptr [[CALL]] ; entry: %call = call ptr @readonly_unknown(ptr %b, ptr %r) nounwind ret ptr %call } declare ptr @readonly_unknown_r1a(ptr, ptr returned) readonly define ptr @not_captured_by_readonly_call_not_returned_either2(ptr %b, ptr %r) { ; TUNIT: Function Attrs: nosync nounwind memory(read) ; TUNIT-LABEL: define ptr @not_captured_by_readonly_call_not_returned_either2 ; TUNIT-SAME: (ptr readonly [[B:%.*]], ptr readonly [[R:%.*]]) #[[ATTR8]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[CALL:%.*]] = call ptr @readonly_unknown_r1a(ptr readonly [[B]], ptr readonly [[R]]) #[[ATTR8]] ; TUNIT-NEXT: ret ptr [[CALL]] ; ; CGSCC: Function Attrs: nosync nounwind memory(read) ; CGSCC-LABEL: define ptr @not_captured_by_readonly_call_not_returned_either2 ; CGSCC-SAME: (ptr readonly [[B:%.*]], ptr readonly [[R:%.*]]) #[[ATTR9]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CALL:%.*]] = call ptr @readonly_unknown_r1a(ptr readonly [[B]], ptr readonly [[R]]) #[[ATTR9]] ; CGSCC-NEXT: ret ptr [[CALL]] ; entry: %call = call ptr @readonly_unknown_r1a(ptr %b, ptr %r) nounwind ret ptr %call } declare ptr @readonly_unknown_r1b(ptr, ptr returned) readonly nounwind define ptr @not_captured_by_readonly_call_not_returned_either3(ptr %b, ptr %r) { ; TUNIT: Function Attrs: nosync nounwind memory(read) ; TUNIT-LABEL: define ptr @not_captured_by_readonly_call_not_returned_either3 ; TUNIT-SAME: (ptr nocapture readonly [[B:%.*]], ptr readonly [[R:%.*]]) #[[ATTR8]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[CALL:%.*]] = call ptr @readonly_unknown_r1b(ptr nocapture readonly [[B]], ptr readonly [[R]]) #[[ATTR8]] ; TUNIT-NEXT: ret ptr [[CALL]] ; ; CGSCC: Function Attrs: nosync nounwind memory(read) ; CGSCC-LABEL: define ptr @not_captured_by_readonly_call_not_returned_either3 ; CGSCC-SAME: (ptr nocapture readonly [[B:%.*]], ptr readonly [[R:%.*]]) #[[ATTR9]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CALL:%.*]] = call ptr @readonly_unknown_r1b(ptr nocapture readonly [[B]], ptr readonly [[R]]) #[[ATTR9]] ; CGSCC-NEXT: ret ptr [[CALL]] ; entry: %call = call ptr @readonly_unknown_r1b(ptr %b, ptr %r) ret ptr %call } define ptr @not_captured_by_readonly_call_not_returned_either4(ptr %b, ptr %r) nounwind { ; TUNIT: Function Attrs: nosync nounwind memory(read) ; TUNIT-LABEL: define ptr @not_captured_by_readonly_call_not_returned_either4 ; TUNIT-SAME: (ptr readonly [[B:%.*]], ptr readonly [[R:%.*]]) #[[ATTR8]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[CALL:%.*]] = call ptr @readonly_unknown_r1a(ptr readonly [[B]], ptr readonly [[R]]) #[[ATTR13]] ; TUNIT-NEXT: ret ptr [[CALL]] ; ; CGSCC: Function Attrs: nosync nounwind memory(read) ; CGSCC-LABEL: define ptr @not_captured_by_readonly_call_not_returned_either4 ; CGSCC-SAME: (ptr readonly [[B:%.*]], ptr readonly [[R:%.*]]) #[[ATTR9]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CALL:%.*]] = call ptr @readonly_unknown_r1a(ptr readonly [[B]], ptr readonly [[R]]) #[[ATTR15]] ; CGSCC-NEXT: ret ptr [[CALL]] ; entry: %call = call ptr @readonly_unknown_r1a(ptr %b, ptr %r) ret ptr %call } declare ptr @unknown_i32p(ptr) define void @nocapture_is_not_subsumed_1(ptr nocapture %b) { ; CHECK-LABEL: define void @nocapture_is_not_subsumed_1 ; CHECK-SAME: (ptr nocapture [[B:%.*]]) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CALL:%.*]] = call ptr @unknown_i32p(ptr [[B]]) ; CHECK-NEXT: store i32 0, ptr [[CALL]], align 4 ; CHECK-NEXT: ret void ; entry: %call = call ptr @unknown_i32p(ptr %b) store i32 0, ptr %call ret void } declare ptr @readonly_i32p(ptr) readonly define void @nocapture_is_not_subsumed_2(ptr nocapture %b) { ; TUNIT: Function Attrs: nosync ; TUNIT-LABEL: define void @nocapture_is_not_subsumed_2 ; TUNIT-SAME: (ptr nocapture [[B:%.*]]) #[[ATTR10:[0-9]+]] { ; TUNIT-NEXT: entry: ; TUNIT-NEXT: [[CALL:%.*]] = call ptr @readonly_i32p(ptr readonly [[B]]) #[[ATTR13]] ; TUNIT-NEXT: store i32 0, ptr [[CALL]], align 4 ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: nosync ; CGSCC-LABEL: define void @nocapture_is_not_subsumed_2 ; CGSCC-SAME: (ptr nocapture [[B:%.*]]) #[[ATTR11:[0-9]+]] { ; CGSCC-NEXT: entry: ; CGSCC-NEXT: [[CALL:%.*]] = call ptr @readonly_i32p(ptr readonly [[B]]) #[[ATTR15]] ; CGSCC-NEXT: store i32 0, ptr [[CALL]], align 4 ; CGSCC-NEXT: ret void ; entry: %call = call ptr @readonly_i32p(ptr %b) store i32 0, ptr %call ret void } ; Make sure %p is not not marked nocapture (Bug #64613). ; Version a failed with the lightweight attributor, b with the full one. define ptr @b64613_a(ptr noundef %p) { ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define noundef ptr @b64613_a ; TUNIT-SAME: (ptr nofree noundef readnone returned "no-capture-maybe-returned" [[P:%.*]]) #[[ATTR11:[0-9]+]] { ; TUNIT-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 1 ; TUNIT-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 1 ; TUNIT-NEXT: ret ptr [[P]] ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define noundef ptr @b64613_a ; CGSCC-SAME: (ptr nofree noundef readnone returned "no-capture-maybe-returned" [[P:%.*]]) #[[ATTR12:[0-9]+]] { ; CGSCC-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 1 ; CGSCC-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 1 ; CGSCC-NEXT: ret ptr [[P]] ; %p.addr = alloca ptr, align 1 store ptr %p, ptr %p.addr, align 1 %r = load ptr, ptr %p.addr, align 1 ret ptr %r } define ptr @b64613_b(ptr noundef %p, i32 %i) { ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define ptr @b64613_b ; TUNIT-SAME: (ptr nofree noundef [[P:%.*]], i32 [[I:%.*]]) #[[ATTR11]] { ; TUNIT-NEXT: [[P_ADDR:%.*]] = alloca <2 x ptr>, align 1 ; TUNIT-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P_ADDR]], i32 [[I]] ; TUNIT-NEXT: store ptr [[P]], ptr [[G]], align 1 ; TUNIT-NEXT: [[R:%.*]] = load ptr, ptr [[P_ADDR]], align 1 ; TUNIT-NEXT: ret ptr [[R]] ; ; CGSCC: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define ptr @b64613_b ; CGSCC-SAME: (ptr nofree noundef [[P:%.*]], i32 [[I:%.*]]) #[[ATTR12]] { ; CGSCC-NEXT: [[P_ADDR:%.*]] = alloca <2 x ptr>, align 1 ; CGSCC-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P_ADDR]], i32 [[I]] ; CGSCC-NEXT: store ptr [[P]], ptr [[G]], align 1 ; CGSCC-NEXT: [[R:%.*]] = load ptr, ptr [[P_ADDR]], align 1 ; CGSCC-NEXT: ret ptr [[R]] ; %p.addr = alloca <2 x ptr>, align 1 %g = getelementptr i8, ptr %p.addr, i32 %i store ptr %p, ptr %g, align 1 %r = load ptr, ptr %p.addr, align 1 ret ptr %r } define void @b64613_positive(ptr noundef %p, i32 %i) { ; TUNIT: Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) ; TUNIT-LABEL: define void @b64613_positive ; TUNIT-SAME: (ptr nocapture nofree noundef [[P:%.*]], i32 [[I:%.*]]) #[[ATTR11]] { ; TUNIT-NEXT: [[P_ADDR:%.*]] = alloca <2 x ptr>, align 1 ; TUNIT-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P_ADDR]], i32 [[I]] ; TUNIT-NEXT: store ptr [[P]], ptr [[G]], align 1 ; TUNIT-NEXT: ret void ; ; CGSCC: Function Attrs: mustprogress nofree nosync nounwind willreturn memory(none) ; CGSCC-LABEL: define void @b64613_positive ; CGSCC-SAME: (ptr nocapture nofree noundef [[P:%.*]], i32 [[I:%.*]]) #[[ATTR13:[0-9]+]] { ; CGSCC-NEXT: [[P_ADDR:%.*]] = alloca <2 x ptr>, align 1 ; CGSCC-NEXT: [[G:%.*]] = getelementptr i8, ptr [[P_ADDR]], i32 [[I]] ; CGSCC-NEXT: store ptr [[P]], ptr [[G]], align 1 ; CGSCC-NEXT: ret void ; %p.addr = alloca <2 x ptr>, align 1 %g = getelementptr i8, ptr %p.addr, i32 %i store ptr %p, ptr %g, align 1 %r = load ptr, ptr %p.addr, align 1 %q = call ptr @b64613_b(ptr %r, i32 %i) ret void } attributes #0 = { noinline nounwind uwtable } ;. ; TUNIT: attributes #[[ATTR0]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable } ; TUNIT: attributes #[[ATTR1]] = { mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable } ; TUNIT: attributes #[[ATTR2]] = { nofree nosync nounwind memory(none) } ; TUNIT: attributes #[[ATTR3]] = { noinline nounwind uwtable } ; TUNIT: attributes #[[ATTR4]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: write) uwtable } ; TUNIT: attributes #[[ATTR5]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(write) uwtable } ; TUNIT: attributes #[[ATTR6:[0-9]+]] = { memory(read) } ; TUNIT: attributes #[[ATTR7]] = { noinline nosync nounwind memory(read) uwtable } ; TUNIT: attributes #[[ATTR8]] = { nosync nounwind memory(read) } ; TUNIT: attributes #[[ATTR9:[0-9]+]] = { nounwind memory(read) } ; TUNIT: attributes #[[ATTR10]] = { nosync } ; TUNIT: attributes #[[ATTR11]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } ; TUNIT: attributes #[[ATTR12]] = { nofree nosync nounwind willreturn memory(write) } ; TUNIT: attributes #[[ATTR13]] = { nosync memory(read) } ;. ; CGSCC: attributes #[[ATTR0]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) uwtable } ; CGSCC: attributes #[[ATTR1]] = { mustprogress nofree noinline nosync nounwind willreturn memory(none) uwtable } ; CGSCC: attributes #[[ATTR2]] = { nofree nosync nounwind memory(none) } ; CGSCC: attributes #[[ATTR3]] = { noinline nounwind uwtable } ; CGSCC: attributes #[[ATTR4]] = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(argmem: write) uwtable } ; CGSCC: attributes #[[ATTR5]] = { mustprogress nofree noinline nosync nounwind willreturn memory(argmem: write) uwtable } ; CGSCC: attributes #[[ATTR6]] = { mustprogress nofree noinline nosync nounwind willreturn memory(write) uwtable } ; CGSCC: attributes #[[ATTR7:[0-9]+]] = { memory(read) } ; CGSCC: attributes #[[ATTR8]] = { noinline nosync nounwind memory(read) uwtable } ; CGSCC: attributes #[[ATTR9]] = { nosync nounwind memory(read) } ; CGSCC: attributes #[[ATTR10:[0-9]+]] = { nounwind memory(read) } ; CGSCC: attributes #[[ATTR11]] = { nosync } ; CGSCC: attributes #[[ATTR12]] = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) } ; CGSCC: attributes #[[ATTR13]] = { mustprogress nofree nosync nounwind willreturn memory(none) } ; CGSCC: attributes #[[ATTR14]] = { nofree nounwind willreturn memory(write) } ; CGSCC: attributes #[[ATTR15]] = { nosync memory(read) } ;.