jdoerfert created this revision.
jdoerfert added reviewers: homerdin, hfinkel, fedor.sergeev, sanjoy, spatel, 
nlopes, nicholas, reames.
Herald added subscribers: cfe-commits, bollu, hiraditya.
Herald added projects: clang, LLVM.

Add the no-capture argument attribute deduction to the Attributor
fixpoint framework.

The new string attributed "no-capture-maybe-returned" is introduced to
allow deduction of no-capture through functions that "capture" an
argument but only by "returning" it.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D59922

Files:
  clang/test/CodeGenObjC/os_log.m
  clang/test/CodeGenOpenCL/as_type.cl
  llvm/include/llvm/Transforms/IPO/Attributor.h
  llvm/lib/Transforms/IPO/Attributor.cpp
  llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll
  llvm/test/Transforms/FunctionAttrs/SCC1.ll
  llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
  llvm/test/Transforms/FunctionAttrs/arg_returned.ll
  llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll
  llvm/test/Transforms/FunctionAttrs/nocapture.ll
  llvm/test/Transforms/FunctionAttrs/readattrs.ll

Index: llvm/test/Transforms/FunctionAttrs/readattrs.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/readattrs.ll
+++ llvm/test/Transforms/FunctionAttrs/readattrs.ll
@@ -12,7 +12,7 @@
   ret void
 }
 
-; CHECK: define i8* @test2(i8* readnone returned %p)
+; CHECK: define i8* @test2(i8* readnone returned "no-capture-maybe-returned" %p)
 define i8* @test2(i8* %p) {
   store i32 0, i32* @x
   ret i8* %p
@@ -54,13 +54,13 @@
   ret void
 }
 
-; CHECK: define i32* @test8_1(i32* readnone returned %p)
+; CHECK: define i32* @test8_1(i32* readnone returned "no-capture-maybe-returned" %p)
 define i32* @test8_1(i32* %p) {
 entry:
   ret i32* %p
 }
 
-; CHECK: define void @test8_2(i32* %p)
+; CHECK: define void @test8_2(i32* nocapture %p)
 define void @test8_2(i32* %p) {
 entry:
   %call = call i32* @test8_1(i32* %p)
Index: llvm/test/Transforms/FunctionAttrs/nocapture.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/nocapture.ll
+++ llvm/test/Transforms/FunctionAttrs/nocapture.ll
@@ -3,7 +3,7 @@
 
 @g = global i32* null		; <i32**> [#uses=1]
 
-; CHECK: define i32* @c1(i32* readnone returned %q)
+; CHECK: define i32* @c1(i32* readnone returned "no-capture-maybe-returned" %q)
 define i32* @c1(i32* %q) {
 	ret i32* %q
 }
@@ -134,7 +134,7 @@
 	ret void
 }
 
-; CHECK: define void @test1_1(i8* nocapture readnone %x1_1, i8* %y1_1)
+; CHECK: define void @test1_1(i8* nocapture %x1_1, i8* nocapture %y1_1)
 ; It would be acceptable to add readnone to %y1_1 and %y1_2.
 define void @test1_1(i8* %x1_1, i8* %y1_1) {
   call i8* @test1_2(i8* %x1_1, i8* %y1_1)
@@ -142,7 +142,7 @@
   ret void
 }
 
-; CHECK: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* returned %y1_2)
+; CHECK: define i8* @test1_2(i8* nocapture %x1_2, i8* returned "no-capture-maybe-returned" %y1_2)
 define i8* @test1_2(i8* %x1_2, i8* %y1_2) {
   call void @test1_1(i8* %x1_2, i8* %y1_2)
   store i32* null, i32** @g
@@ -156,21 +156,21 @@
   ret void
 }
 
-; CHECK: define void @test3(i8* nocapture readnone %x3, i8* nocapture readnone %y3, i8* nocapture readnone %z3)
+; CHECK: define void @test3(i8* nocapture %x3, i8* nocapture readnone %y3, i8* nocapture %z3)
 define void @test3(i8* %x3, i8* %y3, i8* %z3) {
   call void @test3(i8* %z3, i8* %y3, i8* %x3)
   store i32* null, i32** @g
   ret void
 }
 
-; CHECK: define void @test4_1(i8* %x4_1)
+; CHECK: define void @test4_1(i8* nocapture readnone %x4_1)
 define void @test4_1(i8* %x4_1) {
   call i8* @test4_2(i8* %x4_1, i8* %x4_1, i8* %x4_1)
   store i32* null, i32** @g
   ret void
 }
 
-; CHECK: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned %y4_2, i8* nocapture readnone %z4_2)
+; CHECK: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned "no-capture-maybe-returned" %y4_2, i8* nocapture readnone %z4_2)
 define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2) {
   call void @test4_1(i8* null)
   store i32* null, i32** @g
Index: llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll
+++ llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll
@@ -6,21 +6,21 @@
 
 ; Function Attrs: argmemonly
 define i32* @given_argmem_infer_readnone(i32* %p) #0 {
-; CHECK: define i32* @given_argmem_infer_readnone(i32* readnone returned %p) #0 {
+; CHECK: define i32* @given_argmem_infer_readnone(i32* readnone returned "no-capture-maybe-returned" %p) #0 {
 entry:
   ret i32* %p
 }
 
 ; Function Attrs: inaccessiblememonly
 define i32* @given_inaccessible_infer_readnone(i32* %p) #1 {
-; CHECK: define i32* @given_inaccessible_infer_readnone(i32* readnone returned %p) #0 {
+; CHECK: define i32* @given_inaccessible_infer_readnone(i32* readnone returned "no-capture-maybe-returned" %p) #0 {
 entry:
   ret i32* %p
 }
 
 ; Function Attrs: inaccessiblemem_or_argmemonly
 define i32* @given_inaccessible_or_argmem_infer_readnone(i32* %p) #2 {
-; CHECK: define i32* @given_inaccessible_or_argmem_infer_readnone(i32* readnone returned %p) #0 {
+; CHECK: define i32* @given_inaccessible_or_argmem_infer_readnone(i32* readnone returned "no-capture-maybe-returned" %p) #0 {
 entry:
   ret i32* %p
 }
Index: llvm/test/Transforms/FunctionAttrs/arg_returned.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/arg_returned.ll
+++ llvm/test/Transforms/FunctionAttrs/arg_returned.ll
@@ -168,7 +168,7 @@
 
 ; TEST 2
 ;
-; BOTH: define dso_local double* @ptr_sink_r0(double* readnone returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; BOTH: define dso_local double* @ptr_sink_r0(double* readnone returned "no-capture-maybe-returned" %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
 ; BOTH: define dso_local double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]]
 ; BOTH: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r) [[NoInlineNoUnwindReadnoneUwtable]]
 ;
@@ -176,8 +176,8 @@
 ; FNATTR: define dso_local double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b) [[NoInlineNoUnwindReadnoneUwtable]]
 ; FNATTR: define dso_local double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r) [[NoInlineNoUnwindReadnoneUwtable]]
 ;
-; ATTRIBUTOR: define dso_local double* @ptr_sink_r0(double* returned %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
-; ATTRIBUTOR: define dso_local double* @ptr_scc_r1(double* %a, double* returned %r, double* %b) [[NoInlineNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @ptr_sink_r0(double* returned "no-capture-maybe-returned" %r) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @ptr_scc_r1(double* %a, double* returned %r, double* nocapture %b) [[NoInlineNoUnwindReadnoneUwtable]]
 ; ATTRIBUTOR: define dso_local double* @ptr_scc_r2(double* %a, double* %b, double* returned %r) [[NoInlineNoUnwindReadnoneUwtable]]
 ;
 ; double* ptr_scc_r1(double* a, double* b, double* r);
@@ -262,9 +262,9 @@
 ;   return *a ? a : ret0(ret0(ret0(...ret0(a)...)));
 ; }
 ;
-; FEW_IT:  define dso_local i32* @ret0(i32* %a)
+; FEW_IT:  define dso_local i32* @ret0(i32* "no-capture-maybe-returned" %a)
 ; FNATTR:  define dso_local i32* @ret0(i32* readonly %a) [[NoInlineNoUnwindUwtable:#[0-9]*]]
-; BOTH:    define dso_local i32* @ret0(i32* readonly returned %a) [[NoInlineNoReturnNoUnwindReadonlyUwtable:#[0-9]*]]
+; BOTH:    define dso_local i32* @ret0(i32* readonly returned "no-capture-maybe-returned" %a) [[NoInlineNoReturnNoUnwindReadonlyUwtable:#[0-9]*]]
 define dso_local i32* @ret0(i32* %a) #0 {
 entry:
   %v = load i32, i32* %a, align 4
@@ -303,9 +303,9 @@
 ;
 ; BOTH: declare void @unknown_fn(i32* (i32*)*) [[NoInlineNoUnwindUwtable:#[0-9]*]]
 ;
-; BOTH:       define dso_local i32* @calls_unknown_fn(i32* readnone returned %r) [[NoInlineNoUnwindUwtable]]
+; BOTH:       define dso_local i32* @calls_unknown_fn(i32* readnone returned "no-capture-maybe-returned" %r) [[NoInlineNoUnwindUwtable]]
 ; FNATTR:     define dso_local i32* @calls_unknown_fn(i32* readnone %r) [[NoInlineNoUnwindUwtable:#[0-9]*]]
-; ATTRIBUTOR: define dso_local i32* @calls_unknown_fn(i32* returned %r) [[NoInlineNoUnwindUwtable:#[0-9]*]]
+; ATTRIBUTOR: define dso_local i32* @calls_unknown_fn(i32* returned "no-capture-maybe-returned" %r) [[NoInlineNoUnwindUwtable:#[0-9]*]]
 ;
 declare void @unknown_fn(i32* (i32*)*) #0
 
@@ -413,8 +413,8 @@
 ; }
 ;
 ; FNATTR:     define dso_local double* @bitcast(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
-; ATTRIBUTOR: define dso_local double* @bitcast(i32* returned %b) [[NoInlineNoUnwindUwtable]]
-; BOTH:       define dso_local double* @bitcast(i32* readnone returned %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @bitcast(i32* returned "no-capture-maybe-returned" %b) [[NoInlineNoUnwindUwtable]]
+; BOTH:       define dso_local double* @bitcast(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
 ;
 define dso_local double* @bitcast(i32* %b) #0 {
 entry:
@@ -433,8 +433,8 @@
 ; }
 ;
 ; FNATTR:     define dso_local double* @bitcasts_select_and_phi(i32* readnone %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
-; ATTRIBUTOR: define dso_local double* @bitcasts_select_and_phi(i32* returned %b) [[NoInlineNoUnwindUwtable]]
-; BOTH:       define dso_local double* @bitcasts_select_and_phi(i32* readnone returned %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
+; ATTRIBUTOR: define dso_local double* @bitcasts_select_and_phi(i32* returned "no-capture-maybe-returned" %b) [[NoInlineNoUnwindUwtable]]
+; BOTH:       define dso_local double* @bitcasts_select_and_phi(i32* readnone returned "no-capture-maybe-returned" %b) [[NoInlineNoRecurseNoUnwindReadnoneUwtable]]
 ;
 define dso_local double* @bitcasts_select_and_phi(i32* %b) #0 {
 entry:
Index: llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
+++ llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
@@ -27,8 +27,7 @@
 ;   return p == 0;
 ; }
 ;
-; FIXME: no-capture missing for %p
-; CHECK: define dso_local i32 @is_null_return(i32* readnone %p)
+; CHECK: define dso_local i32 @is_null_return(i32* nocapture readnone %p)
 ;
 define dso_local i32 @is_null_return(i32* %p) #0 {
 entry:
@@ -47,8 +46,7 @@
 ;   return 0;
 ; }
 ;
-; FIXME: no-capture missing for %p
-; CHECK: define dso_local i32 @is_null_control(i32* readnone %p)
+; CHECK: define dso_local i32 @is_null_control(i32* nocapture readnone %p)
 ;
 define dso_local i32 @is_null_control(i32* %p) #0 {
 entry:
@@ -98,8 +96,6 @@
 ;   return srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(a))))))))))))))));
 ; }
 ;
-; FIXME: no-capture missing for %a
-; FIXME: returned missing for %a
 ; FIXME: We should *not* derive any attributes for the return value not present on the argument!
 ; CHECK: define dso_local noalias nonnull i32* @srec16(i32* nocapture readnone %a)
 ;        define dso_local i32* @srec16(i32* nocapture readnone %a)
@@ -127,15 +123,12 @@
 
 ; TEST 5:
 ;
-; FIXME: no-capture missing for %a
-; CHECK: define dso_local float* @scc_A(i32* readnone returned %a)
+; CHECK: define dso_local float* @scc_A(i32* readnone returned "no-capture-maybe-returned" %a)
 ;
-; FIXME: no-capture missing for %a
-; CHECK: define dso_local i64* @scc_B(double* readnone returned %a)
+; CHECK: define dso_local i64* @scc_B(double* readnone returned "no-capture-maybe-returned" %a)
 ;
 ; FIXME: readnone missing for %s
-; FIXME: no-capture missing for %a
-; CHECK: define dso_local i8* @scc_C(i16* returned %a)
+; CHECK: define dso_local i8* @scc_C(i16* returned "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);
@@ -264,7 +257,7 @@
 ; }
 ;
 ; There should *not* be a no-capture attribute on %a
-; CHECK: define dso_local i64* @not_captured_but_returned_0(i64* returned %a)
+; CHECK: define dso_local i64* @not_captured_but_returned_0(i64* returned "no-capture-maybe-returned" %a)
 ;
 define dso_local i64* @not_captured_but_returned_0(i64* %a) #0 {
 entry:
@@ -280,7 +273,7 @@
 ; }
 ;
 ; There should *not* be a no-capture attribute on %a
-; CHECK: define dso_local nonnull i64* @not_captured_but_returned_1(i64* %a)
+; CHECK: define dso_local nonnull i64* @not_captured_but_returned_1(i64* "no-capture-maybe-returned" %a)
 ;
 define dso_local i64* @not_captured_but_returned_1(i64* %a) #0 {
 entry:
@@ -296,8 +289,7 @@
 ;   not_captured_but_returned_1(a);
 ; }
 ;
-; FIXME: no-capture missing for %a
-; CHECK: define dso_local void @test_not_captured_but_returned_calls(i64* %a)
+; CHECK: define dso_local void @test_not_captured_but_returned_calls(i64* nocapture %a)
 ;
 define dso_local void @test_not_captured_but_returned_calls(i64* %a) #0 {
 entry:
@@ -313,7 +305,7 @@
 ; }
 ;
 ; There should *not* be a no-capture attribute on %a
-; CHECK: define dso_local i64* @negative_test_not_captured_but_returned_call_0a(i64* returned %a)
+; CHECK: define dso_local i64* @negative_test_not_captured_but_returned_call_0a(i64* returned "no-capture-maybe-returned" %a)
 ;
 define dso_local i64* @negative_test_not_captured_but_returned_call_0a(i64* %a) #0 {
 entry:
@@ -345,7 +337,7 @@
 ; }
 ;
 ; There should *not* be a no-capture attribute on %a
-; CHECK: define dso_local nonnull i64* @negative_test_not_captured_but_returned_call_1a(i64* %a)
+; CHECK: define dso_local nonnull i64* @negative_test_not_captured_but_returned_call_1a(i64* "no-capture-maybe-returned" %a)
 ;
 define dso_local i64* @negative_test_not_captured_but_returned_call_1a(i64* %a) #0 {
 entry:
Index: llvm/test/Transforms/FunctionAttrs/SCC1.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/SCC1.ll
+++ llvm/test/Transforms/FunctionAttrs/SCC1.ll
@@ -35,7 +35,8 @@
 ;   1 functionattrs - Number of functions marked as norecurse
 ;   6 functionattrs - Number of functions marked argmemonly
 ;   6 functionattrs - Number of functions marked as nounwind
-;  16 functionattrs - Number of arguments marked nocapture
+;  10 functionattrs - Number of arguments marked nocapture
+;   6 functionattrs - Number of arguments marked nocapture-maybe-returned
 ;   4 functionattrs - Number of arguments marked readnone
 ;   6 functionattrs - Number of arguments marked writeonly
 ;   6 functionattrs - Number of arguments marked readonly
@@ -45,7 +46,7 @@
 ;
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 
-; CHECK: define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0) #[[NOUNWIND:[0-9]*]]
+; CHECK: define dso_local i32* @external_ret2_nrw(i32* nocapture %n0, i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOUNWIND:[0-9]*]]
 define dso_local i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
   %call = call i32* @internal_ret0_nw(i32* %n0, i32* %w0)
@@ -55,7 +56,7 @@
   ret i32* %call3
 }
 
-; CHECK: define internal i32* @internal_ret0_nw(i32* returned %n0, i32* %w0) #[[NOUNWIND]]
+; CHECK: define internal i32* @internal_ret0_nw(i32* returned "no-capture-maybe-returned" %n0, i32* nocapture %w0) #[[NOUNWIND]]
 define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) {
 entry:
   %r0 = alloca i32, align 4
@@ -83,7 +84,7 @@
   ret i32* %retval.0
 }
 
-; CHECK: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0) #[[NOUNWIND]]
+; CHECK: define internal i32* @internal_ret1_rrw(i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %r1, i32* nocapture %w0) #[[NOUNWIND]]
 define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
 entry:
   %0 = load i32, i32* %r0, align 4
@@ -114,7 +115,7 @@
   ret i32* %retval.0
 }
 
-; CHECK: define dso_local i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned %w0) #[[NOREC_NOUNWIND:[0-9]*]]
+; CHECK: define dso_local i32* @external_sink_ret2_nrw(i32* nocapture readnone %n0, i32* nocapture readonly %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOREC_NOUNWIND:[0-9]*]]
 define dso_local i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
   %tobool = icmp ne i32* %n0, null
@@ -132,7 +133,7 @@
   ret i32* %w0
 }
 
-; CHECK: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0) #[[NOUNWIND]]
+; CHECK: define internal i32* @internal_ret1_rw(i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOUNWIND]]
 define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
 entry:
   %0 = load i32, i32* %r0, align 4
@@ -157,7 +158,7 @@
   ret i32* %retval.0
 }
 
-; CHECK: define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0) #[[NOUNWIND]]
+; CHECK: define dso_local i32* @external_source_ret2_nrw(i32* nocapture %n0, i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %w0) #[[NOUNWIND]]
 define dso_local i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
   %call = call i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0)
Index: llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll
+++ llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll
@@ -15,7 +15,7 @@
 	ret i32* %tmp
 }
 
-; CHECK: define i32* @c(i32* readnone returned %r)
+; CHECK: define i32* @c(i32* readnone returned "no-capture-maybe-returned" %r)
 @g = global i32 0
 define i32* @c(i32 *%r) {
 	%a = icmp eq i32* %r, null
Index: llvm/lib/Transforms/IPO/Attributor.cpp
===================================================================
--- llvm/lib/Transforms/IPO/Attributor.cpp
+++ llvm/lib/Transforms/IPO/Attributor.cpp
@@ -20,6 +20,7 @@
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/CallGraph.h"
 #include "llvm/Analysis/CallGraphSCCPass.h"
+#include "llvm/Analysis/CaptureTracking.h"
 #include "llvm/Analysis/GlobalsModRef.h"
 #include "llvm/IR/Argument.h"
 #include "llvm/IR/Attributes.h"
@@ -45,6 +46,9 @@
 
 STATISTIC(NumFnNoReturn, "Number of functions marked noreturn");
 
+STATISTIC(NumFnArgumentNoCapture,
+          "Number of function arguments marked no-capture");
+
 // TODO: Determine a good default value.
 static cl::opt<unsigned>
     MaxFixpointIterations("attributor-max-iterations", cl::Hidden,
@@ -108,10 +112,15 @@
 /// Helper to adjust the statistics.
 static void bookkeeping(AbstractAttribute::ManifestPosition MP,
                         const Attribute &Attr) {
+  if (!Attr.isEnumAttribute())
+    return;
   switch (Attr.getKindAsEnum()) {
   case Attribute::Returned:
     NumFnArgumentReturned++;
     return;
+  case Attribute::NoCapture:
+    NumFnArgumentNoCapture++;
+    return;
   case Attribute::NoReturn:
     NumFnNoReturn++;
     return;
@@ -192,7 +201,7 @@
   case MP_ARGUMENT: {
     Argument &Arg = *cast<Argument>(getAssociatedValue());
     for (const Attribute &Attr : Attrs) {
-      if (Arg.hasAttribute(Attr.getKindAsEnum())) {
+      if (Attr.isEnumAttribute() && Arg.hasAttribute(Attr.getKindAsEnum())) {
         const Attribute &ExistingAttr =
             Arg.getParent()->getAttributes().getParamAttr(Arg.getArgNo(),
                                                           Attr.getKindAsEnum());
@@ -622,6 +631,280 @@
   }
 };
 
+/// ----------------------- Variable Capturing ---------------------------------
+
+/// A class to hold the state of for no-capture attributes.
+struct AANoCaptureImpl : public AANoCapture, IntegerState<char, 0, 3> {
+
+  /// State encoding bits. A set bit in the state means the property holds.
+  /// BEST_STATE is the best possible state, 0 the worst possible state.
+  enum {
+    NOT_CAPTURED = 1 << 0,
+    NOT_RETURNED = 1 << 1,
+
+    BEST_STATE = NOT_CAPTURED | NOT_RETURNED
+  };
+
+  /// Constructor that takes the value this attribute is associated with (\p V)
+  /// as well as the function this attribute is related to.
+  AANoCaptureImpl(Value &V, Function &F) : AANoCapture(V) {
+    assert(getAssumed() == BEST_STATE);
+
+    // If the value in questions is unused it is not captured.
+    if (V.getNumUses() == 0)
+      indicateFixpoint(/* Optimistic */ true);
+
+    // If the enclosing function cannot capture state, nothing is captured.
+    if (!canFunctionCaptureState(F))
+      indicateFixpoint(/* Optimistic */ true);
+  }
+
+  /// See AANoCapture::isKnownNoCapture().
+  bool isKnownNoCapture() const override { return getKnown() == BEST_STATE; }
+
+  /// See AANoCapture::isAssumedNoCapture(...).
+  bool isAssumedNoCapture() const override {
+    return getAssumed() == BEST_STATE;
+  }
+
+  /// See AANoCapture::isKnownNoCaptureMaybeReturned(...).
+  bool isKnownNoCaptureMaybeReturned() const override {
+    return getKnown() & NOT_CAPTURED;
+  }
+
+  /// See AANoCapture::isAssumedNoCaptureMaybeReturned(...).
+  bool isAssumedNoCaptureMaybeReturned() const override {
+    return getAssumed() & NOT_CAPTURED;
+  }
+
+  /// see AbstractAttribute::isAssumedNoCaptureMaybeReturned(...).
+  virtual void
+  getDeducedAttributes(SmallVectorImpl<Attribute> &Attrs) const override {
+    assert(isAssumedNoCaptureMaybeReturned());
+
+    LLVMContext &Ctx = AnchoredVal.getContext();
+    if (isAssumedNoCapture())
+      Attrs.emplace_back(Attribute::get(Ctx, Attribute::NoCapture));
+    else
+      Attrs.emplace_back(Attribute::get(Ctx, "no-capture-maybe-returned"));
+  }
+
+  /// Return true if the function \p F is generally able to capture state.
+  static bool canFunctionCaptureState(Function &F) {
+    // A function cannot capture state if it only reads memory and has no other
+    // way of communication (exceptions and return values).
+    if (F.onlyReadsMemory() && F.doesNotThrow() &&
+        F.getReturnType()->isVoidTy())
+      return false;
+
+    return true;
+  }
+
+  /// See AbstractAttribute::getState()
+  ///{
+  AbstractState &getState() override { return *this; }
+  const AbstractState &getState() const override { return *this; }
+  ///}
+
+  /// See AbstractState::getAsStr().
+  const std::string getAsStr() const override {
+    if (isKnownNoCapture())
+      return "known not-captured";
+    if (isAssumedNoCapture())
+      return "assumed not-captured";
+    if (isKnownNoCaptureMaybeReturned())
+      return "known not-captured-maybe-returned";
+    if (isAssumedNoCaptureMaybeReturned())
+      return "assumed not-captured-maybe-returned";
+    return "assumed-captured";
+  }
+};
+
+/// Attributor-aware capture tracker.
+struct AACaptureUseTracker final : public CaptureTracker {
+
+  /// Create a capture tracker that can lookup in-flight abstract attributes
+  /// through the attributor \p A. If a use leads to a potential capture,
+  /// \p AssumedNoCapture is set to false and the search is stopped. If a use
+  /// leads to a return instruction, \p MaybeReturned is set to true and
+  /// \p AssumedNoCapture is not changed. If a use is found that is currently
+  /// assumed "no-capture-maybe-returned", the user is added to the
+  /// \p PotentialCopies set. All values in \p PotentialCopies are later tracked
+  /// as well. For every explored use we decrement \p RemainingUsesToExplore.
+  /// Once it reaches 0, the search is stopped and \p AssumedNoCapture is set to
+  /// false.
+  AACaptureUseTracker(Attributor &A, AbstractAttribute &QueryingAA,
+                      bool &AssumedNoCapture, bool &MaybeReturned,
+                      SmallVectorImpl<Value *> &PotentialCopies,
+                      unsigned &RemainingUsesToExplore)
+      : A(A), QueryingAA(QueryingAA), AssumedNoCapture(AssumedNoCapture),
+        MaybeReturned(MaybeReturned), PotentialCopies(PotentialCopies),
+        RemainingUsesToExplore(RemainingUsesToExplore) {}
+
+  /// See CaptureTracker::tooManyUses().
+  void tooManyUses() override { AssumedNoCapture = false; }
+
+  /// See CaptureTracker::captured(...).
+  bool captured(const Use *U) override {
+    LLVM_DEBUG(errs() << "Check use: " << *U->get() << " in " << *U->getUser()
+                      << "\n");
+
+    // Because we may reuse the tracker multiple times we keep track of the
+    // number of explored uses ourselves as well.
+    if (RemainingUsesToExplore-- == 0) {
+      LLVM_DEBUG(errs() << " - too many uses to explore\n");
+      return shouldContinueTraversal(false);
+    }
+
+    // Explicitly catch return instructions.
+    if (isa<ReturnInst>(U->getUser()))
+      return shouldContinueTraversal(true, /* Returns */ true);
+
+    // Special case for comparisons agains null. We consider them as
+    // non-capturing.
+    // TODO: This should only be valid if 0 is not a valid address.
+    if (ICmpInst *ICmp = dyn_cast<ICmpInst>(U->getUser())) {
+      unsigned Idx = (ICmp->getOperand(0) == U->get());
+      if (isa<Constant>(ICmp->getOperand(Idx)))
+        if (cast<Constant>(ICmp->getOperand(Idx))->isNullValue())
+          return shouldContinueTraversal(true);
+      return shouldContinueTraversal(false);
+    }
+
+    // For now we only use special logic for call sites. However, the tracker
+    // itself knows about a lot of other non-capturing cases already.
+    CallSite CS(U->getUser());
+    if (!CS || !CS.isArgOperand(U))
+      return shouldContinueTraversal(false);
+
+    // TODO: This might be checked in the tracker already.
+    unsigned ArgNo = CS.getArgumentNo(U);
+    if (CS.paramHasAttr(ArgNo, Attribute::NoCapture))
+      return shouldContinueTraversal(true);
+
+    // If we do not know the called function we have to assume the use captures.
+    if (!CS.getCalledFunction())
+      return shouldContinueTraversal(false);
+
+    // If the called function cannot capture state, nothing is captured.
+    Function &F = *CS.getCalledFunction();
+    if (!AANoCaptureImpl::canFunctionCaptureState(F))
+      return shouldContinueTraversal(true);
+
+    // Handle var-arg functions.
+    if (F.isVarArg() && F.arg_size() <= ArgNo)
+      return shouldContinueTraversal(false);
+
+    // If we have a abstract no-capture attribute for the argument we can use it
+    // to justify a non-capture attribute here. This allows recursion!
+    auto *ArgNoCaptureAA = A.getAAFor<AANoCapture>(QueryingAA, F, ArgNo);
+    if (ArgNoCaptureAA) {
+      if (ArgNoCaptureAA->isAssumedNoCapture())
+        return shouldContinueTraversal(true);
+      if (ArgNoCaptureAA->isAssumedNoCaptureMaybeReturned()) {
+        PotentialCopies.push_back(CS.getInstruction());
+        return shouldContinueTraversal(true);
+      }
+    }
+
+    // Check for an existing attribute to justify no-capture for this use.
+    if (F.getAttributes().hasParamAttr(ArgNo, "no-capture-maybe-returned")) {
+      PotentialCopies.push_back(CS.getInstruction());
+      return shouldContinueTraversal(true);
+    }
+
+    // Lastly, we could not find a reason no-capture can be assumed so we don't.
+    return shouldContinueTraversal(false);
+  }
+
+  /// See CaptureTracker::shouldExplore(...).
+  bool shouldExplore(const Use *U) override { return true; }
+
+  /// Update the state according to \p ShouldContinue and return the appropriate
+  /// value for use in the CaptureTracker::captured() interface. The \p Returns
+  /// flag indicate if the use allows the value to be "returned" out of the
+  /// current scope.
+  bool shouldContinueTraversal(bool ShouldContinue, bool Returns = false) {
+    LLVM_DEBUG(dbgs() << " - " << (ShouldContinue ? "will not" : "might")
+                      << " capture" << (Returns ? ", but may return\n" : "\n"));
+    AssumedNoCapture = ShouldContinue;
+    MaybeReturned |= Returns;
+    return !AssumedNoCapture;
+  }
+
+private:
+  /// The attributor providing in-flight abstract attributes.
+  Attributor &A;
+
+  /// The abstract attribute currently updated.
+  AbstractAttribute &QueryingAA;
+
+  /// The current state tracking potential capturing.
+  bool &AssumedNoCapture;
+
+  /// The current state tracking potential use as return value.
+  bool &MaybeReturned;
+
+  /// Set of potential copies of the tracked value.
+  SmallVectorImpl<Value *> &PotentialCopies;
+
+  /// Global counter to limit the number of explored uses.
+  unsigned &RemainingUsesToExplore;
+};
+
+/// An AA to represent the no-capture argument attribute.
+struct AANoCaptureArgument final : public AANoCaptureImpl {
+
+  /// See AANoCaptureImpl::AANoCaptureImpl(...).
+  AANoCaptureArgument(Argument &Arg) : AANoCaptureImpl(Arg, *Arg.getParent()) {
+    if (Arg.hasAttribute(Attribute::NoCapture))
+      indicateFixpoint(/* Optimistic */ true);
+  }
+
+  /// See AbstractAttribute::updateImpl(Attributor &A).
+  virtual ChangeStatus updateImpl(Attributor &A) override;
+
+  /// See AbstractAttribute::getManifestPosition().
+  virtual ManifestPosition getManifestPosition() const override {
+    return MP_ARGUMENT;
+  }
+};
+
+ChangeStatus AANoCaptureArgument::updateImpl(Attributor &A) {
+  // The current assumed state used to determine a change.
+  auto AssumedState = getAssumed();
+
+  bool MaybeReturned = false;
+  bool AssumedNoCapture = true;
+  SmallVector<Value *, 4> PotentialCopies;
+  unsigned RemainingUsesToExplore = DefaultMaxUsesToExplore;
+
+  // Use the CaptureTracker interface and logic with the specialized tracker,
+  // defined in AACaptureUseTracker, that can look at in-flight abstract
+  // attributes and directly updates the assumed state.
+  AACaptureUseTracker Tracker(A, *this, AssumedNoCapture, MaybeReturned,
+                              PotentialCopies, RemainingUsesToExplore);
+
+  unsigned Idx = 0;
+  // Check all potential copies of the associated value until we can assume none
+  // will be captured or we have to assume at least one might be.
+  PotentialCopies.push_back(getAssociatedValue());
+  while (AssumedNoCapture && Idx < PotentialCopies.size())
+    PointerMayBeCaptured(PotentialCopies[Idx++], &Tracker);
+
+  // Since we ignored "returns" of the associated value or any of the potential
+  // copies, we can give up if we cannot assume no-capture anymore. If however
+  // no-capture can be assumed we still have to track if the value, or any
+  // potential copy, might be returned.
+  if (!AssumedNoCapture)
+    indicateFixpoint(/* Optimistic */ false);
+  else if (MaybeReturned)
+    Assumed = Assumed & ~NOT_RETURNED;
+
+  return (AssumedState == getAssumed()) ? ChangeStatus::UNCHANGED
+                                        : ChangeStatus::CHANGED;
+}
+
 /// ----------------------------------------------------------------------------
 ///                               Attributor
 /// ----------------------------------------------------------------------------
@@ -805,6 +1088,15 @@
   // Every function might be "no-return".
   registerAA(*new AANoReturnFunction(F));
 
+  // For each argument we check if we can derive attributes.
+  for (Argument &Arg : F.args()) {
+
+    // So far only pointer arguments are interesting. However, "returned"
+    // is also derived but as a "function return attribute" (see above).
+    if (Arg.getType()->isPointerTy())
+      registerAA(*new AANoCaptureArgument(Arg));
+  }
+
   // Walk all instructions to find more attribute opportunities and also
   // interesting instructions that might be querried by abstract attributes
   // during their initialziation or update.
Index: llvm/include/llvm/Transforms/IPO/Attributor.h
===================================================================
--- llvm/include/llvm/Transforms/IPO/Attributor.h
+++ llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -252,6 +252,35 @@
   static constexpr Attribute::AttrKind ID = Attribute::Returned;
 };
 
+/// An abstract interface for all nocapture attributes.
+struct AANoCapture : public AbstractAttribute {
+
+  /// See AbstractAttribute::AbstractAttribute(...).
+  AANoCapture(Value &V) : AbstractAttribute(V) {}
+
+  /// Return true if we know that the underlying value is not captured in its
+  /// respective scope.
+  virtual bool isKnownNoCapture() const = 0;
+
+  /// Return true if we assume that the underlying value is not captured in its
+  /// respective scope.
+  virtual bool isAssumedNoCapture() const = 0;
+
+  /// Return true if we know that the underlying value is not captured in its
+  /// respective scope but we allow it to escape through a "return".
+  virtual bool isKnownNoCaptureMaybeReturned() const = 0;
+
+  /// Return true if we assume that the underlying value is not captured in its
+  /// respective scope but we allow it to escape through a "return".
+  virtual bool isAssumedNoCaptureMaybeReturned() const = 0;
+
+  /// See AbstractState::getAttrKind().
+  Attribute::AttrKind getAttrKind() const override { return ID; }
+
+  /// The identifier used by the Attributor for this class of attributes.
+  static constexpr Attribute::AttrKind ID = Attribute::NoCapture;
+};
+
 /// ----------------------------------------------------------------------------
 ///                       Pass (Manager) Boilerplate
 /// ----------------------------------------------------------------------------
Index: clang/test/CodeGenOpenCL/as_type.cl
===================================================================
--- clang/test/CodeGenOpenCL/as_type.cl
+++ clang/test/CodeGenOpenCL/as_type.cl
@@ -67,7 +67,7 @@
   return __builtin_astype(x, int3);
 }
 
-//CHECK: define spir_func i32 addrspace(1)* @addr_cast(i32* readnone returned %[[x:.*]])
+//CHECK: define spir_func i32 addrspace(1)* @addr_cast(i32* readnone returned "no-capture-maybe-returned" %[[x:.*]])
 //CHECK: %[[cast:.*]] = addrspacecast i32* %[[x]] to i32 addrspace(1)*
 //CHECK: ret i32 addrspace(1)* %[[cast]]
 global int* addr_cast(int *x) {
Index: clang/test/CodeGenObjC/os_log.m
===================================================================
--- clang/test/CodeGenObjC/os_log.m
+++ clang/test/CodeGenObjC/os_log.m
@@ -14,7 +14,7 @@
 #ifdef __x86_64__
 // CHECK-LABEL: define i8* @test_builtin_os_log
 // CHECK-O0-LABEL: define i8* @test_builtin_os_log
-// CHECK: (i8* returned %[[BUF:.*]])
+// CHECK: (i8* returned "no-capture-maybe-returned" %[[BUF:.*]])
 // CHECK-O0: (i8* %[[BUF:.*]])
 void *test_builtin_os_log(void *buf) {
   return __builtin_os_log_format(buf, "capabilities: %@", GenString());
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to