summaryrefslogtreecommitdiffstats
path: root/llvm/test/Transforms/Attributor/nocapture-2.ll
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/test/Transforms/Attributor/nocapture-2.ll')
-rw-r--r--llvm/test/Transforms/Attributor/nocapture-2.ll471
1 files changed, 471 insertions, 0 deletions
diff --git a/llvm/test/Transforms/Attributor/nocapture-2.ll b/llvm/test/Transforms/Attributor/nocapture-2.ll
new file mode 100644
index 00000000000..79075268ed4
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/nocapture-2.ll
@@ -0,0 +1,471 @@
+; RUN: opt -functionattrs -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s
+;
+; 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"
+
+; TEST comparison against NULL
+;
+; int is_null_return(int *p) {
+; return p == 0;
+; }
+;
+; FIXME: no-capture missing for %p
+; CHECK: define i32 @is_null_return(i32* nofree readnone %p)
+define i32 @is_null_return(i32* %p) #0 {
+entry:
+ %cmp = icmp eq i32* %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;
+; }
+;
+; FIXME: no-capture missing for %p
+; CHECK: define i32 @is_null_control(i32* nofree readnone %p)
+define i32 @is_null_control(i32* %p) #0 {
+entry:
+ %retval = alloca i32, align 4
+ %cmp = icmp eq i32* %p, null
+ br i1 %cmp, label %if.then, label %if.end
+
+if.then: ; preds = %entry
+ store i32 1, i32* %retval, align 4
+ br label %return
+
+if.end: ; preds = %entry
+ %cmp1 = icmp eq i32* null, %p
+ br i1 %cmp1, label %if.then2, label %if.end3
+
+if.then2: ; preds = %if.end
+ store i32 1, i32* %retval, align 4
+ br label %return
+
+if.end3: ; preds = %if.end
+ store i32 0, i32* %retval, align 4
+ br label %return
+
+return: ; preds = %if.end3, %if.then2, %if.then
+ %0 = load i32, i32* %retval, align 4
+ ret i32 %0
+}
+
+; TEST singleton SCC
+;
+; double *srec0(double *a) {
+; srec0(a);
+; return 0;
+; }
+;
+; CHECK: define noalias nonnull align 536870912 dereferenceable(4294967295) double* @srec0(double* nocapture nofree readnone %a)
+define double* @srec0(double* %a) #0 {
+entry:
+ %call = call double* @srec0(double* %a)
+ ret double* 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.
+;
+; CHECK: define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @srec16(i32* nocapture nofree readnone %a)
+define i32* @srec16(i32* %a) #0 {
+entry:
+ %call = call i32* @srec16(i32* %a)
+; CHECK-NOT: %call
+; CHECK: unreachable
+ %call1 = call i32* @srec16(i32* %call)
+ %call2 = call i32* @srec16(i32* %call1)
+ %call3 = call i32* @srec16(i32* %call2)
+ %call4 = call i32* @srec16(i32* %call3)
+ %call5 = call i32* @srec16(i32* %call4)
+ %call6 = call i32* @srec16(i32* %call5)
+ %call7 = call i32* @srec16(i32* %call6)
+ %call8 = call i32* @srec16(i32* %call7)
+ %call9 = call i32* @srec16(i32* %call8)
+ %call10 = call i32* @srec16(i32* %call9)
+ %call11 = call i32* @srec16(i32* %call10)
+ %call12 = call i32* @srec16(i32* %call11)
+ %call13 = call i32* @srec16(i32* %call12)
+ %call14 = call i32* @srec16(i32* %call13)
+ %call15 = call i32* @srec16(i32* %call14)
+ ret i32* %call15
+}
+
+; TEST SCC with various calls, casts, and comparisons agains NULL
+;
+; CHECK: define dereferenceable_or_null(4) float* @scc_A(i32* nofree readnone returned dereferenceable_or_null(4) "no-capture-maybe-returned" %a)
+;
+; CHECK: define dereferenceable_or_null(8) i64* @scc_B(double* nofree readnone returned dereferenceable_or_null(8) "no-capture-maybe-returned" %a)
+;
+; CHECK: define dereferenceable_or_null(4) i8* @scc_C(i16* nofree readnone returned dereferenceable_or_null(4) "no-capture-maybe-returned" %a)
+;
+; 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 float* @scc_A(i32* dereferenceable_or_null(4) %a) {
+entry:
+ %tobool = icmp ne i32* %a, null
+ br i1 %tobool, label %cond.true, label %cond.false
+
+cond.true: ; preds = %entry
+ %0 = bitcast i32* %a to i16*
+ %call = call i8* @scc_C(i16* %0)
+ %1 = bitcast i8* %call to double*
+ %call1 = call i64* @scc_B(double* %1)
+ %2 = bitcast i64* %call1 to i32*
+ %call2 = call float* @scc_A(i32* %2)
+ %3 = bitcast float* %call2 to i32*
+ br label %cond.end
+
+cond.false: ; preds = %entry
+ br label %cond.end
+
+cond.end: ; preds = %cond.false, %cond.true
+ %cond = phi i32* [ %3, %cond.true ], [ %a, %cond.false ]
+ %4 = bitcast i32* %cond to float*
+ ret float* %4
+}
+
+define i64* @scc_B(double* dereferenceable_or_null(8) %a) {
+entry:
+ %tobool = icmp ne double* %a, null
+ br i1 %tobool, label %cond.true, label %cond.false
+
+cond.true: ; preds = %entry
+ %0 = bitcast double* %a to i32*
+ %call = call float* @scc_A(i32* %0)
+ %1 = bitcast float* %call to double*
+ %call1 = call i64* @scc_B(double* %1)
+ %2 = bitcast i64* %call1 to i16*
+ %call2 = call i8* @scc_C(i16* %2)
+ br label %cond.end
+
+cond.false: ; preds = %entry
+ %3 = bitcast double* %a to i8*
+ br label %cond.end
+
+cond.end: ; preds = %cond.false, %cond.true
+ %cond = phi i8* [ %call2, %cond.true ], [ %3, %cond.false ]
+ %4 = bitcast i8* %cond to i64*
+ ret i64* %4
+}
+
+define i8* @scc_C(i16* dereferenceable_or_null(2) %a) {
+entry:
+ %bc = bitcast i16* %a to i32*
+ %call = call float* @scc_A(i32* %bc)
+ %bc2 = bitcast float* %call to i8*
+ %tobool = icmp ne i8* %bc2, null
+ br i1 %tobool, label %cond.true, label %cond.false
+
+cond.true: ; preds = %entry
+ %0 = bitcast i16* %a to double*
+ %call1 = call i64* @scc_B(double* %0)
+ %1 = bitcast i64* %call1 to i8*
+ br label %cond.end
+
+cond.false: ; preds = %entry
+ %call2 = call i8* @scc_C(i16* %a)
+ br label %cond.end
+
+cond.end: ; preds = %cond.false, %cond.true
+ %cond = phi i8* [ %1, %cond.true ], [ %call2, %cond.false ]
+ %2 = bitcast i8* %cond to i32*
+ %call3 = call float* @scc_A(i32* %2)
+ %3 = bitcast float* %call3 to i8*
+ ret i8* %3
+}
+
+
+; 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);
+; }
+;
+; CHECK: define void @test_external_no_capture(i32* nocapture %p)
+declare void @external_no_capture(i32* nocapture)
+
+define void @test_external_no_capture(i32* %p) #0 {
+entry:
+ call void @external_no_capture(i32* %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);
+; }
+;
+; CHECK: define void @test_var_arg_call(i8* nocapture %p, i32 %a)
+define void @test_var_arg_call(i8* %p, i32 %a) #0 {
+entry:
+ %call = call i32 (i8*, ...) @printf(i8* %p, i32 %a)
+ ret void
+}
+
+declare i32 @printf(i8* 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
+; CHECK: define nonnull align 8 dereferenceable(8) i64* @not_captured_but_returned_0(i64* nofree nonnull returned writeonly align 8 dereferenceable(8) "no-capture-maybe-returned" %a)
+
+define i64* @not_captured_but_returned_0(i64* %a) #0 {
+entry:
+ store i64 0, i64* %a, align 8
+ ret i64* %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
+; CHECK: define nonnull align 8 dereferenceable(8) i64* @not_captured_but_returned_1(i64* nofree nonnull writeonly align 8 dereferenceable(16) "no-capture-maybe-returned" %a)
+define i64* @not_captured_but_returned_1(i64* %a) #0 {
+entry:
+ %add.ptr = getelementptr inbounds i64, i64* %a, i64 1
+ store i64 1, i64* %add.ptr, align 8
+ ret i64* %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);
+; }
+;
+; CHECK: define void @test_not_captured_but_returned_calls(i64* nocapture nofree writeonly %a)
+define void @test_not_captured_but_returned_calls(i64* %a) #0 {
+entry:
+ %call = call i64* @not_captured_but_returned_0(i64* %a)
+ %call1 = call i64* @not_captured_but_returned_1(i64* %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
+; CHECK: define i64* @negative_test_not_captured_but_returned_call_0a(i64* nofree returned writeonly "no-capture-maybe-returned" %a)
+define i64* @negative_test_not_captured_but_returned_call_0a(i64* %a) #0 {
+entry:
+ %call = call i64* @not_captured_but_returned_0(i64* %a)
+ ret i64* %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
+; CHECK: define void @negative_test_not_captured_but_returned_call_0b(i64* nofree writeonly %a)
+define void @negative_test_not_captured_but_returned_call_0b(i64* %a) #0 {
+entry:
+ %call = call i64* @not_captured_but_returned_0(i64* %a)
+ %0 = ptrtoint i64* %call to i64
+ store i64 %0, i64* %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
+; CHECK: define nonnull align 8 dereferenceable(8) i64* @negative_test_not_captured_but_returned_call_1a(i64* nofree writeonly "no-capture-maybe-returned" %a)
+define i64* @negative_test_not_captured_but_returned_call_1a(i64* %a) #0 {
+entry:
+ %call = call i64* @not_captured_but_returned_1(i64* %a)
+ ret i64* %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
+; CHECK: define void @negative_test_not_captured_but_returned_call_1b(i64* nofree writeonly %a)
+define void @negative_test_not_captured_but_returned_call_1b(i64* %a) #0 {
+entry:
+ %call = call i64* @not_captured_but_returned_1(i64* %a)
+ %0 = ptrtoint i64* %call to i64
+ store i64 %0, i64* %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.
+;
+; CHECK: define i32* @ret_arg_or_unknown(i32* readnone %b)
+; CHECK: define i32* @ret_arg_or_unknown_through_phi(i32* readnone %b)
+
+declare i32* @unknown()
+
+define i32* @ret_arg_or_unknown(i32* %b) #0 {
+entry:
+ %cmp = icmp eq i32* %b, null
+ br i1 %cmp, label %ret_arg, label %ret_unknown
+
+ret_arg:
+ ret i32* %b
+
+ret_unknown:
+ %call = call i32* @unknown()
+ ret i32* %call
+}
+
+define i32* @ret_arg_or_unknown_through_phi(i32* %b) #0 {
+entry:
+ %cmp = icmp eq i32* %b, null
+ br i1 %cmp, label %ret_arg, label %ret_unknown
+
+ret_arg:
+ br label %r
+
+ret_unknown:
+ %call = call i32* @unknown()
+ br label %r
+
+r:
+ %phi = phi i32* [ %b, %ret_arg ], [ %call, %ret_unknown ]
+ ret i32* %phi
+}
+
+
+; TEST not captured by readonly external function
+;
+; CHECK: define void @not_captured_by_readonly_call(i32* nocapture readonly %b)
+declare i32* @readonly_unknown(i32*, i32*) readonly
+
+define void @not_captured_by_readonly_call(i32* %b) #0 {
+entry:
+ %call = call i32* @readonly_unknown(i32* %b, i32* %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.
+;
+; CHECK: define i32* @not_captured_by_readonly_call_not_returned_either1(i32* nocapture readonly %b, i32* readonly returned %r)
+;
+; CHECK: define i32* @not_captured_by_readonly_call_not_returned_either2(i32* nocapture readonly %b, i32* readonly returned %r)
+; CHECK: define i32* @not_captured_by_readonly_call_not_returned_either3(i32* nocapture readonly %b, i32* readonly returned %r)
+;
+; CHECK: define i32* @not_captured_by_readonly_call_not_returned_either4(i32* nocapture readonly %b, i32* readonly returned %r)
+define i32* @not_captured_by_readonly_call_not_returned_either1(i32* %b, i32* returned %r) {
+entry:
+ %call = call i32* @readonly_unknown(i32* %b, i32* %r) nounwind
+ ret i32* %call
+}
+
+declare i32* @readonly_unknown_r1a(i32*, i32* returned) readonly
+define i32* @not_captured_by_readonly_call_not_returned_either2(i32* %b, i32* %r) {
+entry:
+ %call = call i32* @readonly_unknown_r1a(i32* %b, i32* %r) nounwind
+ ret i32* %call
+}
+
+declare i32* @readonly_unknown_r1b(i32*, i32* returned) readonly nounwind
+define i32* @not_captured_by_readonly_call_not_returned_either3(i32* %b, i32* %r) {
+entry:
+ %call = call i32* @readonly_unknown_r1b(i32* %b, i32* %r)
+ ret i32* %call
+}
+
+define i32* @not_captured_by_readonly_call_not_returned_either4(i32* %b, i32* %r) nounwind {
+entry:
+ %call = call i32* @readonly_unknown_r1a(i32* %b, i32* %r)
+ ret i32* %call
+}
+
+
+declare i32* @unknown_i32p(i32*)
+define void @nocapture_is_not_subsumed_1(i32* nocapture %b) {
+; CHECK-LABEL: define {{[^@]+}}@nocapture_is_not_subsumed_1
+; CHECK-SAME: (i32* nocapture [[B:%.*]])
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CALL:%.*]] = call i32* @unknown_i32p(i32* [[B:%.*]])
+; CHECK-NEXT: store i32 0, i32* [[CALL]]
+; CHECK-NEXT: ret void
+;
+entry:
+ %call = call i32* @unknown_i32p(i32* %b)
+ store i32 0, i32* %call
+ ret void
+}
+
+declare i32* @readonly_i32p(i32*) readonly
+define void @nocapture_is_not_subsumed_2(i32* nocapture %b) {
+; CHECK-LABEL: define {{[^@]+}}@nocapture_is_not_subsumed_2
+; CHECK-SAME: (i32* nocapture nofree [[B:%.*]])
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CALL:%.*]] = call i32* @readonly_i32p(i32* readonly [[B:%.*]])
+; CHECK-NEXT: store i32 0, i32* [[CALL]]
+; CHECK-NEXT: ret void
+;
+entry:
+ %call = call i32* @readonly_i32p(i32* %b)
+ store i32 0, i32* %call
+ ret void
+}
+
+attributes #0 = { noinline nounwind uwtable }
OpenPOWER on IntegriCloud