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,7 @@ case Attribute::NoCfCheck: case Attribute::NoUnwind: case Attribute::NoInline: - case Attribute::NoFree: + //case Attribute::NoFree: case Attribute::AlwaysInline: case Attribute::OptimizeForSize: case Attribute::StackProtect: @@ -1551,7 +1551,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,141 @@ void trackStatistics() const override { STATS_DECLTRACK_CS_ATTR(nofree); } }; +/// NoFree attribute for a call site argument. +struct AANoFreeArgument final : AANoFreeImpl { + AANoFreeArgument(const IRPosition &IRP) : AANoFreeImpl(IRP) {} + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { STATS_DECLTRACK_ARG_ATTR(nofree) } + + /// See Abstract Attribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override { + Argument *Arg = getAssociatedArgument(); + Function *F = Arg->getParent(); + + if (!Arg) + return indicatePessimisticFixpoint(); + + 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(); + + 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(); + + Function *CalledF = CB->getCalledFunction(); + + const auto &NoFreeFunc = A.getAAFor( + *this, IRPosition::function(*CalledF)); + + if (NoFreeFunc.isAssumedNoFree()) + continue; + + unsigned ArgNo = U - CB->arg_begin(); + Argument *UseArg = CalledF->getArg(ArgNo); + + const auto &NoFreeArg = + A.getAAFor(*this, IRPosition::argument(*UseArg)); + + if (NoFreeArg.isAssumedNoFree()) + continue; + + return indicatePessimisticFixpoint(); + } + + if (isa(UserI) || isa(UserI)) + for (Use &U : UserI->uses()) + Worklist.push_back(&U); + } + return indicateOptimisticFixpoint(); + } +}; + +/// NoFree attribute for call site arguments. +struct AANoFreeCallSiteArgument final : AANoFreeImpl { + AANoFreeCallSiteArgument(const IRPosition &IRP) : AANoFreeImpl(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 floating values. +struct AANoFreeFloating final : AANoFreeImpl { + AANoFreeFloating(const IRPosition &IRP) : AANoFreeImpl(IRP) {} + + /// See AbstractAttribute::trackStatistics() + void trackStatistics() const override { + STATS_DECLTRACK_FLOATING_ATTR(nofree) + } +}; + +/// NoCapture attribute for function return value. +struct AANoFreeReturned final : AANoFreeImpl { + AANoFreeReturned(const IRPosition &IRP) : AANoFreeImpl(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 {} +}; + +/// NoCapture attribute deduction for a call site return value. +struct AANoFreeCallSiteReturned final : AANoFreeImpl { + AANoFreeCallSiteReturned(const IRPosition &IRP) : AANoFreeImpl(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 +4123,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 +4153,9 @@ // Call site argument attribute "align". getOrCreateAAFor(CSArgPos); + + // Call site argument attribute "nofree". + getOrCreateAAFor(CSArgPos); } } return true; @@ -4283,7 +4424,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 +4438,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/nofree-attributor.ll b/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll --- a/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll +++ b/llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll @@ -1,5 +1,5 @@ ; RUN: opt -functionattrs --disable-nofree-inference=false -S < %s | FileCheck %s --check-prefix=FNATTR -; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR +; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" @@ -11,6 +11,8 @@ declare noalias i8* @realloc(i8* nocapture, i64) local_unnamed_addr #0 declare void @_ZdaPv(i8*) local_unnamed_addr #2 +; declare void @helper(i8* nocapture nofree) + ; TEST 1 (positive case) ; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable @@ -136,11 +138,11 @@ ; TEST 6 (negative case) ; Call realloc ; FNATTR: Function Attrs: noinline nounwind uwtable -; FNATTR-NEXT: define noalias i8* @call_realloc(i8* nocapture %0, i64 %1) local_unnamed_addr +; FNATTR-NEXT: define noalias i8* @call_realloc(i8* nocapture %0, i64 %1, i8* nocapture readnone %2) local_unnamed_addr ; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable ; ATTRIBUTOR-NOT: nofree -; ATTRIBUTOR-NEXT: define noalias i8* @call_realloc(i8* nocapture %0, i64 %1) local_unnamed_addr -define noalias i8* @call_realloc(i8* nocapture %0, i64 %1) local_unnamed_addr #0 { +; ATTRIBUTOR-NEXT: define noalias i8* @call_realloc(i8* nocapture %0, i64 %1, i8* nocapture nofree %2) local_unnamed_addr +define noalias i8* @call_realloc(i8* nocapture %0, i64 %1, i8* nocapture %2) local_unnamed_addr #0 { %ret = tail call i8* @realloc(i8* %0, i64 %1) #2 ret i8* %ret } @@ -240,6 +242,28 @@ ret void } +; TEST 12 NoFree argument - positive. +; CHECK: define double @test12(double* nocapture nofree readonly %a) +define double @test12(double* nocapture readonly %a) { +entry: + %0 = load double, double* %a, align 8 + %call = tail call double @cos(double %0) #2 + ret double %call +} + +declare double @cos(double) local_unnamed_addr #2 + +; TEST 13 NoFree argument - positive. +; CHECK: define noalias i32* @test13(i64* nocapture nofree readonly %a) { +define noalias i32* @test13(i64* nocapture readonly %a) { +entry: + %0 = load i64, i64* %a, align 8 + %call = tail call noalias i8* @malloc(i64 %0) #2 + %1 = bitcast i8* %call to i32* + ret i32* %1 +} + +declare noalias i8* @malloc(i64) attributes #0 = { nounwind uwtable noinline } attributes #1 = { nounwind }