Index: include/llvm/Analysis/InlineCost.h =================================================================== --- include/llvm/Analysis/InlineCost.h +++ include/llvm/Analysis/InlineCost.h @@ -141,6 +141,9 @@ /// Threshold to use when the callsite is considered cold. Optional ColdCallSiteThreshold; + + /// Threshold to use when a function is considered frequently called. + Optional FrequentFuncThreshold; }; /// Generate the parameters to tune the inline cost analysis based only on the Index: lib/Analysis/InlineCost.cpp =================================================================== --- lib/Analysis/InlineCost.cpp +++ lib/Analysis/InlineCost.cpp @@ -66,6 +66,12 @@ cl::ZeroOrMore, cl::desc("Threshold for hot callsites ")); +/// When minsize (-Oz) is specified a function with a number of calls greater +/// or equal this threshold is considered as a frequently used function. +static cl::opt + FrequentFuncThreshold("inline-freq-func-threshold", cl::Hidden, cl::init(3), + cl::desc("Threshold for frequently called functions")); + namespace { class CallAnalyzer : public InstVisitor { @@ -172,6 +178,8 @@ // Custom analysis routines. bool analyzeBlock(BasicBlock *BB, SmallPtrSetImpl &EphValues); + bool isCallOfFrequentFunc(CallSite CS); + // Disable several entry points to the visitor so we don't accidentally use // them by declaring but not defining them here. void visit(Module *); @@ -1192,6 +1200,25 @@ return cast(ConstantInt::get(IntPtrTy, Offset)); } +/// \brief Check if the CallSite is a call of a frequently called function +/// having local linkage. +bool CallAnalyzer::isCallOfFrequentFunc(CallSite CS) { + Function *Callee = CS.getCalledFunction(); + + // We assume non-local functions have many callers, + // and therefore treat them the same way as local functions with many callers. + if (!Callee || !Callee->hasLocalLinkage()) + return true; + + int FrqFuncThreshold = (Params.FrequentFuncThreshold) + ? Params.FrequentFuncThreshold.getValue() + : FrequentFuncThreshold.getDefault().getValue(); + + assert(FrqFuncThreshold > 0); + + return (Callee->getNumUses() >= static_cast(FrqFuncThreshold)); +} + /// \brief Analyze a call site for potential inlining. /// /// Returns true if inlining this call is viable, and false if it is not @@ -1262,7 +1289,15 @@ } // The call instruction also disappears after inlining. Cost -= InlineConstants::InstrCost + InlineConstants::CallPenalty; - + + // Don't consider the call penalty as a penalty if minsize (-Oz) is + // requested and it is a call of a frequently used function. + assert(CS.getCaller() && "CallSite without Caller"); + if (CS.getCaller()->hasFnAttribute(Attribute::MinSize) && + isCallOfFrequentFunc(CS)) { + Cost += InlineConstants::CallPenalty; + } + // If there is only one call of the function, and it has internal linkage, // the cost of inlining it drops dramatically. bool OnlyOneCallAndLocalLinkage = @@ -1579,6 +1614,9 @@ // Set the ColdCallSiteThreshold knob from the -inline-cold-callsite-threshold. Params.ColdCallSiteThreshold = ColdCallSiteThreshold; + // Set the FrequentFuncThreshold knob from the -inline-freq-func-threshold. + Params.FrequentFuncThreshold = FrequentFuncThreshold; + // Set the OptMinSizeThreshold and OptSizeThreshold params only if the // Set the OptMinSizeThreshold and OptSizeThreshold params only if the // -inlinehint-threshold commandline option is not explicitly given. If that Index: test/Transforms/Inline/minsize-fastcc-frq-func.ll =================================================================== --- /dev/null +++ test/Transforms/Inline/minsize-fastcc-frq-func.ll @@ -0,0 +1,88 @@ +; Check that calls of frequently used local functions are not inlined if minsize +; is requested. +; The first test is to check the default threshold for frequent functions +; which is 3. +; The second test is to provide a value for the threshold and to check +; a function is not inlined. +; The third test checks that external functions are treated as +; functions with many callers and are not inlined. +; +; RUN: opt < %s -inline -S | FileCheck -check-prefix=DEFAULT_THRESHOLD %s +; RUN: opt < %s -inline -inline-freq-func-threshold=2 -S | FileCheck -check-prefix=THRESHOLD_VALUE_2 %s +; RUN: opt < %s -inline -S | FileCheck -check-prefix=EXTERNAL_FUNC_CALL %s + +define internal i32 @X9(i32 %x) nounwind { + %x2 = add i32 %x, %x + %x3 = add i32 %x2, %x + %x4 = add i32 %x3, %x + %x5 = add i32 %x4, %x + %x6 = add i32 %x5, %x + %x7 = add i32 %x6, %x + %x8 = add i32 %x7, %x + %x9 = add i32 %x8, %x + + ret i32 %x9 +} + +define internal i32 @Add_10_X9(i32 %x) nounwind { + %x2 = add i32 10, %x + %x3 = add i32 10, %x2 + %x4 = add i32 10, %x3 + %x5 = add i32 10, %x4 + %x6 = add i32 10, %x5 + %x7 = add i32 10, %x6 + %x8 = add i32 10, %x7 + %x9 = add i32 10, %x8 + + ret i32 %x9 +} + +define i32 @f1(i32 %x) nounwind minsize { + %res = call i32 @X9(i32 %x) + ret i32 %res +; DEFAULT_THRESHOLD-LABEL: @f1( +; DEFAULT_THRESHOLD: %res = call i32 @X9 +} + +define i32 @f2(i32 %x) nounwind { + %res = call i32 @X9(i32 %x) + ret i32 %res +} + +define i32 @f3(i32 %x) nounwind { + %res = call i32 @X9(i32 %x) + ret i32 %res +} + +define i32 @f4(i32 %x) nounwind { + %res = call i32 @X9(i32 %x) + ret i32 %res +} + +define i32 @f5(i32 %x) nounwind minsize { + %res1 = call i32 @Add_10_X9(i32 %x) + %res2 = call i32 @Add_10_X9(i32 %res1) + ret i32 %res2 +; THRESHOLD_VALUE_2-LABEL: @f5( +; THRESHOLD_VALUE_2: %res1 = call i32 @Add_10_X9 +; THRESHOLD_VALUE_2-NEXT: %res2 = call i32 @Add_10_X9 + +; DEFAULT_THRESHOLD-LABEL: @f5( +; DEFAULT_THRESHOLD-CHECK-NOT: call +} + +declare void @puts(i8* nocapture) nounwind + +define void @f6(i8* %s) nounwind minsize { + call void @puts(i8* %s) + call void @puts(i8* %s) + ret void +} + +define void @f7(i8* %s) nounwind minsize { + call void @f6(i8* %s) + ret void +; EXTERNAL_FUNC_CALL-LABEL: @f7( +; EXTERNAL_FUNC_CALL: call void @f6( +} +