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 is nonnull. + virtual bool isAssumedNonNull() const = 0; + + /// Return true if we know that underlying value is nonnull. + 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 @@ -20,6 +20,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/Argument.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/InstIterator.h" @@ -656,6 +657,130 @@ 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::isKnownNonNull(). + virtual bool isKnownNonNull() const override { return getKnown(); } + + /// Generate a function to check whether the value is known as nonnull. + /// The generated function returns true if a value satisfies any of + /// following conditions. + /// (i) A value is known nonZero(=nonnull). + /// (ii) A value is associated with AANonNull and its isKnownNonNull() is + /// true. + std::function generatePredicate(Attributor &); +}; + +/// 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; +}; + +/// NonNull attribute for function argument. +struct AANonNullArgument : AANonNullImpl { + + AANonNullArgument(Argument &A, InformationCache &InfoCache) + : AANonNullImpl(A, InfoCache) {} + + /// See AbstractAttribute::getManifestPosition(). + virtual ManifestPosition getManifestPosition() const override { + return MP_ARGUMENT; + } + + /// See AbstractAttriubute::initialize(...). + void initialize(Attributor &A) override { + Argument *Arg = cast(getAssociatedValue()); + if (Arg->hasNonNullAttr()) + indicateOptimisticFixpoint(); + } + + /// See AbstractAttribute::updateImpl(...). + virtual ChangeStatus updateImpl(Attributor &A) override; +}; + +std::function AANonNullImpl::generatePredicate(Attributor &A) { + std::function Pred = [&](Value &RV) -> bool { + + if (isKnownNonZero(&RV, getAnchorScope().getParent()->getDataLayout())) + return true; + + auto *NonNullAA = A.getAAFor(*this, RV); + + ImmutableCallSite ICS(&RV); + + // We can deduce nonnull if all return value has nonnull attribute. + + if ((!NonNullAA || !NonNullAA->isValidState() || + !NonNullAA->isKnownNonNull()) && + (!ICS || !ICS.hasRetAttr(Attribute::NonNull))) + return false; + + return true; + }; + + return Pred; +} + +ChangeStatus AANonNullArgument::updateImpl(Attributor &A) { + // TODO: Currently, we have no deduction for argument nonnull attribute. + indicatePessimisticFixpoint(); + return ChangeStatus::UNCHANGED; +} + +ChangeStatus AANonNullReturned::updateImpl(Attributor &A) { + Function &F = getAnchorScope(); + + auto *AARetValImpl = A.getAAFor(*this, F); + if (!AARetValImpl) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + + std::function Pred = this->generatePredicate(A); + if (!AARetValImpl->checkForallReturnedValues(Pred)) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + return ChangeStatus::UNCHANGED; +} + /// ---------------------------------------------------------------------------- /// Attributor /// ---------------------------------------------------------------------------- @@ -805,6 +930,17 @@ // 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)); + } + + // Every argument with pointer type might be marked nonnull. + for (Argument &Arg : F.args()) { + if (Arg.getType()->isPointerTy()) + registerAA(*new AANonNullArgument(Arg, InfoCache)); } // Walk all instructions to find more attribute opportunities and also Index: llvm/test/Transforms/FunctionAttrs/arg_returned.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/arg_returned.ll +++ llvm/test/Transforms/FunctionAttrs/arg_returned.ll @@ -278,7 +278,7 @@ ; FIXME: no-return missing ; FNATTR: define noalias i32* @rt1(i32* nocapture readonly %a) ; BOTH: Function Attrs: noinline nounwind readonly uwtable -; BOTH-NEXT: define noalias i32* @rt1(i32* nocapture readonly %a) +; BOTH-NEXT: define noalias nonnull i32* @rt1(i32* nocapture readonly %a) define i32* @rt1(i32* %a) #0 { entry: %v = load i32, i32* %a, align 4 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,7 @@ ; Return a pointer trivially nonnull (argument attribute) define i8* @test2(i8* nonnull %p) { ; CHECK: define nonnull i8* @test2 +; ATTRIBUTOR: define nonnull i8* @test2 ret i8* %p } @@ -20,12 +23,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 +41,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 +57,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 +73,7 @@ define i8* @test6() { entry: ; CHECK: define nonnull i8* @test6 +; ATTRIBUTOR: define nonnull i8* @test6 %ret = call i8* @ret_nonnull() br label %loop loop: @@ -73,6 +83,61 @@ ret i8* %phi } +; CHECK: define i8* @test7 +; ATTRIBUTOR: define i8* @test7 +define i8* @test7(i8* %a) { + %b = getelementptr inbounds i8, i8* %a, i64 0 + ret i8* %b +} + +; CHECK: define nonnull i8* @test8 +; ATTRIBUTOR: define nonnull i8* @test8 +define i8* @test8(i8* %a) { + %b = getelementptr inbounds i8, i8* %a, i64 1 + ret i8* %b +} + +; CHECK: define i8* @test9 +; ATTRIBUTOR: define i8* @test9 +define i8* @test9(i8* %a, i64 %n) { + %b = getelementptr inbounds i8, i8* %a, i64 %n + ret i8* %b +} + +declare void @llvm.assume(i1) +; CHECK: define i8* @test10 +; FIXME: missing nonnull +; ATTRIBUTOR: define i8* @test10 +define i8* @test10(i8* %a, i64 %n) { + %cmp = icmp ne i64 %n, 0 + call void @llvm.assume(i1 %cmp) + %b = getelementptr inbounds i8, i8* %a, i64 %n + ret i8* %b +} + +; TEST 11 +; char* test11(char *p) { +; return p? p: nonnull(); +; } +; CHECK: define i8* @test11 +; FIXME: missing nonnull +; ATTRIBUTOR: define i8* @test11 +define i8* @test11(i8*) local_unnamed_addr { + %2 = icmp eq i8* %0, null + br i1 %2, label %3, label %5 + +;