diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h --- a/llvm/include/llvm/IR/InstrTypes.h +++ b/llvm/include/llvm/IR/InstrTypes.h @@ -1756,6 +1756,13 @@ bool onlyReadsMemory() const { return doesNotAccessMemory() || hasFnAttr(Attribute::ReadOnly); } + + /// Returns true if this function is guaranteed to return. + bool willReturn() const { + return hasFnAttr(Attribute::WillReturn) || + hasFnAttr(Attribute::MustProgress); + } + void setOnlyReadsMemory() { addAttribute(AttributeList::FunctionIndex, Attribute::ReadOnly); } diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -413,6 +413,12 @@ return true; } + if (auto *CB = dyn_cast(I)) + // Treat calls that may not return as alive (they could loop infinitely), + // unless the containing function must make progress. + if (!I->getParent()->getParent()->mustProgress() && !CB->willReturn()) + return false; + if (!I->mayHaveSideEffects()) return true; diff --git a/llvm/test/Transforms/Inline/dead-calls-mustprogress.ll b/llvm/test/Transforms/Inline/dead-calls-mustprogress.ll --- a/llvm/test/Transforms/Inline/dead-calls-mustprogress.ll +++ b/llvm/test/Transforms/Inline/dead-calls-mustprogress.ll @@ -46,6 +46,10 @@ define void @caller_no_mustprogress() ssp { ; CHECK-LABEL: @caller_no_mustprogress( ; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[WHILE_BODY_I:%.*]] +; CHECK: while.body.i: +; CHECK-NEXT: br label [[WHILE_BODY_I]] +; CHECK: readnone.exit: ; CHECK-NEXT: ret void ; entry: