//===--- CallGraphAnalysis.h - Analysis of the Call Graph ------*- C++ -*--===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #ifndef SWIFT_SILANALYSIS_CALLGRAPHANALYSIS_H #define SWIFT_SILANALYSIS_CALLGRAPHANALYSIS_H #include "swift/SILAnalysis/Analysis.h" #include "swift/Basic/Range.h" #include "swift/SIL/CFG.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILModule.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/Allocator.h" #include namespace swift { inline bool canHaveIndirectUses(SILFunction *F) { if (swift::isPossiblyUsedExternally(F->getLinkage(), F->getModule().isWholeModule())) return true; // TODO: main is currently marked as internal so we explicitly check // for functions with this name and keep them around. if (F->getName() == SWIFT_ENTRY_POINT_FUNCTION) return true; // ObjC functions are called through the runtime and are therefore alive // even if not referenced inside SIL. if (canBeCalledIndirectly(F->getLoweredFunctionType()->getAbstractCC())) return true; return false; } class CallGraphNode; class CallGraphEdge { public: // SmallSize = 1, because direct calls only need a single entry in the set and // currently we don't handle method calls. // TODO: Consider increasing SmallSize when we handle method calls. typedef llvm::SmallPtrSet CalleeSetType; private: // The call site represented by this call graph edge. ApplyInst *TheApply; // The set of functions potentially called from this call site. This // might include functions that are not actually callable based on // dynamic types. If the int bit is non-zero, the set is complete in // the sense that no function outside the set could be called. llvm::PointerIntPair CalleeSet; public: /// Create a call graph edge for a call site where we will fill in /// the set of potentially called functions later. CallGraphEdge(ApplyInst *TheApply, CalleeSetType &KnownCallees, bool Complete) : TheApply(TheApply), // FIXME: Do not allocate memory for the singleton callee case. CalleeSet(new CalleeSetType, Complete) { // TODO: We will probably have many call sites that can share a // callee set so we should optimize allocation and // deallocation of these accordingly. CalleeSet.getPointer()->insert(KnownCallees.begin(), KnownCallees.end()); } ~CallGraphEdge() { delete CalleeSet.getPointer(); } const ApplyInst *getApply() const { return TheApply; } /// Return a callee set that is known to be complete. const CalleeSetType &getCompleteCalleeSet() const { assert(isCalleeSetComplete() && "Attempt to get an incomplete call set!"); return *CalleeSet.getPointer(); } /// Return a callee set that is not known to be complete. const CalleeSetType &getPartialCalleeSet() { return *CalleeSet.getPointer(); } /// Add the given function to the set of functions that we could /// call from this call site. void addCallee(CallGraphNode *Node) { assert(!isCalleeSetComplete() && "Attempting to add another callee to a complete call set!"); CalleeSet.getPointer()->insert(Node); } /// Return whether the call set is known to be complete. bool isCalleeSetComplete() const { return CalleeSet.getInt(); } }; /// A helper method for use with ArrayRefView. Just returns the /// ApplyInst of E. inline const ApplyInst *getEdgeApplyInst(CallGraphEdge * const &E) { return E->getApply(); } class CallGraphNode { /// The function represented by this call graph node. SILFunction *Function; /// The call graph node ordinal within the SILModule. const unsigned Ordinal; /// Edges representing the known call sites that could call into /// this function. llvm::SmallVector CallerEdges; /// Edges representing the call sites within this function. llvm::SmallVector CalleeEdges; /// Do we know all the potential callers of this function? We initialize this /// to !canHaveIndirectUses(F) optimistically and if we find any use that we /// can not prove does not cause a reference to a function_ref to escape in a /// we set this to false. bool CallerEdgesComplete; public: friend class CallGraph; CallGraphNode(SILFunction *Function, unsigned Ordinal) : Function(Function), Ordinal(Ordinal), CallerEdgesComplete(!canHaveIndirectUses(Function)) { assert(Function && "Cannot build a call graph node with a null function pointer!"); } SILFunction *getFunction() { return Function; } /// Get the complete set of edges associated with call sites that can call /// into this function. const llvm::SmallVectorImpl &getCompleteCallerEdges() const { assert(isCallerEdgesComplete() && "Attempt to get an incomplete caller set!"); return CallerEdges; } // An adaptor that is used to show all of the apply insts which call the // SILFunction of this node. using CallerCallSiteList = ArrayRefView; /// Return the set of apply insts that can call into this function. CallerCallSiteList getCompleteCallerEdgesApplies() const { return CallerCallSiteList(getCompleteCallerEdges()); } /// Get the known set of call graph edges that represent possible /// calls into this function. llvm::SmallVectorImpl &getPartialCallerEdges() { return CallerEdges; } /// Return the known set of apply insts that can call into this function. CallerCallSiteList getPartialCallerEdgesApplies() { return CallerCallSiteList(getPartialCallerEdges()); } /// Get the set of call sites in this function. llvm::SmallVectorImpl &getCalleeEdges() { return CalleeEdges; } /// Do we know that the set of call sites is complete - i.e. that /// there is no other place that we can call from that can reach /// this function? bool isCallerEdgesComplete() const { return CallerEdgesComplete; } unsigned getOrdinal() const { return Ordinal; } private: /// Mark a set of callers as known to not be complete. void markCallerEdgesIncomplete() { CallerEdgesComplete = false; } /// Add an edge representing a call site within this function. void addCalleeEdge(CallGraphEdge *CallSite) { CalleeEdges.push_back(CallSite); } /// Add an edge representing a call site that calls into this function. void addCallerEdge(CallGraphEdge *CallerCallSite) { CallerEdges.push_back(CallerCallSite); } }; struct CallGraphSCC { llvm::SmallVector SCCNodes; }; class CallGraph { llvm::SmallVector CallGraphRoots; llvm::DenseMap FunctionToNodeMap; llvm::SmallVector BottomUpSCCOrder; std::vector BottomUpFunctionOrder; /// An allocator used by the callgraph. llvm::BumpPtrAllocator Allocator; public: CallGraph(SILModule *M, bool completeModule); ~CallGraph() {} llvm::SmallVectorImpl &getCallGraphRoots() { return CallGraphRoots; } CallGraphNode *getCallGraphNode(SILFunction *F) { auto Found = FunctionToNodeMap.find(F); if (Found == FunctionToNodeMap.end()) return nullptr; assert(Found->second && "Unexpected null call graph node in map!"); return Found->second; } llvm::SmallVectorImpl &getBottomUpSCCOrder() { if (BottomUpSCCOrder.empty()) computeBottomUpSCCOrder(); return BottomUpSCCOrder; } std::vector &getBottomUpFunctionOrder() { if (BottomUpFunctionOrder.empty()) computeBottomUpFunctionOrder(); return BottomUpFunctionOrder; } private: void addCallGraphNode(SILFunction *F, unsigned Ordinal); void addEdges(SILFunction *F); bool tryGetCalleeSet(SILValue Callee, CallGraphEdge::CalleeSetType &CalleeSet, bool &Complete); void addEdgesForApply(ApplyInst *AI, CallGraphNode *CallerNode); void computeBottomUpSCCOrder(); void computeBottomUpFunctionOrder(); }; /// The Call Graph Analysis provides information about the call graph. class CallGraphAnalysis : public SILAnalysis { SILModule *M; std::vector BottomUpFunctionOrder; CallGraph *CG; public: virtual ~CallGraphAnalysis() { delete CG; } CallGraphAnalysis(SILModule *MM) : SILAnalysis(AnalysisKind::CallGraph), M(MM), CG(nullptr) {} static bool classof(const SILAnalysis *S) { return S->getKind() == AnalysisKind::CallGraph; } CallGraph &getCallGraph() { if (!CG) CG = new CallGraph(M, false); return *CG; } virtual void invalidate(InvalidationKind K) { if (K >= InvalidationKind::CallGraph) { BottomUpFunctionOrder.clear(); delete CG; CG = nullptr; } } virtual void invalidate(SILFunction*, InvalidationKind K) { invalidate(K); } }; } // end namespace swift #endif