diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1803,8 +1803,8 @@ startup time if the function is not called during program startup. ``noprofile`` This function attribute prevents instrumentation based profiling, used for - coverage or profile based optimization, from being added to a function, - even when inlined. + coverage or profile based optimization, from being added to a function. It + also prevents instrumented functions from being inlined into this function. ``noredzone`` This attribute indicates that the code generator should not use a red zone, even if the target-specific ABI normally permits it. diff --git a/llvm/include/llvm/IR/Attributes.h b/llvm/include/llvm/IR/Attributes.h --- a/llvm/include/llvm/IR/Attributes.h +++ b/llvm/include/llvm/IR/Attributes.h @@ -1256,15 +1256,6 @@ /// attributes for inlining purposes. bool areInlineCompatible(const Function &Caller, const Function &Callee); - -/// Checks if there are any incompatible function attributes between -/// \p A and \p B. -/// -/// \param [in] A - The first function to be compared with. -/// \param [in] B - The second function to be compared with. -/// \returns true if the functions have compatible attributes. -bool areOutlineCompatible(const Function &A, const Function &B); - /// Merge caller's and callee's attributes. void mergeAttributesForInlining(Function &Caller, const Function &Callee); diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -183,7 +183,8 @@ /// Disable Indirect Branch Tracking. def NoCfCheck : EnumAttr<"nocf_check", [FnAttr]>; -/// Function should not be instrumented. +/// This function should not be instrumented and profiled functions cannot be +/// inlined into it. def NoProfile : EnumAttr<"noprofile", [FnAttr]>; /// Function doesn't unwind stack. @@ -340,7 +341,6 @@ def : CompatRule<"isEqual">; def : CompatRule<"isEqual">; def : CompatRule<"isEqual">; -def : CompatRule<"isEqual">; class MergeRule { // The name of the function called to merge the attributes of the caller and diff --git a/llvm/lib/IR/Attributes.cpp b/llvm/lib/IR/Attributes.cpp --- a/llvm/lib/IR/Attributes.cpp +++ b/llvm/lib/IR/Attributes.cpp @@ -2039,14 +2039,17 @@ bool AttributeFuncs::areInlineCompatible(const Function &Caller, const Function &Callee) { + // For some attributes, we want to forbid inlining if the attribute is found + // in the caller but not the callee. For example, when a user adds the + // noprofile attribute we don't want to inline profiled functions. + auto AttrInCallerNotInCallee = [&](Attribute::AttrKind Kind) -> bool { + return Caller.hasFnAttribute(Kind) && !Callee.hasFnAttribute(Kind); + }; + if (AttrInCallerNotInCallee(Attribute::NoProfile)) + return false; return hasCompatibleFnAttrs(Caller, Callee); } -bool AttributeFuncs::areOutlineCompatible(const Function &A, - const Function &B) { - return hasCompatibleFnAttrs(A, B); -} - void AttributeFuncs::mergeAttributesForInlining(Function &Caller, const Function &Callee) { mergeFnAttrs(Caller, Callee); diff --git a/llvm/test/Transforms/Inline/inline_noprofile.ll b/llvm/test/Transforms/Inline/inline_noprofile.ll --- a/llvm/test/Transforms/Inline/inline_noprofile.ll +++ b/llvm/test/Transforms/Inline/inline_noprofile.ll @@ -2,16 +2,12 @@ ; RUN: opt -passes=inline %s -S -pass-remarks-missed=inline 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-INLINE ; RUN: opt -passes=always-inline %s -S | FileCheck %s -; Test that we don't inline when caller and callee don't have matching -; noprofile fn attrs. +; Test that we don't inline profiled callees into noprofile callers. ; CHECK-INLINE: 'profile' not inlined into 'profile_caller' because it should never be inlined (cost=never): conflicting attributes -; CHECK-INLINE: 'noprofile' not inlined into 'noprofile_caller' because it should never be inlined (cost=never): conflicting attributes define i32 @profile() { ret i32 42 } -define i32 @noprofile() noprofile { ret i32 43 } define i32 @profile_aa() alwaysinline { ret i32 44 } -define i32 @noprofile_aa() noprofile alwaysinline { ret i32 45 } define i32 @profile_caller() noprofile { ; CHECK-LABEL: @profile_caller( @@ -23,22 +19,12 @@ ret i32 %2 } -define i32 @noprofile_caller() { -; CHECK-LABEL: @noprofile_caller( -; CHECK-NEXT: [[TMP1:%.*]] = call i32 @noprofile() -; CHECK-NEXT: ret i32 45 -; - call i32 @noprofile() - %2 = call i32 @noprofile_aa() - ret i32 %2 -} - -; Test that we do inline when caller and callee don't have matching -; noprofile fn attrs, when CallInst is alwaysinline. -define i32 @aa_callsite() { +; Test that we do inline profiled callees into no profile callers when CallInst +; is alwaysinline +define i32 @aa_callsite() noprofile { ; CHECK-INLINE-LABEL: @aa_callsite( -; CHECK-INLINE-NEXT: ret i32 43 +; CHECK-INLINE-NEXT: ret i32 42 ; - %1 = call i32 @noprofile() alwaysinline + %1 = call i32 @profile() alwaysinline ret i32 %1 }