summaryrefslogtreecommitdiffstats
path: root/llvm/test/Transforms/FunctionAttrs
diff options
context:
space:
mode:
authorEric Christopher <echristo@gmail.com>2019-04-17 04:52:47 +0000
committerEric Christopher <echristo@gmail.com>2019-04-17 04:52:47 +0000
commitcee313d288a4faf0355d76fb6e0e927e211d08a5 (patch)
treed386075318d761197779a96e5d8fc0dc7b06342b /llvm/test/Transforms/FunctionAttrs
parentc3d6a929fdd92fd06d4304675ade8d7210ee711a (diff)
downloadbcm5719-llvm-cee313d288a4faf0355d76fb6e0e927e211d08a5.tar.gz
bcm5719-llvm-cee313d288a4faf0355d76fb6e0e927e211d08a5.zip
Revert "Temporarily Revert "Add basic loop fusion pass.""
The reversion apparently deleted the test/Transforms directory. Will be re-reverting again. llvm-svn: 358552
Diffstat (limited to 'llvm/test/Transforms/FunctionAttrs')
-rw-r--r--llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll18
-rw-r--r--llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll32
-rw-r--r--llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll14
-rw-r--r--llvm/test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll12
-rw-r--r--llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll12
-rw-r--r--llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll24
-rw-r--r--llvm/test/Transforms/FunctionAttrs/2010-10-30-volatile.ll14
-rw-r--r--llvm/test/Transforms/FunctionAttrs/assume.ll5
-rw-r--r--llvm/test/Transforms/FunctionAttrs/atomic.ll24
-rw-r--r--llvm/test/Transforms/FunctionAttrs/comdat-ipo.ll17
-rw-r--r--llvm/test/Transforms/FunctionAttrs/convergent.ll110
-rw-r--r--llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll32
-rw-r--r--llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll29
-rw-r--r--llvm/test/Transforms/FunctionAttrs/naked_functions.ll25
-rw-r--r--llvm/test/Transforms/FunctionAttrs/nocapture.ll257
-rw-r--r--llvm/test/Transforms/FunctionAttrs/nonnull-global.ll11
-rw-r--r--llvm/test/Transforms/FunctionAttrs/nonnull.ll240
-rw-r--r--llvm/test/Transforms/FunctionAttrs/norecurse.ll91
-rw-r--r--llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll17
-rw-r--r--llvm/test/Transforms/FunctionAttrs/optnone-simple.ll135
-rw-r--r--llvm/test/Transforms/FunctionAttrs/optnone.ll25
-rw-r--r--llvm/test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll31
-rw-r--r--llvm/test/Transforms/FunctionAttrs/readattrs.ll115
-rw-r--r--llvm/test/Transforms/FunctionAttrs/readnone.ll14
-rw-r--r--llvm/test/Transforms/FunctionAttrs/returned.ll31
25 files changed, 1335 insertions, 0 deletions
diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll
new file mode 100644
index 00000000000..6bbd99951ad
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/2008-09-03-Mutual.ll
@@ -0,0 +1,18 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+
+; CHECK: Function Attrs
+; CHECK-SAME: readnone
+; CHECK-NEXT: define i32 @a
+define i32 @a() {
+ %tmp = call i32 @b( ) ; <i32> [#uses=1]
+ ret i32 %tmp
+}
+
+; CHECK: Function Attrs
+; CHECK-SAME: readnone
+; CHECK-NEXT: define i32 @b
+define i32 @b() {
+ %tmp = call i32 @a( ) ; <i32> [#uses=1]
+ ret i32 %tmp
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll
new file mode 100644
index 00000000000..d747fe727fa
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadNone.ll
@@ -0,0 +1,32 @@
+; RUN: opt < %s -basicaa -functionattrs -S | FileCheck %s
+; RUN: opt < %s -aa-pipeline=basic-aa -passes=function-attrs -S | FileCheck %s
+
+@x = global i32 0
+
+; CHECK: Function Attrs
+; CHECK-SAME: readnone
+; CHECK-NEXT: declare i32 @e
+declare i32 @e() readnone
+
+; CHECK: Function Attrs
+; CHECK-SAME: readnone
+; CHECK-NEXT: define i32 @f
+define i32 @f() {
+ %tmp = call i32 @e( ) ; <i32> [#uses=1]
+ ret i32 %tmp
+}
+
+; CHECK: Function Attrs
+; CHECK-SAME: readnone
+; CHECK-NEXT: define i32 @g
+define i32 @g() readonly {
+ ret i32 0
+}
+
+; CHECK: Function Attrs
+; CHECK-SAME: readnone
+; CHECK-NEXT: define i32 @h
+define i32 @h() readnone {
+ %tmp = load i32, i32* @x ; <i32> [#uses=1]
+ ret i32 %tmp
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll
new file mode 100644
index 00000000000..35cb5342d8d
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/2008-09-03-ReadOnly.ll
@@ -0,0 +1,14 @@
+; RUN: opt < %s -basicaa -functionattrs -S | FileCheck %s
+; RUN: opt < %s -aa-pipeline=basic-aa -passes=function-attrs -S | FileCheck %s
+
+; CHECK: define i32 @f() #0
+define i32 @f() {
+entry:
+ %tmp = call i32 @e( )
+ ret i32 %tmp
+}
+
+; CHECK: declare i32 @e() #0
+declare i32 @e() readonly
+
+; CHECK: attributes #0 = { readonly }
diff --git a/llvm/test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll b/llvm/test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll
new file mode 100644
index 00000000000..8212e8945ec
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/2008-09-13-VolatileRead.ll
@@ -0,0 +1,12 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+; PR2792
+
+@g = global i32 0 ; <i32*> [#uses=1]
+
+define i32 @f() {
+ %t = load volatile i32, i32* @g ; <i32> [#uses=1]
+ ret i32 %t
+}
+
+; CHECK-NOT: attributes #{{.*}} read
diff --git a/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll b/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll
new file mode 100644
index 00000000000..ee1a8caa20e
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/2008-12-29-Constant.ll
@@ -0,0 +1,12 @@
+; RUN: opt < %s -basicaa -functionattrs -S | FileCheck %s
+; RUN: opt < %s -aa-pipeline=basic-aa -passes=function-attrs -S | FileCheck %s
+
+@s = external constant i8 ; <i8*> [#uses=1]
+
+; CHECK: define i8 @f() #0
+define i8 @f() {
+ %tmp = load i8, i8* @s ; <i8> [#uses=1]
+ ret i8 %tmp
+}
+
+; CHECK: attributes #0 = { {{.*}} readnone
diff --git a/llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll b/llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll
new file mode 100644
index 00000000000..ce72c416563
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/2009-01-02-LocalStores.ll
@@ -0,0 +1,24 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+
+; CHECK: define i32* @a(i32** nocapture readonly %p)
+define i32* @a(i32** %p) {
+ %tmp = load i32*, i32** %p
+ ret i32* %tmp
+}
+
+; CHECK: define i32* @b(i32* %q)
+define i32* @b(i32 *%q) {
+ %mem = alloca i32*
+ store i32* %q, i32** %mem
+ %tmp = call i32* @a(i32** %mem)
+ ret i32* %tmp
+}
+
+; CHECK: define i32* @c(i32* readnone returned %r)
+@g = global i32 0
+define i32* @c(i32 *%r) {
+ %a = icmp eq i32* %r, null
+ store i32 1, i32* @g
+ ret i32* %r
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/2010-10-30-volatile.ll b/llvm/test/Transforms/FunctionAttrs/2010-10-30-volatile.ll
new file mode 100644
index 00000000000..b9536dce8a4
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/2010-10-30-volatile.ll
@@ -0,0 +1,14 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+; PR8279
+
+@g = constant i32 1
+
+; CHECK: Function Attrs
+; CHECK-SAME: norecurse
+; CHECK-NOT: readonly
+; CHECK-NEXT: void @foo()
+define void @foo() {
+ %tmp = load volatile i32, i32* @g
+ ret void
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/assume.ll b/llvm/test/Transforms/FunctionAttrs/assume.ll
new file mode 100644
index 00000000000..d6296624a2d
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/assume.ll
@@ -0,0 +1,5 @@
+; RUN: opt -S -o - -functionattrs %s | FileCheck %s
+; RUN: opt -S -o - -passes=function-attrs %s | FileCheck %s
+
+; CHECK-NOT: readnone
+declare void @llvm.assume(i1)
diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll
new file mode 100644
index 00000000000..af87a28770e
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll
@@ -0,0 +1,24 @@
+; RUN: opt -basicaa -functionattrs -S < %s | FileCheck %s
+; RUN: opt -aa-pipeline=basic-aa -passes=function-attrs -S < %s | FileCheck %s
+
+; Atomic load/store to local doesn't affect whether a function is
+; readnone/readonly.
+define i32 @test1(i32 %x) uwtable ssp {
+; CHECK: define i32 @test1(i32 %x) #0 {
+entry:
+ %x.addr = alloca i32, align 4
+ store atomic i32 %x, i32* %x.addr seq_cst, align 4
+ %r = load atomic i32, i32* %x.addr seq_cst, align 4
+ ret i32 %r
+}
+
+; A function with an Acquire load is not readonly.
+define i32 @test2(i32* %x) uwtable ssp {
+; CHECK: define i32 @test2(i32* nocapture readonly %x) #1 {
+entry:
+ %r = load atomic i32, i32* %x seq_cst, align 4
+ ret i32 %r
+}
+
+; CHECK: attributes #0 = { norecurse nounwind readnone ssp uwtable }
+; CHECK: attributes #1 = { norecurse nounwind ssp uwtable }
diff --git a/llvm/test/Transforms/FunctionAttrs/comdat-ipo.ll b/llvm/test/Transforms/FunctionAttrs/comdat-ipo.ll
new file mode 100644
index 00000000000..2a149e436b6
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/comdat-ipo.ll
@@ -0,0 +1,17 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+
+; See PR26774
+
+; CHECK-LABEL: define void @bar(i8* readonly) {
+define void @bar(i8* readonly) {
+ call void @foo(i8* %0)
+ ret void
+}
+
+
+; CHECK-LABEL: define linkonce_odr void @foo(i8* readonly) {
+define linkonce_odr void @foo(i8* readonly) {
+ call void @bar(i8* %0)
+ ret void
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/convergent.ll b/llvm/test/Transforms/FunctionAttrs/convergent.ll
new file mode 100644
index 00000000000..0e4b7515d01
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/convergent.ll
@@ -0,0 +1,110 @@
+; 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
+
+; CHECK: Function Attrs
+; CHECK-NOT: convergent
+; CHECK-NEXT: define i32 @nonleaf()
+define i32 @nonleaf() convergent {
+ %a = call i32 @leaf()
+ ret i32 %a
+}
+
+; CHECK: Function Attrs
+; CHECK-NOT: convergent
+; CHECK-NEXT: define i32 @leaf()
+define i32 @leaf() convergent {
+ ret i32 0
+}
+
+; CHECK: Function Attrs
+; CHECK-SAME: convergent
+; CHECK-NEXT: declare i32 @k()
+declare i32 @k() convergent
+
+; CHECK: Function Attrs
+; CHECK-SAME: convergent
+; CHECK-NEXT: define i32 @extern()
+define i32 @extern() convergent {
+ %a = call i32 @k() convergent
+ ret i32 %a
+}
+
+; Convergent should not be removed on the function here. Although the call is
+; not explicitly convergent, it picks up the convergent attr from the callee.
+;
+; CHECK: Function Attrs
+; CHECK-SAME: convergent
+; CHECK-NEXT: define i32 @extern_non_convergent_call()
+define i32 @extern_non_convergent_call() convergent {
+ %a = call i32 @k()
+ ret i32 %a
+}
+
+; CHECK: Function Attrs
+; CHECK-SAME: convergent
+; CHECK-NEXT: define i32 @indirect_convergent_call(
+define i32 @indirect_convergent_call(i32 ()* %f) convergent {
+ %a = call i32 %f() convergent
+ ret i32 %a
+}
+; Give indirect_non_convergent_call the norecurse attribute so we get a
+; "Function Attrs" comment in the output.
+;
+; CHECK: Function Attrs
+; CHECK-INDIRECT-NOT: convergent
+; CHECK-INDIRECT-NEXT: define i32 @indirect_non_convergent_call(
+define i32 @indirect_non_convergent_call(i32 ()* %f) convergent norecurse {
+ %a = call i32 %f()
+ ret i32 %a
+}
+
+; CHECK: Function Attrs
+; CHECK-SAME: convergent
+; CHECK-NEXT: declare void @llvm.nvvm.barrier0()
+declare void @llvm.nvvm.barrier0() convergent
+
+; CHECK: Function Attrs
+; CHECK-SAME: convergent
+; CHECK-NEXT: define i32 @intrinsic()
+define i32 @intrinsic() convergent {
+ ; Implicitly convergent, because the intrinsic is convergent.
+ call void @llvm.nvvm.barrier0()
+ ret i32 0
+}
+
+; CHECK: Function Attrs
+; CHECK-NOT: convergent
+; CHECK-NEXT: define i32 @recursive1()
+define i32 @recursive1() convergent {
+ %a = call i32 @recursive2() convergent
+ ret i32 %a
+}
+
+; CHECK: Function Attrs
+; CHECK-NOT: convergent
+; CHECK-NEXT: define i32 @recursive2()
+define i32 @recursive2() convergent {
+ %a = call i32 @recursive1() convergent
+ ret i32 %a
+}
+
+; CHECK: Function Attrs
+; CHECK-SAME: convergent
+; CHECK-NEXT: define i32 @noopt()
+define i32 @noopt() convergent optnone noinline {
+ %a = call i32 @noopt_friend() convergent
+ ret i32 0
+}
+
+; A function which is mutually-recursive with a convergent, optnone function
+; shouldn't have its convergent attribute stripped.
+; CHECK: Function Attrs
+; CHECK-SAME: convergent
+; CHECK-NEXT: define i32 @noopt_friend()
+define i32 @noopt_friend() convergent {
+ %a = call i32 @noopt()
+ ret i32 0
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll
new file mode 100644
index 00000000000..79af817ff03
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll
@@ -0,0 +1,32 @@
+; RUN: opt -S -o - -functionattrs %s | FileCheck %s
+; RUN: opt -S -o - -passes=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 {
+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 {
+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 {
+entry:
+ ret i32* %p
+}
+
+attributes #0 = { argmemonly }
+attributes #1 = { inaccessiblememonly }
+attributes #2 = { inaccessiblemem_or_argmemonly }
+; CHECK: attributes #0 = { norecurse nounwind readnone }
+; CHECK-NOT: attributes
diff --git a/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll b/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll
new file mode 100644
index 00000000000..24a145908e0
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/int_sideeffect.ll
@@ -0,0 +1,29 @@
+; RUN: opt -S < %s -functionattrs | FileCheck %s
+; RUN: opt -S < %s -passes=function-attrs | FileCheck %s
+
+; CHECK: Function Attrs
+; CHECK-SAME: inaccessiblememonly
+; CHECK-NEXT: declare void @llvm.sideeffect()
+declare void @llvm.sideeffect()
+
+; Don't add readnone or similar attributes when an @llvm.sideeffect() intrinsic
+; is present.
+
+; CHECK: Function Attrs
+; CHECK-NOT: readnone
+; CHECK: define void @test()
+define void @test() {
+ call void @llvm.sideeffect()
+ ret void
+}
+
+; CHECK: Function Attrs
+; CHECK-NOT: readnone
+; CHECK: define void @loop()
+define void @loop() {
+ br label %loop
+
+loop:
+ call void @llvm.sideeffect()
+ br label %loop
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/naked_functions.ll b/llvm/test/Transforms/FunctionAttrs/naked_functions.ll
new file mode 100644
index 00000000000..d34dc0c20d9
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/naked_functions.ll
@@ -0,0 +1,25 @@
+; RUN: opt -S -functionattrs %s | FileCheck %s
+; RUN: opt -S -passes='function-attrs' %s | FileCheck %s
+
+; Don't change the attributes of parameters of naked functions, in particular
+; don't mark them as readnone
+
+@g = common global i32 0, align 4
+
+define i32 @bar() {
+entry:
+ %call = call i32 @foo(i32* @g)
+; CHECK: %call = call i32 @foo(i32* @g)
+ ret i32 %call
+}
+
+define internal i32 @foo(i32*) #0 {
+entry:
+ %retval = alloca i32, align 4
+ call void asm sideeffect "ldr r0, [r0] \0Abx lr \0A", ""()
+ unreachable
+}
+
+; CHECK: define internal i32 @foo(i32*)
+
+attributes #0 = { naked }
diff --git a/llvm/test/Transforms/FunctionAttrs/nocapture.ll b/llvm/test/Transforms/FunctionAttrs/nocapture.ll
new file mode 100644
index 00000000000..0c3fc0af725
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/nocapture.ll
@@ -0,0 +1,257 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+
+@g = global i32* null ; <i32**> [#uses=1]
+
+; CHECK: define i32* @c1(i32* readnone returned %q)
+define i32* @c1(i32* %q) {
+ ret i32* %q
+}
+
+; CHECK: 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)
+define void @c3(i32* %q) {
+ call void @c2(i32* %q)
+ ret void
+}
+
+; CHECK: 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
+ %bit = trunc i32 %tmp2 to i1
+ br i1 %bit, label %l1, label %l0
+l0:
+ ret i1 0 ; escaping value not caught by def-use chaining.
+l1:
+ ret i1 1 ; escaping value not caught by def-use chaining.
+}
+
+@lookup_table = global [2 x i1] [ i1 0, i1 1 ]
+
+; CHECK: 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
+ %bit = and i32 %tmp2, 1
+ ; subtle escape mechanism follows
+ %lookup = getelementptr [2 x i1], [2 x i1]* @lookup_table, i32 0, i32 %bit
+ %val = load i1, i1* %lookup
+ ret i1 %val
+}
+
+declare void @throw_if_bit_set(i8*, i8) readonly
+
+; CHECK: define i1 @c6(i8* readonly %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
+ret0:
+ ret i1 0
+ret1:
+ %exn = landingpad {i8*, i32}
+ cleanup
+ ret i1 1
+}
+
+declare i32 @__gxx_personality_v0(...)
+
+define i1* @lookup_bit(i32* %q, i32 %bitno) readnone nounwind {
+ %tmp = ptrtoint i32* %q to i32
+ %tmp2 = lshr i32 %tmp, %bitno
+ %bit = and i32 %tmp2, 1
+ %lookup = getelementptr [2 x i1], [2 x i1]* @lookup_table, i32 0, i32 %bit
+ ret i1* %lookup
+}
+
+; CHECK: define i1 @c7(i32* readonly %q, i32 %bitno)
+define i1 @c7(i32* %q, i32 %bitno) {
+ %ptr = call i1* @lookup_bit(i32* %q, i32 %bitno)
+ %val = load i1, i1* %ptr
+ ret i1 %val
+}
+
+
+; CHECK: define i32 @nc1(i32* %q, i32* nocapture %p, i1 %b)
+define i32 @nc1(i32* %q, i32* %p, i1 %b) {
+e:
+ br label %l
+l:
+ %x = phi i32* [ %p, %e ]
+ %y = phi i32* [ %q, %e ]
+ %tmp = bitcast i32* %x to i32* ; <i32*> [#uses=2]
+ %tmp2 = select i1 %b, i32* %tmp, i32* %y
+ %val = load i32, i32* %tmp2 ; <i32> [#uses=1]
+ store i32 0, i32* %tmp
+ store i32* %y, i32** @g
+ ret i32 %val
+}
+
+; CHECK: 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
+l:
+ %x = phi i32 addrspace(1)* [ %p, %e ]
+ %y = phi i32* [ %q, %e ]
+ %tmp = addrspacecast i32 addrspace(1)* %x to i32* ; <i32*> [#uses=2]
+ %tmp2 = select i1 %b, i32* %tmp, i32* %y
+ %val = load i32, i32* %tmp2 ; <i32> [#uses=1]
+ store i32 0, i32* %tmp
+ store i32* %y, i32** @g
+ ret i32 %val
+}
+
+; CHECK: 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)
+define void @nc3(void ()* %p) {
+ call void %p()
+ ret void
+}
+
+declare void @external(i8*) readonly nounwind
+; CHECK: 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)
+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)
+; 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)
+ 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)
+ store i32* null, i32** @g
+ ret i8* %y1_2
+}
+
+; CHECK: define void @test2(i8* nocapture readnone %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)
+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)
+ 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)
+ store i32* null, i32** @g
+ ret i8* %y4_2
+}
+
+declare i8* @test5_1(i8* %x5_1)
+
+; CHECK: 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
+ ret void
+}
+
+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)
+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)
+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)
+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)
+define void @test_atomicrmw(i32* %p) {
+ atomicrmw add i32* %p, i32 1 seq_cst
+ ret void
+}
+
+; CHECK: define void @test_volatile(i32* %x)
+define void @test_volatile(i32* %x) {
+entry:
+ %gep = getelementptr i32, i32* %x, i64 1
+ store volatile i32 0, i32* %gep, align 4
+ ret void
+}
+
+; CHECK: nocaptureLaunder(i8* nocapture %p)
+define void @nocaptureLaunder(i8* %p) {
+entry:
+ %b = call i8* @llvm.launder.invariant.group.p0i8(i8* %p)
+ store i8 42, i8* %b
+ ret void
+}
+
+@g2 = global i8* null
+; CHECK: 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)
+define void @nocaptureStrip(i8* %p) {
+entry:
+ %b = call i8* @llvm.strip.invariant.group.p0i8(i8* %p)
+ store i8 42, i8* %b
+ ret void
+}
+
+@g3 = global i8* null
+; CHECK: 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
+}
+
+declare i8* @llvm.launder.invariant.group.p0i8(i8*)
+declare i8* @llvm.strip.invariant.group.p0i8(i8*)
diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull-global.ll b/llvm/test/Transforms/FunctionAttrs/nonnull-global.ll
new file mode 100644
index 00000000000..d79a7ae290a
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/nonnull-global.ll
@@ -0,0 +1,11 @@
+; RUN: opt -S -functionattrs %s | FileCheck %s
+; RUN: opt -S -passes=function-attrs %s | FileCheck %s
+
+@a = external global i8, !absolute_symbol !0
+
+; CHECK-NOT: define nonnull
+define i8* @foo() {
+ ret i8* @a
+}
+
+!0 = !{i64 0, i64 256}
diff --git a/llvm/test/Transforms/FunctionAttrs/nonnull.ll b/llvm/test/Transforms/FunctionAttrs/nonnull.ll
new file mode 100644
index 00000000000..7029be9691d
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/nonnull.ll
@@ -0,0 +1,240 @@
+; RUN: opt -S -functionattrs -enable-nonnull-arg-prop %s | FileCheck %s
+; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s
+
+declare nonnull i8* @ret_nonnull()
+
+; Return a pointer trivially nonnull (call return attribute)
+define i8* @test1() {
+; CHECK: define nonnull i8* @test1
+ %ret = call i8* @ret_nonnull()
+ ret i8* %ret
+}
+
+; Return a pointer trivially nonnull (argument attribute)
+define i8* @test2(i8* nonnull %p) {
+; CHECK: define nonnull i8* @test2
+ ret i8* %p
+}
+
+; Given an SCC where one of the functions can not be marked nonnull,
+; can we still mark the other one which is trivially nonnull
+define i8* @scc_binder() {
+; CHECK: define i8* @scc_binder
+ call i8* @test3()
+ ret i8* null
+}
+
+define i8* @test3() {
+; CHECK: define nonnull i8* @test3
+ call i8* @scc_binder()
+ %ret = call i8* @ret_nonnull()
+ ret i8* %ret
+}
+
+; Given a mutual recursive set of functions, we can mark them
+; nonnull if neither can ever return null. (In this case, they
+; just never return period.)
+define i8* @test4_helper() {
+; CHECK: define noalias nonnull i8* @test4_helper
+ %ret = call i8* @test4()
+ ret i8* %ret
+}
+
+define i8* @test4() {
+; CHECK: define noalias nonnull i8* @test4
+ %ret = call i8* @test4_helper()
+ ret i8* %ret
+}
+
+; Given a mutual recursive set of functions which *can* return null
+; make sure we haven't marked them as nonnull.
+define i8* @test5_helper() {
+; CHECK: define noalias i8* @test5_helper
+ %ret = call i8* @test5()
+ ret i8* null
+}
+
+define i8* @test5() {
+; CHECK: define noalias i8* @test5
+ %ret = call i8* @test5_helper()
+ ret i8* %ret
+}
+
+; Local analysis, but going through a self recursive phi
+define i8* @test6() {
+entry:
+; CHECK: define nonnull i8* @test6
+ %ret = call i8* @ret_nonnull()
+ br label %loop
+loop:
+ %phi = phi i8* [%ret, %entry], [%phi, %loop]
+ br i1 undef, label %loop, label %exit
+exit:
+ ret i8* %phi
+}
+
+; Test propagation of nonnull callsite args back to caller.
+
+declare void @use1(i8* %x)
+declare void @use2(i8* %x, i8* %y);
+declare void @use3(i8* %x, i8* %y, i8* %z);
+
+declare void @use1nonnull(i8* nonnull %x);
+declare void @use2nonnull(i8* nonnull %x, i8* nonnull %y);
+declare void @use3nonnull(i8* nonnull %x, i8* nonnull %y, i8* nonnull %z);
+
+declare i8 @use1safecall(i8* %x) readonly nounwind ; readonly+nounwind guarantees that execution continues to successor
+
+; Can't extend non-null to parent for any argument because the 2nd call is not guaranteed to execute.
+
+define void @parent1(i8* %a, i8* %b, i8* %c) {
+; CHECK-LABEL: @parent1(i8* %a, i8* %b, i8* %c)
+; CHECK-NEXT: call void @use3(i8* %c, i8* %a, i8* %b)
+; CHECK-NEXT: call void @use3nonnull(i8* %b, i8* %c, i8* %a)
+; CHECK-NEXT: ret void
+;
+ call void @use3(i8* %c, i8* %a, i8* %b)
+ call void @use3nonnull(i8* %b, i8* %c, i8* %a)
+ ret void
+}
+
+; Extend non-null to parent for all arguments.
+
+define void @parent2(i8* %a, i8* %b, i8* %c) {
+; CHECK-LABEL: @parent2(i8* nonnull %a, i8* nonnull %b, i8* nonnull %c)
+; CHECK-NEXT: call void @use3nonnull(i8* %b, i8* %c, i8* %a)
+; CHECK-NEXT: call void @use3(i8* %c, i8* %a, i8* %b)
+; CHECK-NEXT: ret void
+;
+ call void @use3nonnull(i8* %b, i8* %c, i8* %a)
+ call void @use3(i8* %c, i8* %a, i8* %b)
+ ret void
+}
+
+; Extend non-null to parent for 1st argument.
+
+define void @parent3(i8* %a, i8* %b, i8* %c) {
+; CHECK-LABEL: @parent3(i8* nonnull %a, i8* %b, i8* %c)
+; CHECK-NEXT: call void @use1nonnull(i8* %a)
+; CHECK-NEXT: call void @use3(i8* %c, i8* %b, i8* %a)
+; CHECK-NEXT: ret void
+;
+ call void @use1nonnull(i8* %a)
+ call void @use3(i8* %c, i8* %b, i8* %a)
+ ret void
+}
+
+; Extend non-null to parent for last 2 arguments.
+
+define void @parent4(i8* %a, i8* %b, i8* %c) {
+; CHECK-LABEL: @parent4(i8* %a, i8* nonnull %b, i8* nonnull %c)
+; CHECK-NEXT: call void @use2nonnull(i8* %c, i8* %b)
+; CHECK-NEXT: call void @use2(i8* %a, i8* %c)
+; CHECK-NEXT: call void @use1(i8* %b)
+; CHECK-NEXT: ret void
+;
+ call void @use2nonnull(i8* %c, i8* %b)
+ call void @use2(i8* %a, i8* %c)
+ call void @use1(i8* %b)
+ ret void
+}
+
+; The callsite must execute in order for the attribute to transfer to the parent.
+; It appears benign to extend non-null to the parent in this case, but we can't do that
+; because it would incorrectly propagate the wrong information to its callers.
+
+define void @parent5(i8* %a, i1 %a_is_notnull) {
+; CHECK-LABEL: @parent5(i8* %a, i1 %a_is_notnull)
+; CHECK-NEXT: br i1 %a_is_notnull, label %t, label %f
+; CHECK: t:
+; CHECK-NEXT: call void @use1nonnull(i8* %a)
+; CHECK-NEXT: ret void
+; CHECK: f:
+; CHECK-NEXT: ret void
+;
+ br i1 %a_is_notnull, label %t, label %f
+t:
+ call void @use1nonnull(i8* %a)
+ ret void
+f:
+ ret void
+}
+
+; The callsite must execute in order for the attribute to transfer to the parent.
+; The volatile load might trap, so there's no guarantee that we'll ever get to the call.
+
+define i8 @parent6(i8* %a, i8* %b) {
+; CHECK-LABEL: @parent6(i8* %a, i8* %b)
+; CHECK-NEXT: [[C:%.*]] = load volatile i8, i8* %b
+; CHECK-NEXT: call void @use1nonnull(i8* %a)
+; CHECK-NEXT: ret i8 [[C]]
+;
+ %c = load volatile i8, i8* %b
+ call void @use1nonnull(i8* %a)
+ ret i8 %c
+}
+
+; The nonnull callsite is guaranteed to execute, so the argument must be nonnull throughout the parent.
+
+define i8 @parent7(i8* %a) {
+; CHECK-LABEL: @parent7(i8* nonnull %a)
+; CHECK-NEXT: [[RET:%.*]] = call i8 @use1safecall(i8* %a)
+; CHECK-NEXT: call void @use1nonnull(i8* %a)
+; CHECK-NEXT: ret i8 [[RET]]
+;
+ %ret = call i8 @use1safecall(i8* %a)
+ call void @use1nonnull(i8* %a)
+ ret i8 %ret
+}
+
+; Make sure that an invoke works similarly to a call.
+
+declare i32 @esfp(...)
+
+define i1 @parent8(i8* %a, i8* %bogus1, i8* %b) personality i8* bitcast (i32 (...)* @esfp to i8*){
+; CHECK-LABEL: @parent8(i8* nonnull %a, i8* nocapture readnone %bogus1, i8* nonnull %b)
+; CHECK-NEXT: entry:
+; CHECK-NEXT: invoke void @use2nonnull(i8* %a, i8* %b)
+; CHECK-NEXT: to label %cont unwind label %exc
+; CHECK: cont:
+; CHECK-NEXT: [[NULL_CHECK:%.*]] = icmp eq i8* %b, null
+; CHECK-NEXT: ret i1 [[NULL_CHECK]]
+; CHECK: exc:
+; CHECK-NEXT: [[LP:%.*]] = landingpad { i8*, i32 }
+; CHECK-NEXT: filter [0 x i8*] zeroinitializer
+; CHECK-NEXT: unreachable
+;
+entry:
+ invoke void @use2nonnull(i8* %a, i8* %b)
+ to label %cont unwind label %exc
+
+cont:
+ %null_check = icmp eq i8* %b, null
+ ret i1 %null_check
+
+exc:
+ %lp = landingpad { i8*, i32 }
+ filter [0 x i8*] zeroinitializer
+ unreachable
+}
+
+; CHECK: define nonnull i32* @gep1(
+define i32* @gep1(i32* %p) {
+ %q = getelementptr inbounds i32, i32* %p, i32 1
+ ret i32* %q
+}
+
+define i32* @gep1_no_null_opt(i32* %p) #0 {
+; Should't be able to derive nonnull based on gep.
+; CHECK: define i32* @gep1_no_null_opt(
+ %q = getelementptr inbounds i32, i32* %p, i32 1
+ ret i32* %q
+}
+
+; CHECK: define i32 addrspace(3)* @gep2(
+define i32 addrspace(3)* @gep2(i32 addrspace(3)* %p) {
+ %q = getelementptr inbounds i32, i32 addrspace(3)* %p, i32 1
+ ret i32 addrspace(3)* %q
+}
+
+attributes #0 = { "null-pointer-is-valid"="true" }
diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll
new file mode 100644
index 00000000000..0293938e479
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/norecurse.ll
@@ -0,0 +1,91 @@
+; RUN: opt < %s -basicaa -functionattrs -rpo-functionattrs -S | FileCheck %s
+; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs),rpo-functionattrs' -S | FileCheck %s
+
+; CHECK: Function Attrs
+; CHECK-SAME: norecurse nounwind readnone
+; CHECK-NEXT: define i32 @leaf()
+define i32 @leaf() {
+ ret i32 1
+}
+
+; CHECK: Function Attrs
+; CHECK-SAME: readnone
+; CHECK-NOT: norecurse
+; CHECK-NEXT: define i32 @self_rec()
+define i32 @self_rec() {
+ %a = call i32 @self_rec()
+ ret i32 4
+}
+
+; CHECK: Function Attrs
+; CHECK-SAME: readnone
+; CHECK-NOT: norecurse
+; CHECK-NEXT: define i32 @indirect_rec()
+define i32 @indirect_rec() {
+ %a = call i32 @indirect_rec2()
+ ret i32 %a
+}
+; CHECK: Function Attrs
+; CHECK-SAME: readnone
+; CHECK-NOT: norecurse
+; CHECK-NEXT: define i32 @indirect_rec2()
+define i32 @indirect_rec2() {
+ %a = call i32 @indirect_rec()
+ ret i32 %a
+}
+
+; CHECK: Function Attrs
+; CHECK-SAME: readnone
+; CHECK-NOT: norecurse
+; CHECK-NEXT: define i32 @extern()
+define i32 @extern() {
+ %a = call i32 @k()
+ ret i32 %a
+}
+
+; CHECK: Function Attrs
+; CHECK-NEXT: declare i32 @k()
+declare i32 @k() readnone
+
+; CHECK: Function Attrs
+; CHECK-SAME: nounwind
+; CHECK-NOT: norecurse
+; CHECK-NEXT: define void @intrinsic(i8* nocapture %dest, i8* nocapture readonly %src, i32 %len)
+define void @intrinsic(i8* %dest, i8* %src, i32 %len) {
+ call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dest, i8* %src, i32 %len, i1 false)
+ ret void
+}
+
+; CHECK: Function Attrs
+; CHECK-NEXT: declare void @llvm.memcpy.p0i8.p0i8.i32
+declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i1)
+
+; CHECK: Function Attrs
+; CHECK-SAME: norecurse readnone
+; CHECK-NEXT: define internal i32 @called_by_norecurse()
+define internal i32 @called_by_norecurse() {
+ %a = call i32 @k()
+ ret i32 %a
+}
+; CHECK: Function Attrs
+; CHECK-NEXT: define void @m()
+define void @m() norecurse {
+ %a = call i32 @called_by_norecurse()
+ ret void
+}
+
+; CHECK: Function Attrs
+; CHECK-SAME: norecurse readnone
+; CHECK-NEXT: define internal i32 @called_by_norecurse_indirectly()
+define internal i32 @called_by_norecurse_indirectly() {
+ %a = call i32 @k()
+ ret i32 %a
+}
+define internal void @o() {
+ %a = call i32 @called_by_norecurse_indirectly()
+ ret void
+}
+define void @p() norecurse {
+ call void @o()
+ ret void
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll b/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll
new file mode 100644
index 00000000000..69808a83c6d
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/operand-bundles-scc.ll
@@ -0,0 +1,17 @@
+; RUN: opt -S -functionattrs < %s | FileCheck %s
+; RUN: opt -S -passes=function-attrs < %s | FileCheck %s
+
+define void @f() {
+; CHECK-LABEL: define void @f() #0 {
+ call void @g() [ "unknown"() ]
+ ret void
+}
+
+define void @g() {
+; CHECK-LABEL: define void @g() #0 {
+ call void @f()
+ ret void
+}
+
+
+; CHECK: attributes #0 = { nounwind }
diff --git a/llvm/test/Transforms/FunctionAttrs/optnone-simple.ll b/llvm/test/Transforms/FunctionAttrs/optnone-simple.ll
new file mode 100644
index 00000000000..beaa588da50
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/optnone-simple.ll
@@ -0,0 +1,135 @@
+; RUN: opt -O3 -S < %s | FileCheck %s
+; Show 'optnone' suppresses optimizations.
+
+; Two attribute groups that differ only by 'optnone'.
+; 'optnone' requires 'noinline' so #0 is 'noinline' by itself,
+; even though it would otherwise be irrelevant to this example.
+attributes #0 = { noinline }
+attributes #1 = { noinline optnone }
+
+; int iadd(int a, int b){ return a + b; }
+
+define i32 @iadd_optimize(i32 %a, i32 %b) #0 {
+entry:
+ %a.addr = alloca i32, align 4
+ %b.addr = alloca i32, align 4
+ store i32 %a, i32* %a.addr, align 4
+ store i32 %b, i32* %b.addr, align 4
+ %0 = load i32, i32* %a.addr, align 4
+ %1 = load i32, i32* %b.addr, align 4
+ %add = add nsw i32 %0, %1
+ ret i32 %add
+}
+
+; CHECK-LABEL: @iadd_optimize
+; CHECK-NOT: alloca
+; CHECK-NOT: store
+; CHECK-NOT: load
+; CHECK: ret
+
+define i32 @iadd_optnone(i32 %a, i32 %b) #1 {
+entry:
+ %a.addr = alloca i32, align 4
+ %b.addr = alloca i32, align 4
+ store i32 %a, i32* %a.addr, align 4
+ store i32 %b, i32* %b.addr, align 4
+ %0 = load i32, i32* %a.addr, align 4
+ %1 = load i32, i32* %b.addr, align 4
+ %add = add nsw i32 %0, %1
+ ret i32 %add
+}
+
+; CHECK-LABEL: @iadd_optnone
+; CHECK: alloca i32
+; CHECK: alloca i32
+; CHECK: store i32
+; CHECK: store i32
+; CHECK: load i32
+; CHECK: load i32
+; CHECK: add nsw i32
+; CHECK: ret i32
+
+; float fsub(float a, float b){ return a - b; }
+
+define float @fsub_optimize(float %a, float %b) #0 {
+entry:
+ %a.addr = alloca float, align 4
+ %b.addr = alloca float, align 4
+ store float %a, float* %a.addr, align 4
+ store float %b, float* %b.addr, align 4
+ %0 = load float, float* %a.addr, align 4
+ %1 = load float, float* %b.addr, align 4
+ %sub = fsub float %0, %1
+ ret float %sub
+}
+
+; CHECK-LABEL: @fsub_optimize
+; CHECK-NOT: alloca
+; CHECK-NOT: store
+; CHECK-NOT: load
+; CHECK: ret
+
+define float @fsub_optnone(float %a, float %b) #1 {
+entry:
+ %a.addr = alloca float, align 4
+ %b.addr = alloca float, align 4
+ store float %a, float* %a.addr, align 4
+ store float %b, float* %b.addr, align 4
+ %0 = load float, float* %a.addr, align 4
+ %1 = load float, float* %b.addr, align 4
+ %sub = fsub float %0, %1
+ ret float %sub
+}
+
+; CHECK-LABEL: @fsub_optnone
+; CHECK: alloca float
+; CHECK: alloca float
+; CHECK: store float
+; CHECK: store float
+; CHECK: load float
+; CHECK: load float
+; CHECK: fsub float
+; CHECK: ret float
+
+; typedef float __attribute__((ext_vector_type(4))) float4;
+; float4 vmul(float4 a, float4 b){ return a * b; }
+
+define <4 x float> @vmul_optimize(<4 x float> %a, <4 x float> %b) #0 {
+entry:
+ %a.addr = alloca <4 x float>, align 16
+ %b.addr = alloca <4 x float>, align 16
+ store <4 x float> %a, <4 x float>* %a.addr, align 16
+ store <4 x float> %b, <4 x float>* %b.addr, align 16
+ %0 = load <4 x float>, <4 x float>* %a.addr, align 16
+ %1 = load <4 x float>, <4 x float>* %b.addr, align 16
+ %mul = fmul <4 x float> %0, %1
+ ret <4 x float> %mul
+}
+
+; CHECK-LABEL: @vmul_optimize
+; CHECK-NOT: alloca
+; CHECK-NOT: store
+; CHECK-NOT: load
+; CHECK: ret
+
+define <4 x float> @vmul_optnone(<4 x float> %a, <4 x float> %b) #1 {
+entry:
+ %a.addr = alloca <4 x float>, align 16
+ %b.addr = alloca <4 x float>, align 16
+ store <4 x float> %a, <4 x float>* %a.addr, align 16
+ store <4 x float> %b, <4 x float>* %b.addr, align 16
+ %0 = load <4 x float>, <4 x float>* %a.addr, align 16
+ %1 = load <4 x float>, <4 x float>* %b.addr, align 16
+ %mul = fmul <4 x float> %0, %1
+ ret <4 x float> %mul
+}
+
+; CHECK-LABEL: @vmul_optnone
+; CHECK: alloca <4 x float>
+; CHECK: alloca <4 x float>
+; CHECK: store <4 x float>
+; CHECK: store <4 x float>
+; CHECK: load <4 x float>
+; CHECK: load <4 x float>
+; CHECK: fmul <4 x float>
+; CHECK: ret
diff --git a/llvm/test/Transforms/FunctionAttrs/optnone.ll b/llvm/test/Transforms/FunctionAttrs/optnone.ll
new file mode 100644
index 00000000000..586a6d4a081
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/optnone.ll
@@ -0,0 +1,25 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+
+@x = global i32 0
+
+define void @test_opt(i8* %p) {
+; CHECK-LABEL: @test_opt
+; CHECK: (i8* nocapture readnone %p) #0 {
+ ret void
+}
+
+define void @test_optnone(i8* %p) noinline optnone {
+; CHECK-LABEL: @test_optnone
+; CHECK: (i8* %p) #1 {
+ ret void
+}
+
+declare i8 @strlen(i8*) noinline optnone
+; CHECK-LABEL: @strlen
+; CHECK: (i8*) #1
+
+; CHECK-LABEL: attributes #0
+; CHECK: = { norecurse nounwind readnone }
+; CHECK-LABEL: attributes #1
+; CHECK: = { noinline optnone }
diff --git a/llvm/test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll b/llvm/test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll
new file mode 100644
index 00000000000..f2294fe22ef
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/out-of-bounds-iterator-bug.ll
@@ -0,0 +1,31 @@
+; RUN: opt -functionattrs -S < %s | FileCheck %s
+; RUN: opt -passes=function-attrs -S < %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.
+; Inferring readonly for %x *is* actually correct, since @va_func is marked
+; readonly, but FunctionAttrs was inferring readonly for the wrong reasons (and
+; we _need_ the readonly on @va_func to trigger the problematic code path). It
+; is possible that in the future FunctionAttrs becomes smart enough to infer
+; readonly for %x for the right reasons, and at that point this test will have
+; to be marked invalid.
+
+declare void @llvm.va_start(i8*)
+declare void @llvm.va_end(i8*)
+
+define void @va_func(i32* readonly %b, ...) readonly nounwind {
+; CHECK-LABEL: define void @va_func(i32* nocapture readonly %b, ...)
+ entry:
+ %valist = alloca i8
+ call void @llvm.va_start(i8* %valist)
+ call void @llvm.va_end(i8* %valist)
+ %x = call i32 @caller(i32* %b)
+ ret void
+}
+
+define i32 @caller(i32* %x) {
+; CHECK-LABEL: define i32 @caller(i32* nocapture %x)
+ entry:
+ call void(i32*,...) @va_func(i32* null, i32 0, i32 0, i32 0, i32* %x)
+ ret i32 42
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/readattrs.ll b/llvm/test/Transforms/FunctionAttrs/readattrs.ll
new file mode 100644
index 00000000000..3728a717972
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/readattrs.ll
@@ -0,0 +1,115 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -aa-pipeline=basic-aa -passes='cgscc(function-attrs)' -S | FileCheck %s
+@x = global i32 0
+
+declare void @test1_1(i8* %x1_1, i8* readonly %y1_1, ...)
+
+; CHECK: define void @test1_2(i8* %x1_2, i8* readonly %y1_2, i8* %z1_2)
+define void @test1_2(i8* %x1_2, i8* %y1_2, i8* %z1_2) {
+ call void (i8*, i8*, ...) @test1_1(i8* %x1_2, i8* %y1_2, i8* %z1_2)
+ store i32 0, i32* @x
+ ret void
+}
+
+; CHECK: define i8* @test2(i8* readnone returned %p)
+define i8* @test2(i8* %p) {
+ store i32 0, i32* @x
+ ret i8* %p
+}
+
+; CHECK: define i1 @test3(i8* readnone %p, i8* readnone %q)
+define i1 @test3(i8* %p, i8* %q) {
+ %A = icmp ult i8* %p, %q
+ ret i1 %A
+}
+
+declare void @test4_1(i8* nocapture) readonly
+
+; CHECK: define void @test4_2(i8* nocapture readonly %p)
+define void @test4_2(i8* %p) {
+ call void @test4_1(i8* %p)
+ ret void
+}
+
+; CHECK: define void @test5(i8** nocapture %p, i8* %q)
+; Missed optz'n: we could make %q readnone, but don't break test6!
+define void @test5(i8** %p, i8* %q) {
+ store i8* %q, i8** %p
+ ret void
+}
+
+declare void @test6_1()
+; CHECK: define void @test6_2(i8** nocapture %p, i8* %q)
+; This is not a missed optz'n.
+define void @test6_2(i8** %p, i8* %q) {
+ store i8* %q, i8** %p
+ call void @test6_1()
+ ret void
+}
+
+; CHECK: define void @test7_1(i32* inalloca nocapture %a)
+; inalloca parameters are always considered written
+define void @test7_1(i32* inalloca %a) {
+ ret void
+}
+
+; CHECK: define i32* @test8_1(i32* readnone returned %p)
+define i32* @test8_1(i32* %p) {
+entry:
+ ret i32* %p
+}
+
+; CHECK: define void @test8_2(i32* %p)
+define void @test8_2(i32* %p) {
+entry:
+ %call = call i32* @test8_1(i32* %p)
+ store i32 10, i32* %call, align 4
+ ret void
+}
+
+; CHECK: declare void @llvm.masked.scatter
+declare void @llvm.masked.scatter.v4i32.v4p0i32(<4 x i32>%val, <4 x i32*>, i32, <4 x i1>)
+
+; CHECK-NOT: readnone
+; CHECK-NOT: readonly
+; CHECK: define void @test9
+define void @test9(<4 x i32*> %ptrs, <4 x i32>%val) {
+ call void @llvm.masked.scatter.v4i32.v4p0i32(<4 x i32>%val, <4 x i32*> %ptrs, i32 4, <4 x i1><i1 true, i1 false, i1 true, i1 false>)
+ ret void
+}
+
+; CHECK: declare <4 x i32> @llvm.masked.gather
+declare <4 x i32> @llvm.masked.gather.v4i32.v4p0i32(<4 x i32*>, i32, <4 x i1>, <4 x i32>)
+; CHECK: readonly
+; CHECK: define <4 x i32> @test10
+define <4 x i32> @test10(<4 x i32*> %ptrs) {
+ %res = call <4 x i32> @llvm.masked.gather.v4i32.v4p0i32(<4 x i32*> %ptrs, i32 4, <4 x i1><i1 true, i1 false, i1 true, i1 false>, <4 x i32>undef)
+ ret <4 x i32> %res
+}
+
+; CHECK: declare <4 x i32> @test11_1
+declare <4 x i32> @test11_1(<4 x i32*>) argmemonly nounwind readonly
+; CHECK: readonly
+; CHECK-NOT: readnone
+; CHECK: define <4 x i32> @test11_2
+define <4 x i32> @test11_2(<4 x i32*> %ptrs) {
+ %res = call <4 x i32> @test11_1(<4 x i32*> %ptrs)
+ ret <4 x i32> %res
+}
+
+declare <4 x i32> @test12_1(<4 x i32*>) argmemonly nounwind
+; CHECK-NOT: readnone
+; CHECK: define <4 x i32> @test12_2
+define <4 x i32> @test12_2(<4 x i32*> %ptrs) {
+ %res = call <4 x i32> @test12_1(<4 x i32*> %ptrs)
+ ret <4 x i32> %res
+}
+
+; CHECK: define i32 @volatile_load(
+; CHECK-NOT: readonly
+; CHECK: ret
+define i32 @volatile_load(i32* %p) {
+ %load = load volatile i32, i32* %p
+ ret i32 %load
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/readnone.ll b/llvm/test/Transforms/FunctionAttrs/readnone.ll
new file mode 100644
index 00000000000..b5a5b30ae21
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/readnone.ll
@@ -0,0 +1,14 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+
+; CHECK: define void @bar(i8* nocapture readnone)
+define void @bar(i8* readonly) {
+ call void @foo(i8* %0)
+ ret void
+}
+
+; CHECK: define void @foo(i8* nocapture readnone)
+define void @foo(i8* readonly) {
+ call void @bar(i8* %0)
+ ret void
+}
diff --git a/llvm/test/Transforms/FunctionAttrs/returned.ll b/llvm/test/Transforms/FunctionAttrs/returned.ll
new file mode 100644
index 00000000000..04ddb7b5ac0
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/returned.ll
@@ -0,0 +1,31 @@
+; RUN: opt < %s -functionattrs -S | FileCheck %s
+; RUN: opt < %s -passes=function-attrs -S | FileCheck %s
+
+; CHECK: define i32 @test1(i32 %p, i32 %q)
+define i32 @test1(i32 %p, i32 %q) {
+entry:
+ %cmp = icmp sgt i32 %p, %q
+ br i1 %cmp, label %cond.end, label %lor.lhs.false
+
+lor.lhs.false: ; preds = %entry
+ %tobool = icmp ne i32 %p, 0
+ %tobool1 = icmp ne i32 %q, 0
+ %or.cond = and i1 %tobool, %tobool1
+ %p.q = select i1 %or.cond, i32 %p, i32 %q
+ ret i32 %p.q
+
+cond.end: ; preds = %entry
+ ret i32 %p
+}
+
+; CHECK: define i32 @test2(i32 %p1, i32 returned %p2)
+define i32 @test2(i32 %p1, i32 returned %p2) {
+ %_tmp4 = icmp eq i32 %p1, %p2
+ br i1 %_tmp4, label %bb2, label %bb1
+
+bb2: ; preds = %0
+ ret i32 %p1
+
+bb1: ; preds = %bb1, %0
+ br label %bb1
+}
OpenPOWER on IntegriCloud