Index: include/llvm/IR/Intrinsics.td =================================================================== --- include/llvm/IR/Intrinsics.td +++ include/llvm/IR/Intrinsics.td @@ -58,6 +58,9 @@ // Throws - This intrinsic can throw. def Throws : IntrinsicProperty; +// Recursive - This intrinsic can be recursive. +def Recursive : IntrinsicProperty; + // NoCapture - The specified argument pointer is not captured by the intrinsic. class NoCapture : IntrinsicProperty { int ArgNo = argNo; @@ -706,12 +709,12 @@ [llvm_i64_ty, llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_vararg_ty], - [Throws]>; + [Throws, Recursive]>; def int_experimental_patchpoint_i64 : Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i32_ty, llvm_ptr_ty, llvm_i32_ty, llvm_vararg_ty], - [Throws]>; + [Throws, Recursive]>; //===------------------------ Garbage Collection Intrinsics ---------------===// Index: test/Analysis/BasicAA/intrinsics.ll =================================================================== --- test/Analysis/BasicAA/intrinsics.ll +++ test/Analysis/BasicAA/intrinsics.ll @@ -22,6 +22,6 @@ declare <8 x i16> @llvm.masked.load.v8i16.p0v8i16(<8 x i16>*, i32, <8 x i1>, <8 x i16>) nounwind readonly declare void @llvm.masked.store.v8i16.p0v8i16(<8 x i16>, <8 x i16>*, i32, <8 x i1>) nounwind -; CHECK: attributes #0 = { argmemonly nounwind readonly } -; CHECK: attributes #1 = { argmemonly nounwind } +; CHECK: attributes #0 = { argmemonly norecurse nounwind readonly } +; CHECK: attributes #1 = { argmemonly norecurse nounwind } ; CHECK: attributes [[ATTR]] = { nounwind } Index: test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll =================================================================== --- test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll +++ test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll @@ -43,13 +43,13 @@ ; This is unusual, since the function is memcpy, but as above, this ; isn't necessarily invalid. -; CHECK: define void @test2_yes(i8* nocapture %p, i8* nocapture %q, i64 %n) #4 { +; CHECK: define void @test2_yes(i8* nocapture %p, i8* nocapture %q, i64 %n) #0 { define void @test2_yes(i8* %p, i8* %q, i64 %n) nounwind { call void @llvm.memcpy.p0i8.p0i8.i64(i8* %p, i8* %q, i64 %n, i32 1, i1 false), !tbaa !1 ret void } -; CHECK: define void @test2_no(i8* nocapture %p, i8* nocapture readonly %q, i64 %n) #3 { +; CHECK: define void @test2_no(i8* nocapture %p, i8* nocapture readonly %q, i64 %n) #1 { define void @test2_no(i8* %p, i8* %q, i64 %n) nounwind { call void @llvm.memcpy.p0i8.p0i8.i64(i8* %p, i8* %q, i64 %n, i32 1, i1 false), !tbaa !2 ret void @@ -76,8 +76,7 @@ ; CHECK: attributes #1 = { norecurse nounwind } ; CHECK: attributes #2 = { nounwind readonly } ; CHECK: attributes #3 = { nounwind } -; CHECK: attributes #4 = { nounwind readnone } -; CHECK: attributes #5 = { argmemonly nounwind } +; CHECK: attributes #4 = { argmemonly norecurse nounwind } ; Root note. !0 = !{ } Index: test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll =================================================================== --- test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll +++ test/Analysis/TypeBasedAliasAnalysis/intrinsics.ll @@ -22,8 +22,8 @@ declare <8 x i16> @llvm.masked.load.v8i16.p0v8i16(<8 x i16>*, i32, <8 x i1>, <8 x i16>) nounwind readonly declare void @llvm.masked.store.v8i16.p0v8i16(<8 x i16>, <8 x i16>*, i32, <8 x i1>) nounwind -; CHECK: attributes #0 = { argmemonly nounwind readonly } -; CHECK: attributes #1 = { argmemonly nounwind } +; CHECK: attributes #0 = { argmemonly norecurse nounwind readonly } +; CHECK: attributes #1 = { argmemonly norecurse nounwind } ; CHECK: attributes [[NUW]] = { nounwind } !0 = !{!"tbaa root"} Index: test/Bitcode/compatibility-3.6.ll =================================================================== --- test/Bitcode/compatibility-3.6.ll +++ test/Bitcode/compatibility-3.6.ll @@ -981,7 +981,7 @@ ; CHECK: select <2 x i1> , <2 x i8> , <2 x i8> call void @f.nobuiltin() builtin - ; CHECK: call void @f.nobuiltin() #34 + ; CHECK: call void @f.nobuiltin() #35 call fastcc noalias i32* @f.noalias() noinline ; CHECK: call fastcc noalias i32* @f.noalias() #11 @@ -1179,12 +1179,13 @@ ; CHECK: attributes #26 = { sspstrong } ; CHECK: attributes #27 = { uwtable } ; CHECK: attributes #28 = { "cpu"="cortex-a8" } -; CHECK: attributes #29 = { nounwind readnone } -; CHECK: attributes #30 = { argmemonly nounwind readonly } -; CHECK: attributes #31 = { argmemonly nounwind } -; CHECK: attributes #32 = { nounwind readonly } -; CHECK: attributes #33 = { inaccessiblemem_or_argmemonly nounwind } -; CHECK: attributes #34 = { builtin } +; CHECK: attributes #29 = { norecurse nounwind readnone } +; CHECK: attributes #30 = { norecurse nounwind } +; CHECK: attributes #31 = { argmemonly norecurse nounwind readonly } +; CHECK: attributes #32 = { argmemonly norecurse nounwind } +; CHECK: attributes #33 = { norecurse nounwind readonly } +; CHECK: attributes #34 = { inaccessiblemem_or_argmemonly norecurse nounwind } +; CHECK: attributes #35 = { builtin } ;; Metadata Index: test/Bitcode/compatibility-3.7.ll =================================================================== --- test/Bitcode/compatibility-3.7.ll +++ test/Bitcode/compatibility-3.7.ll @@ -1022,7 +1022,7 @@ ; CHECK: select <2 x i1> , <2 x i8> , <2 x i8> call void @f.nobuiltin() builtin - ; CHECK: call void @f.nobuiltin() #37 + ; CHECK: call void @f.nobuiltin() #38 call fastcc noalias i32* @f.noalias() noinline ; CHECK: call fastcc noalias i32* @f.noalias() #12 @@ -1242,12 +1242,13 @@ ; CHECK: attributes #29 = { "thunk" } ; CHECK: attributes #30 = { uwtable } ; CHECK: attributes #31 = { "cpu"="cortex-a8" } -; CHECK: attributes #32 = { nounwind readnone } -; CHECK: attributes #33 = { argmemonly nounwind readonly } -; CHECK: attributes #34 = { argmemonly nounwind } -; CHECK: attributes #35 = { nounwind readonly } -; CHECK: attributes #36 = { inaccessiblemem_or_argmemonly nounwind } -; CHECK: attributes #37 = { builtin } +; CHECK: attributes #32 = { norecurse nounwind readnone } +; CHECK: attributes #33 = { norecurse nounwind } +; CHECK: attributes #34 = { argmemonly norecurse nounwind readonly } +; CHECK: attributes #35 = { argmemonly norecurse nounwind } +; CHECK: attributes #36 = { norecurse nounwind readonly } +; CHECK: attributes #37 = { inaccessiblemem_or_argmemonly norecurse nounwind } +; CHECK: attributes #38 = { builtin } ;; Metadata Index: test/Bitcode/compatibility-3.8.ll =================================================================== --- test/Bitcode/compatibility-3.8.ll +++ test/Bitcode/compatibility-3.8.ll @@ -1170,7 +1170,7 @@ ; CHECK: select <2 x i1> , <2 x i8> , <2 x i8> call void @f.nobuiltin() builtin - ; CHECK: call void @f.nobuiltin() #40 + ; CHECK: call void @f.nobuiltin() #41 call fastcc noalias i32* @f.noalias() noinline ; CHECK: call fastcc noalias i32* @f.noalias() #12 @@ -1552,12 +1552,14 @@ ; CHECK: attributes #32 = { norecurse } ; CHECK: attributes #33 = { inaccessiblememonly } ; CHECK: attributes #34 = { inaccessiblemem_or_argmemonly } -; CHECK: attributes #35 = { nounwind readnone } -; CHECK: attributes #36 = { argmemonly nounwind readonly } -; CHECK: attributes #37 = { argmemonly nounwind } -; CHECK: attributes #38 = { nounwind readonly } -; CHECK: attributes #39 = { inaccessiblemem_or_argmemonly nounwind } -; CHECK: attributes #40 = { builtin } +; CHECK: attributes #35 = { norecurse nounwind readnone } +; CHECK: attributes #36 = { norecurse nounwind } +; CHECK: attributes #37 = { argmemonly norecurse nounwind readonly } +; CHECK: attributes #38 = { argmemonly norecurse nounwind } +; CHECK: attributes #39 = { norecurse nounwind readonly } +; CHECK: attributes #40 = { inaccessiblemem_or_argmemonly norecurse nounwind } +; CHECK: attributes #41 = { builtin } + ;; Metadata Index: test/Bitcode/compatibility-3.9.ll =================================================================== --- test/Bitcode/compatibility-3.9.ll +++ test/Bitcode/compatibility-3.9.ll @@ -1241,7 +1241,7 @@ ; CHECK: select <2 x i1> , <2 x i8> , <2 x i8> call void @f.nobuiltin() builtin - ; CHECK: call void @f.nobuiltin() #41 + ; CHECK: call void @f.nobuiltin() #42 call fastcc noalias i32* @f.noalias() noinline ; CHECK: call fastcc noalias i32* @f.noalias() #12 @@ -1588,7 +1588,7 @@ } declare void @f.writeonly() writeonly -; CHECK: declare void @f.writeonly() #40 +; CHECK: declare void @f.writeonly() #41 ; CHECK: attributes #0 = { alignstack=4 } ; CHECK: attributes #1 = { alignstack=8 } @@ -1625,13 +1625,14 @@ ; CHECK: attributes #32 = { norecurse } ; CHECK: attributes #33 = { inaccessiblememonly } ; CHECK: attributes #34 = { inaccessiblemem_or_argmemonly } -; CHECK: attributes #35 = { nounwind readnone } -; CHECK: attributes #36 = { argmemonly nounwind readonly } -; CHECK: attributes #37 = { argmemonly nounwind } -; CHECK: attributes #38 = { nounwind readonly } -; CHECK: attributes #39 = { inaccessiblemem_or_argmemonly nounwind } -; CHECK: attributes #40 = { writeonly } -; CHECK: attributes #41 = { builtin } +; CHECK: attributes #35 = { norecurse nounwind readnone } +; CHECK: attributes #36 = { norecurse nounwind } +; CHECK: attributes #37 = { argmemonly norecurse nounwind readonly } +; CHECK: attributes #38 = { argmemonly norecurse nounwind } +; CHECK: attributes #39 = { norecurse nounwind readonly } +; CHECK: attributes #40 = { inaccessiblemem_or_argmemonly norecurse nounwind } +; CHECK: attributes #41 = { writeonly } +; CHECK: attributes #42 = { builtin } ;; Metadata Index: test/Bitcode/compatibility-4.0.ll =================================================================== --- test/Bitcode/compatibility-4.0.ll +++ test/Bitcode/compatibility-4.0.ll @@ -1241,7 +1241,7 @@ ; CHECK: select <2 x i1> , <2 x i8> , <2 x i8> call void @f.nobuiltin() builtin - ; CHECK: call void @f.nobuiltin() #41 + ; CHECK: call void @f.nobuiltin() #42 call fastcc noalias i32* @f.noalias() noinline ; CHECK: call fastcc noalias i32* @f.noalias() #12 @@ -1606,7 +1606,7 @@ declare void @f.writeonly() writeonly -; CHECK: declare void @f.writeonly() #40 +; CHECK: declare void @f.writeonly() #41 ;; Constant Expressions @@ -1650,13 +1650,14 @@ ; CHECK: attributes #32 = { norecurse } ; CHECK: attributes #33 = { inaccessiblememonly } ; CHECK: attributes #34 = { inaccessiblemem_or_argmemonly } -; CHECK: attributes #35 = { nounwind readnone } -; CHECK: attributes #36 = { argmemonly nounwind readonly } -; CHECK: attributes #37 = { argmemonly nounwind } -; CHECK: attributes #38 = { nounwind readonly } -; CHECK: attributes #39 = { inaccessiblemem_or_argmemonly nounwind } -; CHECK: attributes #40 = { writeonly } -; CHECK: attributes #41 = { builtin } +; CHECK: attributes #35 = { norecurse nounwind readnone } +; CHECK: attributes #36 = { norecurse nounwind } +; CHECK: attributes #37 = { argmemonly norecurse nounwind readonly } +; CHECK: attributes #38 = { argmemonly norecurse nounwind } +; CHECK: attributes #39 = { norecurse nounwind readonly } +; CHECK: attributes #40 = { inaccessiblemem_or_argmemonly norecurse nounwind } +; CHECK: attributes #41 = { writeonly } +; CHECK: attributes #42 = { builtin } ;; Metadata Index: test/Bitcode/compatibility.ll =================================================================== --- test/Bitcode/compatibility.ll +++ test/Bitcode/compatibility.ll @@ -1250,7 +1250,7 @@ ; CHECK: select <2 x i1> , <2 x i8> , <2 x i8> call void @f.nobuiltin() builtin - ; CHECK: call void @f.nobuiltin() #42 + ; CHECK: call void @f.nobuiltin() #43 call fastcc noalias i32* @f.noalias() noinline ; CHECK: call fastcc noalias i32* @f.noalias() #12 @@ -1615,10 +1615,10 @@ declare void @f.writeonly() writeonly -; CHECK: declare void @f.writeonly() #40 +; CHECK: declare void @f.writeonly() #41 declare void @f.speculatable() speculatable -; CHECK: declare void @f.speculatable() #41 +; CHECK: declare void @f.speculatable() #42 ;; Constant Expressions @@ -1662,14 +1662,14 @@ ; CHECK: attributes #32 = { norecurse } ; CHECK: attributes #33 = { inaccessiblememonly } ; CHECK: attributes #34 = { inaccessiblemem_or_argmemonly } -; CHECK: attributes #35 = { nounwind readnone } -; CHECK: attributes #36 = { argmemonly nounwind readonly } -; CHECK: attributes #37 = { argmemonly nounwind } -; CHECK: attributes #38 = { nounwind readonly } -; CHECK: attributes #39 = { inaccessiblemem_or_argmemonly nounwind } -; CHECK: attributes #40 = { writeonly } -; CHECK: attributes #41 = { speculatable } -; CHECK: attributes #42 = { builtin } +; CHECK: attributes #35 = { norecurse nounwind readnone } +; CHECK: attributes #36 = { norecurse nounwind } +; CHECK: attributes #37 = { argmemonly norecurse nounwind readonly } +; CHECK: attributes #38 = { argmemonly norecurse nounwind } +; CHECK: attributes #39 = { norecurse nounwind readonly } +; CHECK: attributes #40 = { inaccessiblemem_or_argmemonly norecurse nounwind } +; CHECK: attributes #41 = { writeonly } +; CHECK: attributes #42 = { speculatable } ;; Metadata Index: test/Bitcode/ptest-new.ll =================================================================== --- test/Bitcode/ptest-new.ll +++ test/Bitcode/ptest-new.ll @@ -23,4 +23,4 @@ declare i32 @llvm.x86.sse41.ptestnzc(<2 x i64>, <2 x i64>) nounwind readnone ; CHECK: attributes #0 = { nounwind } -; CHECK: attributes #1 = { nounwind readnone } +; CHECK: attributes #1 = { norecurse nounwind readnone } Index: test/Bitcode/ptest-old.ll =================================================================== --- test/Bitcode/ptest-old.ll +++ test/Bitcode/ptest-old.ll @@ -24,4 +24,4 @@ declare i32 @llvm.x86.sse41.ptestnzc(<4 x float>, <4 x float>) nounwind readnone ; CHECK: attributes #0 = { nounwind } -; CHECK: attributes #1 = { nounwind readnone } +; CHECK: attributes #1 = { norecurse nounwind readnone } Index: test/CodeGen/AMDGPU/addrspacecast-constantexpr.ll =================================================================== --- test/CodeGen/AMDGPU/addrspacecast-constantexpr.ll +++ test/CodeGen/AMDGPU/addrspacecast-constantexpr.ll @@ -98,7 +98,7 @@ ret i32 addrspace(3)* addrspacecast (i32 addrspace(4)* getelementptr ([256 x i32], [256 x i32] addrspace(4)* addrspacecast ([256 x i32] addrspace(3)* @lds.arr to [256 x i32] addrspace(4)*), i64 0, i64 8) to i32 addrspace(3)*) } -; HSA: attributes #0 = { argmemonly nounwind } +; HSA: attributes #0 = { argmemonly norecurse nounwind } ; HSA: attributes #1 = { nounwind } ; HSA: attributes #2 = { nounwind "amdgpu-queue-ptr" } Index: test/CodeGen/AMDGPU/annotate-kernel-features-hsa.ll =================================================================== --- test/CodeGen/AMDGPU/annotate-kernel-features-hsa.ll +++ test/CodeGen/AMDGPU/annotate-kernel-features-hsa.ll @@ -224,7 +224,7 @@ attributes #0 = { nounwind readnone speculatable } attributes #1 = { nounwind } -; HSA: attributes #0 = { nounwind readnone speculatable } +; HSA: attributes #0 = { norecurse nounwind readnone speculatable } ; HSA: attributes #1 = { nounwind } ; HSA: attributes #2 = { nounwind "amdgpu-work-group-id-y" } ; HSA: attributes #3 = { nounwind "amdgpu-work-group-id-z" } Index: test/Feature/intrinsics.ll =================================================================== --- test/Feature/intrinsics.ll +++ test/Feature/intrinsics.ll @@ -69,5 +69,5 @@ ret void } -; CHECK: attributes #0 = { nounwind readnone speculatable } -; CHECK: attributes #1 = { noreturn nounwind } +; CHECK: attributes #0 = { norecurse nounwind readnone speculatable } +; CHECK: attributes #1 = { norecurse noreturn nounwind } Index: test/Other/cgscc-devirt-iteration.ll =================================================================== --- test/Other/cgscc-devirt-iteration.ll +++ test/Other/cgscc-devirt-iteration.ll @@ -104,7 +104,8 @@ ; creates the memcpy intrinsic call, and we rely on the count of indirect calls ; decreasing and the count of direct calls increasing. define void @test3(i8* %src, i8* %dest, i64 %size) { -; CHECK-NOT: Function Attrs +; AFTER: Function Attrs: norecurse +; BEFORE-NOT: Function Attrs: ; BEFORE: define void @test3(i8* %src, i8* %dest, i64 %size) ; AFTER: define void @test3(i8* nocapture readonly %src, i8* nocapture %dest, i64 %size) %fptr = alloca i8* (i8*, i8*, i64)* Index: test/Other/invariant.group.barrier.ll =================================================================== --- test/Other/invariant.group.barrier.ll +++ test/Other/invariant.group.barrier.ll @@ -54,7 +54,7 @@ declare void @use(i8* readonly) declare void @clobber(i8*) -; CHECK: Function Attrs: argmemonly nounwind readonly +; CHECK: Function Attrs: argmemonly norecurse nounwind readonly ; CHECK-NEXT: declare i8* @llvm.invariant.group.barrier(i8*) declare i8* @llvm.invariant.group.barrier(i8*) Index: test/Transforms/BBVectorize/simple-int.ll =================================================================== --- test/Transforms/BBVectorize/simple-int.ll +++ test/Transforms/BBVectorize/simple-int.ll @@ -503,4 +503,4 @@ ; CHECK: declare <2 x i64> @llvm.ctpop.v2i64(<2 x i64>) #0 ; CHECK: declare <2 x i64> @llvm.ctlz.v2i64(<2 x i64>, i1) #0 ; CHECK: declare <2 x i64> @llvm.cttz.v2i64(<2 x i64>, i1) #0 -; CHECK: attributes #0 = { nounwind readnone speculatable } +; CHECK: attributes #0 = { norecurse nounwind readnone speculatable } Index: test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll =================================================================== --- test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll +++ test/Transforms/DeadArgElim/2010-04-30-DbgInfo.ll @@ -39,7 +39,7 @@ declare void @llvm.dbg.value(metadata, i64, metadata, metadata) nounwind readnone ; CHECK: attributes #0 = { nounwind ssp } -; CHECK: attributes #1 = { nounwind readnone speculatable } +; CHECK: attributes #1 = { norecurse nounwind readnone speculatable } ; CHECK: attributes #2 = { noinline nounwind ssp } ; CHECK: attributes [[NUW]] = { nounwind } Index: test/Transforms/FunctionAttrs/norecurse.ll =================================================================== --- test/Transforms/FunctionAttrs/norecurse.ll +++ test/Transforms/FunctionAttrs/norecurse.ll @@ -30,7 +30,8 @@ } declare i32 @k() readnone -; CHECK: define void @intrinsic(i8* nocapture %dest, i8* nocapture readonly %src, i32 %len) { +; CHECK: Function Attrs: 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, i32 1, i1 false) ret void @@ -61,5 +62,16 @@ ret void } +; CHECK: define void @checkRecursiveIntrinsics() { +define void @checkRecursiveIntrinsics() { + %result = tail call i64 (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.i64(i64 2, i32 15, i8* null, i32 4, i64 0, i64 0, i64 0, i64 0) + tail call void (i64, i32, i8*, i32, ...) @llvm.experimental.patchpoint.void(i64 3, i32 15, i8* null, i32 2, i64 0, i64 %result) + ret void +} + +; CHECK: declare void @llvm.experimental.patchpoint.void +declare void @llvm.experimental.patchpoint.void(i64, i32, i8*, i32, ...) +declare i64 @llvm.experimental.patchpoint.i64(i64, i32, i8*, i32, ...) + ; CHECK: attributes #0 = { norecurse readnone } ; CHECK: attributes #1 = { readnone } Index: test/Transforms/Inline/inline_invoke.ll =================================================================== --- test/Transforms/Inline/inline_invoke.ll +++ test/Transforms/Inline/inline_invoke.ll @@ -343,7 +343,7 @@ ; CHECK-NEXT: call void @_ZSt9terminatev() ; CHECK: attributes [[NUW]] = { nounwind } -; CHECK: attributes #1 = { nounwind readnone } +; CHECK: attributes #1 = { norecurse nounwind readnone } ; CHECK: attributes #2 = { ssp uwtable } -; CHECK: attributes #3 = { argmemonly nounwind } +; CHECK: attributes #3 = { argmemonly norecurse nounwind } ; CHECK: attributes #4 = { noreturn nounwind } Index: test/Transforms/Inline/noalias-calls.ll =================================================================== --- test/Transforms/Inline/noalias-calls.ll +++ test/Transforms/Inline/noalias-calls.ll @@ -2,10 +2,13 @@ target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" +; CHECK: declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) #0 +; CHECK: declare void @hey() #1 declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture, i8* nocapture readonly, i64, i32, i1) #0 -declare void @hey() #0 +declare void @hey() #1 -define void @hello(i8* noalias nocapture %a, i8* noalias nocapture readonly %c, i8* nocapture %b) #1 { +; CHECK: define void @hello(i8* noalias nocapture %a, i8* noalias nocapture readonly %c, i8* nocapture %b) #2 +define void @hello(i8* noalias nocapture %a, i8* noalias nocapture readonly %c, i8* nocapture %b) #2 { entry: %l = alloca i8, i32 512, align 1 call void @llvm.memcpy.p0i8.p0i8.i64(i8* %a, i8* %b, i64 16, i32 16, i1 0) @@ -16,25 +19,31 @@ ret void } -define void @foo(i8* nocapture %a, i8* nocapture readonly %c, i8* nocapture %b) #2 { +define void @foo(i8* nocapture %a, i8* nocapture readonly %c, i8* nocapture %b) #3 { entry: tail call void @hello(i8* %a, i8* %c, i8* %b) ret void } -; CHECK: define void @foo(i8* nocapture %a, i8* nocapture readonly %c, i8* nocapture %b) #2 { +; CHECK: define void @foo(i8* nocapture %a, i8* nocapture readonly %c, i8* nocapture %b) #3 { ; CHECK: entry: -; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %a, i8* %b, i64 16, i32 16, i1 false) #1, !noalias !0 -; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %b, i8* %c, i64 16, i32 16, i1 false) #1, !noalias !3 -; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %a, i8* %c, i64 16, i32 16, i1 false) #1, !alias.scope !5 -; CHECK: call void @hey() #1, !noalias !5 -; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %{{.*}}, i8* %c, i64 16, i32 16, i1 false) #1, !noalias !3 +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %a, i8* %b, i64 16, i32 16, i1 false) #2, !noalias !0 +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %b, i8* %c, i64 16, i32 16, i1 false) #2, !noalias !3 +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %a, i8* %c, i64 16, i32 16, i1 false) #2, !alias.scope !5 +; CHECK: call void @hey() #2, !noalias !5 +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* %{{.*}}, i8* %c, i64 16, i32 16, i1 false) #2, !noalias !3 ; CHECK: ret void ; CHECK: } -attributes #0 = { nounwind argmemonly } -attributes #1 = { nounwind } -attributes #2 = { nounwind uwtable } +; CHECK: attributes #0 = { argmemonly norecurse nounwind } +; CHECK: attributes #1 = { argmemonly nounwind } +; CHECK: attributes #2 = { nounwind } +; CHECK: attributes #3 = { nounwind uwtable } +attributes #0 = { argmemonly norecurse nounwind } +attributes #1 = { argmemonly nounwind } +attributes #2 = { nounwind } +attributes #3 = { nounwind uwtable } + ; CHECK: !0 = !{!1} ; CHECK: !1 = distinct !{!1, !2, !"hello: %c"} Index: test/Transforms/InstCombine/AMDGPU/amdgcn-intrinsics.ll =================================================================== --- test/Transforms/InstCombine/AMDGPU/amdgcn-intrinsics.ll +++ test/Transforms/InstCombine/AMDGPU/amdgcn-intrinsics.ll @@ -1259,7 +1259,7 @@ } ; CHECK-LABEL: @icmp_constant_inputs_true( -; CHECK: %result = call i64 @llvm.read_register.i64(metadata !0) #5 +; CHECK: %result = call i64 @llvm.read_register.i64(metadata !0) #6 define i64 @icmp_constant_inputs_true() { %result = call i64 @llvm.amdgcn.icmp.i32(i32 9, i32 8, i32 34) ret i64 %result @@ -1524,7 +1524,7 @@ } ; CHECK-LABEL: @fcmp_constant_inputs_true( -; CHECK: %result = call i64 @llvm.read_register.i64(metadata !0) #5 +; CHECK: %result = call i64 @llvm.read_register.i64(metadata !0) #6 define i64 @fcmp_constant_inputs_true() { %result = call i64 @llvm.amdgcn.fcmp.f32(float 2.0, float 4.0, i32 4) ret i64 %result @@ -1537,4 +1537,10 @@ ret i64 %result } -; CHECK: attributes #5 = { convergent } +; CHECK: attributes #0 = { norecurse nounwind readnone speculatable } +; CHECK: attributes #1 = { nounwind } +; CHECK: attributes #2 = { norecurse nounwind } +; CHECK: attributes #3 = { convergent norecurse nounwind readnone } +; CHECK: attributes #4 = { norecurse nounwind readonly } +; CHECK: attributes #5 = { nounwind readnone } +; CHECK: attributes #6 = { convergent } Index: test/Transforms/MemCpyOpt/memcpy.ll =================================================================== --- test/Transforms/MemCpyOpt/memcpy.ll +++ test/Transforms/MemCpyOpt/memcpy.ll @@ -234,6 +234,6 @@ declare void @f2(%struct.big*) ; CHECK: attributes [[NUW]] = { nounwind } -; CHECK: attributes #1 = { argmemonly nounwind } +; CHECK: attributes #1 = { argmemonly norecurse nounwind } ; CHECK: attributes #2 = { nounwind ssp } ; CHECK: attributes #3 = { nounwind ssp uwtable } Index: test/Transforms/ObjCARC/basic.ll =================================================================== --- test/Transforms/ObjCARC/basic.ll +++ test/Transforms/ObjCARC/basic.ll @@ -3049,6 +3049,6 @@ !4 = !DIFile(filename: "path/to/file", directory: "/path/to/dir") !5 = !{i32 2, !"Debug Info Version", i32 3} -; CHECK: attributes #0 = { nounwind readnone speculatable } +; CHECK: attributes #0 = { norecurse nounwind readnone speculatable } ; CHECK: attributes [[NUW]] = { nounwind } ; CHECK: ![[RELEASE]] = !{} Index: test/Transforms/ObjCARC/ensure-that-exception-unwind-path-is-visited.ll =================================================================== --- test/Transforms/ObjCARC/ensure-that-exception-unwind-path-is-visited.ll +++ test/Transforms/ObjCARC/ensure-that-exception-unwind-path-is-visited.ll @@ -105,7 +105,7 @@ declare void @llvm.dbg.value(metadata, i64, metadata, metadata) nounwind readnone ; CHECK: attributes #0 = { ssp uwtable } -; CHECK: attributes #1 = { nounwind readnone speculatable } +; CHECK: attributes #1 = { norecurse nounwind readnone speculatable } ; CHECK: attributes #2 = { nonlazybind } ; CHECK: attributes #3 = { noinline ssp uwtable } ; CHECK: attributes [[NUW]] = { nounwind } Index: test/Transforms/ObjCARC/nested.ll =================================================================== --- test/Transforms/ObjCARC/nested.ll +++ test/Transforms/ObjCARC/nested.ll @@ -820,6 +820,6 @@ } -; CHECK: attributes #0 = { argmemonly nounwind } +; CHECK: attributes #0 = { argmemonly norecurse nounwind } ; CHECK: attributes #1 = { nonlazybind } ; CHECK: attributes [[NUW]] = { nounwind } Index: test/Transforms/RewriteStatepointsForGC/statepoint-attrs.ll =================================================================== --- test/Transforms/RewriteStatepointsForGC/statepoint-attrs.ll +++ test/Transforms/RewriteStatepointsForGC/statepoint-attrs.ll @@ -6,11 +6,11 @@ ; copy over norecurse noimplicitfloat to statepoint call define void @test1(i8 addrspace(1)* %arg) gc "statepoint-example" { ; CHECK-LABEL: test1( -; CHECK: call token (i64, i32, void (i8 addrspace(1)*)*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidp1i8f(i64 2882400000, i32 0, void (i8 addrspace(1)*)* @f, i32 1, i32 0, i8 addrspace(1)* %arg, i32 0, i32 0, i8 addrspace(1)* %arg) #1 +; CHECK: call token (i64, i32, void (i8 addrspace(1)*)*, i32, i32, ...) @llvm.experimental.gc.statepoint.p0f_isVoidp1i8f(i64 2882400000, i32 0, void (i8 addrspace(1)*)* @f, i32 1, i32 0, i8 addrspace(1)* %arg, i32 0, i32 0, i8 addrspace(1)* %arg) #[[ATTR:.*]] call void @f(i8 addrspace(1)* %arg) #1 ret void } - +; CHECK: attributes #[[ATTR]] = { noimplicitfloat norecurse } attributes #1 = { norecurse noimplicitfloat } Index: test/Transforms/SLPVectorizer/X86/call.ll =================================================================== --- test/Transforms/SLPVectorizer/X86/call.ll +++ test/Transforms/SLPVectorizer/X86/call.ll @@ -147,5 +147,5 @@ ; CHECK: declare <2 x double> @llvm.pow.v2f64(<2 x double>, <2 x double>) [[ATTR0]] ; CHECK: declare <2 x double> @llvm.exp2.v2f64(<2 x double>) [[ATTR0]] -; CHECK: attributes [[ATTR0]] = { nounwind readnone speculatable } +; CHECK: attributes [[ATTR0]] = { norecurse nounwind readnone speculatable } Index: test/Transforms/SimplifyCFG/switch-on-const-select.ll =================================================================== --- test/Transforms/SimplifyCFG/switch-on-const-select.ll +++ test/Transforms/SimplifyCFG/switch-on-const-select.ll @@ -138,4 +138,4 @@ declare void @bees.b() nounwind ; CHECK: attributes [[NUW]] = { nounwind } -; CHECK: attributes #1 = { noreturn nounwind } +; CHECK: attributes #1 = { norecurse noreturn nounwind } Index: test/Verifier/fp-intrinsics.ll =================================================================== --- test/Verifier/fp-intrinsics.ll +++ test/Verifier/fp-intrinsics.ll @@ -12,7 +12,7 @@ ; attached to the FP intrinsic. ; CHECK1: declare double @llvm.experimental.constrained.fadd.f64(double, double, metadata, metadata) #[[ATTR:[0-9]+]] ; CHECK1: declare double @llvm.experimental.constrained.sqrt.f64(double, metadata, metadata) #[[ATTR]] -; CHECK1: attributes #[[ATTR]] = { inaccessiblememonly nounwind } +; CHECK1: attributes #[[ATTR]] = { inaccessiblememonly norecurse nounwind } ; Note: FP exceptions aren't usually caught through normal unwind mechanisms, ; but we may want to revisit this for asynchronous exception handling. define double @f1(double %a, double %b) { Index: utils/TableGen/CodeGenIntrinsics.h =================================================================== --- utils/TableGen/CodeGenIntrinsics.h +++ utils/TableGen/CodeGenIntrinsics.h @@ -130,6 +130,9 @@ // True if the intrinsic is marked as speculatable. bool isSpeculatable; + /// True if intrinsic can be recursive. + bool isRecursive; + enum ArgAttribute { NoCapture, Returned, ReadOnly, WriteOnly, ReadNone }; std::vector> ArgumentAttributes; Index: utils/TableGen/CodeGenTarget.cpp =================================================================== --- utils/TableGen/CodeGenTarget.cpp +++ utils/TableGen/CodeGenTarget.cpp @@ -519,6 +519,7 @@ isConvergent = false; isSpeculatable = false; hasSideEffects = false; + isRecursive = false; if (DefName.size() <= 4 || std::string(DefName.begin(), DefName.begin() + 4) != "int_") @@ -651,6 +652,8 @@ isCommutative = true; else if (Property->getName() == "Throws") canThrow = true; + else if (Property->getName() == "Recursive") + isRecursive = true; else if (Property->getName() == "IntrNoDuplicate") isNoDuplicate = true; else if (Property->getName() == "IntrConvergent") Index: utils/TableGen/IntrinsicEmitter.cpp =================================================================== --- utils/TableGen/IntrinsicEmitter.cpp +++ utils/TableGen/IntrinsicEmitter.cpp @@ -473,6 +473,9 @@ if (L->canThrow != R->canThrow) return R->canThrow; + if (L->isRecursive != R->isRecursive) + return R->isRecursive; + if (L->isNoDuplicate != R->isNoDuplicate) return R->isNoDuplicate; @@ -610,7 +613,7 @@ } } - if (!intrinsic.canThrow || + if (!intrinsic.canThrow || !intrinsic.isRecursive || intrinsic.ModRef != CodeGenIntrinsic::ReadWriteMem || intrinsic.isNoReturn || intrinsic.isNoDuplicate || intrinsic.isConvergent || intrinsic.isSpeculatable) { @@ -620,6 +623,14 @@ OS << "Attribute::NoUnwind"; addComma = true; } + if (!intrinsic.isRecursive) { + if (addComma) + OS << ","; + OS << "Attribute::NoRecurse"; + addComma = true; + } else + assert(false && "This is recursive"); + if (intrinsic.isNoReturn) { if (addComma) OS << ",";