[Attributor] Deduce the "no-return" attribute for functions
A function is "no-return" if we never reach a return instruction, either
because there are none or the ones that exist are dead.
Test have been adjusted:
- either noreturn was added, or
- noreturn was avoided by modifying the code.
The new noreturn_{sync,async} test make sure we do handle invoke
instructions with a noreturn (and potentially nowunwind) callee
correctly, even in the presence of potential asynchronous exceptions.
llvm-svn: 367948
diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 3ecad8b..dc6bcb5 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/lib/Transforms/IPO/Attributor.cpp
@@ -76,6 +76,7 @@
STATISTIC(NumFnReturnedAlign, "Number of function return values marked align");
STATISTIC(NumFnArgumentAlign, "Number of function arguments marked align");
STATISTIC(NumCSArgumentAlign, "Number of call site arguments marked align");
+STATISTIC(NumFnNoReturn, "Number of functions marked noreturn");
// TODO: Determine a good default value.
//
@@ -179,6 +180,9 @@
case Attribute::WillReturn:
NumFnWillReturn++;
break;
+ case Attribute::NoReturn:
+ NumFnNoReturn++;
+ return;
case Attribute::NoAlias:
NumFnArgumentNoAlias++;
return;
@@ -2336,6 +2340,60 @@
: ChangeStatus::CHANGED;
}
+/// ------------------ Function No-Return Attribute ----------------------------
+struct AANoReturnFunction final : public AANoReturn, BooleanState {
+
+ AANoReturnFunction(Function &F, InformationCache &InfoCache)
+ : AANoReturn(F, InfoCache) {}
+
+ /// See AbstractAttribute::getState()
+ /// {
+ AbstractState &getState() override { return *this; }
+ const AbstractState &getState() const override { return *this; }
+ /// }
+
+ /// Return true if the underlying object is known to never return.
+ bool isKnownNoReturn() const override { return getKnown(); }
+
+ /// Return true if the underlying object is assumed to never return.
+ bool isAssumedNoReturn() const override { return getAssumed(); }
+
+ /// See AbstractAttribute::getManifestPosition().
+ ManifestPosition getManifestPosition() const override { return MP_FUNCTION; }
+
+ /// See AbstractAttribute::getAsStr().
+ const std::string getAsStr() const override {
+ return getAssumed() ? "noreturn" : "may-return";
+ }
+
+ /// See AbstractAttribute::initialize(...).
+ void initialize(Attributor &A) override {
+ Function &F = getAnchorScope();
+ if (F.hasFnAttribute(getAttrKind()))
+ indicateOptimisticFixpoint();
+ }
+
+ /// See AbstractAttribute::updateImpl(Attributor &A).
+ virtual ChangeStatus updateImpl(Attributor &A) override {
+ Function &F = getAnchorScope();
+
+ // The map from instruction opcodes to those instructions in the function.
+ auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F);
+
+ // Look at all return instructions.
+ auto &ReturnInsts = OpcodeInstMap[Instruction::Ret];
+ if (ReturnInsts.empty())
+ return indicateOptimisticFixpoint();
+
+ auto *LivenessAA = A.getAAFor<AAIsDead>(*this, F);
+ if (!LivenessAA ||
+ LivenessAA->isLiveInstSet(ReturnInsts.begin(), ReturnInsts.end()))
+ return indicatePessimisticFixpoint();
+
+ return ChangeStatus::UNCHANGED;
+ }
+};
+
/// ----------------------------------------------------------------------------
/// Attributor
/// ----------------------------------------------------------------------------
@@ -2539,6 +2597,9 @@
// Every function might be "no-free".
registerAA(*new AANoFreeFunction(F, InfoCache));
+ // Every function might be "no-return".
+ registerAA(*new AANoReturnFunction(F, InfoCache));
+
// Return attributes are only appropriate if the return type is non void.
Type *ReturnType = F.getReturnType();
if (!ReturnType->isVoidTy()) {