Author: Nikita Popov Date: 2021-01-21T20:29:33+01:00 New Revision: 65fd034b95d69fa0e634861ee165b502ceb92a12
URL: https://github.com/llvm/llvm-project/commit/65fd034b95d69fa0e634861ee165b502ceb92a12 DIFF: https://github.com/llvm/llvm-project/commit/65fd034b95d69fa0e634861ee165b502ceb92a12.diff LOG: [FunctionAttrs] Infer willreturn for functions without loops If a function doesn't contain loops and does not call non-willreturn functions, then it is willreturn. Loops are detected by checking for backedges in the function. We don't attempt to handle finite loops at this point. Differential Revision: https://reviews.llvm.org/D94633 Added: Modified: clang/test/CodeGenOpenCL/convergent.cl llvm/lib/Transforms/IPO/FunctionAttrs.cpp llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll llvm/test/CodeGen/AMDGPU/inline-attr.ll llvm/test/Transforms/FunctionAttrs/atomic.ll llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll llvm/test/Transforms/FunctionAttrs/nofree.ll llvm/test/Transforms/FunctionAttrs/optnone.ll llvm/test/Transforms/FunctionAttrs/willreturn.ll llvm/test/Transforms/FunctionAttrs/writeonly.ll llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll Removed: ################################################################################ diff --git a/clang/test/CodeGenOpenCL/convergent.cl b/clang/test/CodeGenOpenCL/convergent.cl index 5e4f6fad1b3a..25951a64c114 100644 --- a/clang/test/CodeGenOpenCL/convergent.cl +++ b/clang/test/CodeGenOpenCL/convergent.cl @@ -134,7 +134,7 @@ kernel void assume_convergent_asm() __asm__ volatile("s_barrier"); } -// CHECK: attributes #0 = { nofree noinline norecurse nounwind " +// CHECK: attributes #0 = { nofree noinline norecurse nounwind willreturn " // CHECK: attributes #1 = { {{[^}]*}}convergent{{[^}]*}} } // CHECK: attributes #2 = { {{[^}]*}}convergent{{[^}]*}} } // CHECK: attributes #3 = { {{[^}]*}}convergent noduplicate{{[^}]*}} } diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index 210186a0550e..30a1f81ad0e1 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/AssumptionCache.h" #include "llvm/Analysis/BasicAliasAnalysis.h" +#include "llvm/Analysis/CFG.h" #include "llvm/Analysis/CGSCCPassManager.h" #include "llvm/Analysis/CallGraph.h" #include "llvm/Analysis/CallGraphSCCPass.h" @@ -1425,12 +1426,36 @@ static bool addNoReturnAttrs(const SCCNodeSet &SCCNodes) { return Changed; } +static bool functionWillReturn(const Function &F) { + // Must-progress function without side-effects must return. + if (F.mustProgress() && F.onlyReadsMemory()) + return true; + + // Can only analyze functions with a definition. + if (F.isDeclaration()) + return false; + + // Functions with loops require more sophisticated analysis, as the loop + // may be infinite. For now, don't try to handle them. + SmallVector<std::pair<const BasicBlock *, const BasicBlock *>> Backedges; + FindFunctionBackedges(F, Backedges); + if (!Backedges.empty()) + return false; + + // If there are no loops, then the function is willreturn if all calls in + // it are willreturn. + return all_of(instructions(F), [](const Instruction &I) { + const auto *CB = dyn_cast<CallBase>(&I); + return !CB || CB->hasFnAttr(Attribute::WillReturn); + }); +} + // Set the willreturn function attribute if possible. static bool addWillReturn(const SCCNodeSet &SCCNodes) { bool Changed = false; for (Function *F : SCCNodes) { - if (!F || !F->onlyReadsMemory() || !F->mustProgress() || F->willReturn()) + if (!F || F->willReturn() || !functionWillReturn(*F)) continue; F->setWillReturn(); diff --git a/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll b/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll index 95ac2525b4ad..5bbc86bb69ed 100644 --- a/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll +++ b/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll @@ -72,13 +72,13 @@ define i32 @test3_no(i8* %p) nounwind { declare void @callee(i32* %p) nounwind declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) nounwind -; CHECK: attributes #0 = { norecurse nounwind readnone } -; CHECK: attributes #1 = { nofree norecurse nounwind writeonly } +; CHECK: attributes #0 = { norecurse nounwind readnone willreturn } +; CHECK: attributes #1 = { nofree norecurse nounwind willreturn writeonly } ; CHECK: attributes #2 = { nounwind readonly } ; CHECK: attributes #3 = { nounwind } -; CHECK: attributes #4 = { nounwind readnone } -; CHECK: attributes #5 = { nofree nounwind } -; CHECK: attributes #6 = { nofree norecurse nounwind } +; CHECK: attributes #4 = { nounwind readnone willreturn } +; CHECK: attributes #5 = { nofree nounwind willreturn } +; CHECK: attributes #6 = { nofree norecurse nounwind willreturn } ; CHECK: attributes #7 = { argmemonly nofree nosync nounwind willreturn } ; Root note. diff --git a/llvm/test/CodeGen/AMDGPU/inline-attr.ll b/llvm/test/CodeGen/AMDGPU/inline-attr.ll index c73a3c52e9ae..8192c4837b4b 100644 --- a/llvm/test/CodeGen/AMDGPU/inline-attr.ll +++ b/llvm/test/CodeGen/AMDGPU/inline-attr.ll @@ -6,14 +6,14 @@ ; GCN: define amdgpu_kernel void @caller(float addrspace(1)* nocapture %p) local_unnamed_addr #1 { ; GCN: %mul.i = fmul float %load, 1.500000e+01 -; UNSAFE: attributes #0 = { norecurse nounwind readnone "unsafe-fp-math"="true" } -; UNSAFE: attributes #1 = { nofree norecurse nounwind "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="true" } +; UNSAFE: attributes #0 = { norecurse nounwind readnone willreturn "unsafe-fp-math"="true" } +; UNSAFE: attributes #1 = { nofree norecurse nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "unsafe-fp-math"="true" } -; NOINFS: attributes #0 = { norecurse nounwind readnone "no-infs-fp-math"="true" } -; NOINFS: attributes #1 = { nofree norecurse nounwind "less-precise-fpmad"="false" "no-infs-fp-math"="true" "no-nans-fp-math"="false" "unsafe-fp-math"="false" } +; NOINFS: attributes #0 = { norecurse nounwind readnone willreturn "no-infs-fp-math"="true" } +; NOINFS: attributes #1 = { nofree norecurse nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="true" "no-nans-fp-math"="false" "unsafe-fp-math"="false" } -; NONANS: attributes #0 = { norecurse nounwind readnone "no-nans-fp-math"="true" } -; NONANS: attributes #1 = { nofree norecurse nounwind "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="true" "unsafe-fp-math"="false" } +; NONANS: attributes #0 = { norecurse nounwind readnone willreturn "no-nans-fp-math"="true" } +; NONANS: attributes #1 = { nofree norecurse nounwind willreturn "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="true" "unsafe-fp-math"="false" } define float @foo(float %x) #0 { entry: diff --git a/llvm/test/Transforms/FunctionAttrs/atomic.ll b/llvm/test/Transforms/FunctionAttrs/atomic.ll index 8112996404a5..313c54b5ed3e 100644 --- a/llvm/test/Transforms/FunctionAttrs/atomic.ll +++ b/llvm/test/Transforms/FunctionAttrs/atomic.ll @@ -20,5 +20,5 @@ entry: ret i32 %r } -; CHECK: attributes #0 = { norecurse nounwind readnone ssp uwtable } -; CHECK: attributes #1 = { nofree norecurse nounwind ssp uwtable } +; CHECK: attributes #0 = { norecurse nounwind readnone ssp uwtable willreturn } +; CHECK: attributes #1 = { nofree norecurse nounwind ssp uwtable willreturn } diff --git a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll index 906ae01422c1..4701de3f2e54 100644 --- a/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll +++ b/llvm/test/Transforms/FunctionAttrs/incompatible_fn_attrs.ll @@ -28,5 +28,5 @@ entry: attributes #0 = { argmemonly } attributes #1 = { inaccessiblememonly } attributes #2 = { inaccessiblemem_or_argmemonly } -; CHECK: attributes #0 = { norecurse nounwind readnone } +; CHECK: attributes #0 = { norecurse nounwind readnone willreturn } ; CHECK-NOT: attributes diff --git a/llvm/test/Transforms/FunctionAttrs/nofree.ll b/llvm/test/Transforms/FunctionAttrs/nofree.ll index 4d36cc82bae2..bcb97b34c6cc 100644 --- a/llvm/test/Transforms/FunctionAttrs/nofree.ll +++ b/llvm/test/Transforms/FunctionAttrs/nofree.ll @@ -107,7 +107,7 @@ attributes #5 = { builtin nounwind } ; CHECK: attributes #0 = { uwtable } ; CHECK: attributes #1 = { nounwind uwtable } ; CHECK: attributes #2 = { nounwind } -; CHECK: attributes #3 = { norecurse nounwind readonly uwtable } +; CHECK: attributes #3 = { norecurse nounwind readonly uwtable willreturn } ; CHECK: attributes #4 = { nobuiltin nounwind } ; CHECK: attributes #5 = { builtin nounwind } diff --git a/llvm/test/Transforms/FunctionAttrs/optnone.ll b/llvm/test/Transforms/FunctionAttrs/optnone.ll index b7e9ea3636c3..9455a2ae40a0 100644 --- a/llvm/test/Transforms/FunctionAttrs/optnone.ll +++ b/llvm/test/Transforms/FunctionAttrs/optnone.ll @@ -20,6 +20,6 @@ declare i8 @strlen(i8*) noinline optnone ; CHECK: (i8*) #1 ; CHECK-LABEL: attributes #0 -; CHECK: = { norecurse nounwind readnone } +; CHECK: = { norecurse nounwind readnone willreturn } ; CHECK-LABEL: attributes #1 ; CHECK: = { noinline optnone } diff --git a/llvm/test/Transforms/FunctionAttrs/willreturn.ll b/llvm/test/Transforms/FunctionAttrs/willreturn.ll index 78233769b45e..1f69c3d80d8b 100644 --- a/llvm/test/Transforms/FunctionAttrs/willreturn.ll +++ b/llvm/test/Transforms/FunctionAttrs/willreturn.ll @@ -71,9 +71,10 @@ B: ret i64 0 } +; Function without loops or non-willreturn calls will return. define void @willreturn_no_loop(i1 %c, i32* %p) { -; CHECK-NOT: Function Attrs: {{.*}}willreturn -; CHECK: define void @willreturn_no_loop( +; CHECK: Function Attrs: willreturn +; CHECK-NEXT: define void @willreturn_no_loop( ; br i1 %c, label %if, label %else @@ -90,6 +91,7 @@ end: ret void } +; Calls a function that is not guaranteed to return, not willreturn. define void @willreturn_non_returning_function(i1 %c, i32* %p) { ; CHECK-NOT: Function Attrs: {{.*}}willreturn ; CHECK: define void @willreturn_non_returning_function( @@ -98,6 +100,7 @@ define void @willreturn_non_returning_function(i1 %c, i32* %p) { ret void } +; Infinite loop without mustprogress, will not return. define void @willreturn_loop() { ; CHECK-NOT: Function Attrs: {{.*}}willreturn ; CHECK: define void @willreturn_loop( @@ -108,6 +111,8 @@ loop: br label %loop } +; Finite loop. Could be willreturn but not detected. +; FIXME define void @willreturn_finite_loop() { ; CHECK-NOT: Function Attrs: {{.*}}willreturn ; CHECK: define void @willreturn_finite_loop( @@ -125,5 +130,28 @@ end: ret void } +; Infinite recursion without mustprogress, will not return. +define void @willreturn_recursion() { +; CHECK-NOT: Function Attrs: {{.*}}willreturn +; CHECK: define void @willreturn_recursion( +; + tail call void @willreturn_recursion() + ret void +} + +; Irreducible infinite loop, will not return. +define void @willreturn_irreducible(i1 %c) { +; CHECK-NOT: Function Attrs: {{.*}}willreturn +; CHECK: define void @willreturn_irreducible( +; + br i1 %c, label %bb1, label %bb2 + +bb1: + br label %bb2 + +bb2: + br label %bb1 +} + declare i64 @fn_noread() readnone declare void @fn_willreturn() willreturn diff --git a/llvm/test/Transforms/FunctionAttrs/writeonly.ll b/llvm/test/Transforms/FunctionAttrs/writeonly.ll index 9be998787466..1efea78ba1e3 100644 --- a/llvm/test/Transforms/FunctionAttrs/writeonly.ll +++ b/llvm/test/Transforms/FunctionAttrs/writeonly.ll @@ -25,6 +25,6 @@ nouses-argworn-funwo_entry: ret void } -; CHECK: attributes #0 = { {{.*}} readnone } -; CHECK: attributes #1 = { {{.*}} readonly } +; CHECK: attributes #0 = { {{.*}} readnone {{.*}} } +; CHECK: attributes #1 = { {{.*}} readonly {{.*}} } ; CHECK: attributes #2 = { {{.*}} writeonly } diff --git a/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll b/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll index 5e284fe10be8..1afd97e44e9d 100644 --- a/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll +++ b/llvm/test/Transforms/InferFunctionAttrs/norecurse_debug.ll @@ -52,5 +52,5 @@ attributes #1 = { nounwind readnone speculatable } !28 = !DILocation(line: 9, column: 18, scope: !2) !29 = !DILocation(line: 10, column: 1, scope: !2) -; CHECK: attributes #0 = { nofree norecurse nounwind } +; CHECK: attributes #0 = { nofree norecurse nounwind willreturn } ; CHECK-NOT: foo.coefficient1 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits