1*8bcb0991SDimitry Andric //===-- SpeculateAnalyses.cpp --*- C++ -*-===// 2*8bcb0991SDimitry Andric // 3*8bcb0991SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*8bcb0991SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*8bcb0991SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*8bcb0991SDimitry Andric // 7*8bcb0991SDimitry Andric //===----------------------------------------------------------------------===// 8*8bcb0991SDimitry Andric 9*8bcb0991SDimitry Andric #include "llvm/ExecutionEngine/Orc/SpeculateAnalyses.h" 10*8bcb0991SDimitry Andric #include "llvm/ADT/ArrayRef.h" 11*8bcb0991SDimitry Andric #include "llvm/ADT/DenseMap.h" 12*8bcb0991SDimitry Andric #include "llvm/ADT/STLExtras.h" 13*8bcb0991SDimitry Andric #include "llvm/ADT/SmallPtrSet.h" 14*8bcb0991SDimitry Andric #include "llvm/ADT/SmallVector.h" 15*8bcb0991SDimitry Andric #include "llvm/Analysis/BlockFrequencyInfo.h" 16*8bcb0991SDimitry Andric #include "llvm/Analysis/BranchProbabilityInfo.h" 17*8bcb0991SDimitry Andric #include "llvm/Analysis/CFG.h" 18*8bcb0991SDimitry Andric #include "llvm/IR/PassManager.h" 19*8bcb0991SDimitry Andric #include "llvm/Passes/PassBuilder.h" 20*8bcb0991SDimitry Andric #include "llvm/Support/ErrorHandling.h" 21*8bcb0991SDimitry Andric 22*8bcb0991SDimitry Andric #include <algorithm> 23*8bcb0991SDimitry Andric 24*8bcb0991SDimitry Andric namespace { 25*8bcb0991SDimitry Andric using namespace llvm; 26*8bcb0991SDimitry Andric SmallVector<const BasicBlock *, 8> findBBwithCalls(const Function &F, 27*8bcb0991SDimitry Andric bool IndirectCall = false) { 28*8bcb0991SDimitry Andric SmallVector<const BasicBlock *, 8> BBs; 29*8bcb0991SDimitry Andric 30*8bcb0991SDimitry Andric auto findCallInst = [&IndirectCall](const Instruction &I) { 31*8bcb0991SDimitry Andric if (auto Call = dyn_cast<CallBase>(&I)) 32*8bcb0991SDimitry Andric return Call->isIndirectCall() ? IndirectCall : true; 33*8bcb0991SDimitry Andric else 34*8bcb0991SDimitry Andric return false; 35*8bcb0991SDimitry Andric }; 36*8bcb0991SDimitry Andric for (auto &BB : F) 37*8bcb0991SDimitry Andric if (findCallInst(*BB.getTerminator()) || 38*8bcb0991SDimitry Andric llvm::any_of(BB.instructionsWithoutDebug(), findCallInst)) 39*8bcb0991SDimitry Andric BBs.emplace_back(&BB); 40*8bcb0991SDimitry Andric 41*8bcb0991SDimitry Andric return BBs; 42*8bcb0991SDimitry Andric } 43*8bcb0991SDimitry Andric } // namespace 44*8bcb0991SDimitry Andric 45*8bcb0991SDimitry Andric // Implementations of Queries shouldn't need to lock the resources 46*8bcb0991SDimitry Andric // such as LLVMContext, each argument (function) has a non-shared LLVMContext 47*8bcb0991SDimitry Andric // Plus, if Queries contain states necessary locking scheme should be provided. 48*8bcb0991SDimitry Andric namespace llvm { 49*8bcb0991SDimitry Andric namespace orc { 50*8bcb0991SDimitry Andric 51*8bcb0991SDimitry Andric // Collect direct calls only 52*8bcb0991SDimitry Andric void SpeculateQuery::findCalles(const BasicBlock *BB, 53*8bcb0991SDimitry Andric DenseSet<StringRef> &CallesNames) { 54*8bcb0991SDimitry Andric assert(BB != nullptr && "Traversing Null BB to find calls?"); 55*8bcb0991SDimitry Andric 56*8bcb0991SDimitry Andric auto getCalledFunction = [&CallesNames](const CallBase *Call) { 57*8bcb0991SDimitry Andric auto CalledValue = Call->getCalledOperand()->stripPointerCasts(); 58*8bcb0991SDimitry Andric if (auto DirectCall = dyn_cast<Function>(CalledValue)) 59*8bcb0991SDimitry Andric CallesNames.insert(DirectCall->getName()); 60*8bcb0991SDimitry Andric }; 61*8bcb0991SDimitry Andric for (auto &I : BB->instructionsWithoutDebug()) 62*8bcb0991SDimitry Andric if (auto CI = dyn_cast<CallInst>(&I)) 63*8bcb0991SDimitry Andric getCalledFunction(CI); 64*8bcb0991SDimitry Andric 65*8bcb0991SDimitry Andric if (auto II = dyn_cast<InvokeInst>(BB->getTerminator())) 66*8bcb0991SDimitry Andric getCalledFunction(II); 67*8bcb0991SDimitry Andric } 68*8bcb0991SDimitry Andric 69*8bcb0991SDimitry Andric bool SpeculateQuery::isStraightLine(const Function &F) { 70*8bcb0991SDimitry Andric return llvm::all_of(F.getBasicBlockList(), [](const BasicBlock &BB) { 71*8bcb0991SDimitry Andric return BB.getSingleSuccessor() != nullptr; 72*8bcb0991SDimitry Andric }); 73*8bcb0991SDimitry Andric } 74*8bcb0991SDimitry Andric 75*8bcb0991SDimitry Andric // BlockFreqQuery Implementations 76*8bcb0991SDimitry Andric 77*8bcb0991SDimitry Andric size_t BlockFreqQuery::numBBToGet(size_t numBB) { 78*8bcb0991SDimitry Andric // small CFG 79*8bcb0991SDimitry Andric if (numBB < 4) 80*8bcb0991SDimitry Andric return numBB; 81*8bcb0991SDimitry Andric // mid-size CFG 82*8bcb0991SDimitry Andric else if (numBB < 20) 83*8bcb0991SDimitry Andric return (numBB / 2); 84*8bcb0991SDimitry Andric else 85*8bcb0991SDimitry Andric return (numBB / 2) + (numBB / 4); 86*8bcb0991SDimitry Andric } 87*8bcb0991SDimitry Andric 88*8bcb0991SDimitry Andric BlockFreqQuery::ResultTy BlockFreqQuery::operator()(Function &F) { 89*8bcb0991SDimitry Andric DenseMap<StringRef, DenseSet<StringRef>> CallerAndCalles; 90*8bcb0991SDimitry Andric DenseSet<StringRef> Calles; 91*8bcb0991SDimitry Andric SmallVector<std::pair<const BasicBlock *, uint64_t>, 8> BBFreqs; 92*8bcb0991SDimitry Andric 93*8bcb0991SDimitry Andric PassBuilder PB; 94*8bcb0991SDimitry Andric FunctionAnalysisManager FAM; 95*8bcb0991SDimitry Andric PB.registerFunctionAnalyses(FAM); 96*8bcb0991SDimitry Andric 97*8bcb0991SDimitry Andric auto IBBs = findBBwithCalls(F); 98*8bcb0991SDimitry Andric 99*8bcb0991SDimitry Andric if (IBBs.empty()) 100*8bcb0991SDimitry Andric return None; 101*8bcb0991SDimitry Andric 102*8bcb0991SDimitry Andric auto &BFI = FAM.getResult<BlockFrequencyAnalysis>(F); 103*8bcb0991SDimitry Andric 104*8bcb0991SDimitry Andric for (const auto I : IBBs) 105*8bcb0991SDimitry Andric BBFreqs.push_back({I, BFI.getBlockFreq(I).getFrequency()}); 106*8bcb0991SDimitry Andric 107*8bcb0991SDimitry Andric assert(IBBs.size() == BBFreqs.size() && "BB Count Mismatch"); 108*8bcb0991SDimitry Andric 109*8bcb0991SDimitry Andric llvm::sort(BBFreqs.begin(), BBFreqs.end(), 110*8bcb0991SDimitry Andric [](decltype(BBFreqs)::const_reference BBF, 111*8bcb0991SDimitry Andric decltype(BBFreqs)::const_reference BBS) { 112*8bcb0991SDimitry Andric return BBF.second > BBS.second ? true : false; 113*8bcb0991SDimitry Andric }); 114*8bcb0991SDimitry Andric 115*8bcb0991SDimitry Andric // ignoring number of direct calls in a BB 116*8bcb0991SDimitry Andric auto Topk = numBBToGet(BBFreqs.size()); 117*8bcb0991SDimitry Andric 118*8bcb0991SDimitry Andric for (size_t i = 0; i < Topk; i++) 119*8bcb0991SDimitry Andric findCalles(BBFreqs[i].first, Calles); 120*8bcb0991SDimitry Andric 121*8bcb0991SDimitry Andric assert(!Calles.empty() && "Running Analysis on Function with no calls?"); 122*8bcb0991SDimitry Andric 123*8bcb0991SDimitry Andric CallerAndCalles.insert({F.getName(), std::move(Calles)}); 124*8bcb0991SDimitry Andric 125*8bcb0991SDimitry Andric return CallerAndCalles; 126*8bcb0991SDimitry Andric } 127*8bcb0991SDimitry Andric 128*8bcb0991SDimitry Andric // SequenceBBQuery Implementation 129*8bcb0991SDimitry Andric std::size_t SequenceBBQuery::getHottestBlocks(std::size_t TotalBlocks) { 130*8bcb0991SDimitry Andric if (TotalBlocks == 1) 131*8bcb0991SDimitry Andric return TotalBlocks; 132*8bcb0991SDimitry Andric return TotalBlocks / 2; 133*8bcb0991SDimitry Andric } 134*8bcb0991SDimitry Andric 135*8bcb0991SDimitry Andric // FIXME : find good implementation. 136*8bcb0991SDimitry Andric SequenceBBQuery::BlockListTy 137*8bcb0991SDimitry Andric SequenceBBQuery::rearrangeBB(const Function &F, const BlockListTy &BBList) { 138*8bcb0991SDimitry Andric BlockListTy RearrangedBBSet; 139*8bcb0991SDimitry Andric 140*8bcb0991SDimitry Andric for (auto &Block : F.getBasicBlockList()) 141*8bcb0991SDimitry Andric if (llvm::is_contained(BBList, &Block)) 142*8bcb0991SDimitry Andric RearrangedBBSet.push_back(&Block); 143*8bcb0991SDimitry Andric 144*8bcb0991SDimitry Andric assert(RearrangedBBSet.size() == BBList.size() && 145*8bcb0991SDimitry Andric "BasicBlock missing while rearranging?"); 146*8bcb0991SDimitry Andric return RearrangedBBSet; 147*8bcb0991SDimitry Andric } 148*8bcb0991SDimitry Andric 149*8bcb0991SDimitry Andric void SequenceBBQuery::traverseToEntryBlock(const BasicBlock *AtBB, 150*8bcb0991SDimitry Andric const BlockListTy &CallerBlocks, 151*8bcb0991SDimitry Andric const BackEdgesInfoTy &BackEdgesInfo, 152*8bcb0991SDimitry Andric const BranchProbabilityInfo *BPI, 153*8bcb0991SDimitry Andric VisitedBlocksInfoTy &VisitedBlocks) { 154*8bcb0991SDimitry Andric auto Itr = VisitedBlocks.find(AtBB); 155*8bcb0991SDimitry Andric if (Itr != VisitedBlocks.end()) { // already visited. 156*8bcb0991SDimitry Andric if (!Itr->second.Upward) 157*8bcb0991SDimitry Andric return; 158*8bcb0991SDimitry Andric Itr->second.Upward = false; 159*8bcb0991SDimitry Andric } else { 160*8bcb0991SDimitry Andric // Create hint for newly discoverd blocks. 161*8bcb0991SDimitry Andric WalkDirection BlockHint; 162*8bcb0991SDimitry Andric BlockHint.Upward = false; 163*8bcb0991SDimitry Andric // FIXME: Expensive Check 164*8bcb0991SDimitry Andric if (llvm::is_contained(CallerBlocks, AtBB)) 165*8bcb0991SDimitry Andric BlockHint.CallerBlock = true; 166*8bcb0991SDimitry Andric VisitedBlocks.insert(std::make_pair(AtBB, BlockHint)); 167*8bcb0991SDimitry Andric } 168*8bcb0991SDimitry Andric 169*8bcb0991SDimitry Andric const_pred_iterator PIt = pred_begin(AtBB), EIt = pred_end(AtBB); 170*8bcb0991SDimitry Andric // Move this check to top, when we have code setup to launch speculative 171*8bcb0991SDimitry Andric // compiles for function in entry BB, this triggers the speculative compiles 172*8bcb0991SDimitry Andric // before running the program. 173*8bcb0991SDimitry Andric if (PIt == EIt) // No Preds. 174*8bcb0991SDimitry Andric return; 175*8bcb0991SDimitry Andric 176*8bcb0991SDimitry Andric DenseSet<const BasicBlock *> PredSkipNodes; 177*8bcb0991SDimitry Andric 178*8bcb0991SDimitry Andric // Since we are checking for predecessor's backedges, this Block 179*8bcb0991SDimitry Andric // occurs in second position. 180*8bcb0991SDimitry Andric for (auto &I : BackEdgesInfo) 181*8bcb0991SDimitry Andric if (I.second == AtBB) 182*8bcb0991SDimitry Andric PredSkipNodes.insert(I.first); 183*8bcb0991SDimitry Andric 184*8bcb0991SDimitry Andric // Skip predecessors which source of back-edges. 185*8bcb0991SDimitry Andric for (; PIt != EIt; ++PIt) 186*8bcb0991SDimitry Andric // checking EdgeHotness is cheaper 187*8bcb0991SDimitry Andric if (BPI->isEdgeHot(*PIt, AtBB) && !PredSkipNodes.count(*PIt)) 188*8bcb0991SDimitry Andric traverseToEntryBlock(*PIt, CallerBlocks, BackEdgesInfo, BPI, 189*8bcb0991SDimitry Andric VisitedBlocks); 190*8bcb0991SDimitry Andric } 191*8bcb0991SDimitry Andric 192*8bcb0991SDimitry Andric void SequenceBBQuery::traverseToExitBlock(const BasicBlock *AtBB, 193*8bcb0991SDimitry Andric const BlockListTy &CallerBlocks, 194*8bcb0991SDimitry Andric const BackEdgesInfoTy &BackEdgesInfo, 195*8bcb0991SDimitry Andric const BranchProbabilityInfo *BPI, 196*8bcb0991SDimitry Andric VisitedBlocksInfoTy &VisitedBlocks) { 197*8bcb0991SDimitry Andric auto Itr = VisitedBlocks.find(AtBB); 198*8bcb0991SDimitry Andric if (Itr != VisitedBlocks.end()) { // already visited. 199*8bcb0991SDimitry Andric if (!Itr->second.Downward) 200*8bcb0991SDimitry Andric return; 201*8bcb0991SDimitry Andric Itr->second.Downward = false; 202*8bcb0991SDimitry Andric } else { 203*8bcb0991SDimitry Andric // Create hint for newly discoverd blocks. 204*8bcb0991SDimitry Andric WalkDirection BlockHint; 205*8bcb0991SDimitry Andric BlockHint.Downward = false; 206*8bcb0991SDimitry Andric // FIXME: Expensive Check 207*8bcb0991SDimitry Andric if (llvm::is_contained(CallerBlocks, AtBB)) 208*8bcb0991SDimitry Andric BlockHint.CallerBlock = true; 209*8bcb0991SDimitry Andric VisitedBlocks.insert(std::make_pair(AtBB, BlockHint)); 210*8bcb0991SDimitry Andric } 211*8bcb0991SDimitry Andric 212*8bcb0991SDimitry Andric succ_const_iterator PIt = succ_begin(AtBB), EIt = succ_end(AtBB); 213*8bcb0991SDimitry Andric if (PIt == EIt) // No succs. 214*8bcb0991SDimitry Andric return; 215*8bcb0991SDimitry Andric 216*8bcb0991SDimitry Andric // If there are hot edges, then compute SuccSkipNodes. 217*8bcb0991SDimitry Andric DenseSet<const BasicBlock *> SuccSkipNodes; 218*8bcb0991SDimitry Andric 219*8bcb0991SDimitry Andric // Since we are checking for successor's backedges, this Block 220*8bcb0991SDimitry Andric // occurs in first position. 221*8bcb0991SDimitry Andric for (auto &I : BackEdgesInfo) 222*8bcb0991SDimitry Andric if (I.first == AtBB) 223*8bcb0991SDimitry Andric SuccSkipNodes.insert(I.second); 224*8bcb0991SDimitry Andric 225*8bcb0991SDimitry Andric for (; PIt != EIt; ++PIt) 226*8bcb0991SDimitry Andric if (BPI->isEdgeHot(AtBB, *PIt) && !SuccSkipNodes.count(*PIt)) 227*8bcb0991SDimitry Andric traverseToExitBlock(*PIt, CallerBlocks, BackEdgesInfo, BPI, 228*8bcb0991SDimitry Andric VisitedBlocks); 229*8bcb0991SDimitry Andric } 230*8bcb0991SDimitry Andric 231*8bcb0991SDimitry Andric // Get Block frequencies for blocks and take most frquently executed block, 232*8bcb0991SDimitry Andric // walk towards the entry block from those blocks and discover the basic blocks 233*8bcb0991SDimitry Andric // with call. 234*8bcb0991SDimitry Andric SequenceBBQuery::BlockListTy 235*8bcb0991SDimitry Andric SequenceBBQuery::queryCFG(Function &F, const BlockListTy &CallerBlocks) { 236*8bcb0991SDimitry Andric 237*8bcb0991SDimitry Andric BlockFreqInfoTy BBFreqs; 238*8bcb0991SDimitry Andric VisitedBlocksInfoTy VisitedBlocks; 239*8bcb0991SDimitry Andric BackEdgesInfoTy BackEdgesInfo; 240*8bcb0991SDimitry Andric 241*8bcb0991SDimitry Andric PassBuilder PB; 242*8bcb0991SDimitry Andric FunctionAnalysisManager FAM; 243*8bcb0991SDimitry Andric PB.registerFunctionAnalyses(FAM); 244*8bcb0991SDimitry Andric 245*8bcb0991SDimitry Andric auto &BFI = FAM.getResult<BlockFrequencyAnalysis>(F); 246*8bcb0991SDimitry Andric 247*8bcb0991SDimitry Andric llvm::FindFunctionBackedges(F, BackEdgesInfo); 248*8bcb0991SDimitry Andric 249*8bcb0991SDimitry Andric for (const auto I : CallerBlocks) 250*8bcb0991SDimitry Andric BBFreqs.push_back({I, BFI.getBlockFreq(I).getFrequency()}); 251*8bcb0991SDimitry Andric 252*8bcb0991SDimitry Andric llvm::sort(BBFreqs, [](decltype(BBFreqs)::const_reference Bbf, 253*8bcb0991SDimitry Andric decltype(BBFreqs)::const_reference Bbs) { 254*8bcb0991SDimitry Andric return Bbf.second > Bbs.second; 255*8bcb0991SDimitry Andric }); 256*8bcb0991SDimitry Andric 257*8bcb0991SDimitry Andric ArrayRef<std::pair<const BasicBlock *, uint64_t>> HotBlocksRef(BBFreqs); 258*8bcb0991SDimitry Andric HotBlocksRef = 259*8bcb0991SDimitry Andric HotBlocksRef.drop_back(BBFreqs.size() - getHottestBlocks(BBFreqs.size())); 260*8bcb0991SDimitry Andric 261*8bcb0991SDimitry Andric BranchProbabilityInfo *BPI = 262*8bcb0991SDimitry Andric FAM.getCachedResult<BranchProbabilityAnalysis>(F); 263*8bcb0991SDimitry Andric 264*8bcb0991SDimitry Andric // visit NHotBlocks, 265*8bcb0991SDimitry Andric // traverse upwards to entry 266*8bcb0991SDimitry Andric // traverse downwards to end. 267*8bcb0991SDimitry Andric 268*8bcb0991SDimitry Andric for (auto I : HotBlocksRef) { 269*8bcb0991SDimitry Andric traverseToEntryBlock(I.first, CallerBlocks, BackEdgesInfo, BPI, 270*8bcb0991SDimitry Andric VisitedBlocks); 271*8bcb0991SDimitry Andric traverseToExitBlock(I.first, CallerBlocks, BackEdgesInfo, BPI, 272*8bcb0991SDimitry Andric VisitedBlocks); 273*8bcb0991SDimitry Andric } 274*8bcb0991SDimitry Andric 275*8bcb0991SDimitry Andric BlockListTy MinCallerBlocks; 276*8bcb0991SDimitry Andric for (auto &I : VisitedBlocks) 277*8bcb0991SDimitry Andric if (I.second.CallerBlock) 278*8bcb0991SDimitry Andric MinCallerBlocks.push_back(std::move(I.first)); 279*8bcb0991SDimitry Andric 280*8bcb0991SDimitry Andric return rearrangeBB(F, MinCallerBlocks); 281*8bcb0991SDimitry Andric } 282*8bcb0991SDimitry Andric 283*8bcb0991SDimitry Andric SpeculateQuery::ResultTy SequenceBBQuery::operator()(Function &F) { 284*8bcb0991SDimitry Andric // reduce the number of lists! 285*8bcb0991SDimitry Andric DenseMap<StringRef, DenseSet<StringRef>> CallerAndCalles; 286*8bcb0991SDimitry Andric DenseSet<StringRef> Calles; 287*8bcb0991SDimitry Andric BlockListTy SequencedBlocks; 288*8bcb0991SDimitry Andric BlockListTy CallerBlocks; 289*8bcb0991SDimitry Andric 290*8bcb0991SDimitry Andric CallerBlocks = findBBwithCalls(F); 291*8bcb0991SDimitry Andric if (CallerBlocks.empty()) 292*8bcb0991SDimitry Andric return None; 293*8bcb0991SDimitry Andric 294*8bcb0991SDimitry Andric if (isStraightLine(F)) 295*8bcb0991SDimitry Andric SequencedBlocks = rearrangeBB(F, CallerBlocks); 296*8bcb0991SDimitry Andric else 297*8bcb0991SDimitry Andric SequencedBlocks = queryCFG(F, CallerBlocks); 298*8bcb0991SDimitry Andric 299*8bcb0991SDimitry Andric for (auto BB : SequencedBlocks) 300*8bcb0991SDimitry Andric findCalles(BB, Calles); 301*8bcb0991SDimitry Andric 302*8bcb0991SDimitry Andric CallerAndCalles.insert({F.getName(), std::move(Calles)}); 303*8bcb0991SDimitry Andric return CallerAndCalles; 304*8bcb0991SDimitry Andric } 305*8bcb0991SDimitry Andric 306*8bcb0991SDimitry Andric } // namespace orc 307*8bcb0991SDimitry Andric } // namespace llvm 308