From 12c6ee8fd2046c85ae9693105288fcd749b90733 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 7 Nov 2023 10:36:45 +0100 Subject: [PATCH] [GlobalOpt] Cache whether CC is changeable (#71381) The hasAddressTaken() call in hasOnlyColdCalls() has quadratic complexity if there are many cold calls to a function: We're going to visit each call of the function, and then for each of them iterate all the users of the function. We've recently encountered a case where GlobalOpt spends more than an hour in these hasAddressTaken() checks when full LTO is used. Avoid this by moving the hasAddressTaken() check into hasChangeableCC() and caching its result, so it is only computed once per function. (cherry picked from commit e360a16fee2dc3cb632ace556fb715832f488a90) --- llvm/lib/Transforms/IPO/GlobalOpt.cpp | 30 ++++++++++++++++++++------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp index 1ccc523ead8a..8012e1e650a0 100644 --- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp +++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp @@ -1701,13 +1701,16 @@ static void RemoveAttribute(Function *F, Attribute::AttrKind A) { /// idea here is that we don't want to mess with the convention if the user /// explicitly requested something with performance implications like coldcc, /// GHC, or anyregcc. -static bool hasChangeableCC(Function *F) { +static bool hasChangeableCCImpl(Function *F) { CallingConv::ID CC = F->getCallingConv(); // FIXME: Is it worth transforming x86_stdcallcc and x86_fastcallcc? if (CC != CallingConv::C && CC != CallingConv::X86_ThisCall) return false; + if (F->isVarArg()) + return false; + // FIXME: Change CC for the whole chain of musttail calls when possible. // // Can't change CC of the function that either has musttail calls, or is a @@ -1727,7 +1730,16 @@ static bool hasChangeableCC(Function *F) { if (BB.getTerminatingMustTailCall()) return false; - return true; + return !F->hasAddressTaken(); +} + +using ChangeableCCCacheTy = SmallDenseMap; +static bool hasChangeableCC(Function *F, + ChangeableCCCacheTy &ChangeableCCCache) { + auto Res = ChangeableCCCache.try_emplace(F, false); + if (Res.second) + Res.first->second = hasChangeableCCImpl(F); + return Res.first->second; } /// Return true if the block containing the call site has a BlockFrequency of @@ -1781,7 +1793,8 @@ static void changeCallSitesToColdCC(Function *F) { // coldcc calling convention. static bool hasOnlyColdCalls(Function &F, - function_ref GetBFI) { + function_ref GetBFI, + ChangeableCCCacheTy &ChangeableCCCache) { for (BasicBlock &BB : F) { for (Instruction &I : BB) { if (CallInst *CI = dyn_cast(&I)) { @@ -1800,8 +1813,7 @@ hasOnlyColdCalls(Function &F, if (!CalledFn->hasLocalLinkage()) return false; // Check if it's valid to use coldcc calling convention. - if (!hasChangeableCC(CalledFn) || CalledFn->isVarArg() || - CalledFn->hasAddressTaken()) + if (!hasChangeableCC(CalledFn, ChangeableCCCache)) return false; BlockFrequencyInfo &CallerBFI = GetBFI(F); if (!isColdCallSite(*CI, CallerBFI)) @@ -1931,9 +1943,10 @@ OptimizeFunctions(Module &M, bool Changed = false; + ChangeableCCCacheTy ChangeableCCCache; std::vector AllCallsCold; for (Function &F : llvm::make_early_inc_range(M)) - if (hasOnlyColdCalls(F, GetBFI)) + if (hasOnlyColdCalls(F, GetBFI, ChangeableCCCache)) AllCallsCold.push_back(&F); // Optimize functions. @@ -1995,7 +2008,7 @@ OptimizeFunctions(Module &M, continue; } - if (hasChangeableCC(&F) && !F.isVarArg() && !F.hasAddressTaken()) { + if (hasChangeableCC(&F, ChangeableCCCache)) { NumInternalFunc++; TargetTransformInfo &TTI = GetTTI(F); // Change the calling convention to coldcc if either stress testing is @@ -2005,6 +2018,7 @@ OptimizeFunctions(Module &M, if (EnableColdCCStressTest || (TTI.useColdCCForColdCall(F) && isValidCandidateForColdCC(F, GetBFI, AllCallsCold))) { + ChangeableCCCache.erase(&F); F.setCallingConv(CallingConv::Cold); changeCallSitesToColdCC(&F); Changed = true; @@ -2012,7 +2026,7 @@ OptimizeFunctions(Module &M, } } - if (hasChangeableCC(&F) && !F.isVarArg() && !F.hasAddressTaken()) { + if (hasChangeableCC(&F, ChangeableCCCache)) { // If this function has a calling convention worth changing, is not a // varargs function, and is only called directly, promote it to use the // Fast calling convention.