| //===--- EscapeAnalysis.h - SIL Escape Analysis -----------------*- C++ -*-===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// EscapeAnalysis provides information about whether the lifetime of an object |
| /// exceeds the scope of a function. |
| /// |
| /// We compute escape analysis by building a connection graph for each |
| /// function. For interprocedural analysis the connection graphs are merged |
| /// in bottom-up order of the call graph. |
| /// The idea is based on "Escape analysis for Java." by J.-D. Choi, M. Gupta, M. |
| /// Serrano, V. C. Sreedhar, and S. Midkiff |
| /// http://dx.doi.org/10.1145/320384.320386 |
| /// |
| /// This design is customized for SIL and the Swift memory model as follows: |
| /// |
| /// Each SILValue holding a memory address or object reference is mapped to a |
| /// node in the connection graph. The node's type depends on the value's |
| /// origin. SILArguments have "argument" type. Locally allocated storage and |
| /// values of unknown origin have "value" type. Loaded values have "content" |
| /// type. A "return" type node represents the returned value and has no |
| /// associated SILValue. |
| /// |
| /// "Content" nodes are special in that they represent the identity of some set |
| /// of memory locations. Content nodes are created to represent the memory |
| /// pointed to by one of the other node types. So, except for loads, SILValues |
| /// do not directly map to content nodes. For debugging purposes only, content |
| /// nodes do refer back to the SILValue that originally pointed to them. When |
| /// content nodes are merged, only one of those SILValue back-references is |
| /// arbitrarily preserved. The content of the returned value is the only content |
| /// node that has no back-reference to a SILValue. |
| /// |
| /// This code: |
| /// let a = SomeClass() |
| /// return a |
| /// |
| /// Generates the following connection graph, where 'a' is in the SILValue %0: |
| /// Val %0 Esc: R, Succ: (%0.1) // Represents 'a', and points to 'a's content |
| /// Con %0.1 Esc: G, Succ: // Represents the content of 'a' |
| /// Ret Esc: R, Succ: %0 // The returned value, aliased with 'a' |
| /// |
| /// Each node has an escaping state: None, (R)eturn, (A)rguments, or (G)lobal. |
| /// These states form a lattice in which None is the most refined, or top, state |
| /// and Global is the least refined, or bottom, state. Merging nodes performs a |
| /// meet operation on their escaping states. At a call site, the callee graph is |
| /// merged with the callee graph by merging the respective call argument |
| /// nodes. A node has a "Return" escaping state if it only escapes by being |
| /// returned from the current function. A node has an "Argument" escaping state |
| /// if only escapes by being passed as an incoming argument to this function. |
| /// |
| /// A directed edge between two connection graph nodes indicates that the memory |
| /// represented by the destination node memory is reachable via an address |
| /// contained in the source node. A node may only have one "pointsTo" edge, |
| /// whose destination is always a content node. Additional "defer" edges allow a |
| /// form of aliasing between nodes. A single content node represents any and all |
| /// memory that any other node may point to. This content node can be found by |
| /// following any path of defer edges until the path terminates in a pointsTo |
| /// edge. The final pointsTo edge refers to the representative content node, and |
| /// all such paths in the graph must reach the same content node. To maintain |
| /// this invariant, the algorithm that builds the connection graph must |
| /// incrementally merge content nodes. |
| /// |
| /// Note that a defer edge may occur between any node types. A value node that |
| /// holds a reference may defer to another value or content node whose value was |
| /// merged via a phi; a content node that holds a reference may defer to a value |
| /// node that was stored into the content; a content node may defer to another |
| /// content node that was loaded and stored. |
| /// |
| /// Now consider the same example, but declaring a 'var' instead of a 'let': |
| /// |
| /// var a = SomeClass() |
| /// return a |
| /// |
| /// Generates the following connection graph, where the alloc_stack for variable |
| /// 'a' is in the SILValue %0 and class allocation returns SILValue %3. |
| /// Val %0 Esc: G, Succ: (%0.1) |
| /// Con %0.1 Esc: G, Succ: %3 |
| /// Val %3 Esc: G, Succ: (%3.1) |
| /// Con %3.1 Esc: G, Succ: |
| /// Ret Esc: R, Succ: %3 |
| /// |
| /// The value node for variable 'a' now points to local variable storage |
| /// (%0.1). That local variable storage contains a reference. Assignment into |
| /// that reference creates a defer edge to the allocated reference (%3). The |
| /// allocated reference in turn points to the object storage (%3.1). |
| /// |
| /// Note that a variable holding a single class reference and a variable |
| /// holding a non-trivial struct has the same graph representation. The |
| /// variable's content node only represents the value of the references, not the |
| /// memory pointed-to by the reference. |
| /// |
| /// A pointsTo edge does not necessarily indicate pointer indirection. It may |
| /// simply represent a derived address within the same object. This allows |
| /// escape analysis to view an object's memory in layers, each with separate |
| /// escaping properties. For example, a class object's first-level content node |
| /// represents the object header including the metadata pointer and reference |
| /// count. An object's second level content node only represents the |
| /// reference-holding fields within that object. Consider the connection graph |
| /// for a class with properties: |
| /// |
| /// class HasObj { |
| /// var obj: AnyObject |
| /// } |
| /// func assignProperty(h: HasObj, o: AnyObject) { |
| /// h.obj = o |
| /// } |
| /// |
| /// Which generates this graph where the argument 'h' is %0, and 'o' is %1: |
| /// Arg %0 Esc: A, Succ: (%0.1) |
| /// Con %0.1 Esc: A, Succ: (%0.2) |
| /// Con %0.2 Esc: A, Succ: %1 |
| /// Arg %1 Esc: A, Succ: (%1.1) |
| /// Con %1.1 Esc: A, Succ: (%1.2) |
| /// Con %1.2 Esc: G, Succ: |
| /// |
| /// Node %0.1 represents the header of 'h', including reference count and |
| /// metadata pointer. This node points to %0.2 which represents the 'obj' |
| /// property. The assignment 'h.obj = o' creates a defer edge from %0.2 to |
| /// %1. Similarly, %1.1 represents the header of 'o', and %1.2 represents any |
| /// potential nontrivial properties in 'o' which may have escaped globally when |
| /// 'o' was released. |
| /// |
| /// The connection graph is constructed by summarizing all memory operations in |
| /// a flow-insensitive way. Hint: ConGraph->viewCG() displays the Dot-formatted |
| /// connection graph. |
| /// |
| /// In addition to the connection graph, EscapeAnalysis stores information about |
| /// "use points". Each release operation is a use points. These instructions are |
| /// recorded in a table and given an ID. Each connection graph node stores a |
| /// bitset indicating the use points reachable via the CFG by that node. This |
| /// provides some flow-sensitive information on top of the otherwise flow |
| /// insensitive connection graph. |
| /// |
| /// Note: storing bitsets in each node may be unnecessary overhead since the |
| /// same information can be obtained with a graph traversal, typically of only |
| /// 1-3 hops. |
| // ===---------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ |
| #define SWIFT_SILOPTIMIZER_ANALYSIS_ESCAPEANALYSIS_H_ |
| |
| #include "swift/SIL/SILInstruction.h" |
| #include "swift/SIL/SILFunction.h" |
| #include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h" |
| #include "swift/SILOptimizer/Analysis/BottomUpIPAnalysis.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/SetVector.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/ADT/SmallBitVector.h" |
| |
| struct CGForDotView; |
| |
| namespace swift { |
| |
| class BasicCalleeAnalysis; |
| |
| /// The EscapeAnalysis results for functions in the current module, computed |
| /// bottom-up in the call graph. Each function with valid EscapeAnalysis |
| /// information is associated with a ConnectionGraph. |
| class EscapeAnalysis : public BottomUpIPAnalysis { |
| |
| /// The types of edges in the connection graph. |
| /// Escape information is propagated along edges in the connection graph: |
| /// for an edge a -> b: if a escapes then also b escapes. |
| enum EdgeType { |
| /// Represents a points-to relationship: a pointer points to a content. |
| /// The destination node must always be of type Content. For each pointer p |
| /// there is also a points-to edge p -> c, where c is the content node for p. |
| /// We use the points-to relationship also for a ref_element_addr |
| /// projection (see NodeType::Content). |
| PointsTo = 0, |
| |
| /// Represents an assignment: "a = b" creates a defer-edge a -> b. |
| /// A load "a = *p" is represented by a defer-edge a -> c, where c is p's |
| /// content node. Similarly, A store "*p = b" is represented by c -> b. |
| Defer = 1 |
| }; |
| |
| /// The types of nodes (CGNode) in the connection graph. |
| enum class NodeType : char { |
| /// Represents a "pointer" value. We define a "pointer" to be an address-type |
| /// value, an object reference or any value-type (struct, enum, etc.) which |
| /// contains a reference. If a value-type (e.g. a struct) contains multiple |
| /// references, it is treated as a single "pointer" which may point to any |
| /// of the referenced objects. |
| Value, |
| |
| /// Represents the "memory content" to which a pointer points to. |
| /// The "content" represents all stored properties of the referenced object. |
| /// We also treat the elements of a reference-counted object as a "content" |
| /// of that object. Although ref_element_addr is just a pointer addition, we |
| /// treat it as a "pointer" pointing to the elements. Having this additional |
| /// indirection in the graph, we avoid letting a reference escape just |
| /// because an element address escapes. |
| /// Note that currently we don't consider other projections and we summarize |
| /// all stored properties of an object into a single "content". |
| /// |
| Content, |
| |
| /// A function argument, which is just a special case of Value type. |
| Argument, |
| |
| /// The function return value, which is also just a special case of Value |
| /// type. |
| Return |
| }; |
| |
| /// Indicates to what a value escapes. Note: the order of values is important. |
| enum class EscapeState : char { |
| |
| /// The node's value does not escape. |
| /// The value points to a locally allocated object who's lifetime ends in |
| /// the same function. |
| None, |
| |
| /// The node's value escapes through the return value. |
| /// The value points to a locally allocated object which escapes via the |
| /// return instruction. |
| Return, |
| |
| /// The node's value escapes through a function argument. |
| Arguments, |
| |
| /// The node's value escapes to any global or unidentified memory. |
| Global |
| }; |
| |
| public: |
| class CGNode; |
| class ConnectionGraph; |
| private: |
| class CGNodeMap; |
| |
| /// The int-part is an EdgeType and specifies which kind of predecessor it is. |
| typedef llvm::PointerIntPair<CGNode *, 1> Predecessor; |
| |
| public: |
| |
| /// A node in the connection graph. |
| /// A node basically represents a "pointer" or the "memory content" where a |
| /// pointer points to (see NodeType). |
| class CGNode { |
| |
| /// The associated value in the function. It is only used for debug printing. |
| /// There may be multiple nodes associated to the same value, e.g. a Content |
| /// node has the same V as its points-to predecessor. |
| ValueBase *V; |
| |
| /// The outgoing points-to edge (if any) to a Content node. See also: |
| /// pointsToIsEdge. |
| /// If we ever want to distinguish between different fields of an object, |
| /// then we should replace the single pointsTo edge with multiple edges - |
| /// one for each field. |
| CGNode *pointsTo = nullptr; |
| |
| /// The outgoing defer edges. |
| llvm::SmallVector<CGNode *, 8> defersTo; |
| |
| /// The predecessor edges (points-to and defer). |
| llvm::SmallVector<Predecessor, 8> Preds; |
| |
| /// If this Content node is merged with another Content node, mergeTo is |
| /// the merge destination. |
| CGNode *mergeTo = nullptr; |
| |
| /// Information where the node's value is used in its function. |
| /// Each bit corresponds to an argument/instruction where the value is used. |
| /// The UsePoints on demand when calling ConnectionGraph::getUsePoints(). |
| SmallBitVector UsePoints; |
| |
| /// The actual result of the escape analysis. It tells if and how (global or |
| /// through arguments) the value escapes. |
| EscapeState State = EscapeState::None; |
| |
| /// If true, the pointsTo is a real edge in the graph. Otherwise it is not |
| /// and edge (e.g. this does not appear in the pointsTo Preds list), but |
| /// still must point to the same Content node as all successor nodes. |
| bool pointsToIsEdge = false; |
| |
| /// Used for various worklist algorithms. |
| bool isInWorkList = false; |
| |
| /// True if the merge is finished (see mergeTo). In this state this node |
| /// is completely unlinked from the graph, |
| bool isMerged = false; |
| |
| /// The type of the node (mainly distinguishes between content and value |
| /// nodes). |
| NodeType Type; |
| |
| /// The constructor. |
| CGNode(ValueBase *V, NodeType Type) : V(V), UsePoints(0), Type(Type) { |
| switch (Type) { |
| case NodeType::Argument: |
| case NodeType::Value: |
| assert(V); |
| break; |
| case NodeType::Return: |
| assert(!V); |
| break; |
| case NodeType::Content: |
| // A content node representing the returned value has no associated |
| // SILValue. |
| break; |
| } |
| } |
| |
| /// Merges the state from another state and returns true if it changed. |
| bool mergeEscapeState(EscapeState OtherState) { |
| if (OtherState > State) { |
| State = OtherState; |
| return true; |
| } |
| return false; |
| } |
| |
| /// Merges the use points from another node and returns true if there are |
| /// any changes. |
| bool mergeUsePoints(CGNode *RHS) { |
| bool Changed = RHS->UsePoints.test(UsePoints); |
| UsePoints |= RHS->UsePoints; |
| return Changed; |
| } |
| |
| /// Returns the Content node if this node has an outgoing points-to edge. |
| CGNode *getPointsToEdge() const { |
| return pointsToIsEdge ? pointsTo : nullptr; |
| } |
| |
| /// Finds a successor node in the outgoing defer edges. |
| llvm::SmallVectorImpl<CGNode *>::iterator findDeferred(CGNode *Def) { |
| return std::find(defersTo.begin(), defersTo.end(), Def); |
| } |
| |
| /// Finds a predecessor node in the incoming points-to or defer edges. |
| llvm::SmallVectorImpl<Predecessor>::iterator findPred(Predecessor Pred) { |
| return std::find(Preds.begin(), Preds.end(), Pred); |
| } |
| |
| /// Removes a predecessor node. |
| void removeFromPreds(Predecessor Pred) { |
| auto Iter = findPred(Pred); |
| assert(Iter != Preds.end() && "Predecessor to remove not found"); |
| Preds.erase(Iter); |
| } |
| |
| /// Adds a defer-edge to another node \p To. Not done if \p To is this node. |
| bool addDeferred(CGNode *To) { |
| assert(!To->isMerged); |
| if (To == this) |
| return false; |
| for (auto *Def : defersTo) { |
| if (Def == To) |
| return false; |
| } |
| To->Preds.push_back(Predecessor(this, EdgeType::Defer)); |
| defersTo.push_back(To); |
| return true; |
| } |
| |
| /// Sets the outgoing points-to edge. The \p To node must be a Content node. |
| void setPointsTo(CGNode *To) { |
| assert(!To->mergeTo); |
| assert(To->Type == NodeType::Content && |
| "Wrong node type for points-to edge"); |
| pointsToIsEdge = true; |
| pointsTo = To; |
| To->Preds.push_back(Predecessor(this, EdgeType::PointsTo)); |
| } |
| |
| /// If this node was merged with another node, the final merge target is |
| /// returned. |
| CGNode *getMergeTarget() { |
| CGNode *Target = this; |
| while (Target->mergeTo) { |
| Target = Target->mergeTo; |
| assert(Target->Type == NodeType::Content); |
| } |
| return Target; |
| } |
| |
| void setUsePointBit(int Idx) { |
| UsePoints.resize(Idx + 1, false); |
| UsePoints.set(Idx); |
| } |
| |
| /// For debug dumping. |
| void dump() const; |
| |
| /// Returns a string representation of the node type. Also for debug dumping. |
| const char *getTypeStr() const; |
| |
| /// Checks an invariant of the connection graph: The points-to nodes of |
| /// the defer-successors must match with the points-to of this node. |
| bool matchPointToOfDefers() const { |
| for (CGNode *Def : defersTo) { |
| if (pointsTo != Def->pointsTo) |
| return false; |
| } |
| /// A defer-path in the graph must not end without the specified points-to |
| /// node. |
| if (pointsTo && !pointsToIsEdge && defersTo.empty()) |
| return false; |
| return true; |
| } |
| |
| friend class CGNodeMap; |
| friend class ConnectionGraph; |
| friend struct ::CGForDotView; |
| |
| public: |
| |
| /// Returns the escape state. |
| EscapeState getEscapeState() const { return State; } |
| |
| /// Returns true if the node's value escapes within the function or via |
| /// the return instruction. |
| bool escapes() const { return getEscapeState() != EscapeState::None; } |
| |
| /// Returns true if the node's value escapes within the function. This |
| /// means that any unidentified pointer in the function may alias to |
| /// the node's value. |
| /// Note that in the false-case the node's value can still escape via |
| /// the return instruction. |
| bool escapesInsideFunction(bool isNotAliasingArgument) const { |
| switch (getEscapeState()) { |
| case EscapeState::None: |
| case EscapeState::Return: |
| return false; |
| case EscapeState::Arguments: |
| return !isNotAliasingArgument; |
| case EscapeState::Global: |
| return true; |
| } |
| |
| llvm_unreachable("Unhandled EscapeState in switch."); |
| } |
| |
| /// Returns the content node if of this node if it exists in the graph. |
| CGNode *getContentNodeOrNull() const { |
| return pointsTo; |
| } |
| }; |
| |
| private: |
| |
| /// Mapping from nodes in a callee-graph to nodes in a caller-graph. |
| class CGNodeMap { |
| /// The map itself. |
| llvm::DenseMap<CGNode *, CGNode *> Map; |
| |
| /// The list of source nodes (= keys in Map), which is used as a work-list. |
| llvm::SmallVector<CGNode *, 8> MappedNodes; |
| public: |
| |
| /// Adds a mapping and pushes the \p From node into the work-list |
| /// MappedNodes. |
| void add(CGNode *From, CGNode *To) { |
| assert(From && To && !From->isMerged && !To->isMerged); |
| Map[From] = To; |
| if (!From->isInWorkList) { |
| MappedNodes.push_back(From); |
| From->isInWorkList = true; |
| } |
| } |
| /// Looks up a node in the mapping. |
| CGNode *get(CGNode *From) const { |
| auto Iter = Map.find(From); |
| if (Iter == Map.end()) |
| return nullptr; |
| |
| return Iter->second->getMergeTarget(); |
| } |
| const SmallVectorImpl<CGNode *> &getMappedNodes() const { |
| return MappedNodes; |
| } |
| }; |
| |
| public: |
| |
| /// The connection graph for a function. See also: EdgeType, NodeType and |
| /// CGNode. |
| /// A connection graph has these invariants: |
| /// 1) A defer-edge must not form a self cycle, i.e. must have different |
| /// source and target nodes. |
| /// 2) A node can only have a single outgoing points-to edge (is enforced by |
| /// CGNode::pointsTo being a single pointer and not a vector). |
| /// 3) The target of a points-to edge must be a Content node. |
| /// 4) For any node N, all paths starting at N which consist of only |
| /// defer-edges and a single trailing points-to edge must lead to the same |
| /// Content node. |
| class ConnectionGraph { |
| |
| /// Backlink to the graph's function. |
| SILFunction *F; |
| |
| /// Mapping from pointer SIL values to nodes in the graph. Such a value can |
| /// never be a projection, because in case of projection-instruction the |
| /// based operand value is used instead. |
| /// Multiple values can map to the same node. See setNode(). |
| llvm::DenseMap<ValueBase *, CGNode *> Values2Nodes; |
| |
| /// All nodes. |
| llvm::SmallVector<CGNode *, 16> Nodes; |
| |
| /// A to-do list of nodes to merge. |
| llvm::SmallVector<CGNode *, 8> ToMerge; |
| |
| /// The pseudo node which represents the return value. It's type is |
| /// NodeType::Return. |
| CGNode *ReturnNode = nullptr; |
| |
| /// The list of use points. |
| llvm::SmallVector<SILNode *, 16> UsePointTable; |
| |
| /// Mapping of use points to bit indices in CGNode::UsePoints. |
| llvm::DenseMap<SILNode *, int> UsePoints; |
| |
| /// The allocator for nodes. |
| llvm::SpecificBumpPtrAllocator<CGNode> NodeAllocator; |
| |
| /// True if this is a summary graph. |
| bool isSummaryGraph; |
| |
| /// Constructs a connection graph for a function. |
| ConnectionGraph(SILFunction *F, bool isSummaryGraph) : |
| F(F), isSummaryGraph(isSummaryGraph) { |
| } |
| |
| /// Returns true if the connection graph is empty. |
| bool isEmpty() { |
| return Values2Nodes.empty() && Nodes.empty() && UsePoints.empty(); |
| } |
| |
| /// Removes all nodes from the graph. |
| void clear(); |
| |
| /// Allocates a node of a given type. |
| CGNode *allocNode(ValueBase *V, NodeType Type) { |
| CGNode *Node = new (NodeAllocator.Allocate()) CGNode(V, Type); |
| Nodes.push_back(Node); |
| return Node; |
| } |
| |
| /// Adds a defer-edge and updates pointsTo of all defer-reachable nodes. |
| /// The addition of a defer-edge may invalidate the graph invariance 4). |
| /// If this is the case, all "mismatching" Content nodes are merged until |
| /// invariance 4) is reached again. |
| bool addDeferEdge(CGNode *From, CGNode *To); |
| |
| /// Adds the node \p From (to be merged with \p To) to the ToMerge list. |
| /// The actual merging is done in mergeAllScheduledNodes(). |
| void scheduleToMerge(CGNode *From, CGNode *To) { |
| assert(From->Type == NodeType::Content); |
| assert(To->Type == NodeType::Content); |
| CGNode *FromMergeTarget = From->getMergeTarget(); |
| CGNode *ToMergeTarget = To->getMergeTarget(); |
| if (FromMergeTarget != ToMergeTarget) { |
| FromMergeTarget->mergeTo = ToMergeTarget; |
| ToMerge.push_back(FromMergeTarget); |
| } |
| } |
| |
| /// Merges all nodes which are added to the ToMerge list. |
| void mergeAllScheduledNodes(); |
| |
| /// Transitively updates pointsTo of all nodes in the defer-edge web, |
| /// starting at \p InitialNode. |
| /// If a node in the web already points to another content node, the other |
| /// content node is scheduled to be merged with \p pointsTo. |
| void updatePointsTo(CGNode *InitialNode, CGNode *pointsTo); |
| |
| /// Utility function to clear the isInWorkList flags of all nodes in |
| /// \p WorkList. |
| static void clearWorkListFlags(const SmallVectorImpl<CGNode *> &WorkList) { |
| for (CGNode *Node : WorkList) { |
| Node->isInWorkList = false; |
| } |
| } |
| |
| /// Gets or creates a node for a value \p V. |
| /// If V is a projection(-path) then the base of the projection(-path) is |
| /// taken. This means the node is always created for the "outermost" value |
| /// where V is contained. |
| /// Returns null, if V is not a "pointer". |
| CGNode *getNode(ValueBase *V, EscapeAnalysis *EA, bool createIfNeeded = true); |
| |
| /// Gets or creates a content node to which \a AddrNode points to during |
| /// initial graph construction. This may not be called after defer edges |
| /// have been created. Doing so would break the invariant that all |
| /// non-content nodes ultimately have a pointsTo edge to a single content |
| /// node. |
| CGNode *getContentNode(CGNode *AddrNode); |
| |
| /// Get or creates a pseudo node for the function return value. |
| CGNode *getReturnNode() { |
| if (!ReturnNode) { |
| ReturnNode = allocNode(nullptr, NodeType::Return); |
| if (!isSummaryGraph) |
| ReturnNode->mergeEscapeState(EscapeState::Return); |
| } |
| return ReturnNode; |
| } |
| |
| /// Returns the node for the function return value if present. |
| CGNode *getReturnNodeOrNull() const { |
| return ReturnNode; |
| } |
| |
| /// Returns the node of the "exact" value \p V (no projections are skipped) |
| /// if one exists. |
| CGNode *lookupNode(ValueBase *V) { |
| CGNode *Node = Values2Nodes.lookup(V); |
| if (Node) |
| return Node->getMergeTarget(); |
| return nullptr; |
| } |
| |
| /// Re-uses a node for another SIL value. |
| void setNode(ValueBase *V, CGNode *Node) { |
| assert(Values2Nodes.find(V) == Values2Nodes.end()); |
| Values2Nodes[V] = Node; |
| } |
| |
| /// Adds an argument/instruction in which the node's value is used. |
| int addUsePoint(CGNode *Node, SILNode *User) { |
| if (Node->getEscapeState() >= EscapeState::Global) |
| return -1; |
| |
| User = User->getRepresentativeSILNodeInObject(); |
| int Idx = (int)UsePoints.size(); |
| assert(UsePoints.count(User) == 0 && "value is already a use-point"); |
| UsePoints[User] = Idx; |
| UsePointTable.push_back(User); |
| assert(UsePoints.size() == UsePointTable.size()); |
| Node->setUsePointBit(Idx); |
| return Idx; |
| } |
| |
| /// Specifies that the node's value escapes to global or unidentified |
| /// memory. |
| void setEscapesGlobal(CGNode *Node) { |
| Node->mergeEscapeState(EscapeState::Global); |
| |
| // Make sure to have a content node. Otherwise we may end up not merging |
| // the global-escape state into a caller graph (only content nodes are |
| // merged). Either the node itself is a content node or we let the node |
| // point to one. |
| if (Node->Type != NodeType::Content) |
| getContentNode(Node); |
| } |
| |
| /// Creates a defer-edge between \p From and \p To. |
| /// This may trigger node merges to keep the graph invariance 4). |
| /// Returns the \p From node or its merge-target in case \p From was merged |
| /// during adding the edge. |
| /// The \p EdgeAdded is set to true if there was no defer-edge between |
| /// \p From and \p To, yet. |
| CGNode *defer(CGNode *From, CGNode *To, bool &EdgeAdded) { |
| if (addDeferEdge(From, To)) |
| EdgeAdded = true; |
| mergeAllScheduledNodes(); |
| return From->getMergeTarget(); |
| } |
| |
| /// Creates a defer-edge between \p From and \p To. |
| /// This may trigger node merges to keep the graph invariance 4). |
| /// Returns the \p From node or its merge-target in case \p From was merged |
| /// during adding the edge. |
| CGNode *defer(CGNode *From, CGNode *To) { |
| bool UnusedEdgeAddedFlag = false; |
| return defer(From, To, UnusedEdgeAddedFlag); |
| } |
| |
| /// Merges the \p SourceGraph into this graph. The \p Mapping contains the |
| /// initial node mapping of the nodes to start the merge. |
| bool mergeFrom(ConnectionGraph *SourceGraph, CGNodeMap &Mapping); |
| |
| /// Propagates the escape states through the graph. |
| void propagateEscapeStates(); |
| |
| /// Removes a value from the graph. |
| /// It does not delete its node but makes sure that the value cannot be |
| /// lookup-up with getNode() anymore. |
| void removeFromGraph(ValueBase *V) { Values2Nodes.erase(V); } |
| |
| /// Returns true if there is a path from \p From to \p To. |
| bool isReachable(CGNode *From, CGNode *To); |
| |
| public: |
| |
| /// Gets or creates a node for a value \p V. |
| /// If V is a projection(-path) then the base of the projection(-path) is |
| /// taken. This means the node is always created for the "outermost" value |
| /// where V is contained. |
| /// Returns null, if V is not a "pointer". |
| CGNode *getNodeOrNull(ValueBase *V, EscapeAnalysis *EA) { |
| return getNode(V, EA, false); |
| } |
| |
| /// Returns the number of use-points of a node. |
| int getNumUsePoints(CGNode *Node) { |
| assert(!Node->escapes() && |
| "Use points are only valid for non-escaping nodes"); |
| return Node->UsePoints.count(); |
| } |
| |
| /// Returns true if \p UsePoint is a use of \p Node, i.e. UsePoint may |
| /// (indirectly) somehow refer to the Node's value. |
| /// Use-points are only values which are relevant for lifeness computation, |
| /// e.g. release or apply instructions. |
| bool isUsePoint(SILNode *UsePoint, CGNode *Node); |
| |
| /// Returns all use points of \p Node in \p UsePoints. |
| void getUsePoints(CGNode *Node, |
| llvm::SmallVectorImpl<SILNode *> &UsePoints); |
| |
| /// Computes the use point information. |
| void computeUsePoints(); |
| |
| /// Debug print the graph. |
| void print(llvm::raw_ostream &OS) const; |
| |
| /// Debug dump the graph. |
| void dump() const; |
| |
| /// This function is meant for use from the debugger. You can just say 'call |
| /// CG->viewCG()' and a dot graph viewer window should pop up from the |
| /// program, displaying the connection graph. This depends on there being a |
| /// dot graph viewer program, like 'graphviz', in your path. |
| /// |
| /// Defer-edges are gray, points-to edges are black. |
| /// Content nodes are rounded rectangles, argument/return nodes are bold. |
| /// Global escaping nodes are red, argument escaping nodes are blue. |
| void viewCG() const; |
| |
| /// Checks if the graph is OK. |
| void verify() const; |
| |
| /// Just verifies the graph structure. This function can also be called |
| /// during the graph is modified, e.g. in mergeAllScheduledNodes(). |
| void verifyStructure() const; |
| |
| friend struct ::CGForDotView; |
| friend class EscapeAnalysis; |
| }; |
| |
| private: |
| |
| /// All the information we keep for a function. |
| struct FunctionInfo : public FunctionInfoBase<FunctionInfo> { |
| FunctionInfo(SILFunction *F) : Graph(F, false), SummaryGraph(F, true) { } |
| |
| /// The connection graph for the function. This is what clients of the |
| /// analysis will see. |
| /// On invalidation, this graph is invalidated and recomputed on demand. |
| ConnectionGraph Graph; |
| |
| /// The summary graph for the function. It is used when computing the |
| /// connection graph of caller functions. |
| /// This graph is _not_ be invalidated on invalidation. It is only updated |
| /// when explicitly calling recompute(). |
| ConnectionGraph SummaryGraph; |
| |
| /// If true, at least one of the callee graphs has changed. We have to merge |
| /// them again. |
| bool NeedUpdateSummaryGraph = true; |
| |
| /// Clears the analysis data on invalidation. |
| void clear() { |
| Graph.clear(); |
| SummaryGraph.clear(); |
| } |
| }; |
| |
| typedef BottomUpFunctionOrder<FunctionInfo> FunctionOrder; |
| |
| enum { |
| /// The maximum call-graph recursion depth for recomputing the analysis. |
| /// This is a relatively small number to reduce compile time in case of |
| /// large cycles in the call-graph. |
| /// In case of no cycles, we should not hit this limit at all because the |
| /// pass manager processes functions in bottom-up order. |
| MaxRecursionDepth = 3, |
| |
| /// A limit for the number of call-graph iterations in recompute(). |
| MaxGraphMerges = 4 |
| }; |
| |
| /// The connection graphs for all functions (does not include external |
| /// functions). |
| llvm::DenseMap<SILFunction *, FunctionInfo *> Function2Info; |
| |
| /// The allocator for the connection graphs in Function2ConGraph. |
| llvm::SpecificBumpPtrAllocator<FunctionInfo> Allocator; |
| |
| /// Cache for isPointer(). |
| llvm::DenseMap<SILType, bool> isPointerCache; |
| |
| SILModule *M; |
| |
| /// The Array<Element> type of the stdlib. |
| NominalTypeDecl *ArrayType; |
| |
| /// Callee analysis, used for determining the callees at call sites. |
| BasicCalleeAnalysis *BCA; |
| |
| /// Returns true if \p V may encapsulate a "pointer" value. |
| /// See EscapeAnalysis::NodeType::Value. |
| bool isPointer(ValueBase *V); |
| |
| /// If \p pointer is a pointer, set it to global escaping. |
| void setEscapesGlobal(ConnectionGraph *ConGraph, ValueBase *pointer) { |
| if (CGNode *Node = ConGraph->getNode(pointer, this)) |
| ConGraph->setEscapesGlobal(Node); |
| } |
| |
| /// Gets or creates FunctionEffects for \p F. |
| FunctionInfo *getFunctionInfo(SILFunction *F) { |
| FunctionInfo *&FInfo = Function2Info[F]; |
| if (!FInfo) |
| FInfo = new (Allocator.Allocate()) FunctionInfo(F); |
| return FInfo; |
| } |
| |
| /// Build a connection graph for reach callee from the callee list. |
| bool buildConnectionGraphForCallees(SILInstruction *Caller, |
| CalleeList Callees, |
| FunctionInfo *FInfo, |
| FunctionOrder &BottomUpOrder, |
| int RecursionDepth); |
| |
| /// Build a connection graph for the destructor invoked for a provided |
| /// SILValue. |
| bool buildConnectionGraphForDestructor(SILValue V, |
| SILInstruction *Caller, |
| FunctionInfo *FInfo, |
| FunctionOrder &BottomUpOrder, |
| int RecursionDepth); |
| |
| /// Builds the connection graph for a function, including called functions. |
| /// Visited callees are added to \p BottomUpOrder until \p RecursionDepth |
| /// reaches MaxRecursionDepth. |
| void buildConnectionGraph(FunctionInfo *FInfo, FunctionOrder &BottomUpOrder, |
| int RecursionDepth); |
| |
| /// Updates the graph by analyzing instruction \p I. |
| /// Visited callees are added to \p BottomUpOrder until \p RecursionDepth |
| /// reaches MaxRecursionDepth. |
| void analyzeInstruction(SILInstruction *I, FunctionInfo *FInfo, |
| FunctionOrder &BottomUpOrder, |
| int RecursionDepth); |
| |
| /// Updates the graph by analyzing instruction \p SI, which may be a |
| /// select_enum, select_enum_addr or select_value. |
| template<class SelectInst> |
| void analyzeSelectInst(SelectInst *SI, ConnectionGraph *ConGraph); |
| |
| /// Returns true if a release of \p V is known to not capture its content. |
| bool deinitIsKnownToNotCapture(SILValue V); |
| |
| /// Sets all operands and results of \p I as global escaping. |
| void setAllEscaping(SILInstruction *I, ConnectionGraph *ConGraph); |
| |
| /// Recomputes the connection graph for the function \p Initial and |
| /// all called functions, up to a recursion depth of MaxRecursionDepth. |
| void recompute(FunctionInfo *Initial); |
| |
| /// Merges the graph of a callee function into the graph of |
| /// a caller function, whereas \p FAS is the call-site. |
| bool mergeCalleeGraph(SILInstruction *FAS, |
| ConnectionGraph *CallerGraph, |
| ConnectionGraph *CalleeGraph); |
| |
| /// Merge the \p Graph into \p SummaryGraph. |
| bool mergeSummaryGraph(ConnectionGraph *SummaryGraph, |
| ConnectionGraph *Graph); |
| |
| /// Returns true if the value \p V can escape to the \p UsePoint, where |
| /// \p UsePoint is either a release-instruction or a function call. |
| bool canEscapeToUsePoint(SILValue V, SILNode *UsePoint, |
| ConnectionGraph *ConGraph); |
| |
| friend struct ::CGForDotView; |
| |
| public: |
| EscapeAnalysis(SILModule *M); |
| |
| static bool classof(const SILAnalysis *S) { |
| return S->getKind() == SILAnalysisKind::Escape; |
| } |
| |
| virtual void initialize(SILPassManager *PM) override; |
| |
| /// Gets the connection graph for \a F. |
| ConnectionGraph *getConnectionGraph(SILFunction *F) { |
| FunctionInfo *FInfo = getFunctionInfo(F); |
| if (!FInfo->isValid()) |
| recompute(FInfo); |
| return &FInfo->Graph; |
| } |
| |
| /// Returns true if the value \p V can escape to the function call \p FAS. |
| /// This means that the called function may access the value \p V. |
| /// If \p V has reference semantics, this function returns false if only the |
| /// address of a contained property escapes, but not the object itself. |
| bool canEscapeTo(SILValue V, FullApplySite FAS); |
| |
| /// Returns true if the value \p V can escape to the release-instruction \p |
| /// RI. This means that \p RI may release \p V or any called destructor may |
| /// access (or release) \p V. |
| /// Note that if \p RI is a retain-instruction always false is returned. |
| bool canEscapeTo(SILValue V, RefCountingInst *RI); |
| |
| /// Returns true if the value \p V can escape to any other pointer \p To. |
| /// This means that either \p To is the same as \p V or contains a reference |
| /// to \p V. |
| bool canEscapeToValue(SILValue V, SILValue To); |
| |
| /// Returns true if the parameter with index \p ParamIdx can escape in the |
| /// called function of apply site \p FAS. |
| /// If it is an indirect parameter and \p checkContentOfIndirectParam is true |
| /// then the escape status is not checked for the address itself but for the |
| /// referenced pointer (if the referenced type is a pointer). |
| bool canParameterEscape(FullApplySite FAS, int ParamIdx, |
| bool checkContentOfIndirectParam); |
| |
| /// Returns true if the pointers \p V1 and \p V2 can possibly point to the |
| /// same memory. |
| /// If at least one of the pointers refers to a local object and the |
| /// connection-graph-nodes of both pointers do not point to the same content |
| /// node, the pointers do not alias. |
| bool canPointToSameMemory(SILValue V1, SILValue V2); |
| |
| /// Invalidate all information in this analysis. |
| virtual void invalidate() override; |
| |
| /// Invalidate all of the information for a specific function. |
| virtual void invalidate(SILFunction *F, InvalidationKind K) override; |
| |
| /// Notify the analysis about a newly created function. |
| virtual void notifyAddedOrModifiedFunction(SILFunction *F) override {} |
| |
| /// Notify the analysis about a function which will be deleted from the |
| /// module. |
| virtual void notifyWillDeleteFunction(SILFunction *F) override { |
| invalidate(F, InvalidationKind::Nothing); |
| } |
| |
| /// Notify the analysis about changed witness or vtables. |
| virtual void invalidateFunctionTables() override { } |
| |
| virtual void handleDeleteNotification(SILNode *N) override; |
| |
| virtual bool needsNotifications() override { return true; } |
| |
| virtual void verify() const override { |
| #ifndef NDEBUG |
| for (auto Iter : Function2Info) { |
| FunctionInfo *FInfo = Iter.second; |
| FInfo->Graph.verify(); |
| FInfo->SummaryGraph.verify(); |
| } |
| #endif |
| } |
| |
| virtual void verify(SILFunction *F) const override { |
| #ifndef NDEBUG |
| if (FunctionInfo *FInfo = Function2Info.lookup(F)) { |
| FInfo->Graph.verify(); |
| FInfo->SummaryGraph.verify(); |
| } |
| #endif |
| } |
| |
| }; |
| |
| } // end namespace swift |
| |
| #endif |