jdoerfert updated this revision to Diff 215713.
jdoerfert added a comment.

Add more test coverage and improve capture information based on 
dereferenceability


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/align.ll
  llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
  llvm/test/Transforms/FunctionAttrs/arg_returned.ll
  llvm/test/Transforms/FunctionAttrs/dereferenceable.ll
  llvm/test/Transforms/FunctionAttrs/noalias_returned.ll
  llvm/test/Transforms/FunctionAttrs/nocapture.ll
  llvm/test/Transforms/FunctionAttrs/nonnull.ll
  llvm/test/Transforms/FunctionAttrs/nosync.ll
  llvm/test/Transforms/FunctionAttrs/read_write_returned_arguments_scc.ll

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
@@ -103,7 +103,7 @@
 }
 
 ; CHECK: Function Attrs: nofree norecurse nosync nounwind
-; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned %w0)
+; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* readnone %n0, i32* nocapture readonly %r0, i32* returned "no-capture-maybe-returned" %w0)
 define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
 entry:
   %tobool = icmp ne i32* %n0, null
Index: llvm/test/Transforms/FunctionAttrs/nosync.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/nosync.ll
+++ llvm/test/Transforms/FunctionAttrs/nosync.ll
@@ -28,7 +28,7 @@
 ; FNATTR: Function Attrs: norecurse nounwind optsize readnone ssp uwtable
 ; FNATTR-NEXT: define nonnull i32* @foo(%struct.ST* readnone %s)
 ; ATTRIBUTOR: Function Attrs: nofree nosync nounwind optsize readnone ssp uwtable
-; ATTRIBUTOR-NEXT: define nonnull i32* @foo(%struct.ST* %s)
+; ATTRIBUTOR-NEXT: define nonnull i32* @foo(%struct.ST* "no-capture-maybe-returned" %s)
 define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp {
 entry:
   %arrayidx = getelementptr inbounds %struct.ST, %struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13
@@ -186,7 +186,7 @@
 ; FNATTR: Function Attrs: nofree noinline nounwind uwtable
 ; FNATTR-NEXT: define i32 @scc1(i32* %0)
 ; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable
-; ATTRIBUTOR-NEXT: define i32 @scc1(i32* %0)
+; ATTRIBUTOR-NEXT: define i32 @scc1(i32* nocapture %0)
 define i32 @scc1(i32* %0) noinline nounwind uwtable {
   tail call void @scc2(i32* %0);
   %val = tail call i32 @volatile_load(i32* %0);
@@ -196,7 +196,7 @@
 ; FNATTR: Function Attrs: nofree noinline nounwind uwtable
 ; FNATTR-NEXT: define void @scc2(i32* %0)
 ; ATTRIBUTOR: Function Attrs: nofree noinline noreturn nosync nounwind uwtable
-; ATTRIBUTOR-NEXT: define void @scc2(i32* %0)
+; ATTRIBUTOR-NEXT: define void @scc2(i32* nocapture %0)
 define void @scc2(i32* %0) noinline nounwind uwtable {
   tail call i32 @scc1(i32* %0);
   ret void;
@@ -224,7 +224,7 @@
 ; FNATTR: Function Attrs: nofree norecurse nounwind
 ; FNATTR-NEXT: define void @foo1(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
 ; ATTRIBUTOR-NOT: nosync
-; ATTRIBUTOR: define void @foo1(i32* %0, %"struct.std::atomic"* %1)
+; ATTRIBUTOR: define void @foo1(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
 define void @foo1(i32* %0, %"struct.std::atomic"* %1) {
   store i32 100, i32* %0, align 4
   fence release
@@ -236,7 +236,7 @@
 ; FNATTR: Function Attrs: nofree norecurse nounwind
 ; FNATTR-NEXT: define void @bar(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
 ; ATTRIBUTOR-NOT: nosync
-; ATTRIBUTOR: define void @bar(i32* %0, %"struct.std::atomic"* %1)
+; ATTRIBUTOR: define void @bar(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
 define void @bar(i32* %0, %"struct.std::atomic"* %1) {
   %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
   br label %4
@@ -256,7 +256,7 @@
 ; FNATTR: Function Attrs: nofree norecurse nounwind
 ; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
 ; ATTRIBUTOR: Function Attrs: nofree nosync
-; ATTRIBUTOR: define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1)
+; ATTRIBUTOR: define void @foo1_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
 define void @foo1_singlethread(i32* %0, %"struct.std::atomic"* %1) {
   store i32 100, i32* %0, align 4
   fence syncscope("singlethread") release
@@ -268,7 +268,7 @@
 ; FNATTR: Function Attrs: nofree norecurse nounwind
 ; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone %0, %"struct.std::atomic"* nocapture readonly %1)
 ; ATTRIBUTOR: Function Attrs: nofree nosync
-; ATTRIBUTOR: define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1)
+; ATTRIBUTOR: define void @bar_singlethread(i32* nocapture %0, %"struct.std::atomic"* nocapture %1)
 define void @bar_singlethread(i32* %0, %"struct.std::atomic"* %1) {
   %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0
   br label %4
@@ -289,9 +289,11 @@
 
 ; TEST 14 - negative, checking volatile intrinsics.
 
+; It is odd to add nocapture but a result of the llvm.memcpy nocapture.
+;
 ; ATTRIBUTOR: Function Attrs: nounwind
 ; ATTRIBUTOR-NOT: nosync
-; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2)
+; ATTRIBUTOR-NEXT: define i32 @memcpy_volatile(i8* nocapture %ptr1, i8* nocapture %ptr2)
 define i32 @memcpy_volatile(i8* %ptr1, i8* %ptr2) {
   call void @llvm.memcpy(i8* %ptr1, i8* %ptr2, i32 8, i1 1)
   ret i32 4
@@ -299,8 +301,10 @@
 
 ; TEST 15 - positive, non-volatile intrinsic.
 
+; It is odd to add nocapture but a result of the llvm.memset nocapture.
+;
 ; ATTRIBUTOR: Function Attrs: nosync
-; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* %ptr1, i8 %val)
+; ATTRIBUTOR-NEXT: define i32 @memset_non_volatile(i8* nocapture %ptr1, i8 %val)
 define i32 @memset_non_volatile(i8* %ptr1, i8 %val) {
   call void @llvm.memset(i8* %ptr1, i8 %val, i32 8, i1 0)
   ret i32 4
Index: llvm/test/Transforms/FunctionAttrs/nonnull.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/nonnull.ll
+++ llvm/test/Transforms/FunctionAttrs/nonnull.ll
@@ -159,7 +159,7 @@
   ret void
 }
 define internal void @test13(i8* %a, i8* %b, i8* %c) {
-; ATTRIBUTOR: define internal void @test13(i8* nonnull %a, i8* %b, i8* %c) 
+; ATTRIBUTOR: define internal void @test13(i8* nocapture nonnull %a, i8* nocapture %b, i8* nocapture %c) 
   ret void
 }
 
@@ -402,7 +402,7 @@
 define i1 @parent8(i8* %a, i8* %bogus1, i8* %b) personality i8* bitcast (i32 (...)* @esfp to i8*){
 ; FNATTR-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b)
 ; FIXME : missing "nonnull", it should be @parent8(i8* nonnull %a, i8* %bogus1, i8* nonnull %b)
-; ATTRIBUTOR-LABEL: @parent8(i8* %a, i8* %bogus1, i8* %b)
+; ATTRIBUTOR-LABEL: @parent8(i8* %a, i8* nocapture %bogus1, i8* %b)
 ; BOTH-NEXT:  entry:
 ; FNATTR-NEXT:    invoke void @use2nonnull(i8* %a, i8* %b)
 ; ATTRIBUTOR-NEXT:    invoke void @use2nonnull(i8* nonnull %a, i8* nonnull %b)
Index: llvm/test/Transforms/FunctionAttrs/nocapture.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/nocapture.ll
+++ llvm/test/Transforms/FunctionAttrs/nocapture.ll
@@ -1,27 +1,30 @@
-; RUN: opt < %s -functionattrs -S | FileCheck %s
-; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefixes=FNATTR,EITHER
+; RUN: opt -passes=function-attrs -S < %s | FileCheck %s --check-prefixes=FNATTR,EITHER
+; RUN: opt -attributor -attributor-disable=false -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,EITHER
+; RUN: opt -passes=attributor -attributor-disable=false -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,EITHER
 
 @g = global i32* null		; <i32**> [#uses=1]
 
-; CHECK: define i32* @c1(i32* readnone returned %q)
+; FNATTR: define i32* @c1(i32* readnone returned %q)
+; ATTRIBUTOR: define i32* @c1(i32* returned "no-capture-maybe-returned" %q)
 define i32* @c1(i32* %q) {
 	ret i32* %q
 }
 
-; CHECK: define void @c2(i32* %q)
+; EITHER: define void @c2(i32* %q)
 ; It would also be acceptable to mark %q as readnone. Update @c3 too.
 define void @c2(i32* %q) {
 	store i32* %q, i32** @g
 	ret void
 }
 
-; CHECK: define void @c3(i32* %q)
+; EITHER: define void @c3(i32* %q)
 define void @c3(i32* %q) {
 	call void @c2(i32* %q)
 	ret void
 }
 
-; CHECK: define i1 @c4(i32* %q, i32 %bitno)
+; EITHER: define i1 @c4(i32* %q, i32 %bitno)
 define i1 @c4(i32* %q, i32 %bitno) {
 	%tmp = ptrtoint i32* %q to i32
 	%tmp2 = lshr i32 %tmp, %bitno
@@ -35,7 +38,7 @@
 
 @lookup_table = global [2 x i1] [ i1 0, i1 1 ]
 
-; CHECK: define i1 @c5(i32* %q, i32 %bitno)
+; EITHER: define i1 @c5(i32* %q, i32 %bitno)
 define i1 @c5(i32* %q, i32 %bitno) {
 	%tmp = ptrtoint i32* %q to i32
 	%tmp2 = lshr i32 %tmp, %bitno
@@ -48,7 +51,8 @@
 
 declare void @throw_if_bit_set(i8*, i8) readonly
 
-; CHECK: define i1 @c6(i8* readonly %q, i8 %bit)
+; FNATTR: define i1 @c6(i8* readonly %q, i8 %bit)
+; ATTRIBUTOR: define i1 @c6(i8* %q, i8 %bit)
 define i1 @c6(i8* %q, i8 %bit) personality i32 (...)* @__gxx_personality_v0 {
 	invoke void @throw_if_bit_set(i8* %q, i8 %bit)
 		to label %ret0 unwind label %ret1
@@ -70,7 +74,8 @@
 	ret i1* %lookup
 }
 
-; CHECK: define i1 @c7(i32* readonly %q, i32 %bitno)
+; FNATTR: define i1 @c7(i32* readonly %q, i32 %bitno)
+; ATTRIBUTOR: define i1 @c7(i32* %q, i32 %bitno)
 define i1 @c7(i32* %q, i32 %bitno) {
 	%ptr = call i1* @lookup_bit(i32* %q, i32 %bitno)
 	%val = load i1, i1* %ptr
@@ -78,7 +83,7 @@
 }
 
 
-; CHECK: define i32 @nc1(i32* %q, i32* nocapture %p, i1 %b)
+; EITHER: define i32 @nc1(i32* %q, i32* nocapture %p, i1 %b)
 define i32 @nc1(i32* %q, i32* %p, i1 %b) {
 e:
 	br label %l
@@ -93,7 +98,7 @@
 	ret i32 %val
 }
 
-; CHECK: define i32 @nc1_addrspace(i32* %q, i32 addrspace(1)* nocapture %p, i1 %b)
+; EITHER: 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
@@ -108,78 +113,93 @@
 	ret i32 %val
 }
 
-; CHECK: define void @nc2(i32* nocapture %p, i32* %q)
+; EITHER: 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)
+; EITHER: 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)
+; FNATTR: define void @nc4(i8* nocapture readonly %p)
+; ATTRIBUTOR: define void @nc4(i8* nocapture %p)
 define void @nc4(i8* %p) {
 	call void @external(i8* %p)
 	ret void
 }
 
-; CHECK: define void @nc5(void (i8*)* nocapture %f, i8* nocapture %p)
+; EITHER: 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)
+; FNATTR:     define void @test1_1(i8* nocapture readnone %x1_1, i8* %y1_1, i1 %c)
+; ATTRIBUTOR: define void @test1_1(i8* nocapture %x1_1, i8* nocapture %y1_1, i1 %c)
 ; 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)
+define void @test1_1(i8* %x1_1, i8* %y1_1, i1 %c) {
+  call i8* @test1_2(i8* %x1_1, i8* %y1_1, i1 %c)
   store i32* null, i32** @g
   ret void
 }
 
-; CHECK: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* returned %y1_2)
-define i8* @test1_2(i8* %x1_2, i8* %y1_2) {
-  call void @test1_1(i8* %x1_2, i8* %y1_2)
+; FNATTR: define i8* @test1_2(i8* nocapture readnone %x1_2, i8* returned %y1_2, i1 %c)
+; ATTRIBUTOR: define i8* @test1_2(i8* nocapture %x1_2, i8* returned "no-capture-maybe-returned" %y1_2, i1 %c)
+define i8* @test1_2(i8* %x1_2, i8* %y1_2, i1 %c) {
+  br i1 %c, label %t, label %f
+t:
+  call void @test1_1(i8* %x1_2, i8* %y1_2, i1 %c)
   store i32* null, i32** @g
+  br label %f
+f:
   ret i8* %y1_2
 }
 
-; CHECK: define void @test2(i8* nocapture readnone %x2)
+; FNATTR: 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)
+; FNATTR: define void @test3(i8* nocapture readnone %x3, i8* nocapture readnone %y3, i8* nocapture readnone %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)
-define void @test4_1(i8* %x4_1) {
-  call i8* @test4_2(i8* %x4_1, i8* %x4_1, i8* %x4_1)
+; FNATTR: define void @test4_1(i8* %x4_1, i1 %c)
+; ATTRIBUTOR: define void @test4_1(i8* nocapture %x4_1, i1 %c)
+define void @test4_1(i8* %x4_1, i1 %c) {
+  call i8* @test4_2(i8* %x4_1, i8* %x4_1, i8* %x4_1, i1 %c)
   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)
-define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2) {
-  call void @test4_1(i8* null)
+; FNATTR: define i8* @test4_2(i8* nocapture readnone %x4_2, i8* readnone returned %y4_2, i8* nocapture readnone %z4_2, i1 %c)
+; ATTRIBUTOR: define i8* @test4_2(i8* nocapture %x4_2, i8* returned "no-capture-maybe-returned" %y4_2, i8* nocapture %z4_2, i1 %c)
+define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2, i1 %c) {
+  br i1 %c, label %t, label %f
+t:
+  call void @test4_1(i8* null, i1 %c)
   store i32* null, i32** @g
+  br label %f
+f:
   ret i8* %y4_2
 }
 
 declare i8* @test5_1(i8* %x5_1)
 
-; CHECK: define void @test5_2(i8* %x5_2)
+; EITHER: define void @test5_2(i8* %x5_2)
 define void @test5_2(i8* %x5_2) {
   call i8* @test5_1(i8* %x5_2)
   store i32* null, i32** @g
@@ -188,32 +208,32 @@
 
 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)
+; EITHER: 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
   ret void
 }
 
-; CHECK: define void @test_cmpxchg(i32* nocapture %p)
+; EITHER: 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)
+; EITHER: 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)
+; EITHER: define void @test_atomicrmw(i32* nocapture %p)
 define void @test_atomicrmw(i32* %p) {
   atomicrmw add i32* %p, i32 1 seq_cst
   ret void
 }
 
-; CHECK: define void @test_volatile(i32* %x)
+; EITHER: define void @test_volatile(i32* %x)
 define void @test_volatile(i32* %x) {
 entry:
   %gep = getelementptr i32, i32* %x, i64 1
@@ -221,7 +241,7 @@
   ret void
 }
 
-; CHECK: nocaptureLaunder(i8* nocapture %p)
+; EITHER: nocaptureLaunder(i8* nocapture %p)
 define void @nocaptureLaunder(i8* %p) {
 entry:
   %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p)
@@ -230,14 +250,14 @@
 }
 
 @g2 = global i8* null
-; CHECK: define void @captureLaunder(i8* %p)
+; EITHER: define void @captureLaunder(i8* %p)
 define void @captureLaunder(i8* %p) {
   %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p)
   store i8* %b, i8** @g2
   ret void
 }
 
-; CHECK: @nocaptureStrip(i8* nocapture %p)
+; EITHER: @nocaptureStrip(i8* nocapture %p)
 define void @nocaptureStrip(i8* %p) {
 entry:
   %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
@@ -246,26 +266,29 @@
 }
 
 @g3 = global i8* null
-; CHECK: define void @captureStrip(i8* %p)
+; EITHER: define void @captureStrip(i8* %p)
 define void @captureStrip(i8* %p) {
   %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
   store i8* %b, i8** @g3
   ret void
 }
 
-; CHECK: define i1 @captureICmp(i32* readnone %x)
+; FNATTR: define i1 @captureICmp(i32* readnone %x)
+; ATTRIBUTOR: define i1 @captureICmp(i32* %x)
 define i1 @captureICmp(i32* %x) {
   %1 = icmp eq i32* %x, null
   ret i1 %1
 }
 
-; CHECK: define i1 @captureICmpRev(i32* readnone %x)
+; FNATTR: define i1 @captureICmpRev(i32* readnone %x)
+; ATTRIBUTOR: define i1 @captureICmpRev(i32* %x)
 define i1 @captureICmpRev(i32* %x) {
   %1 = icmp eq i32* null, %x
   ret i1 %1
 }
 
-; CHECK: define i1 @nocaptureInboundsGEPICmp(i32* nocapture readnone %x)
+; FNATTR: 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*
@@ -273,7 +296,8 @@
   ret i1 %3
 }
 
-; CHECK: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture readnone %x)
+; FNATTR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture readnone %x)
+; ATTRIBUTOR: define i1 @nocaptureInboundsGEPICmpRev(i32* nocapture %x)
 define i1 @nocaptureInboundsGEPICmpRev(i32* %x) {
   %1 = getelementptr inbounds i32, i32* %x, i32 5
   %2 = bitcast i32* %1 to i8*
@@ -281,14 +305,16 @@
   ret i1 %3
 }
 
-; CHECK: define i1 @nocaptureDereferenceableOrNullICmp(i32* nocapture readnone dereferenceable_or_null(4) %x)
+; FNATTR: 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
   ret i1 %2
 }
 
-; CHECK: define i1 @captureDereferenceableOrNullICmp(i32* readnone dereferenceable_or_null(4) %x)
+; FNATTR: define i1 @captureDereferenceableOrNullICmp(i32* readnone dereferenceable_or_null(4) %x)
+; ATTRIBUTOR: define i1 @captureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x)
 define i1 @captureDereferenceableOrNullICmp(i32* dereferenceable_or_null(4) %x) "null-pointer-is-valid"="true" {
   %1 = bitcast i32* %x to i8*
   %2 = icmp eq i8* %1, null
Index: llvm/test/Transforms/FunctionAttrs/noalias_returned.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/noalias_returned.ll
+++ llvm/test/Transforms/FunctionAttrs/noalias_returned.ll
@@ -29,6 +29,17 @@
   ret i8* %1
 }
 
+define void @nocapture(i8* %a){
+  ret void
+}
+
+; CHECK: define noalias i8* @return_noalias_looks_like_capture()
+define i8* @return_noalias_looks_like_capture(){
+  %1 = tail call noalias i8* @malloc(i64 4)
+  call void @nocapture(i8* %1)
+  ret i8* %1
+}
+
 declare i8* @alias()
 
 ; TEST 3
Index: llvm/test/Transforms/FunctionAttrs/dereferenceable.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/dereferenceable.ll
+++ llvm/test/Transforms/FunctionAttrs/dereferenceable.ll
@@ -5,7 +5,7 @@
 ; take mininimum of return values
 ;
 define i32* @test1(i32* dereferenceable(4) %0, double* dereferenceable(8) %1, i1 zeroext %2) local_unnamed_addr {
-; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test1(i32* nonnull dereferenceable(4) %0, double* nonnull dereferenceable(8) %1, i1 zeroext %2)
+; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test1(i32* nonnull dereferenceable(4) "no-capture-maybe-returned" %0, double* nonnull dereferenceable(8) "no-capture-maybe-returned" %1, i1 zeroext %2)
   %4 = bitcast double* %1 to i32*
   %5 = select i1 %2, i32* %0, i32* %4
   ret i32* %5
@@ -13,7 +13,7 @@
 
 ; TEST 2
 define i32* @test2(i32* dereferenceable_or_null(4) %0, double* dereferenceable(8) %1, i1 zeroext %2) local_unnamed_addr {
-; ATTRIBUTOR: define dereferenceable_or_null(4) i32* @test2(i32* dereferenceable_or_null(4) %0, double* nonnull dereferenceable(8) %1, i1 zeroext %2)
+; ATTRIBUTOR: define dereferenceable_or_null(4) i32* @test2(i32* dereferenceable_or_null(4) "no-capture-maybe-returned" %0, double* nonnull dereferenceable(8) "no-capture-maybe-returned" %1, i1 zeroext %2)
   %4 = bitcast double* %1 to i32*
   %5 = select i1 %2, i32* %0, i32* %4
   ret i32* %5
@@ -22,20 +22,20 @@
 ; TEST 3
 ; GEP inbounds
 define i32* @test3_1(i32* dereferenceable(8) %0) local_unnamed_addr {
-; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test3_1(i32* nonnull dereferenceable(8) %0)
+; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test3_1(i32* nonnull dereferenceable(8) "no-capture-maybe-returned" %0)
   %ret = getelementptr inbounds i32, i32* %0, i64 1
   ret i32* %ret
 }
 
 define i32* @test3_2(i32* dereferenceable_or_null(32) %0) local_unnamed_addr {
 ; FIXME: Argument should be mark dereferenceable because of GEP `inbounds`.
-; ATTRIBUTOR: define nonnull dereferenceable(16) i32* @test3_2(i32* dereferenceable_or_null(32) %0)
+; ATTRIBUTOR: define nonnull dereferenceable(16) i32* @test3_2(i32* dereferenceable_or_null(32) "no-capture-maybe-returned" %0)
   %ret = getelementptr inbounds i32, i32* %0, i64 4
   ret i32* %ret
 }
 
 define i32* @test3_3(i32* dereferenceable(8) %0, i32* dereferenceable(16) %1, i1 %2) local_unnamed_addr {
-; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test3_3(i32* nonnull dereferenceable(8) %0, i32* nonnull dereferenceable(16) %1, i1 %2) local_unnamed_addr
+; ATTRIBUTOR: define nonnull dereferenceable(4) i32* @test3_3(i32* nonnull dereferenceable(8) "no-capture-maybe-returned" %0, i32* nonnull dereferenceable(16) "no-capture-maybe-returned" %1, i1 %2) local_unnamed_addr
   %ret1 = getelementptr inbounds i32, i32* %0, i64 1
   %ret2 = getelementptr inbounds i32, i32* %1, i64 2
   %ret = select i1 %2, i32* %ret1, i32* %ret2
@@ -46,7 +46,7 @@
 ; Better than known in IR.
 
 define dereferenceable(4) i32* @test4(i32* dereferenceable(8) %0) local_unnamed_addr {
-; ATTRIBUTOR: define nonnull dereferenceable(8) i32* @test4(i32* nonnull returned dereferenceable(8) %0)
+; ATTRIBUTOR: define nonnull dereferenceable(8) i32* @test4(i32* nonnull returned dereferenceable(8) "no-capture-maybe-returned" %0)
   ret i32* %0
 }
 
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: nofree noinline norecurse nosync 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: nofree noinline nosync nounwind readnone uwtable
 ; BOTH-NEXT: define double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b)
 ; BOTH: Function Attrs: nofree noinline nosync nounwind readnone uwtable
@@ -171,9 +171,9 @@
 ; FNATTR: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r)
 ;
 ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
-; ATTRIBUTOR-NEXT: define double* @ptr_sink_r0(double* returned %r)
+; ATTRIBUTOR-NEXT: define double* @ptr_sink_r0(double* returned "no-capture-maybe-returned" %r)
 ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
-; ATTRIBUTOR-NEXT: define double* @ptr_scc_r1(double* %a, double* returned %r, double* %b)
+; ATTRIBUTOR-NEXT: define double* @ptr_scc_r1(double* %a, double* returned %r, double* nocapture %b)
 ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable
 ; ATTRIBUTOR-NEXT: define double* @ptr_scc_r2(double* %a, double* %b, double* returned %r)
 ;
@@ -261,7 +261,7 @@
 ;
 ; FNATTR:  define i32* @rt0(i32* readonly %a)
 ; BOTH: Function Attrs: nofree noinline noreturn nosync nounwind readonly uwtable
-; BOTH-NEXT:    define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* readonly %a)
+; BOTH-NEXT:    define noalias nonnull align 536870912 dereferenceable(4294967295) i32* @rt0(i32* nocapture readonly %a)
 define i32* @rt0(i32* %a) #0 {
 entry:
   %v = load i32, i32* %a, align 4
@@ -294,7 +294,7 @@
 ; FNATTR:  define i32* @rt2_helper(i32* %a)
 ; FNATTR:  define i32* @rt2(i32* readnone %a, i32* readnone %b)
 ; BOTH:    define i32* @rt2_helper(i32* returned %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)
@@ -319,8 +319,8 @@
 ;
 ; 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)
+; BOTH:    define i32* @rt3_helper(i32* %a, i32* returned "no-capture-maybe-returned" %b)
+; BOTH:    define i32* @rt3(i32* readnone %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)
@@ -353,9 +353,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 {
@@ -502,12 +502,12 @@
 ; }
 ;
 ; BOTH: Function Attrs: nofree noinline norecurse nosync 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: Function Attrs: nofree noinline nosync nounwind uwtable
-; ATTRIBUTOR-NEXT: define double* @bitcast(i32* returned %b)
+; ATTRIBUTOR-NEXT: define double* @bitcast(i32* returned "no-capture-maybe-returned" %b)
 define double* @bitcast(i32* %b) #0 {
 entry:
   %bc0 = bitcast i32* %b to double*
Index: llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
+++ llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll
@@ -5,122 +5,14 @@
 ;
 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* 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* 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 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 readnone %a)
-define i32* @srec16(i32* %a) #0 {
-entry:
-  %call = call i32* @srec16(i32* %a)
-; CHECK:      %call = call noalias nonnull align 536870912 dereferenceable(4294967295) i32* @srec16(i32* %a)
-; CHECK-NEXT: 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
 ;
-; FIXME: no-capture missing for %a
-; CHECK: define float* @scc_A(i32* readnone returned %a)
+; CHECK: define dereferenceable_or_null(4) float* @scc_A(i32* readnone returned dereferenceable_or_null(4) "no-capture-maybe-returned" %a)
 ;
-; FIXME: no-capture missing for %a
-; CHECK: define i64* @scc_B(double* readnone returned %a)
+; CHECK: define dereferenceable_or_null(8) i64* @scc_B(double* readnone returned dereferenceable_or_null(8) "no-capture-maybe-returned" %a)
 ;
 ; FIXME: readnone missing for %s
-; FIXME: no-capture missing for %a
-; CHECK: define i8* @scc_C(i16* returned %a)
+; CHECK: define dereferenceable_or_null(2) i8* @scc_C(i16* returned dereferenceable_or_null(2) "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);
@@ -133,7 +25,7 @@
 ; void *scc_C(short *a) {
 ;   return scc_A((int*)(scc_A(a) ? scc_B((double*)a) : scc_C(a)));
 ; }
-define float* @scc_A(i32* %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
@@ -157,7 +49,7 @@
   ret float* %4
 }
 
-define i64* @scc_B(double* %a) {
+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
@@ -181,7 +73,7 @@
   ret i64* %4
 }
 
-define i8* @scc_C(i16* %a) {
+define i8* @scc_C(i16* dereferenceable_or_null(2) %a) {
 entry:
   %bc = bitcast i16* %a to i32*
   %call = call float* @scc_A(i32* %bc)
@@ -208,238 +100,5 @@
 }
 
 
-; 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 i64* @not_captured_but_returned_0(i64* 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 i64* @not_captured_but_returned_1(i64* %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);
-; }
-;
-; FIXME: no-capture missing for %a
-; CHECK: define void @test_not_captured_but_returned_calls(i64* %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* 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* %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 i64* @negative_test_not_captured_but_returned_call_1a(i64* %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* %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 %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.
-;
-; 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)
-;
-; 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)
-;
-; 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)
-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
-  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) #0 {
-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) #0 {
-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) #0 {
-entry:
-  %call = call i32* @readonly_unknown_r1a(i32* %b, i32* %r)
-  ret i32* %call
-}
 
 attributes #0 = { noinline nounwind uwtable }
Index: llvm/test/Transforms/FunctionAttrs/align.ll
===================================================================
--- llvm/test/Transforms/FunctionAttrs/align.ll
+++ llvm/test/Transforms/FunctionAttrs/align.ll
@@ -7,26 +7,26 @@
 
 
 ; TEST 1
-; ATTRIBUTOR: define align 8 i32* @test1(i32* returned align 8 %0)
+; ATTRIBUTOR: define align 8 i32* @test1(i32* returned align 8 "no-capture-maybe-returned" %0)
 define i32* @test1(i32* align 8 %0) #0 {
   ret i32* %0
 }
 
 ; TEST 2
-; ATTRIBUTOR: define i32* @test2(i32* returned %0)
+; ATTRIBUTOR: define i32* @test2(i32* returned "no-capture-maybe-returned" %0)
 define i32* @test2(i32* %0) #0 {
   ret i32* %0
 }
 
 ; TEST 3
-; ATTRIBUTOR: define align 4 i32* @test3(i32* align 8 %0, i32* align 4 %1, i1 %2)
+; ATTRIBUTOR: define align 4 i32* @test3(i32* align 8 "no-capture-maybe-returned" %0, i32* align 4 "no-capture-maybe-returned" %1, i1 %2)
 define i32* @test3(i32* align 8 %0, i32* align 4 %1, i1 %2) #0 {
   %ret = select i1 %2, i32* %0, i32* %1
   ret i32* %ret
 }
 
 ; TEST 4
-; ATTRIBUTOR: define align 32 i32* @test4(i32* align 32 %0, i32* align 32 %1, i1 %2)
+; ATTRIBUTOR: define align 32 i32* @test4(i32* align 32 "no-capture-maybe-returned" %0, i32* align 32 "no-capture-maybe-returned" %1, i1 %2)
 define i32* @test4(i32* align 32 %0, i32* align 32 %1, i1 %2) #0 {
   %ret = select i1 %2, i32* %0, i32* %1
   ret i32* %ret
@@ -87,7 +87,7 @@
 
 ; Function Attrs: nounwind readnone ssp uwtable
 define internal i8* @f1(i8* readnone %0) local_unnamed_addr #0 {
-; ATTRIBUTOR: define internal nonnull align 8 dereferenceable(1) i8* @f1(i8* nonnull readnone align 8 dereferenceable(1) %0)
+; ATTRIBUTOR: define internal nonnull align 8 dereferenceable(1) i8* @f1(i8* nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" %0)
   %2 = icmp eq i8* %0, null
   br i1 %2, label %3, label %5
 
@@ -103,13 +103,13 @@
 
 ; Function Attrs: nounwind readnone ssp uwtable
 define internal i8* @f2(i8* readnone %0) local_unnamed_addr #0 {
-; ATTRIBUTOR: define internal nonnull align 8 dereferenceable(1) i8* @f2(i8* nonnull readnone align 8 dereferenceable(1) %0)
+; ATTRIBUTOR: define internal nonnull align 8 dereferenceable(1) i8* @f2(i8* nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" %0)
   %2 = icmp eq i8* %0, null
   br i1 %2, label %5, label %3
 
 ; <label>:3:                                      ; preds = %1
 
-; ATTRIBUTOR: %4 = tail call nonnull align 8 dereferenceable(1) i8* @f1(i8* nonnull align 8 dereferenceable(1) %0)
+; ATTRIBUTOR: %4 = tail call nonnull align 8 dereferenceable(1) i8* @f1(i8* nonnull align 8 dereferenceable(1) "no-capture-maybe-returned" %0)
   %4 = tail call i8* @f1(i8* nonnull %0)
   br label %7
 
@@ -125,7 +125,7 @@
 
 ; Function Attrs: nounwind readnone ssp uwtable
 define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
-; ATTRIBUTOR: define internal nonnull align 8 dereferenceable(1) i8* @f3(i8* nonnull readnone align 16 dereferenceable(1) %0)
+; ATTRIBUTOR: define internal nonnull align 8 dereferenceable(1) i8* @f3(i8* nocapture nonnull readnone align 16 dereferenceable(1) %0)
   %2 = icmp eq i8* %0, null
   br i1 %2, label %3, label %5
 
@@ -141,7 +141,7 @@
 
 ; TEST 7
 ; Better than IR information
-; ATTRIBUTOR: define align 32 i32* @test7(i32* returned align 32 %p)
+; ATTRIBUTOR: define align 32 i32* @test7(i32* returned align 32 "no-capture-maybe-returned" %p)
 define align 4 i32* @test7(i32* align 32 %p) #0 {
   ret i32* %p
 }
@@ -163,7 +163,7 @@
 }
 
 define internal void @test8(i32* %a, i32* %b, i32* %c) {
-; ATTRIBUTOR: define internal void @test8(i32* align 4 %a, i32* align 4 %b, i32* %c)
+; ATTRIBUTOR: define internal void @test8(i32* nocapture align 4 %a, i32* nocapture align 4 %b, i32* nocapture %c)
   ret void
 }
 
Index: llvm/lib/Transforms/IPO/Attributor.cpp
===================================================================
--- llvm/lib/Transforms/IPO/Attributor.cpp
+++ llvm/lib/Transforms/IPO/Attributor.cpp
@@ -1277,8 +1277,8 @@
       const auto &AA = A.getAAFor<AANonNull>(*this, IRPosition::value(V));
       if (!Stripped && this == &AA) {
         if (!isKnownNonZero(&V, DL, 0, /* TODO: AC */ nullptr,
-                         /* TODO: CtxI */ nullptr,
-                         /* TODO: DT */ nullptr))
+                            /* TODO: CtxI */ nullptr,
+                            /* TODO: DT */ nullptr))
           T.indicatePessimisticFixpoint();
       } else {
         // Use abstract attribute information.
@@ -1524,19 +1524,13 @@
       if (!ICS)
         return false;
 
-      const auto &NoAliasAA =
-          A.getAAFor<AANoAlias>(*this, IRPosition::callsite_returned(ICS));
+      const IRPosition &RVPos = IRPosition::value(RV);
+      const auto &NoAliasAA = A.getAAFor<AANoAlias>(*this, RVPos);
       if (!NoAliasAA.isAssumedNoAlias())
         return false;
 
-      /// FIXME: We can improve capture check in two ways:
-      /// 1. Use the AANoCapture facilities.
-      /// 2. Use the location of return insts for escape queries.
-      if (PointerMayBeCaptured(&RV, /* ReturnCaptures */ false,
-                               /* StoreCaptures */ true))
-        return false;
-
-      return true;
+      const auto &NoCaptureAA = A.getAAFor<AANoCapture>(*this, RVPos);
+      return NoCaptureAA.isAssumedNoCaptureMaybeReturned();
     };
 
     if (!A.checkForAllReturnedValues(CheckReturnValue, *this))
@@ -2226,6 +2220,343 @@
 /// NoReturn attribute deduction for a call sites.
 using AANoReturnCallSite = AANoReturnFunction;
 
+/// ----------------------- Variable Capturing ---------------------------------
+
+/// A class to hold the state of for no-capture attributes.
+struct AANoCaptureImpl : public AANoCapture {
+  AANoCaptureImpl(const IRPosition &IRP) : AANoCapture(IRP) {}
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {
+    if (hasAttr(Attribute::NoCapture)) {
+      indicateOptimisticFixpoint();
+      return;
+    }
+
+    const IRPosition &IRP = getIRPosition();
+    const Function *F =
+        getArgNo() >= 0 ? IRP.getAssociatedFunction() : IRP.getAnchorScope();
+
+    // Check what state the associated function can actually capture.
+    if (F)
+      determineFunctionCaptureCapabilities(*F, *this);
+
+    if (!F || !F->hasExactDefinition())
+      indicatePessimisticFixpoint();
+  }
+
+  /// See AbstractAttribute::updateImpl(...).
+  ChangeStatus updateImpl(Attributor &A) override;
+
+  /// see AbstractAttribute::isAssumedNoCaptureMaybeReturned(...).
+  virtual void
+  getDeducedAttributes(LLVMContext &Ctx,
+                       SmallVectorImpl<Attribute> &Attrs) const override {
+    if (!isAssumedNoCaptureMaybeReturned())
+      return;
+
+    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 associated with \p IRP to capture
+  /// state in memory and through "returning/throwing", respectively.
+  static void determineFunctionCaptureCapabilities(const 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, it can
+    // however return/throw state and the state might be influenced by the pointer
+    // value, e.g., loading from a returned pointer might reveal a bit.
+    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 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,
+                      const AAIsDead &IsDeadAA, IntegerState &State,
+                      SmallVectorImpl<const Value *> &PotentialCopies,
+                      unsigned &RemainingUsesToExplore)
+      : A(A), NoCaptureAA(NoCaptureAA), IsDeadAA(IsDeadAA), 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 {
+      State.indicatePessimisticFixpoint();
+    }
+    return State.isAssumed(AANoCapture::NO_CAPTURE_MAYBE_RETURNED);
+  }
+
+  /// See CaptureTracker::tooManyUses().
+  void tooManyUses() override {
+    State.removeAssumedBits(AANoCapture::NO_CAPTURE);
+  }
+
+  bool isDereferenceableOrNull(Value *O, const DataLayout &DL) override {
+    if (CaptureTracker::isDereferenceableOrNull(O, DL))
+      return true;
+    const auto &DerefAA = A.getAAFor<AADereferenceable>(NoCaptureAA, IRPosition::value(*O));
+    return DerefAA.getAssumedDereferenceableBytes();
+  }
+
+  /// See CaptureTracker::captured(...).
+  bool captured(const Use *U) override {
+    Instruction *UInst = cast<Instruction>(U->getUser());
+    LLVM_DEBUG(dbgs() << "Check use: " << *U->get() << " in " << *UInst
+                      << "\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(dbgs() << " - too many uses to explore!\n");
+      return isCapturedIn(/* Memory */ true, /* Integer */ true,
+                          /* Return */ true);
+    }
+
+    // Deal with ptr2int by following uses.
+    if (isa<PtrToIntInst>(UInst)) {
+      LLVM_DEBUG(dbgs() << " - ptr2int assume the worst!\n");
+      return valueMayBeCaptured(UInst);
+    }
+
+    // Explicitly catch return instructions.
+    if (isa<ReturnInst>(UInst))
+      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(UInst);
+    if (!CS || !CS.isArgOperand(U))
+      return isCapturedIn(/* Memory */ true, /* Integer */ true,
+                          /* Return */ true);
+
+    unsigned ArgNo = CS.getArgumentNo(U);
+    const IRPosition &CSArgPos = IRPosition::callsite_argument(CS, 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, CSArgPos);
+    if (ArgNoCaptureAA.isAssumedNoCapture())
+      return isCapturedIn(/* Memory */ false, /* Integer */ false,
+                          /* Return */ false);
+    if (ArgNoCaptureAA.isAssumedNoCaptureMaybeReturned()) {
+      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 {
+    // Check liveness.
+    return !IsDeadAA.isAssumedDead(cast<Instruction>(U->getUser()));
+  }
+
+  /// 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(AANoCapture::NOT_CAPTURED_IN_MEM);
+    if (CapturedInInt)
+      State.removeAssumedBits(AANoCapture::NOT_CAPTURED_IN_INT);
+    if (CapturedInRet)
+      State.removeAssumedBits(AANoCapture::NOT_CAPTURED_IN_RET);
+    return !State.isAssumed(AANoCapture::NO_CAPTURE_MAYBE_RETURNED);
+  }
+
+private:
+  /// The attributor providing in-flight abstract attributes.
+  Attributor &A;
+
+  /// The abstract attribute currently updated.
+  AANoCapture &NoCaptureAA;
+
+  /// The abstract liveness state.
+  const AAIsDead &IsDeadAA;
+
+  /// The state currently updated.
+  IntegerState &State;
+
+  /// Set of potential copies of the tracked value.
+  SmallVectorImpl<const Value *> &PotentialCopies;
+
+  /// Global counter to limit the number of explored uses.
+  unsigned &RemainingUsesToExplore;
+};
+
+ChangeStatus AANoCaptureImpl::updateImpl(Attributor &A) {
+  const IRPosition &IRP = getIRPosition();
+  const Value *V =
+      getArgNo() >= 0 ? IRP.getAssociatedArgument() : &IRP.getAssociatedValue();
+  if (!V)
+    return indicatePessimisticFixpoint();
+
+  const Function *F =
+      getArgNo() >= 0 ? IRP.getAssociatedFunction() : IRP.getAnchorScope();
+  assert(F && "Expected a function!");
+  const auto &IsDeadAA = A.getAAFor<AAIsDead>(*this, IRPosition::function(*F));
+
+  AAAlign::StateType T;
+  // 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.
+
+  // 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.
+  SmallVector<const Value *, 4> PotentialCopies;
+  unsigned RemainingUsesToExplore = DefaultMaxUsesToExplore;
+  AACaptureUseTracker Tracker(A, *this, IsDeadAA, T, 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(V);
+  while (T.isAssumed(NO_CAPTURE_MAYBE_RETURNED) && Idx < PotentialCopies.size())
+    Tracker.valueMayBeCaptured(PotentialCopies[Idx++]);
+
+  AAAlign::StateType &S = getState();
+  auto Assumed = S.getAssumed();
+  S.intersectAssumedBits(T.getAssumed());
+  return Assumed == S.getAssumed() ? ChangeStatus::UNCHANGED
+                                   : ChangeStatus::CHANGED;
+}
+
+/// NoCapture attribute for function arguments.
+struct AANoCaptureArgument final : AANoCaptureImpl {
+  AANoCaptureArgument(const IRPosition &IRP)
+      : AANoCaptureImpl(IRP) {}
+
+  /// See AbstractAttribute::trackStatistics()
+  void trackStatistics() const override {
+    STATS_DECLTRACK_ARG_ATTR(nocapture)
+  }
+};
+
+/// NoCapture attribute for call site arguments.
+struct AANoCaptureCallSiteArgument final : AANoCaptureImpl {
+  AANoCaptureCallSiteArgument(const IRPosition &IRP) : AANoCaptureImpl(IRP) {}
+
+  /// See AbstractAttribute::updateImpl(...).
+  ChangeStatus updateImpl(Attributor &A) override {
+    // TODO: Once we have call site specific value information we can provide
+    //       call site specific liveness liveness information and then it makes
+    //       sense to specialize attributes for call sites arguments instead of
+    //       redirecting requests to the callee argument.
+    Argument *Arg = getAssociatedArgument();
+    if (!Arg)
+      return indicatePessimisticFixpoint();
+    const IRPosition &ArgPos = IRPosition::argument(*Arg);
+    auto &ArgAA = A.getAAFor<AANoCapture>(*this, ArgPos);
+    return clampStateAndIndicateChange(
+        getState(),
+        static_cast<const AANoCapture::StateType &>(ArgAA.getState()));
+  }
+
+  /// See AbstractAttribute::trackStatistics()
+  void trackStatistics() const override{STATS_DECLTRACK_CSARG_ATTR(nocapture)};
+};
+
+/// NoCapture attribute for floating values.
+struct AANoCaptureFloating final : AANoCaptureImpl {
+  AANoCaptureFloating(const IRPosition &IRP)
+      : AANoCaptureImpl(IRP) {}
+
+  /// See AbstractAttribute::trackStatistics()
+  void trackStatistics() const override {
+    STATS_DECLTRACK_FLOATING_ATTR(nocapture)
+  }
+};
+
+/// NoCapture attribute for function return value.
+struct AANoCaptureReturned final : AANoCaptureImpl {
+  AANoCaptureReturned(const IRPosition &IRP) : AANoCaptureImpl(IRP) {
+    llvm_unreachable("NoCapture is not applicable to function returns!");
+  }
+
+  /// See AbstractAttribute::initialize(...).
+  void initialize(Attributor &A) override {
+    llvm_unreachable("NoCapture is not applicable to function returns!");
+  }
+
+  /// See AbstractAttribute::updateImpl(...).
+  ChangeStatus updateImpl(Attributor &A) override {
+    llvm_unreachable("NoCapture is not applicable to function returns!");
+  }
+
+  /// See AbstractAttribute::trackStatistics()
+  void trackStatistics() const override {}
+};
+
+/// NoCapture attribute deduction for a call site return value.
+using AANoCaptureCallSiteReturned = AANoCaptureFloating;
+
 /// ----------------------------------------------------------------------------
 ///                               Attributor
 /// ----------------------------------------------------------------------------
@@ -2626,6 +2957,9 @@
 
       // Every argument with pointer type might be marked align.
       checkAndRegisterAA<AAAlignArgument>(ArgPos, *this, Whitelist);
+
+      // Every argument with pointer type might be marked nocapture.
+      checkAndRegisterAA<AANoCaptureArgument>(ArgPos, *this, Whitelist);
     }
   }
 
@@ -2862,6 +3196,7 @@
 const char AAIsDead::ID = 0;
 const char AADereferenceable::ID = 0;
 const char AAAlign::ID = 0;
+const char AANoCapture::ID = 0;
 
 // Macro magic to create the static generator function for attributes that
 // follow the naming scheme.
@@ -2922,6 +3257,7 @@
 CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoAlias)
 CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AADereferenceable)
 CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAAlign)
+CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoCapture)
 
 #undef CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION
 #undef CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION
Index: llvm/include/llvm/Transforms/IPO/Attributor.h
===================================================================
--- llvm/include/llvm/Transforms/IPO/Attributor.h
+++ llvm/include/llvm/Transforms/IPO/Attributor.h
@@ -269,6 +269,25 @@
   }
   ///}
 
+  /// Return the associated argument, if any.
+  ///
+  ///{
+  Argument *getAssociatedArgument() {
+    if (auto *Arg = dyn_cast<Argument>(&getAnchorValue()))
+      return Arg;
+    int ArgNo = getArgNo();
+    if (ArgNo < 0)
+      return nullptr;
+    Function *AssociatedFn = getAssociatedFunction();
+    if (!AssociatedFn || AssociatedFn->arg_size() <= ArgNo)
+      return nullptr;
+    return AssociatedFn->arg_begin() + ArgNo;
+  }
+  const Argument *getAssociatedArgument() const {
+    return const_cast<IRPosition *>(this)->getAssociatedArgument();
+  }
+  ///}
+
   /// Return the Function surrounding the anchor value.
   ///
   ///{
@@ -1390,6 +1409,56 @@
   static const char ID;
 };
 
+/// An abstract interface for all nocapture attributes.
+struct AANoCapture
+    : public IRAttribute<Attribute::NoCapture,
+                         StateWrapper<IntegerState, AbstractAttribute>> {
+  AANoCapture(const IRPosition &IRP) : IRAttribute(IRP) {}
+
+  /// 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,
+  };
+
+  /// Return true if we know that the underlying value is not captured in its
+  /// respective scope.
+  bool isKnownNoCapture() const { return isKnown(NO_CAPTURE); }
+
+  /// Return true if we assume that the underlying value is not captured in its
+  /// respective scope.
+  bool isAssumedNoCapture() const { return isAssumed(NO_CAPTURE); }
+
+  /// 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".
+  bool isKnownNoCaptureMaybeReturned() const {
+    return isKnown(NO_CAPTURE_MAYBE_RETURNED);
+  }
+
+  /// 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".
+  bool isAssumedNoCaptureMaybeReturned() const {
+    return isAssumed(NO_CAPTURE_MAYBE_RETURNED);
+  }
+
+  /// Create an abstract attribute view for the position \p IRP.
+  static AANoCapture &createForPosition(const IRPosition &IRP, Attributor &A);
+
+  /// Unique ID (due to the unique address)
+  static const char ID;
+};
+
 } // 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