duke@435: /* duke@435: * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved. duke@435: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@435: * duke@435: * This code is free software; you can redistribute it and/or modify it duke@435: * under the terms of the GNU General Public License version 2 only, as duke@435: * published by the Free Software Foundation. duke@435: * duke@435: * This code is distributed in the hope that it will be useful, but WITHOUT duke@435: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@435: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@435: * version 2 for more details (a copy is included in the LICENSE file that duke@435: * accompanied this code). duke@435: * duke@435: * You should have received a copy of the GNU General Public License version duke@435: * 2 along with this work; if not, write to the Free Software Foundation, duke@435: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@435: * duke@435: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@435: * CA 95054 USA or visit www.sun.com if you need additional information or duke@435: * have any questions. duke@435: * duke@435: */ duke@435: duke@435: // duke@435: // Adaptation for C2 of the escape analysis algorithm described in: duke@435: // duke@435: // [Choi99] Jong-Deok Shoi, Manish Gupta, Mauricio Seffano, Vugranam C. Sreedhar, duke@435: // Sam Midkiff, "Escape Analysis for Java", Procedings of ACM SIGPLAN duke@435: // OOPSLA Conference, November 1, 1999 duke@435: // duke@435: // The flow-insensitive analysis described in the paper has been implemented. duke@435: // duke@435: // The analysis requires construction of a "connection graph" (CG) for the method being duke@435: // analyzed. The nodes of the connection graph are: duke@435: // duke@435: // - Java objects (JO) duke@435: // - Local variables (LV) duke@435: // - Fields of an object (OF), these also include array elements duke@435: // duke@435: // The CG contains 3 types of edges: duke@435: // duke@435: // - PointsTo (-P>) {LV,OF} to JO duke@435: // - Deferred (-D>) from {LV, OF} to {LV, OF} duke@435: // - Field (-F>) from JO to OF duke@435: // duke@435: // The following utility functions is used by the algorithm: duke@435: // duke@435: // PointsTo(n) - n is any CG node, it returns the set of JO that n could duke@435: // point to. duke@435: // duke@435: // The algorithm describes how to construct the connection graph in the following 4 cases: duke@435: // duke@435: // Case Edges Created duke@435: // duke@435: // (1) p = new T() LV -P> JO duke@435: // (2) p = q LV -D> LV duke@435: // (3) p.f = q JO -F> OF, OF -D> LV duke@435: // (4) p = q.f JO -F> OF, LV -D> OF duke@435: // duke@435: // In all these cases, p and q are local variables. For static field references, we can duke@435: // construct a local variable containing a reference to the static memory. duke@435: // duke@435: // C2 does not have local variables. However for the purposes of constructing duke@435: // the connection graph, the following IR nodes are treated as local variables: duke@435: // Phi (pointer values) duke@435: // LoadP duke@435: // Proj (value returned from callnodes including allocations) duke@435: // CheckCastPP duke@435: // duke@435: // The LoadP, Proj and CheckCastPP behave like variables assigned to only once. Only duke@435: // a Phi can have multiple assignments. Each input to a Phi is treated duke@435: // as an assignment to it. duke@435: // duke@435: // The following note types are JavaObject: duke@435: // duke@435: // top() duke@435: // Allocate duke@435: // AllocateArray duke@435: // Parm (for incoming arguments) duke@435: // CreateEx duke@435: // ConP duke@435: // LoadKlass duke@435: // duke@435: // AddP nodes are fields. duke@435: // duke@435: // After building the graph, a pass is made over the nodes, deleting deferred duke@435: // nodes and copying the edges from the target of the deferred edge to the duke@435: // source. This results in a graph with no deferred edges, only: duke@435: // duke@435: // LV -P> JO duke@435: // OF -P> JO duke@435: // JO -F> OF duke@435: // duke@435: // Then, for each node which is GlobalEscape, anything it could point to duke@435: // is marked GlobalEscape. Finally, for any node marked ArgEscape, anything duke@435: // it could point to is marked ArgEscape. duke@435: // duke@435: duke@435: class Compile; duke@435: class Node; duke@435: class CallNode; duke@435: class PhiNode; duke@435: class PhaseTransform; duke@435: class Type; duke@435: class TypePtr; duke@435: class VectorSet; duke@435: duke@435: class PointsToNode { duke@435: friend class ConnectionGraph; duke@435: public: duke@435: typedef enum { duke@435: UnknownType = 0, duke@435: JavaObject = 1, duke@435: LocalVar = 2, duke@435: Field = 3 duke@435: } NodeType; duke@435: duke@435: typedef enum { duke@435: UnknownEscape = 0, duke@435: NoEscape = 1, duke@435: ArgEscape = 2, duke@435: GlobalEscape = 3 duke@435: } EscapeState; duke@435: duke@435: typedef enum { duke@435: UnknownEdge = 0, duke@435: PointsToEdge = 1, duke@435: DeferredEdge = 2, duke@435: FieldEdge = 3 duke@435: } EdgeType; duke@435: duke@435: private: duke@435: enum { duke@435: EdgeMask = 3, duke@435: EdgeShift = 2, duke@435: duke@435: INITIAL_EDGE_COUNT = 4 duke@435: }; duke@435: duke@435: NodeType _type; duke@435: EscapeState _escape; duke@435: GrowableArray* _edges; // outgoing edges duke@435: int _offset; // for fields duke@435: duke@435: bool _unique_type; // For allocated objects, this node may be a unique type duke@435: public: duke@435: Node* _node; // Ideal node corresponding to this PointsTo node duke@435: int _inputs_processed; // the number of Phi inputs that have been processed so far duke@435: bool _hidden_alias; // this node is an argument to a function which may return it duke@435: // creating a hidden alias duke@435: duke@435: duke@435: PointsToNode(): _offset(-1), _type(UnknownType), _escape(UnknownEscape), _edges(NULL), _node(NULL), _inputs_processed(0), _hidden_alias(false), _unique_type(true) {} duke@435: duke@435: EscapeState escape_state() const { return _escape; } duke@435: NodeType node_type() const { return _type;} duke@435: int offset() { return _offset;} duke@435: duke@435: void set_offset(int offs) { _offset = offs;} duke@435: void set_escape_state(EscapeState state) { _escape = state; } duke@435: void set_node_type(NodeType ntype) { duke@435: assert(_type == UnknownType || _type == ntype, "Can't change node type"); duke@435: _type = ntype; duke@435: } duke@435: duke@435: // count of outgoing edges duke@435: uint edge_count() const { return (_edges == NULL) ? 0 : _edges->length(); } duke@435: // node index of target of outgoing edge "e" duke@435: uint edge_target(uint e) const; duke@435: // type of outgoing edge "e" duke@435: EdgeType edge_type(uint e) const; duke@435: // add a edge of the specified type pointing to the specified target duke@435: void add_edge(uint targIdx, EdgeType et); duke@435: // remove an edge of the specified type pointing to the specified target duke@435: void remove_edge(uint targIdx, EdgeType et); duke@435: #ifndef PRODUCT duke@435: void dump() const; duke@435: #endif duke@435: duke@435: }; duke@435: duke@435: class ConnectionGraph: public ResourceObj { duke@435: private: duke@435: enum { duke@435: INITIAL_NODE_COUNT = 100 // initial size of _nodes array duke@435: }; duke@435: duke@435: duke@435: GrowableArray* _nodes; // connection graph nodes Indexed by ideal duke@435: // node index duke@435: Unique_Node_List _deferred; // Phi's to be processed after parsing duke@435: VectorSet _processed; // records which nodes have been processed duke@435: bool _collecting; // indicates whether escape information is duke@435: // still being collected. If false, no new duke@435: // nodes will be processed duke@435: uint _phantom_object; // index of globally escaping object that duke@435: // pointer values loaded from a field which duke@435: // has not been set are assumed to point to duke@435: Compile * _compile; // Compile object for current compilation duke@435: duke@435: // address of an element in _nodes. Used when the element is to be modified duke@435: PointsToNode *ptnode_adr(uint idx) { duke@435: if ((uint)_nodes->length() <= idx) { duke@435: // expand _nodes array duke@435: PointsToNode dummy = _nodes->at_grow(idx); duke@435: } duke@435: return _nodes->adr_at(idx); duke@435: } duke@435: duke@435: // offset of a field reference duke@435: int type_to_offset(const Type *t); duke@435: duke@435: // compute the escape state for arguments to a call duke@435: void process_call_arguments(CallNode *call, PhaseTransform *phase); duke@435: duke@435: // compute the escape state for the return value of a call duke@435: void process_call_result(ProjNode *resproj, PhaseTransform *phase); duke@435: duke@435: // compute the escape state of a Phi. This may be called multiple duke@435: // times as new inputs are added to the Phi. duke@435: void process_phi_escape(PhiNode *phi, PhaseTransform *phase); duke@435: duke@435: // compute the escape state of an ideal node. duke@435: void record_escape_work(Node *n, PhaseTransform *phase); duke@435: duke@435: // walk the connection graph starting at the node corresponding to "n" and duke@435: // add the index of everything it could point to, to "ptset". This may cause duke@435: // Phi's encountered to get (re)processed (which requires "phase".) duke@435: void PointsTo(VectorSet &ptset, Node * n, PhaseTransform *phase); duke@435: duke@435: // Edge manipulation. The "from_i" and "to_i" arguments are the duke@435: // node indices of the source and destination of the edge duke@435: void add_pointsto_edge(uint from_i, uint to_i); duke@435: void add_deferred_edge(uint from_i, uint to_i); duke@435: void add_field_edge(uint from_i, uint to_i, int offs); duke@435: duke@435: duke@435: // Add an edge to node given by "to_i" from any field of adr_i whose offset duke@435: // matches "offset" A deferred edge is added if to_i is a LocalVar, and duke@435: // a pointsto edge is added if it is a JavaObject duke@435: void add_edge_from_fields(uint adr, uint to_i, int offs); duke@435: duke@435: // Add a deferred edge from node given by "from_i" to any field of adr_i whose offset duke@435: // matches "offset" duke@435: void add_deferred_edge_to_fields(uint from_i, uint adr, int offs); duke@435: duke@435: duke@435: // Remove outgoing deferred edges from the node referenced by "ni". duke@435: // Any outgoing edges from the target of the deferred edge are copied duke@435: // to "ni". duke@435: void remove_deferred(uint ni); duke@435: duke@435: Node_Array _node_map; // used for bookeeping during type splitting duke@435: // Used for the following purposes: duke@435: // Memory Phi - most recent unique Phi split out duke@435: // from this Phi duke@435: // MemNode - new memory input for this node duke@435: // ChecCastPP - allocation that this is a cast of duke@435: // allocation - CheckCastPP of the allocation duke@435: void split_AddP(Node *addp, Node *base, PhaseGVN *igvn); duke@435: PhiNode *create_split_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, PhaseGVN *igvn, bool &new_created); duke@435: PhiNode *split_memory_phi(PhiNode *orig_phi, int alias_idx, GrowableArray &orig_phi_worklist, PhaseGVN *igvn); duke@435: Node *find_mem(Node *mem, int alias_idx, PhaseGVN *igvn); duke@435: // Propagate unique types created for unescaped allocated objects duke@435: // through the graph duke@435: void split_unique_types(GrowableArray &alloc_worklist); duke@435: duke@435: // manage entries in _node_map duke@435: void set_map(int idx, Node *n) { _node_map.map(idx, n); } duke@435: void set_map_phi(int idx, PhiNode *p) { _node_map.map(idx, (Node *) p); } duke@435: Node *get_map(int idx) { return _node_map[idx]; } duke@435: PhiNode *get_map_phi(int idx) { duke@435: Node *phi = _node_map[idx]; duke@435: return (phi == NULL) ? NULL : phi->as_Phi(); duke@435: } duke@435: duke@435: // Notify optimizer that a node has been modified duke@435: // Node: This assumes that escape analysis is run before duke@435: // PhaseIterGVN creation duke@435: void record_for_optimizer(Node *n) { duke@435: _compile->record_for_igvn(n); duke@435: } duke@435: duke@435: // Set the escape state of a node duke@435: void set_escape_state(uint ni, PointsToNode::EscapeState es); duke@435: duke@435: // bypass any casts and return the node they refer to duke@435: Node * skip_casts(Node *n); duke@435: duke@435: // Get Compile object for current compilation. duke@435: Compile *C() const { return _compile; } duke@435: duke@435: public: duke@435: ConnectionGraph(Compile *C); duke@435: duke@435: // record a Phi for later processing. duke@435: void record_for_escape_analysis(Node *n); duke@435: duke@435: // process a node and fill in its connection graph node duke@435: void record_escape(Node *n, PhaseTransform *phase); duke@435: duke@435: // All nodes have been recorded, compute the escape information duke@435: void compute_escape(); duke@435: duke@435: // escape state of a node duke@435: PointsToNode::EscapeState escape_state(Node *n, PhaseTransform *phase); duke@435: duke@435: bool hidden_alias(Node *n) { duke@435: if (_collecting) duke@435: return true; duke@435: PointsToNode ptn = _nodes->at_grow(n->_idx); duke@435: return (ptn.escape_state() != PointsToNode::NoEscape) || ptn._hidden_alias; duke@435: } duke@435: duke@435: #ifndef PRODUCT duke@435: void dump(); duke@435: #endif duke@435: };