Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -663,6 +663,26 @@ static constexpr Attribute::AttrKind ID = Attribute::Returned; }; +/// An abstract interface for all nonnull attributes. +struct AANonNull : public AbstractAttribute { + + /// See AbstractAttribute::AbstractAttribute(...). + AANonNull(Value &V, InformationCache &InfoCache) + : AbstractAttribute(V, InfoCache) {} + + /// Return true if we assume that the underlying value does not alias. + virtual bool isAssumedNonNull() const = 0; + + /// Return true if we know that underlying value is noalias. + virtual bool isKnownNonNull() const = 0; + + /// See AbastractState::getAttrKind(). + Attribute::AttrKind getAttrKind() const override { return ID; } + + /// The identifier used by the Attributor for this class of attributes. + static constexpr Attribute::AttrKind ID = Attribute::NonNull; +}; + } // end namespace llvm #endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -656,6 +656,93 @@ return Changed; } +/// ------------------------ NonNull Argument Attribute ------------------------ + +struct AANonNullImpl : AANonNull, BooleanState { + + AANonNullImpl(Value &V, InformationCache &InfoCache) + : AANonNull(V, InfoCache) {} + + /// See AbstractAttribute::getState() + /// { + AbstractState &getState() override { return *this; } + const AbstractState &getState() const override { return *this; } + /// } + + virtual const std::string getAsStr() const override { + return getAssumed() ? "nonnull" : "may-null"; + } + + /// See AANonNull::isAssumedNonNull(). + virtual bool isAssumedNonNull() const override { return getAssumed(); } + + /// See AANonNull::isKnowndNonNull(). + virtual bool isKnownNonNull() const override { return getKnown(); } +}; + +/// NonNull attribute for function return value. +struct AANonNullReturned : AANonNullImpl { + + AANonNullReturned(Function &F, InformationCache &InfoCache) + : AANonNullImpl(F, InfoCache) {} + + /// See AbstractAttribute::getManifestPosition(). + virtual ManifestPosition getManifestPosition() const override { + return MP_RETURNED; + } + + /// See AbstractAttriubute::initialize(...). + void initialize(Attributor &A) override { + Function &F = getAnchorScope(); + + // Already nonnull. + if (F.getAttributes().hasAttribute(AttributeList::ReturnIndex, + Attribute::NonNull)) + indicateOptimisticFixpoint(); + } + + /// See AbstractAttribute::updateImpl(...). + virtual ChangeStatus updateImpl(Attributor &A) override; +}; + +ChangeStatus AANonNullReturned::updateImpl(Attributor &A) { + Function &F = getAnchorScope(); + + auto *AARetValImpl = A.getAAFor(*this, F); + if (!AARetValImpl) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + + std::function Pred = [&](Value &RV) -> bool { + if (Constant *C = dyn_cast(&RV)) { + if (C->isNullValue() || isa(C)) + return false; + else + return true; + } + + /// For now, we can only deduce nonnull if we have call sites. + /// FIXME: add more support. + ImmutableCallSite ICS(&RV); + if (!ICS) + return false; + + auto *NonNullAA = A.getAAFor(*this, RV); + if ((!NonNullAA || !NonNullAA->isValidState() || + !NonNullAA->isAssumedNonNull()) && + !ICS.hasRetAttr(Attribute::NonNull)) { + return false; + } + return true; + }; + if (!AARetValImpl->checkForallReturnedValues(Pred)) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + return ChangeStatus::UNCHANGED; +} + /// ---------------------------------------------------------------------------- /// Attributor /// ---------------------------------------------------------------------------- @@ -805,6 +892,11 @@ // though it is an argument attribute. if (!Whitelist || Whitelist->count(AAReturnedValues::ID)) registerAA(*new AAReturnedValuesImpl(F, InfoCache)); + + // Every function with pointer return type might be marked nonnull. + if (ReturnType->isPointerTy() && + (!Whitelist || Whitelist->count(AANonNullReturned::ID))) + registerAA(*new AANonNullReturned(F, InfoCache)); } // Walk all instructions to find more attribute opportunities and also Index: llvm/test/Transforms/FunctionAttrs/nonnull.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nonnull.ll +++ llvm/test/Transforms/FunctionAttrs/nonnull.ll @@ -1,11 +1,13 @@ ; RUN: opt -S -functionattrs -enable-nonnull-arg-prop %s | FileCheck %s ; RUN: opt -S -passes=function-attrs -enable-nonnull-arg-prop %s | FileCheck %s +; RUN: opt -attributor --attributor-disable=false -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR declare nonnull i8* @ret_nonnull() ; Return a pointer trivially nonnull (call return attribute) define i8* @test1() { ; CHECK: define nonnull i8* @test1 +; ATTRIBUTOR: define nonnull i8* @test1 %ret = call i8* @ret_nonnull() ret i8* %ret } @@ -13,6 +15,8 @@ ; Return a pointer trivially nonnull (argument attribute) define i8* @test2(i8* nonnull %p) { ; CHECK: define nonnull i8* @test2 +; FIXME: missing "nonnull" +; ATTRIBUTOR: define i8* @test2 ret i8* %p } @@ -20,12 +24,14 @@ ; can we still mark the other one which is trivially nonnull define i8* @scc_binder() { ; CHECK: define i8* @scc_binder +; ATTRIBUTOR: define i8* @scc_binder call i8* @test3() ret i8* null } define i8* @test3() { ; CHECK: define nonnull i8* @test3 +; ATTRIBUTOR: define nonnull i8* @test3 call i8* @scc_binder() %ret = call i8* @ret_nonnull() ret i8* %ret @@ -36,12 +42,14 @@ ; just never return period.) define i8* @test4_helper() { ; CHECK: define noalias nonnull i8* @test4_helper +; ATTRIBUTOR: define nonnull i8* @test4_helper %ret = call i8* @test4() ret i8* %ret } define i8* @test4() { ; CHECK: define noalias nonnull i8* @test4 +; ATTRIBUTOR: define nonnull i8* @test4 %ret = call i8* @test4_helper() ret i8* %ret } @@ -50,12 +58,14 @@ ; make sure we haven't marked them as nonnull. define i8* @test5_helper() { ; CHECK: define noalias i8* @test5_helper +; ATTRIBUTOR: define i8* @test5_helper %ret = call i8* @test5() ret i8* null } define i8* @test5() { ; CHECK: define noalias i8* @test5 +; ATTRIBUTOR: define i8* @test5 %ret = call i8* @test5_helper() ret i8* %ret } @@ -64,6 +74,7 @@ define i8* @test6() { entry: ; CHECK: define nonnull i8* @test6 +; ATTRIBUTOR: define nonnull i8* @test6 %ret = call i8* @ret_nonnull() br label %loop loop: