| //===- FunctionPropertiesAnalysis.cpp - Function Properties Analysis ------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file defines the FunctionPropertiesInfo and FunctionPropertiesAnalysis | |
| // classes used to extract function properties. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Analysis/FunctionPropertiesAnalysis.h" | |
| #include "llvm/ADT/STLExtras.h" | |
| #include "llvm/ADT/SetVector.h" | |
| #include "llvm/ADT/Statistic.h" | |
| #include "llvm/Analysis/LoopInfo.h" | |
| #include "llvm/IR/CFG.h" | |
| #include "llvm/IR/Constants.h" | |
| #include "llvm/IR/Dominators.h" | |
| #include "llvm/IR/Instructions.h" | |
| #include "llvm/IR/IntrinsicInst.h" | |
| #include "llvm/Support/CommandLine.h" | |
| #include "llvm/Support/Compiler.h" | |
| #include <deque> | |
| using namespace llvm; | |
| #define DEBUG_TYPE "func-properties-stats" | |
| #define FUNCTION_PROPERTY(Name, Description) \ | |
| STATISTIC(Num##Name, Description); \ | |
| STATISTIC(Num##Name##PreOptimization, Description " (before " \ | |
| "optimizations)"); | |
| #define DETAILED_FUNCTION_PROPERTY(Name, Description) \ | |
| STATISTIC(Num##Name, Description); \ | |
| STATISTIC(Num##Name##PreOptimization, Description " (before " \ | |
| "optimizations)"); | |
| #include "llvm/IR/FunctionProperties.def" | |
| namespace llvm { | |
| LLVM_ABI cl::opt<bool> EnableDetailedFunctionProperties( | |
| "enable-detailed-function-properties", cl::Hidden, cl::init(false), | |
| cl::desc("Whether or not to compute detailed function properties.")); | |
| static cl::opt<unsigned> BigBasicBlockInstructionThreshold( | |
| "big-basic-block-instruction-threshold", cl::Hidden, cl::init(500), | |
| cl::desc("The minimum number of instructions a basic block should contain " | |
| "before being considered big.")); | |
| static cl::opt<unsigned> MediumBasicBlockInstructionThreshold( | |
| "medium-basic-block-instruction-threshold", cl::Hidden, cl::init(15), | |
| cl::desc("The minimum number of instructions a basic block should contain " | |
| "before being considered medium-sized.")); | |
| } // namespace llvm | |
| static cl::opt<unsigned> CallWithManyArgumentsThreshold( | |
| "call-with-many-arguments-threshold", cl::Hidden, cl::init(4), | |
| cl::desc("The minimum number of arguments a function call must have before " | |
| "it is considered having many arguments.")); | |
| namespace { | |
| int64_t getNumBlocksFromCond(const BasicBlock &BB) { | |
| int64_t Ret = 0; | |
| if (const auto *BI = dyn_cast<CondBrInst>(BB.getTerminator())) { | |
| Ret += BI->getNumSuccessors(); | |
| } else if (const auto *SI = dyn_cast<SwitchInst>(BB.getTerminator())) { | |
| Ret += (SI->getNumCases() + (nullptr != SI->getDefaultDest())); | |
| } | |
| return Ret; | |
| } | |
| int64_t getUses(const Function &F) { | |
| return ((!F.hasLocalLinkage()) ? 1 : 0) + F.getNumUses(); | |
| } | |
| } // namespace | |
| void FunctionPropertiesInfo::reIncludeBB(const BasicBlock &BB) { | |
| updateForBB(BB, +1); | |
| } | |
| void FunctionPropertiesInfo::updateForBB(const BasicBlock &BB, | |
| int64_t Direction) { | |
| assert(Direction == 1 || Direction == -1); | |
| BasicBlockCount += Direction; | |
| BlocksReachedFromConditionalInstruction += | |
| (Direction * getNumBlocksFromCond(BB)); | |
| for (const auto &I : BB) { | |
| if (auto *CS = dyn_cast<CallBase>(&I)) { | |
| const auto *Callee = CS->getCalledFunction(); | |
| if (Callee && !Callee->isIntrinsic() && !Callee->isDeclaration()) | |
| DirectCallsToDefinedFunctions += Direction; | |
| } | |
| if (I.getOpcode() == Instruction::Load) { | |
| LoadInstCount += Direction; | |
| } else if (I.getOpcode() == Instruction::Store) { | |
| StoreInstCount += Direction; | |
| } | |
| } | |
| TotalInstructionCount += Direction * BB.size(); | |
| if (EnableDetailedFunctionProperties) { | |
| unsigned SuccessorCount = succ_size(&BB); | |
| if (SuccessorCount == 1) | |
| BasicBlocksWithSingleSuccessor += Direction; | |
| else if (SuccessorCount == 2) | |
| BasicBlocksWithTwoSuccessors += Direction; | |
| else if (SuccessorCount > 2) | |
| BasicBlocksWithMoreThanTwoSuccessors += Direction; | |
| if (BB.hasNPredecessors(1)) | |
| BasicBlocksWithSinglePredecessor += Direction; | |
| else if (BB.hasNPredecessors(2)) | |
| BasicBlocksWithTwoPredecessors += Direction; | |
| else if (BB.hasNPredecessorsOrMore(3)) | |
| BasicBlocksWithMoreThanTwoPredecessors += Direction; | |
| if (TotalInstructionCount > BigBasicBlockInstructionThreshold) | |
| BigBasicBlocks += Direction; | |
| else if (TotalInstructionCount > MediumBasicBlockInstructionThreshold) | |
| MediumBasicBlocks += Direction; | |
| else | |
| SmallBasicBlocks += Direction; | |
| // Calculate critical edges by looking through all successors of a basic | |
| // block that has multiple successors and finding ones that have multiple | |
| // predecessors, which represent critical edges. | |
| if (SuccessorCount > 1) { | |
| for (const auto *Successor : successors(&BB)) { | |
| if (Successor->hasNPredecessorsOrMore(2)) | |
| CriticalEdgeCount += Direction; | |
| } | |
| } | |
| ControlFlowEdgeCount += Direction * SuccessorCount; | |
| const Instruction *TI = BB.getTerminator(); | |
| if (isa<UncondBrInst>(TI)) { | |
| BranchInstructionCount += Direction; | |
| BranchSuccessorCount += Direction; | |
| UnconditionalBranchCount += Direction; | |
| } else if (isa<CondBrInst>(TI)) { | |
| BranchInstructionCount += Direction; | |
| BranchSuccessorCount += Direction * 2; | |
| ConditionalBranchCount += Direction; | |
| } else if (const auto *SI = dyn_cast<SwitchInst>(TI)) { | |
| SwitchInstructionCount += Direction; | |
| SwitchSuccessorCount += Direction * SI->getNumSuccessors(); | |
| } | |
| for (const Instruction &I : BB) { | |
| if (I.isCast()) | |
| CastInstructionCount += Direction; | |
| if (I.getType()->isFloatTy()) | |
| FloatingPointInstructionCount += Direction; | |
| else if (I.getType()->isIntegerTy()) | |
| IntegerInstructionCount += Direction; | |
| if (isa<IntrinsicInst>(I)) | |
| ++IntrinsicCount; | |
| if (const auto *Call = dyn_cast<CallInst>(&I)) { | |
| if (Call->doesNotReturn()) | |
| NoReturnCallCount += Direction; | |
| if (Call->isIndirectCall()) | |
| IndirectCallCount += Direction; | |
| else | |
| DirectCallCount += Direction; | |
| if (Call->getType()->isIntegerTy()) | |
| CallReturnsIntegerCount += Direction; | |
| else if (Call->getType()->isFloatingPointTy()) | |
| CallReturnsFloatCount += Direction; | |
| else if (Call->getType()->isPointerTy()) | |
| CallReturnsPointerCount += Direction; | |
| else if (Call->getType()->isVectorTy()) { | |
| if (Call->getType()->getScalarType()->isIntegerTy()) | |
| CallReturnsVectorIntCount += Direction; | |
| else if (Call->getType()->getScalarType()->isFloatingPointTy()) | |
| CallReturnsVectorFloatCount += Direction; | |
| else if (Call->getType()->getScalarType()->isPointerTy()) | |
| CallReturnsVectorPointerCount += Direction; | |
| } | |
| if (Call->arg_size() > CallWithManyArgumentsThreshold) | |
| CallWithManyArgumentsCount += Direction; | |
| for (const auto &Arg : Call->args()) { | |
| if (Arg->getType()->isPointerTy()) { | |
| CallWithPointerArgumentCount += Direction; | |
| break; | |
| } | |
| } | |
| } | |
| #define COUNT_OPERAND(OPTYPE) \ | |
| if (isa<OPTYPE>(Operand)) { \ | |
| OPTYPE##OperandCount += Direction; \ | |
| continue; \ | |
| } | |
| for (unsigned int OperandIndex = 0; OperandIndex < I.getNumOperands(); | |
| ++OperandIndex) { | |
| Value *Operand = I.getOperand(OperandIndex); | |
| COUNT_OPERAND(GlobalValue) | |
| COUNT_OPERAND(ConstantInt) | |
| COUNT_OPERAND(ConstantFP) | |
| COUNT_OPERAND(Constant) | |
| COUNT_OPERAND(Instruction) | |
| COUNT_OPERAND(BasicBlock) | |
| COUNT_OPERAND(InlineAsm) | |
| COUNT_OPERAND(Argument) | |
| // We only get to this point if we haven't matched any of the other | |
| // operand types. | |
| UnknownOperandCount += Direction; | |
| } | |
| #undef CHECK_OPERAND | |
| } | |
| } | |
| if (IR2VecVocab) { | |
| // We instantiate the IR2Vec embedder each time, as having an unique | |
| // pointer to the embedder as member of the class would make it | |
| // non-copyable. Instantiating the embedder in itself is not costly. | |
| auto Embedder = ir2vec::Embedder::create(IR2VecKind::Symbolic, | |
| *BB.getParent(), *IR2VecVocab); | |
| if (!Embedder) { | |
| BB.getContext().emitError("Error creating IR2Vec embeddings"); | |
| return; | |
| } | |
| const auto &BBEmbedding = Embedder->getBBVector(BB); | |
| // Subtract BBEmbedding from Function embedding if the direction is -1, | |
| // and add it if the direction is +1. | |
| if (Direction == -1) | |
| FunctionEmbedding -= BBEmbedding; | |
| else | |
| FunctionEmbedding += BBEmbedding; | |
| } | |
| } | |
| void FunctionPropertiesInfo::updateAggregateStats(const Function &F, | |
| const LoopInfo &LI) { | |
| Uses = getUses(F); | |
| TopLevelLoopCount = llvm::size(LI); | |
| MaxLoopDepth = 0; | |
| std::deque<const Loop *> Worklist; | |
| llvm::append_range(Worklist, LI); | |
| while (!Worklist.empty()) { | |
| const auto *L = Worklist.front(); | |
| MaxLoopDepth = | |
| std::max(MaxLoopDepth, static_cast<int64_t>(L->getLoopDepth())); | |
| Worklist.pop_front(); | |
| llvm::append_range(Worklist, L->getSubLoops()); | |
| } | |
| } | |
| FunctionPropertiesInfo FunctionPropertiesInfo::getFunctionPropertiesInfo( | |
| Function &F, FunctionAnalysisManager &FAM) { | |
| // We use the cached result of the IR2VecVocabAnalysis run by | |
| // InlineAdvisorAnalysis. If the IR2VecVocabAnalysis is not run, we don't | |
| // use IR2Vec embeddings. | |
| auto Vocabulary = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F) | |
| .getCachedResult<IR2VecVocabAnalysis>(*F.getParent()); | |
| return getFunctionPropertiesInfo(F, FAM.getResult<DominatorTreeAnalysis>(F), | |
| FAM.getResult<LoopAnalysis>(F), Vocabulary); | |
| } | |
| FunctionPropertiesInfo FunctionPropertiesInfo::getFunctionPropertiesInfo( | |
| const Function &F, const DominatorTree &DT, const LoopInfo &LI, | |
| const ir2vec::Vocabulary *Vocabulary) { | |
| FunctionPropertiesInfo FPI; | |
| if (Vocabulary && Vocabulary->isValid()) { | |
| FPI.IR2VecVocab = Vocabulary; | |
| FPI.FunctionEmbedding = ir2vec::Embedding(Vocabulary->getDimension(), 0.0); | |
| } | |
| for (const auto &BB : F) | |
| if (DT.isReachableFromEntry(&BB)) | |
| FPI.reIncludeBB(BB); | |
| FPI.updateAggregateStats(F, LI); | |
| return FPI; | |
| } | |
| bool FunctionPropertiesInfo::operator==( | |
| const FunctionPropertiesInfo &FPI) const { | |
| if (BasicBlockCount != FPI.BasicBlockCount || | |
| BlocksReachedFromConditionalInstruction != | |
| FPI.BlocksReachedFromConditionalInstruction || | |
| Uses != FPI.Uses || | |
| DirectCallsToDefinedFunctions != FPI.DirectCallsToDefinedFunctions || | |
| LoadInstCount != FPI.LoadInstCount || | |
| StoreInstCount != FPI.StoreInstCount || | |
| MaxLoopDepth != FPI.MaxLoopDepth || | |
| TopLevelLoopCount != FPI.TopLevelLoopCount || | |
| TotalInstructionCount != FPI.TotalInstructionCount || | |
| BasicBlocksWithSingleSuccessor != FPI.BasicBlocksWithSingleSuccessor || | |
| BasicBlocksWithTwoSuccessors != FPI.BasicBlocksWithTwoSuccessors || | |
| BasicBlocksWithMoreThanTwoSuccessors != | |
| FPI.BasicBlocksWithMoreThanTwoSuccessors || | |
| BasicBlocksWithSinglePredecessor != | |
| FPI.BasicBlocksWithSinglePredecessor || | |
| BasicBlocksWithTwoPredecessors != FPI.BasicBlocksWithTwoPredecessors || | |
| BasicBlocksWithMoreThanTwoPredecessors != | |
| FPI.BasicBlocksWithMoreThanTwoPredecessors || | |
| BigBasicBlocks != FPI.BigBasicBlocks || | |
| MediumBasicBlocks != FPI.MediumBasicBlocks || | |
| SmallBasicBlocks != FPI.SmallBasicBlocks || | |
| CastInstructionCount != FPI.CastInstructionCount || | |
| FloatingPointInstructionCount != FPI.FloatingPointInstructionCount || | |
| IntegerInstructionCount != FPI.IntegerInstructionCount || | |
| ConstantIntOperandCount != FPI.ConstantIntOperandCount || | |
| ConstantFPOperandCount != FPI.ConstantFPOperandCount || | |
| ConstantOperandCount != FPI.ConstantOperandCount || | |
| InstructionOperandCount != FPI.InstructionOperandCount || | |
| BasicBlockOperandCount != FPI.BasicBlockOperandCount || | |
| GlobalValueOperandCount != FPI.GlobalValueOperandCount || | |
| InlineAsmOperandCount != FPI.InlineAsmOperandCount || | |
| ArgumentOperandCount != FPI.ArgumentOperandCount || | |
| UnknownOperandCount != FPI.UnknownOperandCount || | |
| CriticalEdgeCount != FPI.CriticalEdgeCount || | |
| ControlFlowEdgeCount != FPI.ControlFlowEdgeCount || | |
| UnconditionalBranchCount != FPI.UnconditionalBranchCount || | |
| IntrinsicCount != FPI.IntrinsicCount || | |
| NoReturnCallCount != FPI.NoReturnCallCount || | |
| DirectCallCount != FPI.DirectCallCount || | |
| IndirectCallCount != FPI.IndirectCallCount || | |
| CallReturnsIntegerCount != FPI.CallReturnsIntegerCount || | |
| CallReturnsFloatCount != FPI.CallReturnsFloatCount || | |
| CallReturnsPointerCount != FPI.CallReturnsPointerCount || | |
| CallReturnsVectorIntCount != FPI.CallReturnsVectorIntCount || | |
| CallReturnsVectorFloatCount != FPI.CallReturnsVectorFloatCount || | |
| CallReturnsVectorPointerCount != FPI.CallReturnsVectorPointerCount || | |
| CallWithManyArgumentsCount != FPI.CallWithManyArgumentsCount || | |
| CallWithPointerArgumentCount != FPI.CallWithPointerArgumentCount) { | |
| return false; | |
| } | |
| // Check the equality of the function embeddings. We don't check the equality | |
| // of Vocabulary as it remains the same. | |
| if (!FunctionEmbedding.approximatelyEquals(FPI.FunctionEmbedding)) | |
| return false; | |
| return true; | |
| } | |
| void FunctionPropertiesInfo::print(raw_ostream &OS) const { | |
| #define FUNCTION_PROPERTY(Name, Description) OS << #Name ": " << Name << "\n"; | |
| #define DETAILED_FUNCTION_PROPERTY(Name, Description) \ | |
| if (EnableDetailedFunctionProperties) { \ | |
| OS << #Name ": " << Name << "\n"; \ | |
| } | |
| #include "llvm/IR/FunctionProperties.def" | |
| #undef FUNCTION_PROPERTY | |
| #undef DETAILED_FUNCTION_PROPERTY | |
| OS << "\n"; | |
| } | |
| AnalysisKey FunctionPropertiesAnalysis::Key; | |
| FunctionPropertiesInfo | |
| FunctionPropertiesAnalysis::run(Function &F, FunctionAnalysisManager &FAM) { | |
| return FunctionPropertiesInfo::getFunctionPropertiesInfo(F, FAM); | |
| } | |
| PreservedAnalyses | |
| FunctionPropertiesPrinterPass::run(Function &F, FunctionAnalysisManager &AM) { | |
| OS << "Printing analysis results of CFA for function " | |
| << "'" << F.getName() << "':" | |
| << "\n"; | |
| AM.getResult<FunctionPropertiesAnalysis>(F).print(OS); | |
| return PreservedAnalyses::all(); | |
| } | |
| PreservedAnalyses | |
| FunctionPropertiesStatisticsPass::run(Function &F, | |
| FunctionAnalysisManager &FAM) { | |
| LLVM_DEBUG(dbgs() << "STATSCOUNT: running on function " << F.getName() | |
| << "\n"); | |
| auto &AnalysisResults = FAM.getResult<FunctionPropertiesAnalysis>(F); | |
| if (IsPreOptimization) { | |
| #define FUNCTION_PROPERTY(Name, Description) \ | |
| Num##Name##PreOptimization += AnalysisResults.Name; | |
| #define DETAILED_FUNCTION_PROPERTY(Name, Description) \ | |
| Num##Name##PreOptimization += AnalysisResults.Name; | |
| #include "llvm/IR/FunctionProperties.def" | |
| #undef FUNCTION_PROPERTY | |
| #undef DETAILED_FUNCTION_PROPERTY | |
| } else { | |
| #define FUNCTION_PROPERTY(Name, Description) Num##Name += AnalysisResults.Name; | |
| #define DETAILED_FUNCTION_PROPERTY(Name, Description) \ | |
| Num##Name += AnalysisResults.Name; | |
| #include "llvm/IR/FunctionProperties.def" | |
| #undef FUNCTION_PROPERTY | |
| #undef DETAILED_FUNCTION_PROPERTY | |
| } | |
| return PreservedAnalyses::all(); | |
| } | |
| FunctionPropertiesUpdater::FunctionPropertiesUpdater( | |
| FunctionPropertiesInfo &FPI, CallBase &CB) | |
| : FPI(FPI), CallSiteBB(*CB.getParent()), Caller(*CallSiteBB.getParent()) { | |
| assert(isa<CallInst>(CB) || isa<InvokeInst>(CB)); | |
| // For BBs that are likely to change, we subtract from feature totals their | |
| // contribution. Some features, like max loop counts or depths, are left | |
| // invalid, as they will be updated post-inlining. | |
| SmallPtrSet<const BasicBlock *, 4> LikelyToChangeBBs; | |
| // The CB BB will change - it'll either be split or the callee's body (single | |
| // BB) will be pasted in. | |
| LikelyToChangeBBs.insert(&CallSiteBB); | |
| // The caller's entry BB may change due to new alloca instructions. | |
| LikelyToChangeBBs.insert(&*Caller.begin()); | |
| // The users of the value returned by call instruction can change | |
| // leading to the change in embeddings being computed, when used. | |
| // We conservatively add the BBs with such uses to LikelyToChangeBBs. | |
| for (const auto *User : CB.users()) | |
| CallUsers.insert(dyn_cast<Instruction>(User)->getParent()); | |
| // CallSiteBB can be removed from CallUsers if present, it's taken care | |
| // separately. | |
| CallUsers.erase(&CallSiteBB); | |
| LikelyToChangeBBs.insert_range(CallUsers); | |
| // The successors may become unreachable in the case of `invoke` inlining. | |
| // We track successors separately, too, because they form a boundary, together | |
| // with the CB BB ('Entry') between which the inlined callee will be pasted. | |
| Successors.insert_range(successors(&CallSiteBB)); | |
| // the outcome of the inlining may be that some edges get lost (DCEd BBs | |
| // because inlining brought some constant, for example). We don't know which | |
| // edges will be removed, so we list all of them as potentially removable. | |
| // Some BBs have (at this point) duplicate edges. Remove duplicates, otherwise | |
| // the DT updater will not apply changes correctly. | |
| DenseSet<const BasicBlock *> Inserted; | |
| for (auto *Succ : successors(&CallSiteBB)) | |
| if (Inserted.insert(Succ).second) | |
| DomTreeUpdates.emplace_back(DominatorTree::UpdateKind::Delete, | |
| const_cast<BasicBlock *>(&CallSiteBB), | |
| const_cast<BasicBlock *>(Succ)); | |
| // Reuse Inserted (which has some allocated capacity at this point) below, if | |
| // we have an invoke. | |
| Inserted.clear(); | |
| // Inlining only handles invoke and calls. If this is an invoke, and inlining | |
| // it pulls another invoke, the original landing pad may get split, so as to | |
| // share its content with other potential users. So the edge up to which we | |
| // need to invalidate and then re-account BB data is the successors of the | |
| // current landing pad. We can leave the current lp, too - if it doesn't get | |
| // split, then it will be the place traversal stops. Either way, the | |
| // discounted BBs will be checked if reachable and re-added. | |
| if (const auto *II = dyn_cast<InvokeInst>(&CB)) { | |
| const auto *UnwindDest = II->getUnwindDest(); | |
| Successors.insert_range(successors(UnwindDest)); | |
| // Same idea as above, we pretend we lose all these edges. | |
| for (auto *Succ : successors(UnwindDest)) | |
| if (Inserted.insert(Succ).second) | |
| DomTreeUpdates.emplace_back(DominatorTree::UpdateKind::Delete, | |
| const_cast<BasicBlock *>(UnwindDest), | |
| const_cast<BasicBlock *>(Succ)); | |
| } | |
| // Exclude the CallSiteBB, if it happens to be its own successor (1-BB loop). | |
| // We are only interested in BBs the graph moves past the callsite BB to | |
| // define the frontier past which we don't want to re-process BBs. Including | |
| // the callsite BB in this case would prematurely stop the traversal in | |
| // finish(). | |
| Successors.erase(&CallSiteBB); | |
| LikelyToChangeBBs.insert_range(Successors); | |
| // Commit the change. While some of the BBs accounted for above may play dual | |
| // role - e.g. caller's entry BB may be the same as the callsite BB - set | |
| // insertion semantics make sure we account them once. This needs to be | |
| // followed in `finish`, too. | |
| for (const auto *BB : LikelyToChangeBBs) | |
| FPI.updateForBB(*BB, -1); | |
| } | |
| DominatorTree &FunctionPropertiesUpdater::getUpdatedDominatorTree( | |
| FunctionAnalysisManager &FAM) const { | |
| auto &DT = | |
| FAM.getResult<DominatorTreeAnalysis>(const_cast<Function &>(Caller)); | |
| SmallVector<DominatorTree::UpdateType, 2> FinalDomTreeUpdates; | |
| DenseSet<const BasicBlock *> Inserted; | |
| for (auto *Succ : successors(&CallSiteBB)) | |
| if (Inserted.insert(Succ).second) | |
| FinalDomTreeUpdates.push_back({DominatorTree::UpdateKind::Insert, | |
| const_cast<BasicBlock *>(&CallSiteBB), | |
| const_cast<BasicBlock *>(Succ)}); | |
| // Perform the deletes last, so that any new nodes connected to nodes | |
| // participating in the edge deletion are known to the DT. | |
| for (auto &Upd : DomTreeUpdates) | |
| if (!llvm::is_contained(successors(Upd.getFrom()), Upd.getTo())) | |
| FinalDomTreeUpdates.push_back(Upd); | |
| DT.applyUpdates(FinalDomTreeUpdates); | |
| #ifdef EXPENSIVE_CHECKS | |
| assert(DT.verify(DominatorTree::VerificationLevel::Full)); | |
| #endif | |
| return DT; | |
| } | |
| void FunctionPropertiesUpdater::finish(FunctionAnalysisManager &FAM) const { | |
| // Update feature values from the BBs that were copied from the callee, or | |
| // might have been modified because of inlining. The latter have been | |
| // subtracted in the FunctionPropertiesUpdater ctor. | |
| // There could be successors that were reached before but now are only | |
| // reachable from elsewhere in the CFG. | |
| // One example is the following diamond CFG (lines are arrows pointing down): | |
| // A | |
| // / \ | |
| // B C | |
| // | | | |
| // | D | |
| // | | | |
| // | E | |
| // \ / | |
| // F | |
| // There's a call site in C that is inlined. Upon doing that, it turns out | |
| // it expands to | |
| // call void @llvm.trap() | |
| // unreachable | |
| // F isn't reachable from C anymore, but we did discount it when we set up | |
| // FunctionPropertiesUpdater, so we need to re-include it here. | |
| // At the same time, D and E were reachable before, but now are not anymore, | |
| // so we need to leave D out (we discounted it at setup), and explicitly | |
| // remove E. | |
| SetVector<const BasicBlock *> Reinclude; | |
| SetVector<const BasicBlock *> Unreachable; | |
| auto &DT = getUpdatedDominatorTree(FAM); | |
| if (&CallSiteBB != &*Caller.begin()) | |
| Reinclude.insert(&*Caller.begin()); | |
| // Reinclude the BBs which use the values returned by call instruction | |
| Reinclude.insert_range(CallUsers); | |
| // Distribute the successors to the 2 buckets. | |
| for (const auto *Succ : Successors) | |
| if (DT.isReachableFromEntry(Succ)) | |
| Reinclude.insert(Succ); | |
| else | |
| Unreachable.insert(Succ); | |
| // For reinclusion, we want to stop at the reachable successors, who are at | |
| // the beginning of the worklist; but, starting from the callsite bb and | |
| // ending at those successors, we also want to perform a traversal. | |
| // IncludeSuccessorsMark is the index after which we include successors. | |
| const auto IncludeSuccessorsMark = Reinclude.size(); | |
| bool CSInsertion = Reinclude.insert(&CallSiteBB); | |
| (void)CSInsertion; | |
| assert(CSInsertion); | |
| for (size_t I = 0; I < Reinclude.size(); ++I) { | |
| const auto *BB = Reinclude[I]; | |
| FPI.reIncludeBB(*BB); | |
| if (I >= IncludeSuccessorsMark) | |
| Reinclude.insert_range(successors(BB)); | |
| } | |
| // For exclusion, we don't need to exclude the set of BBs that were successors | |
| // before and are now unreachable, because we already did that at setup. For | |
| // the rest, as long as a successor is unreachable, we want to explicitly | |
| // exclude it. | |
| const auto AlreadyExcludedMark = Unreachable.size(); | |
| for (size_t I = 0; I < Unreachable.size(); ++I) { | |
| const auto *U = Unreachable[I]; | |
| if (I >= AlreadyExcludedMark) | |
| FPI.updateForBB(*U, -1); | |
| for (const auto *Succ : successors(U)) | |
| if (!DT.isReachableFromEntry(Succ)) | |
| Unreachable.insert(Succ); | |
| } | |
| const auto &LI = FAM.getResult<LoopAnalysis>(const_cast<Function &>(Caller)); | |
| FPI.updateAggregateStats(Caller, LI); | |
| #ifdef EXPENSIVE_CHECKS | |
| assert(isUpdateValid(Caller, FPI, FAM)); | |
| #endif | |
| } | |
| bool FunctionPropertiesUpdater::isUpdateValid(Function &F, | |
| const FunctionPropertiesInfo &FPI, | |
| FunctionAnalysisManager &FAM) { | |
| if (!FAM.getResult<DominatorTreeAnalysis>(F).verify( | |
| DominatorTree::VerificationLevel::Full)) | |
| return false; | |
| DominatorTree DT(F); | |
| LoopInfo LI(DT); | |
| auto Vocabulary = FAM.getResult<ModuleAnalysisManagerFunctionProxy>(F) | |
| .getCachedResult<IR2VecVocabAnalysis>(*F.getParent()); | |
| auto Fresh = | |
| FunctionPropertiesInfo::getFunctionPropertiesInfo(F, DT, LI, Vocabulary); | |
| return FPI == Fresh; | |
| } |