diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -1502,7 +1502,6 @@ case Attribute::NoCfCheck: case Attribute::NoUnwind: case Attribute::NoInline: - case Attribute::NoFree: case Attribute::AlwaysInline: case Attribute::OptimizeForSize: case Attribute::StackProtect: @@ -1551,7 +1550,7 @@ /// arguments. static bool isFuncOrArgAttr(Attribute::AttrKind Kind) { return Kind == Attribute::ReadOnly || Kind == Attribute::WriteOnly || - Kind == Attribute::ReadNone; + Kind == Attribute::ReadNone || Kind == Attribute::NoFree; } void Verifier::verifyAttributeTypes(AttributeSet Attrs, bool IsFunction, 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 @@ -1406,6 +1406,149 @@ void trackStatistics() const override { STATS_DECLTRACK_CS_ATTR(nofree); } }; +/// NoFree attribute for floating values. +struct AANoFreeFloating : AANoFreeImpl { + AANoFreeFloating(const IRPosition &IRP) : AANoFreeImpl(IRP) {} + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override{STATS_DECLTRACK_FLOATING_ATTR(nofree)} + + /// See Abstract Attribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override { + Argument *Arg = getAssociatedArgument(); + Function *F = Arg->getParent(); + + if (!Arg) + return indicatePessimisticFixpoint(); + + const auto &NoFreeAA = + A.getAAFor(*this, IRPosition::function(*F)); + if (NoFreeAA.isAssumedNoFree()) + return ChangeStatus::UNCHANGED; + + const auto *TLI = + A.getInfoCache().getTargetLibraryInfoForFunction(*(Arg->getParent())); + + SmallPtrSet Visited; + SmallVector Worklist; + + for (Use &U : Arg->uses()) + Worklist.push_back(&U); + + while (!Worklist.empty()) { + const Use *U = Worklist.pop_back_val(); + if (!Visited.insert(U).second) + continue; + + auto *UserI = U->getUser(); + if (!UserI) + continue; + + const AAIsDead &LivenessAA = + A.getAAFor(*this, IRPosition::function(*F)); + + if(LivenessAA.isAssumedDead(dyn_cast(UserI))) + continue; + + // Use is freed, we are done. + if (auto *CB = dyn_cast(UserI)) { + if (!CB->isArgOperand(U)) + continue; + if (isFreeCall(UserI, TLI)) + return indicatePessimisticFixpoint(); + + const auto &NoFreeFunc = A.getAAFor( + *this, IRPosition::callsite_function(*CB)); + + if (NoFreeFunc.isAssumedNoFree()) + continue; + + unsigned ArgNo = U - CB->arg_begin(); + + const auto &NoFreeArg = A.getAAFor( + *this, IRPosition::callsite_argument(*CB, ArgNo)); + + if (NoFreeArg.isAssumedNoFree()) + continue; + + return indicatePessimisticFixpoint(); + } + + if (isa(UserI) || isa(UserI)){ + for (Use &U : UserI->uses()) + Worklist.push_back(&U); + continue; + } + + // Unknown user. + return indicatePessimisticFixpoint(); + } + return ChangeStatus::UNCHANGED; + } +}; + +/// NoFree attribute for a call site argument. +struct AANoFreeArgument final : AANoFreeFloating { + AANoFreeArgument(const IRPosition &IRP) : AANoFreeFloating(IRP) {} + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(nofree) } +}; + + +/// NoFree attribute for call site arguments. +struct AANoFreeCallSiteArgument final : AANoFreeFloating { + AANoFreeCallSiteArgument(const IRPosition &IRP) : AANoFreeFloating(IRP) {} + + /// See AbstractAttribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override { + // TODO: Once we have call site specific value information we can provide + // call site specific liveness information and then it makes + // sense to specialize attributes for call sites arguments instead of + // redirecting requests to the callee argument. + Argument *Arg = getAssociatedArgument(); + if (!Arg) + return indicatePessimisticFixpoint(); + const IRPosition &ArgPos = IRPosition::argument(*Arg); + auto &ArgAA = A.getAAFor(*this, ArgPos); + return clampStateAndIndicateChange( + getState(), + static_cast(ArgAA.getState())); + } + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override{STATS_DECLTRACK_CSARG_ATTR(nofree)}; +}; + +/// NoFree attribute for function return value. +struct AANoFreeReturned final : AANoFreeFloating { + AANoFreeReturned(const IRPosition &IRP) : AANoFreeFloating(IRP) { + llvm_unreachable("NoFree is not applicable to function returns!"); + } + + /// See AbstractAttribute::initialize(...). + void initialize(Attributor &A) override { + llvm_unreachable("NoFree is not applicable to function returns!"); + } + + /// See AbstractAttribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override { + llvm_unreachable("NoFree is not applicable to function returns!"); + } + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override {} +}; + +/// NoFree attribute deduction for a call site return value. +struct AANoFreeCallSiteReturned final : AANoFreeFloating { + AANoFreeCallSiteReturned(const IRPosition &IRP) : AANoFreeFloating(IRP) {} + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { + STATS_DECLTRACK_CSRET_ATTR(nofree) + } +}; /// ------------------------ NonNull Argument Attribute ------------------------ struct AANonNullImpl : AANonNull { AANonNullImpl(const IRPosition &IRP) : AANonNull(IRP) {} @@ -3988,6 +4131,9 @@ // Every argument with pointer type might be marked nocapture. getOrCreateAAFor(ArgPos); + + // Every argument with pointer type might be marked nofree. + getOrCreateAAFor(ArgPos); } } @@ -4015,6 +4161,9 @@ // Call site argument attribute "align". getOrCreateAAFor(CSArgPos); + + // Call site argument attribute "nofree". + getOrCreateAAFor(CSArgPos); } } return true; @@ -4283,7 +4432,7 @@ CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoUnwind) CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoSync) -CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFree) +// CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFree) CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoRecurse) CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAWillReturn) CREATE_FUNCTION_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoReturn) @@ -4297,6 +4446,7 @@ CREATE_VALUE_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoCapture) CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAValueSimplify) +CREATE_ALL_ABSTRACT_ATTRIBUTE_FOR_POSITION(AANoFree) CREATE_FUNCTION_ONLY_ABSTRACT_ATTRIBUTE_FOR_POSITION(AAHeapToStack) diff --git a/llvm/test/Transforms/FunctionAttrs/align.ll b/llvm/test/Transforms/FunctionAttrs/align.ll --- a/llvm/test/Transforms/FunctionAttrs/align.ll +++ b/llvm/test/Transforms/FunctionAttrs/align.ll @@ -7,26 +7,26 @@ ; TEST 1 -; ATTRIBUTOR: define align 8 i32* @test1(i32* returned align 8 "no-capture-maybe-returned" %0) +; ATTRIBUTOR: define align 8 i32* @test1(i32* nofree returned align 8 "no-capture-maybe-returned" %0) define i32* @test1(i32* align 8 %0) #0 { ret i32* %0 } ; TEST 2 -; ATTRIBUTOR: define i32* @test2(i32* returned "no-capture-maybe-returned" %0) +; ATTRIBUTOR: define i32* @test2(i32* nofree returned "no-capture-maybe-returned" %0) define i32* @test2(i32* %0) #0 { ret i32* %0 } ; TEST 3 -; ATTRIBUTOR: define align 4 i32* @test3(i32* align 8 "no-capture-maybe-returned" %0, i32* align 4 "no-capture-maybe-returned" %1, i1 %2) +; ATTRIBUTOR: define align 4 i32* @test3(i32* nofree align 8 "no-capture-maybe-returned" %0, i32* nofree align 4 "no-capture-maybe-returned" %1, i1 %2) define i32* @test3(i32* align 8 %0, i32* align 4 %1, i1 %2) #0 { %ret = select i1 %2, i32* %0, i32* %1 ret i32* %ret } ; TEST 4 -; ATTRIBUTOR: define align 32 i32* @test4(i32* align 32 "no-capture-maybe-returned" %0, i32* align 32 "no-capture-maybe-returned" %1, i1 %2) +; ATTRIBUTOR: define align 32 i32* @test4(i32* nofree align 32 "no-capture-maybe-returned" %0, i32* nofree align 32 "no-capture-maybe-returned" %1, i1 %2) define i32* @test4(i32* align 32 %0, i32* align 32 %1, i1 %2) #0 { %ret = select i1 %2, i32* %0, i32* %1 ret i32* %ret @@ -83,12 +83,12 @@ ; Function Attrs: nounwind readnone ssp uwtable define internal i8* @f1(i8* readnone %0) local_unnamed_addr #0 { -; ATTRIBUTOR: define internal nonnull align 8 dereferenceable(1) i8* @f1(i8* nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" %0) +; ATTRIBUTOR: define internal nonnull align 8 dereferenceable(1) i8* @f1(i8* nofree nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" %0) %2 = icmp eq i8* %0, null br i1 %2, label %3, label %5 ;