Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -42,6 +42,7 @@ "Number of abstract attributes in a valid fixpoint state"); STATISTIC(NumAttributesManifested, "Number of abstract attributes manifested in IR"); +STATISTIC(NumFnNoFree, "Number of function marked nofree"); // TODO: Determine a good default value. // @@ -86,10 +87,13 @@ if (!Attr.isEnumAttribute()) return; - //switch (Attr.getKindAsEnum()) { - //default: - // return; - //} + switch (Attr.getKindAsEnum()) { + case Attribute::NoFree: + NumFnNoFree++; + break; + default: + return; + } } /// Helper to identify the correct offset into an attribute list. @@ -241,6 +245,72 @@ return const_cast(this)->getAnchorScope(); } +/// ------------------------ No-Free Attributes ---------------------------- + +struct AANoFreeFunction : AbstractAttribute, BooleanState { + + /// See AbstractAttribute::AbstractAttribute(...). + AANoFreeFunction(Function &F, InformationCache &InfoCache) + : AbstractAttribute(F, InfoCache) {} + + /// See AbstractAttribute::getState() + ///{ + AbstractState &getState() override { return *this; } + const AbstractState &getState() const override { return *this; } + ///} + + /// See AbstractAttribute::getManifestPosition(). + virtual ManifestPosition getManifestPosition() const override { + return MP_FUNCTION; + } + + /// See AbstractAttribute::getAsStr(). + virtual const std::string getAsStr() const override { + return getAssumed() ? "nofree" : "may-free"; + } + + /// See AbstractAttribute::updateImpl(...). + virtual ChangeStatus updateImpl(Attributor &A) override; + + /// See AbstractAttribute::getAttrKind(). + virtual Attribute::AttrKind getAttrKind() const override { return ID; } + + /// Return true if "nofree" is assumed. + bool isAssumedNoFree() const { return getAssumed(); } + + /// Return true if "nofree" is known. + bool isKnownNoFree() const { return getKnown(); } + + /// The identifier used by the Attributor for this class of attributes. + static constexpr Attribute::AttrKind ID = Attribute::NoFree; +}; + +ChangeStatus AANoFreeFunction::updateImpl(Attributor &A) { + Function &F = getAnchorScope(); + + // The map from instruction opcodes to those instructions in the function. + auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F); + + for (unsigned Opcode : + {(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr, + (unsigned)Instruction::Call}) { + for (Instruction *I : OpcodeInstMap[Opcode]) { + // FIXME: Assume that all intrinsic function would free memory for now. + + auto ICS = ImmutableCallSite(I); + auto *NoFreeAA = A.getAAFor(*this, *I); + + if ((!NoFreeAA || !NoFreeAA->isValidState() || + !NoFreeAA->isAssumedNoFree()) && + !ICS.hasFnAttr(Attribute::NoFree)) { + indicatePessimisticFixpoint(); + return ChangeStatus::CHANGED; + } + } + } + return ChangeStatus::UNCHANGED; +} + /// ---------------------------------------------------------------------------- /// Attributor /// ---------------------------------------------------------------------------- @@ -383,6 +453,9 @@ Function &F, InformationCache &InfoCache, DenseSet *Whitelist) { + // Every function might be "no-free". + registerAA(*new AANoFreeFunction(F, InfoCache)); + // Walk all instructions to find more attribute opportunities and also // interesting instructions that might be queried by abstract attributes // during their initialization or update. @@ -396,11 +469,18 @@ // opcode we store them in the InfoCache. As not all opcodes are interesting // to concrete attributes we only cache the ones that are as identified in // the following switch. - // Note: There are no concrete attributes now so this is initially empty. - //switch (I.getOpcode()) { - //default: - // break; - //} + switch (I.getOpcode()) { + default: + assert((!ImmutableCallSite(&I)) && (!isa(&I)) && + "New call site/base instruction type needs to be known in the " + "attributor!"); + break; + case Instruction::Call: + case Instruction::CallBr: + case Instruction::Invoke: + // Call-like instructions are interesting for AANoFreeFunction. + IsInterestingOpcode = true; + } if (IsInterestingOpcode) InstOpcodeMap[I.getOpcode()].push_back(&I); if (I.mayReadOrWriteMemory()) Index: llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll =================================================================== --- llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll +++ llvm/test/Transforms/FunctionAttrs/nofree-attributor.ll @@ -1,4 +1,6 @@ -; RUN: opt -functionattrs -S < %s | FileCheck %s +; RUN: opt -functionattrs -S < %s | FileCheck %s --check-prefix=FNATTR +; RUN: opt -attributor --attributor-disable=false -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" ; Test cases specifically designed for the "nofree" function attribute. @@ -11,29 +13,33 @@ ; TEST 1 (positive case) -; FIXME: missing "nofree" -; CHECK: Function Attrs: noinline norecurse nounwind readnone uwtable -; CHECK: define void @only_return() +; FNATTR: Function Attrs: noinline norecurse nounwind readnone uwtable +; FNATTR-NEXT: define void @only_return() +; ATTRIBUTOR: Function Attrs: nofree noinline nounwind uwtable +; ATTRIBUTOR-NEXT: define void @only_return() define void @only_return() #0 { ret void } -; TEST 2 (nagative case) +; TEST 2 (negative case) ; Only free ; void only_free(char* p) { ; free(p); ; } -; CHECK: Function Attrs: noinline nounwind uwtable -; CHECK: define void @only_free(i8* nocapture) local_unnamed_addr +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR-NEXT: define void @only_free(i8* nocapture) local_unnamed_addr +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: nofree +; ATTRIBUTOR-NEXT: define void @only_free(i8* nocapture) local_unnamed_addr #1 define void @only_free(i8* nocapture) local_unnamed_addr #0 { tail call void @free(i8* %0) #1 ret void } -; TEST 3 (nagative case) +; TEST 3 (negative case) ; Free occurs in same scc. ; void free_in_scc1(char*p){ ; free_in_scc2(p); @@ -44,17 +50,23 @@ ; } -; CHECK: Function Attrs: noinline nounwind uwtable -; CHECK: define void @free_in_scc1(i8* nocapture) local_unnamed_addr +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR-NEXT: define void @free_in_scc1(i8* nocapture) local_unnamed_addr +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: nofree +; ATTRIBUTOR-NEXT :define void @free_in_scc1(i8* nocapture) local_unnamed_addr define void @free_in_scc1(i8* nocapture) local_unnamed_addr #0 { tail call void @free_in_scc2(i8* %0) #1 ret void } -; CHECK: Function Attrs: noinline nounwind uwtable -; CHECK: define void @free_in_scc2(i8* nocapture) local_unnamed_addr -define void @free_in_scc2(i8*) local_unnamed_addr #0 { +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR-NEXT: define void @free_in_scc2(i8* nocapture) local_unnamed_addr +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: nofree +; ATTRIBUTOR: define void @free_in_scc2(i8* nocapture) local_unnamed_addr +define void @free_in_scc2(i8* nocapture) local_unnamed_addr #0 { tail call void @free_in_scc1(i8* %0) tail call void @free(i8* %0) #1 ret void @@ -71,17 +83,19 @@ ; } -; FIXME: missing "nofree" -; CHECK: Function Attrs: noinline nounwind readnone uwtable -; CHECK: define void @mutual_recursion1() +; FNATTR: Function Attrs: noinline nounwind readnone uwtable +; FNATTR-NEXT: define void @mutual_recursion1() +; ATTRIBUTOR: Function Attrs: nofree noinline nounwind uwtable +; ATTRIBUTOR-NEXT: define void @mutual_recursion1() define void @mutual_recursion1() #0 { call void @mutual_recursion2() ret void } -; FIXME: missing "nofree" -; CHECK: Function Attrs: noinline nounwind readnone uwtable -; CHECK: define void @mutual_recursion2() +; FNATTR: Function Attrs: noinline nounwind readnone uwtable +; FNATTR-NEXT: define void @mutual_recursion2() +; ATTRIBUTOR: Function Attrs: nofree noinline nounwind uwtable +; ATTRIBUTOR-NEXT: define void @mutual_recursion2() define void @mutual_recursion2() #0 { call void @mutual_recursion1() ret void @@ -89,13 +103,16 @@ ; TEST 5 -; C++ delete operation (nagative case) +; C++ delete operation (negative case) ; void delete_op (char p[]){ ; delete [] p; ; } -; CHECK: Function Attrs: noinline nounwind uwtable -; CHECK: define void @_Z9delete_opPc(i8*) local_unnamed_addr +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR-NEXT: define void @_Z9delete_opPc(i8*) local_unnamed_addr +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: "nofree" +; ATTRIBUTOR-NEXT: define void @_Z9delete_opPc(i8*) local_unnamed_addr #1 define void @_Z9delete_opPc(i8*) local_unnamed_addr #0 { %2 = icmp eq i8* %0, null br i1 %2, label %4, label %3 @@ -111,9 +128,12 @@ ; TEST 6 (negative case) ; Call realloc -; CHECK: Function Attrs: noinline nounwind uwtable -; CHECK: define noalias i8* @call_realloc(i8* nocapture, i64) local_unnamed_addr -define noalias i8* @call_realloc(i8*nocapture, i64) local_unnamed_addr #0 { +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR-NEXT: define noalias i8* @call_realloc(i8* nocapture, i64) local_unnamed_addr +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: "nofree" +; ATTRIBUTOR-NEXT: define noalias i8* @call_realloc(i8* nocapture, i64) local_unnamed_addr +define noalias i8* @call_realloc(i8* nocapture, i64) local_unnamed_addr #0 { %ret = tail call i8* @realloc(i8* %0, i64 %1) #2 ret i8* %ret } @@ -122,35 +142,50 @@ ; TEST 7 (positive case) ; Call function declaration with "nofree" -declare void @nofree_function() "nofree" readnone #0 -; FIXME: missing "nofree" -; Function Attrs: noinline nounwind readnone uwtable -; CHECK: define void @call_nofree_function() +; FNATTR: Function Attrs: nofree noinline nounwind readnone uwtable +; FNATTR-NEXT: declare void @nofree_function() +; ATTRIBUTOR: Function Attrs: nofree noinline nounwind readnone uwtable +; ATTRIBUTOR-NEXT: declare void @nofree_function() +declare void @nofree_function() nofree readnone #0 + +; FNATTR: Function Attrs: noinline nounwind readnone uwtable +; FNATTR-NEXT: define void @call_nofree_function() +; ATTRIBUTOR: Function Attrs: nofree noinline nounwind uwtable +; ATTRIBUTOR-NEXT: define void @call_nofree_function() define void @call_nofree_function() #0 { tail call void @nofree_function() ret void } -; TEST 8 (nagative case) +; TEST 8 (negative case) ; Call function declaration without "nofree" + +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NEXT: declare void @maybe_free() declare void @maybe_free() #0 -; CHECK: Function Attrs: noinline nounwind uwtable -; CHECK: define void @call_maybe_free() +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR: define void @call_maybe_free() +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: nofree +; ATTRIBUTOR-NEXT: define void @call_maybe_free() define void @call_maybe_free() #0 { tail call void @maybe_free() ret void } -; TEST 9 (nagative case) +; TEST 9 (negative case) ; Call both of above functions -; CHECK: Function Attrs: noinline nounwind uwtable -; CHECK: define void @call_both() +; FNATTR: Function Attrs: noinline nounwind uwtable +; FNATTR-NEXT: define void @call_both() +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NOT: nofree +; ATTRIBUTOR-NEXT: define void @call_both() define void @call_both() #0 { tail call void @maybe_free() tail call void @nofree_function() @@ -158,6 +193,47 @@ } +; TEST 10 (positive case) +; Call intrinsic function +; FNATTRS: Function Attrs: noinline readnone speculatable +; FNATTRS-NEXT: declare float @llvm.floor.f32(float) +; ATTRIBUTOR: Function Attrs: nounwind readnone speculatable +; ATTRIBUTOR-NEXT: declare float @llvm.floor.f32(float) +declare float @llvm.floor.f32(float) + +; FNATTRS: Function Attrs: noinline nounwind uwtable +; FNATTRS-NEXT: define void @call_floor(float %a) +; FIXME: missing nofree +; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR-NEXT: define void @call_floor(float %a) + +define void @call_floor(float %a) #0 { + tail call float @llvm.floor.f32(float %a) + ret void +} + +; TEST 11 (positive case) +; Check propagation. + +; FNATTRS: Function Attrs: noinline nounwind uwtable +; FNATTRS-NEXT: define void @f1() +; ATTRIBUTOR: Function Attrs: nofree noinline nounwind uwtable +; ATTRIBUTOR-NEXT: define void @f1() +define void @f1() #0 { + tail call void @nofree_function() + ret void +} + +; FNATTRS: Function Attrs: noinline nounwind uwtable +; FNATTRS-NEXT: define void @f2() +; ATTRIBUTOR: Function Attrs: nofree noinline nounwind uwtable +; ATTRIBUTOR-NEXT: define void @f2() +define void @f2() #0 { + tail call void @f1() + ret void +} + + attributes #0 = { nounwind uwtable noinline } attributes #1 = { nounwind } attributes #2 = { nobuiltin nounwind }