summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/lib/Transforms/IPO/FunctionAttrs.cpp31
-rw-r--r--llvm/test/Analysis/TypeBasedAliasAnalysis/functionattrs.ll7
-rw-r--r--llvm/test/Transforms/FunctionAttrs/norecurse.ll7
-rw-r--r--llvm/test/Transforms/FunctionAttrs/optnone.ll4
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 }
OpenPOWER on IntegriCloud