diff options
-rw-r--r-- | llvm/lib/Transforms/IPO/FunctionAttrs.cpp | 31 | ||||
-rw-r--r-- | llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll | 7 | ||||
-rw-r--r-- | llvm/test/Transforms/FunctionAttrs/norecurse.ll | 7 | ||||
-rw-r--r-- | llvm/test/Transforms/FunctionAttrs/optnone.ll | 4 |
4 files changed, 28 insertions, 21 deletions
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp index 3c0e7ae799f..0cd9b88ea92 100644 --- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp @@ -991,31 +991,32 @@ static bool setDoesNotRecurse(Function &F) { return true; } -static bool addNoRecurseAttrs(const CallGraphSCC &SCC) { +static bool addNoRecurseAttrs(const SCCNodeSet &SCCNodes) { // Try and identify functions that do not recurse. // If the SCC contains multiple nodes we know for sure there is recursion. - if (!SCC.isSingular()) + if (SCCNodes.size() != 1) return false; - const CallGraphNode *CGN = *SCC.begin(); - Function *F = CGN->getFunction(); + Function *F = *SCCNodes.begin(); if (!F || F->isDeclaration() || F->doesNotRecurse()) return false; // If all of the calls in F are identifiable and are to norecurse functions, F // is norecurse. This check also detects self-recursion as F is not currently // marked norecurse, so any called from F to F will not be marked norecurse. - if (std::all_of(CGN->begin(), CGN->end(), - [](const CallGraphNode::CallRecord &CR) { - Function *F = CR.second->getFunction(); - return F && F->doesNotRecurse(); - })) - // Function calls a potentially recursive function. - return setDoesNotRecurse(*F); - - // Nothing else we can deduce usefully during the postorder traversal. - return false; + for (Instruction &I : instructions(*F)) + if (auto CS = CallSite(&I)) { + Function *Callee = CS.getCalledFunction(); + if (!Callee || Callee == F || !Callee->doesNotRecurse()) + // Function calls a potentially recursive function. + return false; + } + + // Every call was to a non-recursive function other than this function, and + // we have no indirect recursion as the SCC size is one. This function cannot + // recurse. + return setDoesNotRecurse(*F); } bool PostOrderFunctionAttrs::runOnSCC(CallGraphSCC &SCC) { @@ -1060,9 +1061,9 @@ bool PostOrderFunctionAttrs::runOnSCC(CallGraphSCC &SCC) { Changed |= addNoAliasAttrs(SCCNodes); Changed |= addNonNullAttrs(SCCNodes, *TLI); Changed |= removeConvergentAttrs(SCCNodes); + Changed |= addNoRecurseAttrs(SCCNodes); } - Changed |= addNoRecurseAttrs(SCC); return Changed; } diff --git a/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll b/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll index fe2fdd74b41..ed091466165 100644 --- a/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll +++ b/llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll @@ -43,13 +43,13 @@ define void @test1_no(i32* %p) nounwind { ; 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) #0 { +; CHECK: define void @test2_yes(i8* nocapture %p, i8* nocapture %q, i64 %n) #4 { 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) #1 { +; CHECK: define void @test2_no(i8* nocapture %p, i8* nocapture readonly %q, i64 %n) #3 { 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,7 +76,8 @@ declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i32, i1) nounwind ; CHECK: attributes #1 = { norecurse nounwind } ; CHECK: attributes #2 = { nounwind readonly } ; CHECK: attributes #3 = { nounwind } -; CHECK: attributes #4 = { argmemonly nounwind } +; CHECK: attributes #4 = { nounwind readnone } +; CHECK: attributes #5 = { argmemonly nounwind } ; Root note. !0 = !{ } diff --git a/llvm/test/Transforms/FunctionAttrs/norecurse.ll b/llvm/test/Transforms/FunctionAttrs/norecurse.ll index d5a2d820840..221b539655a 100644 --- a/llvm/test/Transforms/FunctionAttrs/norecurse.ll +++ b/llvm/test/Transforms/FunctionAttrs/norecurse.ll @@ -29,6 +29,13 @@ define i32 @extern() { } declare i32 @k() readnone +; CHECK: 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 +} +declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i32, i1) + ; CHECK: define internal i32 @called_by_norecurse() #0 define internal i32 @called_by_norecurse() { %a = call i32 @k() diff --git a/llvm/test/Transforms/FunctionAttrs/optnone.ll b/llvm/test/Transforms/FunctionAttrs/optnone.ll index 441ff4da65e..cd08c75d75b 100644 --- a/llvm/test/Transforms/FunctionAttrs/optnone.ll +++ b/llvm/test/Transforms/FunctionAttrs/optnone.ll @@ -16,11 +16,9 @@ define void @test_optnone(i8* %p) noinline optnone { declare i8 @strlen(i8*) noinline optnone ; CHECK-LABEL: @strlen -; CHECK: (i8*) #2 +; CHECK: (i8*) #1 ; CHECK-LABEL: attributes #0 ; CHECK: = { norecurse readnone } ; CHECK-LABEL: attributes #1 -; CHECK: = { noinline norecurse optnone } -; CHECK-LABEL: attributes #2 ; CHECK: = { noinline optnone } |