Index: lib/Analysis/InlineCost.cpp =================================================================== --- lib/Analysis/InlineCost.cpp +++ lib/Analysis/InlineCost.cpp @@ -124,6 +124,15 @@ /// Tunable parameters that control the analysis. const InlineParams &Params; + /// The depth of recursion that this CallAnalyzer sits at. + unsigned CallsAnalyzedRecursionDepth = 0; + + /// The maximum depth of recursion for this CallAnalyzer. + const unsigned MaxCallsAnalyzedRecursionDepth = 100; + + /// True if this CallAnalyzer hit its maximum recursion depth. + bool HitMaxNumAnalyzeCalls = false; + int Threshold; int Cost; bool ComputeFullInlineCost; @@ -271,12 +280,14 @@ std::function &GetAssumptionCache, Optional> &GetBFI, ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE, - Function &Callee, CallSite CSArg, const InlineParams &Params) + Function &Callee, CallSite CSArg, const InlineParams &Params, + const unsigned &Depth) : TTI(TTI), GetAssumptionCache(GetAssumptionCache), GetBFI(GetBFI), PSI(PSI), F(Callee), DL(F.getParent()->getDataLayout()), ORE(ORE), - CandidateCS(CSArg), Params(Params), Threshold(Params.DefaultThreshold), - Cost(0), ComputeFullInlineCost(OptComputeFullInlineCost || - Params.ComputeFullInlineCost || ORE), + CandidateCS(CSArg), Params(Params), CallsAnalyzedRecursionDepth(Depth), + Threshold(Params.DefaultThreshold), Cost(0), + ComputeFullInlineCost(OptComputeFullInlineCost || + Params.ComputeFullInlineCost || ORE), IsCallerRecursive(false), IsRecursiveCall(false), ExposesReturnsTwice(false), HasDynamicAlloca(false), ContainsNoDuplicateCall(false), HasReturn(false), HasIndirectBr(false), @@ -1191,6 +1202,10 @@ } bool CallAnalyzer::visitCallSite(CallSite CS) { + // If we've recursed too deep, then quit. + if (HitMaxNumAnalyzeCalls) + return false; + if (CS.hasFnAttr(Attribute::ReturnsTwice) && !F.hasFnAttribute(Attribute::ReturnsTwice)) { // This aborts the entire analysis. @@ -1279,13 +1294,20 @@ auto IndirectCallParams = Params; IndirectCallParams.DefaultThreshold = InlineConstants::IndirectCallThreshold; CallAnalyzer CA(TTI, GetAssumptionCache, GetBFI, PSI, ORE, *F, CS, - IndirectCallParams); + IndirectCallParams, CallsAnalyzedRecursionDepth); + + // Analyze CS and update the depth of recursion. if (CA.analyzeCall(CS)) { // We were able to inline the indirect call! Subtract the cost from the // threshold to get the bonus we want to apply, but don't go below zero. Cost -= std::max(0, CA.getThreshold() - CA.getCost()); } + // If we hit the maximum recursion when calling analyzeCall, update this + // CallAnalyzer's max recursion flag. + if (CA.HitMaxNumAnalyzeCalls) + HitMaxNumAnalyzeCalls = true; + if (!F->onlyReadsMemory()) disableLoadElimination(); return Base::visitCallSite(CS); @@ -1682,6 +1704,14 @@ /// is below the computed threshold, then inlining was forcibly disabled by /// some artifact of the routine. bool CallAnalyzer::analyzeCall(CallSite CS) { + ++CallsAnalyzedRecursionDepth; + + // Update the depth of recursion. If we've gone too deep, bail out. + if (CallsAnalyzedRecursionDepth >= MaxCallsAnalyzedRecursionDepth) { + HitMaxNumAnalyzeCalls = true; + return false; + } + ++NumCallsAnalyzed; // Perform some tweaks to the cost and threshold based on the direct @@ -1883,6 +1913,8 @@ DEBUG_PRINT_STAT(ContainsNoDuplicateCall); DEBUG_PRINT_STAT(Cost); DEBUG_PRINT_STAT(Threshold); + DEBUG_PRINT_STAT(HitMaxNumAnalyzeCalls); + DEBUG_PRINT_STAT(MaxCallsAnalyzedRecursionDepth); #undef DEBUG_PRINT_STAT } #endif @@ -1978,7 +2010,7 @@ << "... (caller:" << Caller->getName() << ")\n"); CallAnalyzer CA(CalleeTTI, GetAssumptionCache, GetBFI, PSI, ORE, *Callee, CS, - Params); + Params, 0); bool ShouldInline = CA.analyzeCall(CS); DEBUG(CA.dump()); Index: test/Transforms/Inline/inline-functionptr.ll =================================================================== --- /dev/null +++ test/Transforms/Inline/inline-functionptr.ll @@ -0,0 +1,29 @@ +; RUN: opt -passes='cgscc(inline)' -S %s | FileCheck %s + +;; The inliner should quit when trying to inline the call to Bar into Bar. +define void @Bar(i8* nocapture %FunctionPtr) #0 { +; CHECK-LABEL: Bar +; CHECK: %0 = bitcast i8* %FunctionPtr to void (i8*)* +; CHECK-NEXT: call void %0(i8* bitcast (void (i8*)* @Bar to i8*)) #0 +; CHECK-NEXT: call void %0(i8* bitcast (void (i8*)* @Bar to i8*)) #0 +; CHECK-NEXT: ret void +entry: + %0 = bitcast i8* %FunctionPtr to void (i8*)* + call void %0(i8* bitcast (void (i8*)* @Bar to i8*)) #0 + call void %0(i8* bitcast (void (i8*)* @Bar to i8*)) #0 + ret void +} + +;; The inliner should be able to inline Bar into main +define i32 @main(i32 %argc, i8** nocapture readnone %argv) +local_unnamed_addr #0 { +; CHECK-LABEL: main +; CHECK: call void @Bar(i8* bitcast (void (i8*)* @Bar to i8*)) #0 +; CHECK-NEXT: call void @Bar(i8* bitcast (void (i8*)* @Bar to i8*)) #0 +; CHECK-NEXT: ret i32 0 +entry: + tail call void @Bar(i8* bitcast (void (i8*)* @Bar to i8*)) #0 + ret i32 0 +} + +attributes #0 = { nounwind ssp uwtable }