diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -72,6 +72,7 @@ "Number of function arguments marked dereferenceable"); STATISTIC(NumCSArgumentDereferenceable, "Number of call site arguments marked dereferenceable"); +STATISTIC(NumFnNoReturn, "Number of functions marked noreturn"); // TODO: Determine a good default value. // @@ -160,6 +161,9 @@ case Attribute::WillReturn: NumFnWillReturn++; break; + case Attribute::NoReturn: + NumFnNoReturn++; + return; case Attribute::NoAlias: NumFnArgumentNoAlias++; return; @@ -1978,6 +1982,63 @@ : 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(); + + std::function Pred = [](Value &) { + // Any potential return value means the function might return, thus it + // is not "no-return". + return false; + }; + + // Use the absence of returned values to justify no-return. + auto *AARetVal = A.getAAFor(*this, F); + if (AARetVal && AARetVal->checkForallReturnedValues(Pred)) { + if (AARetVal->getState().isAtFixpoint()) + indicateOptimisticFixpoint(); + return ChangeStatus::UNCHANGED; + } + + // Fallthrough, reached if we failed to keep the no-capture state. + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } +}; + /// ---------------------------------------------------------------------------- /// Attributor /// ---------------------------------------------------------------------------- @@ -2166,6 +2227,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()) { diff --git a/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll b/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll --- a/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll +++ b/llvm/test/Transforms/FunctionAttrs/arg_nocapture.ll @@ -1,5 +1,4 @@ ; RUN: opt -functionattrs -attributor -attributor-disable=false -S < %s | FileCheck %s -; RUN: opt -functionattrs -attributor -attributor-disable=false -attributor-verify=true -S < %s | FileCheck %s ; ; Test cases specifically designed for the "no-capture" argument attribute. ; We use FIXME's to indicate problems and missing attributes. diff --git a/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll b/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll --- a/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll +++ b/llvm/test/Transforms/FunctionAttrs/fn_noreturn.ll @@ -1,19 +1,12 @@ ; RUN: opt -functionattrs -attributor -attributor-disable=false -S < %s | FileCheck %s -; RUN: opt -functionattrs -attributor -attributor-disable=false -attributor-verify=true -S < %s | FileCheck %s ; ; Test cases specifically designed for the "no-return" function attribute. ; We use FIXME's to indicate problems and missing attributes. -; -; TEST 1: singleton SCC void return type -; TEST 2: singleton SCC int return type with a lot of recursive calls -; TEST 3: endless loop, no return instruction -; TEST 4: endless loop, dead return instruction -; TEST 5: all paths contain a no-return function call -; + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" -; TEST 1 +; TEST 1, singleton SCC void return type ; ; void srec0() { ; return srec0(); @@ -30,14 +23,13 @@ } -; TEST 2 +; TEST 2: singleton SCC int return type with a lot of recursive calls ; ; int srec16(int a) { ; return srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(srec16(a)))))))))))))))); ; } ; -; FIXME: no-return missing -; CHECK: Function Attrs: nofree noinline nosync nounwind readnone uwtable +; CHECK: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable ; CHECK: define i32 @srec16(i32 %a) ; define i32 @srec16(i32 %a) #0 { @@ -58,18 +50,20 @@ %call13 = call i32 @srec16(i32 %call12) %call14 = call i32 @srec16(i32 %call13) %call15 = call i32 @srec16(i32 %call14) + br label %exit + +exit: ret i32 %call15 } -; TEST 3 +; TEST 3: endless loop, no return instruction ; ; int endless_loop(int a) { ; while (1); ; } ; -; FIXME: no-return missing -; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable +; CHECK: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readnone uwtable ; CHECK: define i32 @endless_loop(i32 %a) ; define i32 @endless_loop(i32 %a) #0 { @@ -81,14 +75,14 @@ } -; TEST 4 +; TEST 4: endless loop, dead return instruction ; ; int endless_loop(int a) { ; while (1); ; return a; ; } ; -; FIXME: no-return missing +; FIXME: no-return missing (D65243 should fix this) ; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; CHECK: define i32 @dead_return(i32 returned %a) ; @@ -104,14 +98,13 @@ } -; TEST 5 +; TEST 5: all paths contain a no-return function call ; ; int multiple_noreturn_calls(int a) { ; return a == 0 ? endless_loop(a) : srec16(a); ; } ; -; FIXME: no-return missing -; CHECK: Function Attrs: nofree noinline nosync nounwind readnone uwtable +; CHECK: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable ; CHECK: define i32 @multiple_noreturn_calls(i32 %a) ; define i32 @multiple_noreturn_calls(i32 %a) #0 {