jdoerfert updated this revision to Diff 208811.
jdoerfert added a comment.
Herald added a subscriber: jfb.

Rebase


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D59922/new/

https://reviews.llvm.org/D59922

Files:
  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/arg_nocapture.ll
  llvm/test/Transforms/FunctionAttrs/arg_returned.ll
  llvm/test/Transforms/FunctionAttrs/convergent.ll
  llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll
  llvm/test/Transforms/FunctionAttrs/nocapture.ll
  llvm/test/Transforms/FunctionAttrs/nonnull.ll
  llvm/test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll
  llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll
  llvm/test/Transforms/FunctionAttrs/readattrs.ll
  llvm/test/Transforms/FunctionAttrs/readnone.ll

Index: llvm/test/Transforms/FunctionAttrs/readnone.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/readnone.ll
+++ llvm/test/Transforms/FunctionAttrs/readnone.ll
@@ -1,13 +1,17 @@
-; RUN: opt < %s -functionattrs -S | FileCheck %s
-; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+; RUN: opt < %s -attributor-disable=false -attributor -functionattrs -S | FileCheck %s
+; RUN: opt < %s -attributor-disable=false -passes="attributor,cgscc(function-attrs)" -S | FileCheck %s
 
-; CHECK: define void @bar(i8* nocapture readnone)
+; FIXME: Because nocapture and readnone deduction in the functionattrs pass are interleaved, it doesn't
+;        trigger when nocapture is already present. Once the Attributor derives memory behavior,
+;        this should be fixed.
+; FIXME: readnone missing for %0 two times
+; CHECK: define void @bar(i8* nocapture readonly)
 define void @bar(i8* readonly) {
   call void @foo(i8* %0)
     ret void
 }
 
-; CHECK: define void @foo(i8* nocapture readnone)
+; CHECK: define void @foo(i8* nocapture readonly)
 define void @foo(i8* readonly) {
   call void @bar(i8* %0)
   ret void
Index: llvm/test/Transforms/FunctionAttrs/readattrs.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/readattrs.ll
+++ llvm/test/Transforms/FunctionAttrs/readattrs.ll
@@ -1,5 +1,5 @@
-; RUN: opt < %s -functionattrs -S | FileCheck %s
-; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs)' -S | FileCheck %s
+; RUN: opt < %s  -attributor -attributor-disable=false -functionattrs -S | FileCheck %s
+; RUN: opt < %s  -attributor-disable=false -aa-pipeline=basic-aa -passes='attributor,cgscc(function-attrs)' -S | FileCheck %s
 @x = global i32 0
 
 declare void @test1_1(i8* %x1_1, i8* readonly %y1_1, ...)
@@ -13,7 +13,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
@@ -55,13 +55,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/read_write_returned_arguments_scc.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll
+++ llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll
@@ -1,5 +1,5 @@
-; RUN: opt -functionattrs -enable-nonnull-arg-prop -attributor -attributor-disable=false -S < %s | FileCheck %s
-; RUN: opt -functionattrs -enable-nonnull-arg-prop -attributor -attributor-disable=false -attributor-verify=true -S < %s | FileCheck %s
+; RUN: opt -attributor -functionattrs -enable-nonnull-arg-prop -attributor-disable=false -S < %s | FileCheck %s
+; RUN: opt -attributor -functionattrs -enable-nonnull-arg-prop -attributor-disable=false -attributor-verify=true -S < %s | FileCheck %s
 ;
 ; This is an evolved example to stress test SCC parameter attribute propagation.
 ; The SCC in this test is made up of the following six function, three of which
@@ -18,20 +18,23 @@
 ; as well as how the parameters are (transitively) used (n = readnone,
 ; r = readonly, w = writeonly).
 ;
-; What we should see is something along the lines of:
-;   1 - Number of functions marked as norecurse
-;   6 - Number of functions marked argmemonly
-;   6 - Number of functions marked as nounwind
-;  16 - Number of arguments marked nocapture
-;   4 - Number of arguments marked readnone
-;   6 - Number of arguments marked writeonly
-;   6 - Number of arguments marked readonly
-;   6 - Number of arguments marked returned
+; What we should get is something along the lines of:
+;   1 functionattrs - Number of functions marked as norecurse
+;   6 functionattrs - Number of functions marked argmemonly
+;   6 functionattrs - Number of functions marked as nounwind
+;  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
+;   6 functionattrs - Number of arguments marked returned
+;  --
+;  51
 ;
 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
 
 ; CHECK: Function Attrs: nofree nounwind
-; CHECK-NEXT: define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
+; CHECK-NEXT: define i32* @external_ret2_nrw(i32* nocapture nonnull %n0, i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %w0)
 define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
   %call = call i32* @internal_ret0_nw(i32* %n0, i32* %w0)
@@ -42,8 +45,8 @@
 }
 
 ; CHECK: Function Attrs: nofree nounwind
-; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* returned %n0, i32* %w0)
-define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) {
+; CHECK-NEXT: define internal nonnull i32* @internal_ret0_nw(i32* returned dereferenceable(1) "no-capture-maybe-returned" %n0, i32* nocapture %w0)
+define internal i32* @internal_ret0_nw(i32* dereferenceable(1) %n0, i32* %w0) {
 entry:
   %r0 = alloca i32, align 4
   %r1 = alloca i32, align 4
@@ -71,7 +74,7 @@
 }
 
 ; CHECK: Function Attrs: nofree nounwind
-; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* %r0, i32* returned %r1, i32* %w0)
+; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %r1, i32* nocapture %w0)
 define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
 entry:
   %0 = load i32, i32* %r0, align 4
@@ -103,8 +106,8 @@
 }
 
 ; CHECK: Function Attrs: nofree norecurse nounwind
-; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned %w0)
-define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
+; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* nocapture readnone dereferenceable(1) %n0, i32* nocapture readonly %r0, i32* returned "no-capture-maybe-returned" %w0)
+define i32* @external_sink_ret2_nrw(i32* dereferenceable(1) %n0, i32* %r0, i32* %w0) {
 entry:
   %tobool = icmp ne i32* %n0, null
   br i1 %tobool, label %if.end, label %if.then
@@ -122,8 +125,8 @@
 }
 
 ; CHECK: Function Attrs: nofree nounwind
-; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* %r0, i32* returned %w0)
-define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
+; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* nocapture dereferenceable(1) %r0, i32* returned "no-capture-maybe-returned" %w0)
+define internal i32* @internal_ret1_rw(i32* dereferenceable(1) %r0, i32* %w0) {
 entry:
   %0 = load i32, i32* %r0, align 4
   %tobool = icmp ne i32 %0, 0
@@ -148,7 +151,7 @@
 }
 
 ; CHECK: Function Attrs: nofree nounwind
-; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* returned %w0)
+; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* nocapture nonnull %n0, i32* nocapture %r0, i32* returned "no-capture-maybe-returned" %w0)
 define 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/out-of-bounds-iterator-bug.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll
+++ llvm/test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll
@@ -1,5 +1,5 @@
-; RUN: opt -functionattrs -S < %s | FileCheck %s
-; RUN: opt -passes=function-attrs -S < %s | FileCheck %s
+; RUN: opt < %s -attributor-disable=false -attributor -functionattrs -S | FileCheck %s
+; RUN: opt < %s -attributor-disable=false -passes="attributor,cgscc(function-attrs)" -S | FileCheck %s
 
 ; This checks for an iterator wraparound bug in FunctionAttrs.  The previous
 ; "incorrect" behavior was inferring readonly for the %x argument in @caller.
Index: llvm/test/Transforms/FunctionAttrs/nonnull.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/nonnull.ll
+++ llvm/test/Transforms/FunctionAttrs/nonnull.ll
@@ -1,5 +1,5 @@
-; RUN: opt -S -functionattrs -enable-nonnull-arg-prop %s | FileCheck %s
-; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s
+; RUN: opt -S -attributor-disable=false -attributor -functionattrs -enable-nonnull-arg-prop %s | FileCheck %s
+; RUN: opt -S -attributor-disable=false -passes="attributor,cgscc(function-attrs)" -enable-nonnull-arg-prop %s | FileCheck %s
 
 declare nonnull i8* @ret_nonnull()
 
Index: llvm/test/Transforms/FunctionAttrs/nocapture.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/nocapture.ll
+++ llvm/test/Transforms/FunctionAttrs/nocapture.ll
@@ -1,9 +1,11 @@
-; RUN: opt < %s -functionattrs -S | FileCheck %s
-; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+; RUN: opt < %s -attributor-disable=false -attributor -S | FileCheck %s --check-prefix=ATTRIBUTOR
+; RUN: opt < %s -attributor-disable=false -attributor -functionattrs -S | FileCheck %s
+; RUN: opt < %s -attributor-disable=false -passes='attributor,cgscc(function-attrs)' -S | FileCheck %s --check-prefix=NPM
 
 @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)
+; ATTRIBUTOR: define i32* @c1(i32* returned "no-capture-maybe-returned" %q)
 define i32* @c1(i32* %q) {
 	ret i32* %q
 }
@@ -94,6 +96,7 @@
 }
 
 ; CHECK: define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* nocapture %p, i1 %b)
+; ATTRIBUTOR: define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* nocapture %p, i1 %b)
 define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* %p, i1 %b) {
 e:
 	br label %l
@@ -109,32 +112,42 @@
 }
 
 ; CHECK: define void @nc2(i32* nocapture %p, i32* %q)
+; ATTRIBUTOR: define void @nc2(i32* nocapture %p, i32* %q)
 define void @nc2(i32* %p, i32* %q) {
 	%1 = call i32 @nc1(i32* %q, i32* %p, i1 0)		; <i32> [#uses=0]
 	ret void
 }
 
 ; CHECK: define void @nc3(void ()* nocapture %p)
+; ATTRIBUTOR: define void @nc3(void ()* nocapture %p)
 define void @nc3(void ()* %p) {
 	call void %p()
 	ret void
 }
 
 declare void @external(i8*) readonly nounwind
-; CHECK: define void @nc4(i8* nocapture readonly %p)
+
+; FIXME: Because nocapture and readonly deduction in the functionattrs pass are interleaved, it doesn't
+;        trigger when nocapture is already present. Once the Attributor derives memory behavior,
+;        this should be fixed.
+; CHECK: define void @nc4(i8* nocapture %p)
+; ATTRIBUTOR: define void @nc4(i8* nocapture %p)
+; NPM: define void @nc4(i8* nocapture readonly %p)
 define void @nc4(i8* %p) {
 	call void @external(i8* %p)
 	ret void
 }
 
 ; CHECK: define void @nc5(void (i8*)* nocapture %f, i8* nocapture %p)
+; ATTRIBUTOR: define void @nc5(void (i8*)* nocapture %f, i8* nocapture %p)
 define void @nc5(void (i8*)* %f, i8* %p) {
 	call void %f(i8* %p) readonly nounwind
 	call void %f(i8* nocapture %p)
 	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)
+; ATTRIBUTOR: 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 +155,8 @@
   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)
+; ATTRIBUTOR: 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
@@ -150,27 +164,31 @@
 }
 
 ; CHECK: define void @test2(i8* nocapture readnone %x2)
+; ATTRIBUTOR: define void @test2(i8* nocapture %x2)
 define void @test2(i8* %x2) {
   call void @test2(i8* %x2)
   store i32* null, i32** @g
   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)
+; ATTRIBUTOR: define void @test3(i8* nocapture %x3, i8* nocapture %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)
+; ATTRIBUTOR: define void @test4_1(i8* nocapture %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)
+; ATTRIBUTOR: define i8* @test4_2(i8* nocapture %x4_2, i8* returned "no-capture-maybe-returned" %y4_2, i8* nocapture %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
@@ -189,6 +207,7 @@
 declare void @test6_1(i8* %x6_1, i8* nocapture %y6_1, ...)
 
 ; CHECK: define void @test6_2(i8* %x6_2, i8* nocapture %y6_2, i8* %z6_2)
+; ATTRIBUTOR: define void @test6_2(i8* %x6_2, i8* nocapture %y6_2, i8* %z6_2)
 define void @test6_2(i8* %x6_2, i8* %y6_2, i8* %z6_2) {
   call void (i8*, i8*, ...) @test6_1(i8* %x6_2, i8* %y6_2, i8* %z6_2)
   store i32* null, i32** @g
@@ -196,18 +215,21 @@
 }
 
 ; CHECK: define void @test_cmpxchg(i32* nocapture %p)
+; ATTRIBUTOR: define void @test_cmpxchg(i32* nocapture %p)
 define void @test_cmpxchg(i32* %p) {
   cmpxchg i32* %p, i32 0, i32 1 acquire monotonic
   ret void
 }
 
 ; CHECK: define void @test_cmpxchg_ptr(i32** nocapture %p, i32* %q)
+; ATTRIBUTOR: define void @test_cmpxchg_ptr(i32** nocapture %p, i32* %q)
 define void @test_cmpxchg_ptr(i32** %p, i32* %q) {
   cmpxchg i32** %p, i32* null, i32* %q acquire monotonic
   ret void
 }
 
 ; CHECK: define void @test_atomicrmw(i32* nocapture %p)
+; ATTRIBUTOR: define void @test_atomicrmw(i32* nocapture %p)
 define void @test_atomicrmw(i32* %p) {
   atomicrmw add i32* %p, i32 1 seq_cst
   ret void
@@ -222,6 +244,7 @@
 }
 
 ; CHECK: nocaptureLaunder(i8* nocapture %p)
+; ATTRIBUTOR: nocaptureLaunder(i8* nocapture %p)
 define void @nocaptureLaunder(i8* %p) {
 entry:
   %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p)
@@ -238,6 +261,7 @@
 }
 
 ; CHECK: @nocaptureStrip(i8* nocapture %p)
+; ATTRIBUTOR: @nocaptureStrip(i8* nocapture %p)
 define void @nocaptureStrip(i8* %p) {
 entry:
   %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
@@ -260,6 +284,7 @@
 }
 
 ; CHECK: define i1 @nocaptureInboundsGEPICmp(i32* nocapture readnone %x)
+; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmp(i32* nocapture %x)
 define i1 @nocaptureInboundsGEPICmp(i32* %x) {
   %1 = getelementptr inbounds i32, i32* %x, i32 5
   %2 = bitcast i32* %1 to i8*
@@ -268,6 +293,7 @@
 }
 
 ; CHECK: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture readnone dereferenceable_or_null(4) %x)
+; ATTRIBUTOR: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture dereferenceable_or_null(4) %x)
 define i1 @nocaptureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) {
   %1 = bitcast i32* %x to i8*
   %2 = icmp eq i8* %1, null
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
@@ -1,26 +1,26 @@
-; RUN: opt -S -o - -functionattrs %s | FileCheck %s
-; RUN: opt -S -o - -passes=function-attrs %s | FileCheck %s
+; RUN: opt -S -o - -attributor -attributor-disable=false -functionattrs %s | FileCheck %s
+; RUN: opt -S -o - -attributor-disable=false -passes="attributor,cgscc(function-attrs)" %s | FileCheck %s
 
 ; Verify we remove argmemonly/inaccessiblememonly/inaccessiblemem_or_argmemonly
 ; function attributes when we derive readnone.
 
 ; 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/convergent.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/convergent.ll
+++ llvm/test/Transforms/FunctionAttrs/convergent.ll
@@ -1,8 +1,8 @@
 ; FIXME: convert CHECK-INDIRECT into CHECK (and remove -check-prefixes) as soon
 ; FIXME: as new-pass-manager's handling of indirect_non_convergent_call is fixed
 ;
-; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-INDIRECT
-; RUN: opt -passes=function-attrs -S < %s | FileCheck %s
+; RUN: opt -attributor-disable=false -attributor -functionattrs -S < %s | FileCheck %s --check-prefixes=CHECK,CHECK-INDIRECT
+; RUN: opt -attributor-disable=false -passes="attributor,cgscc(function-attrs)" -S < %s | FileCheck %s
 
 ; CHECK: Function Attrs
 ; CHECK-NOT: convergent
Index: llvm/test/Transforms/FunctionAttrs/arg_returned.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/arg_returned.ll
+++ llvm/test/Transforms/FunctionAttrs/arg_returned.ll
@@ -160,7 +160,7 @@
 ; TEST SCC test returning a pointer value argument
 ;
 ; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
-; BOTH-NEXT: define double* @ptr_sink_r0(double* readnone returned %r)
+; BOTH-NEXT: define double* @ptr_sink_r0(double* readnone returned "no-capture-maybe-returned" %r)
 ; BOTH: Function Attrs: noinline nounwind readnone uwtable
 ; BOTH-NEXT: define double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b)
 ; BOTH: Function Attrs: noinline nounwind readnone uwtable
@@ -170,8 +170,8 @@
 ; FNATTR: define double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b)
 ; FNATTR: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r)
 ;
-; ATTRIBUTOR: define double* @ptr_sink_r0(double* returned %r)
-; ATTRIBUTOR: define double* @ptr_scc_r1(double* %a, double* returned %r, double* %b)
+; ATTRIBUTOR: define double* @ptr_sink_r0(double* returned "no-capture-maybe-returned" %r)
+; ATTRIBUTOR: define double* @ptr_scc_r1(double* %a, double* returned %r, double* nocapture %b)
 ; ATTRIBUTOR: define double* @ptr_scc_r2(double* %a, double* %b, double* returned %r)
 ;
 ; double* ptr_scc_r1(double* a, double* b, double* r);
@@ -259,7 +259,7 @@
 ; FIXME: no-return missing
 ; FNATTR:  define i32* @rt0(i32* readonly %a)
 ; BOTH: Function Attrs: noinline nounwind readonly uwtable
-; BOTH-NEXT:    define i32* @rt0(i32* readonly returned %a)
+; BOTH-NEXT:    define i32* @rt0(i32* readonly returned "no-capture-maybe-returned" %a)
 define i32* @rt0(i32* %a) #0 {
 entry:
   %v = load i32, i32* %a, align 4
@@ -293,7 +293,7 @@
 ; FNATTR:  define i32* @rt2_helper(i32* %a)
 ; FNATTR:  define i32* @rt2(i32* readnone %a, i32* readnone %b)
 ; BOTH:    define i32* @rt2_helper(i32* %a)
-; BOTH:    define i32* @rt2(i32* readnone %a, i32* readnone %b)
+; BOTH:    define i32* @rt2(i32* readnone %a, i32* readnone "no-capture-maybe-returned" %b)
 define i32* @rt2_helper(i32* %a) #0 {
 entry:
   %call = call i32* @rt2(i32* %a, i32* %a)
@@ -316,17 +316,17 @@
 
 ; TEST another SCC test
 ;
-; FNATTR:  define i32* @rt3_helper(i32* %a, i32* %b)
-; FNATTR:  define i32* @rt3(i32* readnone %a, i32* readnone %b)
-; BOTH:    define i32* @rt3_helper(i32* %a, i32* returned %b)
-; BOTH:    define i32* @rt3(i32* readnone %a, i32* readnone returned %b)
+; FNATTR:  define i32* @rt3_helper(i32* nocapture readnone %a, i32* %b)
+; FNATTR:  define i32* @rt3(i32* nocapture readnone dereferenceable(1) %a, i32* readnone %b)
+; BOTH:    define i32* @rt3_helper(i32* nocapture readnone %a, i32* returned "no-capture-maybe-returned" %b)
+; BOTH:    define i32* @rt3(i32* nocapture readnone dereferenceable(1) %a, i32* readnone returned "no-capture-maybe-returned" %b)
 define i32* @rt3_helper(i32* %a, i32* %b) #0 {
 entry:
   %call = call i32* @rt3(i32* %a, i32* %b)
   ret i32* %call
 }
 
-define i32* @rt3(i32* %a, i32 *%b) #0 {
+define i32* @rt3(i32* dereferenceable(1) %a, i32 *%b) #0 {
 entry:
   %cmp = icmp eq i32* %a, null
   br i1 %cmp, label %if.then, label %if.end
@@ -352,9 +352,9 @@
 ; BOTH: declare void @unknown_fn(i32* (i32*)*)
 ;
 ; BOTH:       Function Attrs: noinline nounwind uwtable
-; BOTH-NEXT:  define i32* @calls_unknown_fn(i32* readnone returned %r)
+; BOTH-NEXT:  define i32* @calls_unknown_fn(i32* readnone returned "no-capture-maybe-returned" %r)
 ; FNATTR:     define i32* @calls_unknown_fn(i32* readnone returned %r)
-; ATTRIBUTOR: define i32* @calls_unknown_fn(i32* returned %r)
+; ATTRIBUTOR: define i32* @calls_unknown_fn(i32* returned "no-capture-maybe-returned" %r)
 declare void @unknown_fn(i32* (i32*)*) #0
 
 define i32* @calls_unknown_fn(i32* %r) #0 {
@@ -498,10 +498,9 @@
 ; }
 ;
 ; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
-; BOTH-NEXT:  define double* @bitcast(i32* readnone returned %b)
-;
+; BOTH-NEXT:  define double* @bitcast(i32* readnone returned "no-capture-maybe-returned" %b)
 ; FNATTR:     define double* @bitcast(i32* readnone %b)
-; ATTRIBUTOR: define double* @bitcast(i32* returned %b)
+; ATTRIBUTOR: define double* @bitcast(i32* returned "no-capture-maybe-returned" %b)
 define double* @bitcast(i32* %b) #0 {
 entry:
   %bc0 = bitcast i32* %b to double*
@@ -555,7 +554,7 @@
 ; }
 ;
 ; BOTH: Function Attrs: noinline norecurse nounwind readnone uwtable
-; BOTH-NEXT:  define double* @ret_arg_arg_undef(i32* readnone returned %b)
+; BOTH-NEXT: define double* @ret_arg_arg_undef(i32* readnone returned %b)
 ;
 ; FNATTR:     define double* @ret_arg_arg_undef(i32* readnone %b)
 ; ATTRIBUTOR: define double* @ret_arg_arg_undef(i32* returned %b)
@@ -655,7 +654,7 @@
 ; int* ret_arg_or_unknown(int* b) {
 ;   if (b == 0)
 ;     return b;
-;   return unknown();
+;   return unknown(b);
 ; }
 ;
 ; Verify we do not assume b is returned
Index: llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
+++ llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
@@ -12,9 +12,8 @@
 ;   return p == 0;
 ; }
 ;
-; FIXME: no-capture missing for %p
-; CHECK: define i32 @is_null_return(i32* readnone %p)
-define i32 @is_null_return(i32* %p) #0 {
+; CHECK: define i32 @is_null_return(i32* nocapture readnone dereferenceable(1) %p)
+define i32 @is_null_return(i32* dereferenceable(1) %p) #0 {
 entry:
   %cmp = icmp eq i32* %p, null
   %conv = zext i1 %cmp to i32
@@ -31,9 +30,8 @@
 ;   return 0;
 ; }
 ;
-; FIXME: no-capture missing for %p
-; CHECK: define i32 @is_null_control(i32* readnone %p)
-define i32 @is_null_control(i32* %p) #0 {
+; CHECK: define i32 @is_null_control(i32* nocapture readnone dereferenceable_or_null(1) %p)
+define i32 @is_null_control(i32* dereferenceable_or_null(1) %p) #0 {
 entry:
   %retval = alloca i32, align 4
   %cmp = icmp eq i32* %p, null
@@ -44,7 +42,7 @@
   br label %return
 
 if.end:                                           ; preds = %entry
-  %cmp1 = icmp eq i32* null, %p
+  %cmp1 = icmp eq i32* %p, null
   br i1 %cmp1, label %if.then2, label %if.end3
 
 if.then2:                                         ; preds = %if.end
@@ -112,15 +110,15 @@
 
 ; TEST SCC with various calls, casts, and comparisons agains NULL
 ;
-; FIXME: no-capture missing for %a
-; CHECK: define float* @scc_A(i32* readnone returned %a)
+; FIXME: "no-capture-maybe-returned" missing for %a in all functions because
+; dereferenceable_or_null is not propagated to the return value of scc_C.
 ;
-; FIXME: no-capture missing for %a
-; CHECK: define i64* @scc_B(double* readnone returned %a)
+; CHECK: define float* @scc_A(i32* readnone returned dereferenceable_or_null(1) %a)
+;
+; CHECK: define i64* @scc_B(double* readnone returned dereferenceable_or_null(1) %a)
 ;
 ; FIXME: readnone missing for %s
-; FIXME: no-capture missing for %a
-; CHECK: define i8* @scc_C(i16* returned %a)
+; CHECK: define i8* @scc_C(i16* returned dereferenceable_or_null(1) %a)
 ;
 ; float *scc_A(int *a) {
 ;   return (float*)(a ? (int*)scc_A((int*)scc_B((double*)scc_C((short*)a))) : a);
@@ -133,7 +131,7 @@
 ; void *scc_C(short *a) {
 ;   return scc_A((int*)(scc_C(a) ? scc_B((double*)a) : scc_C(a)));
 ; }
-define float* @scc_A(i32* %a) {
+define float* @scc_A(i32* dereferenceable_or_null(1) %a) {
 entry:
   %tobool = icmp ne i32* %a, null
   br i1 %tobool, label %cond.true, label %cond.false
@@ -157,7 +155,7 @@
   ret float* %4
 }
 
-define i64* @scc_B(double* %a) {
+define i64* @scc_B(double* dereferenceable_or_null(1) %a) {
 entry:
   %tobool = icmp ne double* %a, null
   br i1 %tobool, label %cond.true, label %cond.false
@@ -181,7 +179,7 @@
   ret i64* %4
 }
 
-define i8* @scc_C(i16* %a) {
+define i8* @scc_C(i16* dereferenceable_or_null(1) %a) {
 entry:
   %call = call i8* @scc_C(i16* %a)
   %tobool = icmp ne i8* %call, null
@@ -246,7 +244,7 @@
 ; }
 ;
 ; There should *not* be a no-capture attribute on %a
-; CHECK: define i64* @not_captured_but_returned_0(i64* returned %a)
+; CHECK: define i64* @not_captured_but_returned_0(i64* returned "no-capture-maybe-returned" %a)
 define i64* @not_captured_but_returned_0(i64* %a) #0 {
 entry:
   store i64 0, i64* %a, align 8
@@ -260,8 +258,9 @@
 ;   return a + 1;
 ; }
 ;
-; There should *not* be a no-capture attribute on %a
-; CHECK: define nonnull i64* @not_captured_but_returned_1(i64* %a)
+; There should *not* be a no-capture attribute on %a, no-capture-maybe-returned is fine though.
+;
+; CHECK: define nonnull i64* @not_captured_but_returned_1(i64* "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
@@ -276,8 +275,7 @@
 ;   not_captured_but_returned_1(a);
 ; }
 ;
-; FIXME: no-capture missing for %a
-; CHECK: define void @test_not_captured_but_returned_calls(i64* %a)
+; CHECK: define void @test_not_captured_but_returned_calls(i64* nocapture %a)
 define void @test_not_captured_but_returned_calls(i64* %a) #0 {
 entry:
   %call = call i64* @not_captured_but_returned_0(i64* %a)
@@ -291,8 +289,8 @@
 ;   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* returned %a)
+; There should *not* be a no-capture attribute on %a, no-capture-maybe-returned is fine though.
+; CHECK: define i64* @negative_test_not_captured_but_returned_call_0a(i64* returned "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)
@@ -321,8 +319,8 @@
 ;   return not_captured_but_returned_1(a);
 ; }
 ;
-; There should *not* be a no-capture attribute on %a
-; CHECK: define nonnull i64* @negative_test_not_captured_but_returned_call_1a(i64* %a)
+; There should *not* be a no-capture attribute on %a, no-capture-maybe-returned is fine though.
+; CHECK: define nonnull i64* @negative_test_not_captured_but_returned_call_1a(i64* "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)
@@ -353,13 +351,18 @@
 ;   return unknown();
 ; }
 ;
-; Verify we do *not* assume b is returned or not captured.
+; Verify we do *not* assume b is returned or not captured, no-capture-maybe-returned is fine though.
+;
+; FNATTR:     define i32* @ret_arg_or_unknown(i32* dereferenceable_or_null(1) %b)
+; FNATTR:     define i32* @ret_arg_or_unknown_through_phi(i32* dereferenceable_or_null(1) %b)
+; ATTRIBUTOR: define i32* @ret_arg_or_unknown(i32* dereferenceable_or_null(1) "no-capture-maybe-returned" %b)
+; ATTRIBUTOR: define i32* @ret_arg_or_unknown_through_phi(i32* dereferenceable_or_null(1) "no-capture-maybe-returned" %b)
+; BOTH:       define i32* @ret_arg_or_unknown(i32* dereferenceable_or_null(1) "no-capture-maybe-returned" %b)
+; BOTH:       define i32* @ret_arg_or_unknown_through_phi(i32* dereferenceable_or_null(1) "no-capture-maybe-returned" %b)
 ;
-; 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 {
+define i32* @ret_arg_or_unknown(i32* dereferenceable_or_null(1) %b) #0 {
 entry:
   %cmp = icmp eq i32* %b, null
   br i1 %cmp, label %ret_arg, label %ret_unknown
@@ -392,7 +395,9 @@
 
 ; TEST not captured by readonly external function
 ;
-; CHECK: define void @not_captured_by_readonly_call(i32* nocapture %b)
+; ATTRIBUTOR: define i32* @readonly(i32* "no-capture-maybe-returned")
+; ATTRIBUTOR: define void @not_captured_by_readonly_call(i32* nocapture %b)
+;
 declare i32* @readonly_unknown(i32*, i32*) readonly
 
 define void @not_captured_by_readonly_call(i32* %b) #0 {
@@ -407,13 +412,13 @@
 ; Make sure the returned flag on %r is strong enough to justify nocapture on %b but **not** on %r.
 ;
 ; FIXME: The "returned" information is not propagated to the fullest extend causing us to miss "nocapture" on %b in the following:
-; CHECK: define i32* @not_captured_by_readonly_call_not_returned_either1(i32* readonly %b, i32* readonly returned %r)
+; ATTRIBUTOR: define i32* @not_captured_by_readonly_call_not_returned_either1(i32* "no-capture-maybe-returned" %b, i32* returned "no-capture-maybe-returned" %r) #0 {
 ;
-; CHECK: define i32* @not_captured_by_readonly_call_not_returned_either2(i32* readonly %b, i32* readonly returned %r)
-; CHECK: define i32* @not_captured_by_readonly_call_not_returned_either3(i32* readonly %b, i32* readonly returned %r)
+; ATTRIBUTOR: define i32* @not_captured_by_readonly_call_not_returned_either2(i32* nocapture %b, i32* returned "no-capture-maybe-returned" %r) #0 {
+; ATTRIBUTOR: define i32* @not_captured_by_readonly_call_not_returned_either3(i32* nocapture %b, i32* returned "no-capture-maybe-returned" %r) #0 {
 ;
 ; FIXME: The "nounwind" information is not derived to the fullest extend causing us to miss "nocapture" on %b in the following:
-; CHECK: define i32* @not_captured_by_readonly_call_not_returned_either4(i32* readonly %b, i32* readonly returned %r)
+; ATTRIBUTOR: define i32* @not_captured_by_readonly_call_not_returned_either4(i32* "no-capture-maybe-returned" %b, i32* returned "no-capture-maybe-returned" %r) #0 {
 define i32* @not_captured_by_readonly_call_not_returned_either1(i32* %b, i32* returned %r) #0 {
 entry:
   %call = call i32* @readonly_unknown(i32* %b, i32* %r) nounwind
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
@@ -1,5 +1,5 @@
-; RUN: opt < %s -functionattrs -S | FileCheck %s
-; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+; RUN: opt < %s -attributor-disable=false -attributor -functionattrs -S | FileCheck %s
+; RUN: opt < %s -attributor-disable=false -passes="attributor,cgscc(function-attrs)" -S | FileCheck %s
 
 ; CHECK: define i32* @a(i32** nocapture readonly %p)
 define i32* @a(i32** %p) {
Index: llvm/lib/Transforms/IPO/Attributor.cpp
===================================================================
--- llvm/lib/Transforms/IPO/Attributor.cpp
+++ llvm/lib/Transforms/IPO/Attributor.cpp
@@ -19,6 +19,7 @@
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/CaptureTracking.h"
 #include "llvm/Analysis/GlobalsModRef.h"
 #include "llvm/IR/Argument.h"
 #include "llvm/IR/Attributes.h"
@@ -49,6 +50,9 @@
 STATISTIC(NumFnArgumentReturned,
           "Number of function arguments marked returned");
 
+STATISTIC(NumFnArgumentNoCapture,
+          "Number of function arguments marked no-capture");
+
 // TODO: Determine a good default value.
 //
 // In the LLVM-TS and SPEC2006, 32 seems to not induce compile time overheads
@@ -99,6 +103,9 @@
   case Attribute::Returned:
     NumFnArgumentReturned++;
     return;
+  case Attribute::NoCapture:
+    NumFnArgumentNoCapture++;
+    return;
   default:
     return;
   }
@@ -719,6 +726,342 @@
   return Changed;
 }
 
+/// ----------------------- Variable Capturing ---------------------------------
+
+/// A class to hold the state of for no-capture attributes.
+struct AANoCaptureImpl : public AANoCapture, IntegerState {
+
+  /// State encoding bits. A set bit in the state means the property holds.
+  /// NO_CAPTURE is the best possible state, 0 the worst possible state.
+  enum {
+    NOT_CAPTURED_IN_MEM = 1 << 0,
+    NOT_CAPTURED_IN_INT = 1 << 1,
+    NOT_CAPTURED_IN_RET = 1 << 2,
+
+    /// If we do not capture the value in memory or through integers we can only
+    /// communicate it back as a derived pointer.
+    NO_CAPTURE_MAYBE_RETURNED = NOT_CAPTURED_IN_MEM | NOT_CAPTURED_IN_INT,
+
+    /// If we do not capture the value in memory, through integers, or as a
+    /// derived pointer we know it is not captured.
+    NO_CAPTURE =
+        NOT_CAPTURED_IN_MEM | NOT_CAPTURED_IN_INT | NOT_CAPTURED_IN_RET,
+  };
+
+  /// 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, InformationCache &InfoCache)
+      : AANoCapture(V, InfoCache), IntegerState(NO_CAPTURE) {
+    assert(getAssumed() == NO_CAPTURE);
+  }
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {
+    Value &V = *getAssociatedValue();
+
+    // If the value in questions is unused it is not captured.
+    if (V.getNumUses() == 0) {
+      indicateOptimisticFixpoint();
+      return;
+    }
+
+    // Check what state the enclosing function can actually capture state.
+    Function &F = getAnchorScope();
+    determineFunctionCaptureCapabilities(F, *this);
+  }
+
+  /// See AANoCapture::isKnownNoCapture().
+  bool isKnownNoCapture() const override { return getKnown() == NO_CAPTURE; }
+
+  /// See AANoCapture::isAssumedNoCapture(...).
+  bool isAssumedNoCapture() const override { return isAssumed(NO_CAPTURE); }
+
+  /// See AANoCapture::isKnownNoCaptureMaybeReturned(...).
+  bool isKnownNoCaptureMaybeReturned() const override {
+    return isKnown(NO_CAPTURE_MAYBE_RETURNED);
+  }
+
+  /// See AANoCapture::isAssumedNoCaptureMaybeReturned(...).
+  bool isAssumedNoCaptureMaybeReturned() const override {
+    return isAssumed(NO_CAPTURE_MAYBE_RETURNED);
+  }
+
+  /// see AbstractAttribute::isAssumedNoCaptureMaybeReturned(...).
+  virtual void
+  getDeducedAttributes(SmallVectorImpl<Attribute> &Attrs) const override {
+    if (!isAssumedNoCaptureMaybeReturned())
+      return;
+
+    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"));
+  }
+
+  /// Set the NOT_CAPTURED_IN_MEM and NOT_CAPTURED_IN_RET bits in \p Known
+  /// depending on the ability of the function \p F to capture state in memory
+  /// and through "returning/throwing", respectively.
+  static void determineFunctionCaptureCapabilities(Function &F,
+                                                   IntegerState &State) {
+    // TODO: Once we have memory behavior attributes we should use them here.
+
+    // If we know we cannot communicate or write to memory, we do not care about
+    // ptr2int anymore.
+    if (F.onlyReadsMemory() && F.doesNotThrow() &&
+        F.getReturnType()->isVoidTy()) {
+      State.addKnownBits(NO_CAPTURE);
+      return;
+    }
+
+    // A function cannot capture state in memory if it only reads memory.
+    if (F.onlyReadsMemory())
+      State.addKnownBits(NOT_CAPTURED_IN_MEM);
+
+    // A function cannot communicate state back if it does not through
+    // exceptions and doesn not return values.
+    if (F.doesNotThrow() && F.getReturnType()->isVoidTy())
+      State.addKnownBits(NOT_CAPTURED_IN_RET);
+  }
+
+  /// 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 CapturedInMemory is set and the
+  /// search is stopped. If a use leads to a return instruction,
+  /// \p CommunicatedBack is set to true and \p CapturedInMemory is not changed.
+  /// If a use leads to a ptr2int which may capute the value,
+  /// \p CapturedInInteger is set. 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 aswell. For every
+  /// explored use we decrement \p RemainingUsesToExplore. Once it reaches 0,
+  /// the search is stopped with \p CapturedInMemory and \p CapturedInInteger
+  /// conservatively set to true.
+  AACaptureUseTracker(Attributor &A, AANoCapture &NoCaptureAA,
+                      IntegerState &State,
+                      SmallVectorImpl<Value *> &PotentialCopies,
+                      unsigned &RemainingUsesToExplore)
+      : A(A), NoCaptureAA(NoCaptureAA), State(State),
+        PotentialCopies(PotentialCopies),
+        RemainingUsesToExplore(RemainingUsesToExplore) {}
+
+  /// Determine if \p V maybe captured. *Also updates the state!*
+  bool valueMayBeCaptured(const Value *V) {
+    if (V->getType()->isPointerTy()) {
+      PointerMayBeCaptured(V, this);
+    } else if (isa<InvokeInst>(V) || V->getNumUses() > 0)
+      State.removeAssumedBits(AANoCaptureImpl::NOT_CAPTURED_IN_INT);
+    return State.isAssumed(AANoCaptureImpl::NO_CAPTURE_MAYBE_RETURNED);
+  }
+
+  /// See CaptureTracker::tooManyUses().
+  void tooManyUses() override {
+    State.removeAssumedBits(AANoCaptureImpl::NO_CAPTURE);
+  }
+
+  /// 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 isCapturedIn(/* Memory */ true, /* Integer */ true,
+                          /* Return */ true);
+    }
+
+    // Deal with ptr2int by following uses.
+    if (isa<PtrToIntInst>(U->getUser())) {
+      LLVM_DEBUG(errs() << " - delegate to ptr2int users!\n");
+      return valueMayBeCaptured(U->getUser());
+    }
+
+    // Explicitly catch return instructions.
+    if (isa<ReturnInst>(U->getUser()))
+      return isCapturedIn(/* Memory */ false, /* Integer */ false,
+                          /* Return */ true);
+
+    // 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 isCapturedIn(/* Memory */ true, /* Integer */ true,
+                          /* Return */ true);
+
+    // If we do not know the called function we have to assume the use captures.
+    if (!CS.getCalledFunction())
+      return isCapturedIn(/* Memory */ true, /* Integer */ true,
+                          /* Return */ true);
+
+    // If the called function cannot capture state, nothing is captured.
+    Function &F = *CS.getCalledFunction();
+
+    // Check what we know about the callee already from the IR. If that suffices
+    // to justify no-caputre(-in-memory) we take it. Note that a similar
+    // reasoning is applied for assumed capture capabilities but implicitly
+    // through the recursive use of a AANoCapture attribute below.
+    IntegerState KnownCaptureInfo;
+    AANoCaptureImpl::determineFunctionCaptureCapabilities(F, KnownCaptureInfo);
+    if (KnownCaptureInfo.isKnown(AANoCaptureImpl::NO_CAPTURE))
+      return isCapturedIn(/* Memory */ false, /* Integer */ false,
+                          /* Return */ false);
+    if (KnownCaptureInfo.isKnown(AANoCaptureImpl::NO_CAPTURE_MAYBE_RETURNED)) {
+      addPotentialCopyIfNecessary(CS);
+      return isCapturedIn(/* Memory */ false, /* Integer */ false,
+                          /* Return */ false);
+    }
+
+    unsigned ArgNo = CS.getArgumentNo(U);
+    // Exclude var-arg arguments.
+    if (F.arg_size() > ArgNo) {
+      // 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>(NoCaptureAA, F, ArgNo);
+      if (ArgNoCaptureAA && ArgNoCaptureAA->getState().isValidState()) {
+        if (ArgNoCaptureAA->isAssumedNoCapture())
+          return isCapturedIn(/* Memory */ false, /* Integer */ false,
+                              /* Return */ false);
+        if (ArgNoCaptureAA->isAssumedNoCaptureMaybeReturned()) {
+          addPotentialCopyIfNecessary(CS);
+          return isCapturedIn(/* Memory */ false, /* Integer */ false,
+                              /* Return */ false);
+        }
+      }
+
+      // Check for an existing attribute to justify no-capture for this use.
+      if (F.getAttributes().hasParamAttr(ArgNo, "no-capture-maybe-returned")) {
+        addPotentialCopyIfNecessary(CS);
+        return isCapturedIn(/* Memory */ false, /* Integer */ false,
+                            /* Return */ false);
+      }
+    }
+
+    // Lastly, we could not find a reason no-capture can be assumed so we don't.
+    return isCapturedIn(/* Memory */ true, /* Integer */ true,
+                        /* Return */ true);
+  }
+
+  /// Register \p CS as potential copy of the value we are checking.
+  void addPotentialCopyIfNecessary(CallSite CS) {
+    PotentialCopies.push_back(CS.getInstruction());
+  }
+
+  /// See CaptureTracker::shouldExplore(...).
+  bool shouldExplore(const Use *U) override { return true; }
+
+  /// Update the state according to \p CapturedInMem, \p CapturedInInt, and
+  /// \p CapturedInRet, then return the appropriate value for use in the
+  /// CaptureTracker::captured() interface.
+  bool isCapturedIn(bool CapturedInMem, bool CapturedInInt,
+                    bool CapturedInRet) {
+    LLVM_DEBUG(dbgs() << " - captures [Mem " << CapturedInMem << "|Int "
+                      << CapturedInInt << "|Ret " << CapturedInRet << "]\n");
+    if (CapturedInMem)
+      State.removeAssumedBits(AANoCaptureImpl::NOT_CAPTURED_IN_MEM);
+    if (CapturedInInt)
+      State.removeAssumedBits(AANoCaptureImpl::NOT_CAPTURED_IN_INT);
+    if (CapturedInRet)
+      State.removeAssumedBits(AANoCaptureImpl::NOT_CAPTURED_IN_RET);
+    return !State.isAssumed(AANoCaptureImpl::NO_CAPTURE_MAYBE_RETURNED);
+  }
+
+private:
+  /// The attributor providing in-flight abstract attributes.
+  Attributor &A;
+
+  /// The abstract attribute currently updated.
+  AANoCapture &NoCaptureAA;
+
+  /// The state currently updated.
+  IntegerState &State;
+
+  /// 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, InformationCache &InfoCache)
+      : AANoCaptureImpl(Arg, InfoCache) {}
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {
+    Argument &Arg = cast<Argument>(*getAssociatedValue());
+    if (Arg.hasAttribute(Attribute::NoCapture))
+      indicateOptimisticFixpoint();
+  }
+
+  /// 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) {
+  Argument &Arg = cast<Argument>(*getAssociatedValue());
+
+  // The current assumed state used to determine a change.
+  auto AssumedState = getAssumed();
+
+  // TODO: Once we have memory behavior attributes we should use them here
+  // similar to the reasoning in
+  // AANoCaptureImpl::determineFunctionCaptureCapabilities(...).
+
+  // TODO: Use the AAReturnedValues to learn if the argument can return or not.
+
+  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, *this, PotentialCopies,
+                              RemainingUsesToExplore);
+
+  // 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.
+  unsigned Idx = 0;
+  PotentialCopies.push_back(&Arg);
+  while (isAssumedNoCaptureMaybeReturned() && Idx < PotentialCopies.size())
+    Tracker.valueMayBeCaptured(PotentialCopies[Idx++]);
+
+  return (AssumedState == getAssumed()) ? ChangeStatus::UNCHANGED
+                                        : ChangeStatus::CHANGED;
+}
+
 /// ----------------------------------------------------------------------------
 ///                               Attributor
 /// ----------------------------------------------------------------------------
@@ -873,6 +1216,17 @@
       registerAA(*new AAReturnedValuesImpl(F, InfoCache));
   }
 
+  // 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()) {
+      if (!Whitelist || Whitelist->count(AANoCaptureArgument::ID))
+        registerAA(*new AANoCaptureArgument(Arg, InfoCache));
+    }
+  }
+
   // Walk all instructions to find more attribute opportunities and also
   // interesting instructions that might be queried by abstract attributes
   // during their initialization or update.
Index: llvm/include/llvm/Transforms/IPO/Attributor.h
===================================================================
--- llvm/include/llvm/Transforms/IPO/Attributor.h
+++ llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -680,6 +680,36 @@
     virtual bool isKnownNoUnwind() const = 0;
 };
 
+/// An abstract interface for all nocapture attributes.
+struct AANoCapture : public AbstractAttribute {
+
+  /// See AbstractAttribute::AbstractAttribute(...).
+  AANoCapture(Value &V, InformationCache &InfoCache)
+      : AbstractAttribute(V, InfoCache) {}
+
+  /// 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;
+};
+
 } // end namespace llvm
 
 #endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to