[llvm-branch-commits] [llvm] 8cf1cc5 - [FuncAttrs] Infer noreturn
Arthur Eubanks via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Jan 5 13:31:10 PST 2021
Author: Arthur Eubanks
Date: 2021-01-05T13:25:42-08:00
New Revision: 8cf1cc578d3288f5b9cfe1584e524f8b1517dc97
URL: https://github.com/llvm/llvm-project/commit/8cf1cc578d3288f5b9cfe1584e524f8b1517dc97
DIFF: https://github.com/llvm/llvm-project/commit/8cf1cc578d3288f5b9cfe1584e524f8b1517dc97.diff
LOG: [FuncAttrs] Infer noreturn
A function is noreturn if all blocks terminating with a ReturnInst
contain a call to a noreturn function. Skip looking at naked functions
since there may be asm that returns.
This can be further refined in the future by checking unreachable blocks
and taking into account recursion. It looks like the attributor pass
does this, but that is not yet enabled by default.
This seems to help with code size under the new PM since PruneEH does
not run under the new PM, missing opportunities to mark some functions
noreturn, which in turn doesn't allow simplifycfg to clean up dead code.
https://bugs.llvm.org/show_bug.cgi?id=46858.
Reviewed By: rnk
Differential Revision: https://reviews.llvm.org/D93946
Added:
llvm/test/Transforms/FunctionAttrs/noreturn.ll
Modified:
llvm/lib/Transforms/IPO/FunctionAttrs.cpp
llvm/test/Transforms/PruneEH/simplenoreturntest.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
index 0d25d5d319e7..62e3e5ad0a52 100644
--- a/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionAttrs.cpp
@@ -1388,6 +1388,42 @@ static bool addNoRecurseAttrs(const SCCNodeSet &SCCNodes) {
return true;
}
+static bool instructionDoesNotReturn(Instruction &I) {
+ if (auto *CB = dyn_cast<CallBase>(&I)) {
+ Function *Callee = CB->getCalledFunction();
+ return Callee && Callee->doesNotReturn();
+ }
+ return false;
+}
+
+// A basic block can only return if it terminates with a ReturnInst and does not
+// contain calls to noreturn functions.
+static bool basicBlockCanReturn(BasicBlock &BB) {
+ if (!isa<ReturnInst>(BB.getTerminator()))
+ return false;
+ return none_of(BB, instructionDoesNotReturn);
+}
+
+// Set the noreturn function attribute if possible.
+static bool addNoReturnAttrs(const SCCNodeSet &SCCNodes) {
+ bool Changed = false;
+
+ for (Function *F : SCCNodes) {
+ if (!F || !F->hasExactDefinition() || F->hasFnAttribute(Attribute::Naked) ||
+ F->doesNotReturn())
+ continue;
+
+ // The function can return if any basic blocks can return.
+ // FIXME: this doesn't handle recursion or unreachable blocks.
+ if (none_of(*F, basicBlockCanReturn)) {
+ F->setDoesNotReturn();
+ Changed = true;
+ }
+ }
+
+ return Changed;
+}
+
static SCCNodesResult createSCCNodeSet(ArrayRef<Function *> Functions) {
SCCNodesResult Res;
Res.HasUnknownCall = false;
@@ -1431,6 +1467,7 @@ static bool deriveAttrsInPostOrder(ArrayRef<Function *> Functions,
Changed |= addReadAttrs(Nodes.SCCNodes, AARGetter);
Changed |= addArgumentAttrs(Nodes.SCCNodes);
Changed |= inferConvergent(Nodes.SCCNodes);
+ Changed |= addNoReturnAttrs(Nodes.SCCNodes);
// If we have no external nodes participating in the SCC, we can deduce some
// more precise attributes as well.
diff --git a/llvm/test/Transforms/FunctionAttrs/noreturn.ll b/llvm/test/Transforms/FunctionAttrs/noreturn.ll
new file mode 100644
index 000000000000..acc538d3ccee
--- /dev/null
+++ b/llvm/test/Transforms/FunctionAttrs/noreturn.ll
@@ -0,0 +1,66 @@
+; RUN: opt < %s -passes='function-attrs' -S | FileCheck %s
+
+declare i32 @f()
+
+; CHECK: Function Attrs: noreturn
+; CHECK-NEXT: @noreturn()
+declare i32 @noreturn() noreturn
+
+; CHECK: Function Attrs: noreturn
+; CHECK-NEXT: @caller()
+define i32 @caller() {
+ %c = call i32 @noreturn()
+ ret i32 %c
+}
+
+; CHECK: Function Attrs: noreturn
+; CHECK-NEXT: @caller2()
+define i32 @caller2() {
+ %c = call i32 @caller()
+ ret i32 %c
+}
+
+; CHECK: Function Attrs: noreturn
+; CHECK-NEXT: @caller3()
+define i32 @caller3() {
+entry:
+ br label %end
+end:
+ %c = call i32 @noreturn()
+ ret i32 %c
+}
+
+; CHECK-NOT: Function Attrs: {{.*}}noreturn
+; CHECK: define i32 @caller4()
+define i32 @caller4() {
+entry:
+ br label %end
+end:
+ %c = call i32 @f()
+ ret i32 %c
+}
+
+; CHECK-NOT: Function Attrs: {{.*}}noreturn
+; CHECK: @caller5()
+; We currently don't handle unreachable blocks.
+define i32 @caller5() {
+entry:
+ %c = call i32 @noreturn()
+ ret i32 %c
+unreach:
+ %d = call i32 @f()
+ ret i32 %d
+}
+
+; CHECK-NOT: Function Attrs: {{.*}}noreturn
+; CHECK: @caller6()
+define i32 @caller6() naked {
+ %c = call i32 @noreturn()
+ ret i32 %c
+}
+
+; CHECK: Function Attrs: {{.*}}noreturn
+; CHECK-NEXT: @alreadynoreturn()
+define i32 @alreadynoreturn() noreturn {
+ unreachable
+}
diff --git a/llvm/test/Transforms/PruneEH/simplenoreturntest.ll b/llvm/test/Transforms/PruneEH/simplenoreturntest.ll
index 814f8b4a686f..ccb9f5eb6f59 100644
--- a/llvm/test/Transforms/PruneEH/simplenoreturntest.ll
+++ b/llvm/test/Transforms/PruneEH/simplenoreturntest.ll
@@ -1,4 +1,5 @@
; RUN: opt < %s -prune-eh -S -enable-new-pm=0 | not grep "ret i32"
+; RUN: opt < %s -passes='function-attrs,function(simplifycfg)' -S | not grep "ret i32"
declare void @noreturn() noreturn
More information about the llvm-branch-commits
mailing list