6991188: C2 Crashes while compiling method
authorkvn
Sat, 06 Nov 2010 20:35:36 -0700
changeset 7122 23b82ce71b4e
parent 7121 69928525c55c
child 7123 523bb0f29d61
6991188: C2 Crashes while compiling method Summary: Do several iterations to build EA Connection Graph. Reviewed-by: never, twisti, ysr
hotspot/src/share/vm/opto/escape.cpp
hotspot/src/share/vm/opto/escape.hpp
--- a/hotspot/src/share/vm/opto/escape.cpp	Sat Nov 06 18:52:07 2010 -0700
+++ b/hotspot/src/share/vm/opto/escape.cpp	Sat Nov 06 20:35:36 2010 -0700
@@ -85,6 +85,7 @@
   _nodes(C->comp_arena(), C->unique(), C->unique(), PointsToNode()),
   _processed(C->comp_arena()),
   _collecting(true),
+  _progress(false),
   _compile(C),
   _igvn(igvn),
   _node_map(C->comp_arena()) {
@@ -113,7 +114,7 @@
   assert(f->node_type() != PointsToNode::UnknownType && t->node_type() != PointsToNode::UnknownType, "node types must be set");
   assert(f->node_type() == PointsToNode::LocalVar || f->node_type() == PointsToNode::Field, "invalid source of PointsTo edge");
   assert(t->node_type() == PointsToNode::JavaObject, "invalid destination of PointsTo edge");
-  f->add_edge(to_i, PointsToNode::PointsToEdge);
+  add_edge(f, to_i, PointsToNode::PointsToEdge);
 }
 
 void ConnectionGraph::add_deferred_edge(uint from_i, uint to_i) {
@@ -126,7 +127,7 @@
   // don't add a self-referential edge, this can occur during removal of
   // deferred edges
   if (from_i != to_i)
-    f->add_edge(to_i, PointsToNode::DeferredEdge);
+    add_edge(f, to_i, PointsToNode::DeferredEdge);
 }
 
 int ConnectionGraph::address_offset(Node* adr, PhaseTransform *phase) {
@@ -157,7 +158,7 @@
   assert (t->offset() == -1 || t->offset() == offset, "conflicting field offsets");
   t->set_offset(offset);
 
-  f->add_edge(to_i, PointsToNode::FieldEdge);
+  add_edge(f, to_i, PointsToNode::FieldEdge);
 }
 
 void ConnectionGraph::set_escape_state(uint ni, PointsToNode::EscapeState es) {
@@ -995,7 +996,7 @@
   GrowableArray<Node *>  memnode_worklist;
   GrowableArray<PhiNode *>  orig_phis;
 
-  PhaseGVN  *igvn = _igvn;
+  PhaseIterGVN  *igvn = _igvn;
   uint new_index_start = (uint) _compile->num_alias_types();
   Arena* arena = Thread::current()->resource_area();
   VectorSet visited(arena);
@@ -1531,14 +1532,9 @@
       has_allocations = true;
     }
     if(n->is_AddP()) {
-      // Collect address nodes which directly reference an allocation.
-      // Use them during stage 3 below to build initial connection graph
-      // field edges. Other field edges could be added after StoreP/LoadP
-      // nodes are processed during stage 4 below.
-      Node* base = get_addp_base(n);
-      if(base->is_Proj() && base->in(0)->is_Allocate()) {
-        cg_worklist.append(n->_idx);
-      }
+      // Collect address nodes. Use them during stage 3 below
+      // to build initial connection graph field edges.
+      cg_worklist.append(n->_idx);
     } else if (n->is_MergeMem()) {
       // Collect all MergeMem nodes to add memory slices for
       // scalar replaceable objects in split_unique_types().
@@ -1562,18 +1558,28 @@
     build_connection_graph(n, igvn);
   }
 
-  // 3. Pass to create fields edges (Allocate -F-> AddP).
+  // 3. Pass to create initial fields edges (JavaObject -F-> AddP)
+  //    to reduce number of iterations during stage 4 below.
   uint cg_length = cg_worklist.length();
   for( uint next = 0; next < cg_length; ++next ) {
     int ni = cg_worklist.at(next);
-    build_connection_graph(ptnode_adr(ni)->_node, igvn);
+    Node* n = ptnode_adr(ni)->_node;
+    Node* base = get_addp_base(n);
+    if (base->is_Proj())
+      base = base->in(0);
+    PointsToNode::NodeType nt = ptnode_adr(base->_idx)->node_type();
+    if (nt == PointsToNode::JavaObject) {
+      build_connection_graph(n, igvn);
+    }
   }
 
   cg_worklist.clear();
   cg_worklist.append(_phantom_object);
+  GrowableArray<uint>  worklist;
 
   // 4. Build Connection Graph which need
   //    to walk the connection graph.
+  _progress = false;
   for (uint ni = 0; ni < nodes_size(); ni++) {
     PointsToNode* ptn = ptnode_adr(ni);
     Node *n = ptn->_node;
@@ -1581,13 +1587,52 @@
       build_connection_graph(n, igvn);
       if (ptn->node_type() != PointsToNode::UnknownType)
         cg_worklist.append(n->_idx); // Collect CG nodes
+      if (!_processed.test(n->_idx))
+        worklist.append(n->_idx); // Collect C/A/L/S nodes
     }
   }
 
+  // After IGVN user nodes may have smaller _idx than
+  // their inputs so they will be processed first in
+  // previous loop. Because of that not all Graph
+  // edges will be created. Walk over interesting
+  // nodes again until no new edges are created.
+  //
+  // Normally only 1-3 passes needed to build
+  // Connection Graph depending on graph complexity.
+  // Set limit to 10 to catch situation when something
+  // did go wrong and recompile the method without EA.
+
+#define CG_BUILD_ITER_LIMIT 10
+
+  uint length = worklist.length();
+  int iterations = 0;
+  while(_progress && (iterations++ < CG_BUILD_ITER_LIMIT)) {
+    _progress = false;
+    for( uint next = 0; next < length; ++next ) {
+      int ni = worklist.at(next);
+      PointsToNode* ptn = ptnode_adr(ni);
+      Node* n = ptn->_node;
+      assert(n != NULL, "should be known node");
+      build_connection_graph(n, igvn);
+    }
+  }
+  if (iterations >= CG_BUILD_ITER_LIMIT) {
+    assert(iterations < CG_BUILD_ITER_LIMIT,
+           err_msg("infinite EA connection graph build with %d nodes and worklist size %d",
+           nodes_size(), length));
+    // Possible infinite build_connection_graph loop,
+    // retry compilation without escape analysis.
+    C->record_failure(C2Compiler::retry_no_escape_analysis());
+    _collecting = false;
+    return false;
+  }
+#undef CG_BUILD_ITER_LIMIT
+
   Arena* arena = Thread::current()->resource_area();
   VectorSet ptset(arena);
-  GrowableArray<uint>  deferred_edges;
   VectorSet visited(arena);
+  worklist.clear();
 
   // 5. Remove deferred edges from the graph and adjust
   //    escape state of nonescaping objects.
@@ -1597,7 +1642,7 @@
     PointsToNode* ptn = ptnode_adr(ni);
     PointsToNode::NodeType nt = ptn->node_type();
     if (nt == PointsToNode::LocalVar || nt == PointsToNode::Field) {
-      remove_deferred(ni, &deferred_edges, &visited);
+      remove_deferred(ni, &worklist, &visited);
       Node *n = ptn->_node;
       if (n->is_AddP()) {
         // Search for objects which are not scalar replaceable
@@ -1608,7 +1653,7 @@
   }
 
   // 6. Propagate escape states.
-  GrowableArray<int>  worklist;
+  worklist.clear();
   bool has_non_escaping_obj = false;
 
   // push all GlobalEscape nodes on the worklist
@@ -2444,13 +2489,14 @@
 
   // Don't set processed bit for AddP, LoadP, StoreP since
   // they may need more then one pass to process.
+  // Also don't mark as processed Call nodes since their
+  // arguments may need more then one pass to process.
   if (_processed.test(n_idx))
     return; // No need to redefine node's state.
 
   if (n->is_Call()) {
     CallNode *call = n->as_Call();
     process_call_arguments(call, phase);
-    _processed.set(n_idx);
     return;
   }
 
--- a/hotspot/src/share/vm/opto/escape.hpp	Sat Nov 06 18:52:07 2010 -0700
+++ b/hotspot/src/share/vm/opto/escape.hpp	Sat Nov 06 20:35:36 2010 -0700
@@ -219,6 +219,9 @@
                                        // is still being collected. If false,
                                        // no new nodes will be processed.
 
+  bool                    _progress;   // Indicates whether new Graph's edges
+                                       // were created.
+
   uint                _phantom_object; // Index of globally escaping object
                                        // that pointer values loaded from
                                        // a field which has not been set
@@ -266,6 +269,13 @@
   void add_deferred_edge(uint from_i, uint to_i);
   void add_field_edge(uint from_i, uint to_i, int offs);
 
+  // Add an edge of the specified type pointing to the specified target.
+  // Set _progress if new edge is added.
+  void add_edge(PointsToNode *f, uint to_i, PointsToNode::EdgeType et) {
+    uint e_cnt = f->edge_count();
+    f->add_edge(to_i, et);
+    _progress |= (f->edge_count() != e_cnt);
+  }
 
   // Add an edge to node given by "to_i" from any field of adr_i whose offset
   // matches "offset"  A deferred edge is added if to_i is a LocalVar, and