Index: llvm/trunk/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/trunk/lib/Transforms/IPO/Attributor.cpp +++ llvm/trunk/lib/Transforms/IPO/Attributor.cpp @@ -50,6 +50,7 @@ STATISTIC(NumFnArgumentReturned, "Number of function arguments marked returned"); STATISTIC(NumFnNoSync, "Number of functions marked nosync"); +STATISTIC(NumFnNoFree, "Number of functions marked nofree"); // TODO: Determine a good default value. // @@ -104,6 +105,9 @@ case Attribute::NoSync: NumFnNoSync++; break; + case Attribute::NoFree: + NumFnNoFree++; + break; default: return; } @@ -909,6 +913,69 @@ return ChangeStatus::UNCHANGED; } +/// ------------------------ 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(). + ManifestPosition getManifestPosition() const override { return MP_FUNCTION; } + + /// See AbstractAttribute::getAsStr(). + const std::string getAsStr() const override { + return getAssumed() ? "nofree" : "may-free"; + } + + /// See AbstractAttribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override; + + /// See AbstractAttribute::getAttrKind(). + 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]) { + + 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 /// ---------------------------------------------------------------------------- @@ -1057,6 +1124,9 @@ // Every function might be marked "nosync" registerAA(*new AANoSyncFunction(F, InfoCache)); + // Every function might be "no-free". + registerAA(*new AANoFreeFunction(F, InfoCache)); + // Return attributes are only appropriate if the return type is non void. Type *ReturnType = F.getReturnType(); if (!ReturnType->isVoidTy()) { Index: llvm/trunk/test/Transforms/FunctionAttrs/arg_returned.ll =================================================================== --- llvm/trunk/test/Transforms/FunctionAttrs/arg_returned.ll +++ llvm/trunk/test/Transforms/FunctionAttrs/arg_returned.ll @@ -8,13 +8,13 @@ ; TEST SCC test returning an integer value argument ; -; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; BOTH-NEXT: define i32 @sink_r0(i32 returned %r) -; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; BOTH-NEXT: define i32 @scc_r1(i32 %a, i32 returned %r, i32 %b) -; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; BOTH-NEXT: define i32 @scc_r2(i32 %a, i32 %b, i32 returned %r) -; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; BOTH-NEXT: define i32 @scc_rX(i32 %a, i32 %b, i32 %r) ; ; FNATTR: define i32 @sink_r0(i32 returned %r) @@ -159,22 +159,22 @@ ; TEST SCC test returning a pointer value argument ; -; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; BOTH-NEXT: define double* @ptr_sink_r0(double* readnone returned %r) -; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; BOTH-NEXT: define double* @ptr_scc_r1(double* %a, double* readnone returned %r, double* nocapture readnone %b) -; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; BOTH-NEXT: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone returned %r) ; ; FNATTR: define double* @ptr_sink_r0(double* readnone returned %r) ; FNATTR: define double* @ptr_scc_r1(double* %a, double* readnone %r, double* nocapture readnone %b) ; FNATTR: define double* @ptr_scc_r2(double* readnone %a, double* readnone %b, double* readnone %r) ; -; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define double* @ptr_sink_r0(double* returned %r) -; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define double* @ptr_scc_r1(double* %a, double* returned %r, double* %b) -; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define double* @ptr_scc_r2(double* %a, double* %b, double* returned %r) ; ; double* ptr_scc_r1(double* a, double* b, double* r); @@ -261,7 +261,7 @@ ; ; FIXME: no-return missing ; FNATTR: define i32* @rt0(i32* readonly %a) -; BOTH: Function Attrs: noinline nosync nounwind readonly uwtable +; BOTH: Function Attrs: nofree noinline nosync nounwind readonly uwtable ; BOTH-NEXT: define i32* @rt0(i32* readonly returned %a) define i32* @rt0(i32* %a) #0 { entry: @@ -280,7 +280,7 @@ ; ; FIXME: no-return missing ; FNATTR: define noalias i32* @rt1(i32* nocapture readonly %a) -; BOTH: Function Attrs: noinline nosync nounwind readonly uwtable +; BOTH: Function Attrs: nofree noinline nosync nounwind readonly uwtable ; BOTH-NEXT: define noalias i32* @rt1(i32* nocapture readonly %a) define i32* @rt1(i32* %a) #0 { entry: @@ -441,11 +441,11 @@ ; return b == 0? b : x; ; } ; -; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; BOTH-NEXT: define double @select_and_phi(double returned %b) ; ; FNATTR: define double @select_and_phi(double %b) -; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define double @select_and_phi(double returned %b) define double @select_and_phi(double %b) #0 { entry: @@ -472,12 +472,12 @@ ; return b == 0? b : x; ; } ; -; BOTH: Function Attrs: noinline nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; BOTH-NEXT: define double @recursion_select_and_phi(i32 %a, double returned %b) ; ; FNATTR: define double @recursion_select_and_phi(i32 %a, double %b) ; -; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define double @recursion_select_and_phi(i32 %a, double returned %b) define double @recursion_select_and_phi(i32 %a, double %b) #0 { entry: @@ -503,12 +503,12 @@ ; return (double*)b; ; } ; -; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; BOTH-NEXT: define double* @bitcast(i32* readnone returned %b) ; ; FNATTR: define double* @bitcast(i32* readnone %b) ; -; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define double* @bitcast(i32* returned %b) define double* @bitcast(i32* %b) #0 { entry: @@ -526,12 +526,12 @@ ; return b != 0 ? b : x; ; } ; -; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; BOTH-NEXT: define double* @bitcasts_select_and_phi(i32* readnone returned %b) ; ; FNATTR: define double* @bitcasts_select_and_phi(i32* readnone %b) ; -; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define double* @bitcasts_select_and_phi(i32* returned %b) define double* @bitcasts_select_and_phi(i32* %b) #0 { entry: @@ -564,12 +564,12 @@ ; /* return undef */ ; } ; -; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; BOTH-NEXT: define double* @ret_arg_arg_undef(i32* readnone returned %b) ; ; FNATTR: define double* @ret_arg_arg_undef(i32* readnone %b) ; -; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define double* @ret_arg_arg_undef(i32* returned %b) define double* @ret_arg_arg_undef(i32* %b) #0 { entry: @@ -602,12 +602,12 @@ ; /* return undef */ ; } ; -; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; BOTH-NEXT: define double* @ret_undef_arg_arg(i32* readnone returned %b) ; ; FNATTR: define double* @ret_undef_arg_arg(i32* readnone %b) ; -; ATTRIBUTOR: Function Attrs: noinline nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define double* @ret_undef_arg_arg(i32* returned %b) define double* @ret_undef_arg_arg(i32* %b) #0 { entry: @@ -640,7 +640,7 @@ ; /* return undef */ ; } ; -; BOTH: Function Attrs: noinline norecurse nosync nounwind readnone uwtable +; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; BOTH-NEXT: define double* @ret_undef_arg_undef(i32* readnone returned %b) ; ; FNATTR: define double* @ret_undef_arg_undef(i32* readnone %b) @@ -744,8 +744,8 @@ attributes #0 = { noinline nounwind uwtable } ; BOTH-NOT: attributes # -; BOTH-DAG: attributes #{{[0-9]*}} = { noinline norecurse nosync nounwind readnone uwtable } -; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nosync nounwind readnone uwtable } -; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nosync nounwind readonly uwtable } +; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline norecurse nosync nounwind readnone uwtable } +; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline nosync nounwind readnone uwtable } +; BOTH-DAG: attributes #{{[0-9]*}} = { nofree noinline nosync nounwind readonly uwtable } ; BOTH-DAG: attributes #{{[0-9]*}} = { noinline nounwind uwtable } ; BOTH-NOT: attributes # Index: llvm/trunk/test/Transforms/FunctionAttrs/fn_noreturn.ll =================================================================== --- llvm/trunk/test/Transforms/FunctionAttrs/fn_noreturn.ll +++ llvm/trunk/test/Transforms/FunctionAttrs/fn_noreturn.ll @@ -20,7 +20,7 @@ ; } ; ; FIXME: no-return missing -; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable +; CHECK: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; CHECK: define void @srec0() ; define void @srec0() #0 { @@ -37,7 +37,7 @@ ; } ; ; FIXME: no-return missing -; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable +; CHECK: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; CHECK: define i32 @srec16(i32 %a) ; define i32 @srec16(i32 %a) #0 { @@ -69,7 +69,7 @@ ; } ; ; FIXME: no-return missing -; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable +; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; CHECK: define i32 @endless_loop(i32 %a) ; define i32 @endless_loop(i32 %a) #0 { @@ -89,7 +89,7 @@ ; } ; ; FIXME: no-return missing -; CHECK: Function Attrs: noinline norecurse nosync nounwind readnone uwtable +; CHECK: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable ; CHECK: define i32 @dead_return(i32 returned %a) ; define i32 @dead_return(i32 %a) #0 { @@ -111,7 +111,7 @@ ; } ; ; FIXME: no-return missing -; CHECK: Function Attrs: noinline nosync nounwind readnone uwtable +; CHECK: Function Attrs: nofree noinline nosync nounwind readnone uwtable ; CHECK: define i32 @multiple_noreturn_calls(i32 %a) ; define i32 @multiple_noreturn_calls(i32 %a) #0 { Index: llvm/trunk/test/Transforms/FunctionAttrs/nofree-attributor.ll =================================================================== --- llvm/trunk/test/Transforms/FunctionAttrs/nofree-attributor.ll +++ llvm/trunk/test/Transforms/FunctionAttrs/nofree-attributor.ll @@ -1,4 +1,6 @@ -; RUN: opt -functionattrs --disable-nofree-inference=false -S < %s | FileCheck %s +; RUN: opt -functionattrs --disable-nofree-inference=false -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 nosync 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 nosync 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 nosync 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" + +; 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 -; FIXME: missing "nofree" -; Function Attrs: noinline nounwind readnone uwtable -; CHECK: define void @call_nofree_function() +; FNATTR: Function Attrs: noinline nounwind readnone uwtable +; FNATTR-NEXT: define void @call_nofree_function() +; ATTRIBUTOR: Function Attrs: nofree noinline nosync 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 nosync 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 nosync 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 nosync 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 } Index: llvm/trunk/test/Transforms/FunctionAttrs/nosync.ll =================================================================== --- llvm/trunk/test/Transforms/FunctionAttrs/nosync.ll +++ llvm/trunk/test/Transforms/FunctionAttrs/nosync.ll @@ -27,7 +27,7 @@ ; FNATTR: Function Attrs: norecurse nounwind optsize readnone ssp uwtable ; FNATTR-NEXT: define nonnull i32* @foo(%struct.ST* readnone %s) -; ATTRIBUTOR: Function Attrs: nosync nounwind optsize readnone ssp uwtable +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind optsize readnone ssp uwtable ; ATTRIBUTOR-NEXT: define i32* @foo(%struct.ST* %s) define i32* @foo(%struct.ST* %s) nounwind uwtable readnone optsize ssp { entry: @@ -44,7 +44,7 @@ ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable ; FNATTR-NEXT: define i32 @load_monotonic(i32* nocapture readonly) -; ATTRIBUTOR: Function Attrs: norecurse nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define i32 @load_monotonic(i32* nocapture readonly) define i32 @load_monotonic(i32* nocapture readonly) norecurse nounwind uwtable { %2 = load atomic i32, i32* %0 monotonic, align 4 @@ -60,7 +60,7 @@ ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable ; FNATTR-NEXT: define void @store_monotonic(i32* nocapture) -; ATTRIBUTOR: Function Attrs: norecurse nosync nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind uwtable ; ATTRIBUTOR-NEXT: define void @store_monotonic(i32* nocapture) define void @store_monotonic(i32* nocapture) norecurse nounwind uwtable { store atomic i32 10, i32* %0 monotonic, align 4 @@ -76,7 +76,7 @@ ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable ; FNATTR-NEXT: define i32 @load_acquire(i32* nocapture readonly) -; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define i32 @load_acquire(i32* nocapture readonly) define i32 @load_acquire(i32* nocapture readonly) norecurse nounwind uwtable { @@ -92,7 +92,7 @@ ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable ; FNATTR-NEXT: define void @load_release(i32* nocapture) -; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define void @load_release(i32* nocapture) define void @load_release(i32* nocapture) norecurse nounwind uwtable { @@ -104,7 +104,7 @@ ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable ; FNATTR-NEXT: define void @load_volatile_release(i32* nocapture) -; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define void @load_volatile_release(i32* nocapture) define void @load_volatile_release(i32* nocapture) norecurse nounwind uwtable { @@ -120,7 +120,7 @@ ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable ; FNATTR-NEXT: define void @volatile_store(i32*) -; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define void @volatile_store(i32*) define void @volatile_store(i32*) norecurse nounwind uwtable { @@ -137,7 +137,7 @@ ; FNATTR: Function Attrs: nofree norecurse nounwind uwtable ; FNATTR-NEXT: define i32 @volatile_load(i32*) -; ATTRIBUTOR: Function Attrs: norecurse nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree norecurse nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define i32 @volatile_load(i32*) define i32 @volatile_load(i32*) norecurse nounwind uwtable { @@ -185,7 +185,7 @@ ; FNATTR: Function Attrs: nofree noinline nounwind uwtable ; FNATTR-NEXT: define i32 @scc1(i32*) -; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define i32 @scc1(i32*) define i32 @scc1(i32*) noinline nounwind uwtable { @@ -196,7 +196,7 @@ ; FNATTR: Function Attrs: nofree noinline nounwind uwtable ; FNATTR-NEXT: define void @scc2(i32*) -; ATTRIBUTOR: Function Attrs: noinline nounwind uwtable +; ATTRIBUTOR: Function Attrs: nofree noinline nounwind uwtable ; ATTRIBUTOR-NOT: nosync ; ATTRIBUTOR-NEXT: define void @scc2(i32*) define void @scc2(i32*) noinline nounwind uwtable { @@ -257,7 +257,7 @@ ; TEST 13 - Fence syncscope("singlethread") seq_cst ; FNATTR: Function Attrs: nofree norecurse nounwind ; FNATTR-NEXT: define void @foo1_singlethread(i32* nocapture, %"struct.std::atomic"* nocapture) -; ATTRIBUTOR: Function Attrs: nosync +; ATTRIBUTOR: Function Attrs: nofree nosync ; ATTRIBUTOR: define void @foo1_singlethread(i32*, %"struct.std::atomic"*) define void @foo1_singlethread(i32*, %"struct.std::atomic"*) { store i32 100, i32* %0, align 4 @@ -269,7 +269,7 @@ ; FNATTR: Function Attrs: nofree norecurse nounwind ; FNATTR-NEXT: define void @bar_singlethread(i32* nocapture readnone, %"struct.std::atomic"* nocapture readonly) -; ATTRIBUTOR: Function Attrs: nosync +; ATTRIBUTOR: Function Attrs: nofree nosync ; ATTRIBUTOR: define void @bar_singlethread(i32*, %"struct.std::atomic"*) define void @bar_singlethread(i32 *, %"struct.std::atomic"*) { %3 = getelementptr inbounds %"struct.std::atomic", %"struct.std::atomic"* %1, i64 0, i32 0, i32 0 Index: llvm/trunk/test/Transforms/FunctionAttrs/nounwind.ll =================================================================== --- llvm/trunk/test/Transforms/FunctionAttrs/nounwind.ll +++ llvm/trunk/test/Transforms/FunctionAttrs/nounwind.ll @@ -4,7 +4,7 @@ ; TEST 1 ; CHECK: Function Attrs: norecurse nounwind readnone ; CHECK-NEXT: define i32 @foo1() -; ATTRIBUTOR: Function Attrs: nosync nounwind +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind ; ATTRIBUTOR-NEXT: define i32 @foo1() define i32 @foo1() { ret i32 1 @@ -13,7 +13,7 @@ ; TEST 2 ; CHECK: Function Attrs: nounwind readnone ; CHECK-NEXT: define i32 @scc1_foo() -; ATTRIBUTOR: Function Attrs: nosync nounwind +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind ; ATTRIBUTOR-NEXT: define i32 @scc1_foo() define i32 @scc1_foo() { %1 = call i32 @scc1_bar() @@ -24,7 +24,7 @@ ; TEST 3 ; CHECK: Function Attrs: nounwind readnone ; CHECK-NEXT: define i32 @scc1_bar() -; ATTRIBUTOR: Function Attrs: nosync nounwind +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind ; ATTRIBUTOR-NEXT: define i32 @scc1_bar() define i32 @scc1_bar() { %1 = call i32 @scc1_foo()