[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()) {