src/share/vm/opto/compile.cpp

Print this page
rev 3904 : 8005071: Incremental inlining for JSR 292
Summary: post parse inlining driven by number of live nodes.
Reviewed-by:

@@ -134,11 +134,11 @@
   return lo;  // inexact match
 }
 
 void Compile::register_intrinsic(CallGenerator* cg) {
   if (_intrinsics == NULL) {
-    _intrinsics = new GrowableArray<CallGenerator*>(60);
+    _intrinsics = new (comp_arena())GrowableArray<CallGenerator*>(comp_arena(), 60, 0, NULL);
   }
   // This code is stolen from ciObjectFactory::insert.
   // Really, GrowableArray should have methods for
   // insert_at, remove_at, and binary_search.
   int len = _intrinsics->length();

@@ -363,10 +363,26 @@
       record_dead_node(node_idx);
     }
   }
 }
 
+void Compile::remove_useless_late_inlines(Unique_Node_List &useful, bool string) {
+  int shift = 0;
+  GrowableArray<CallGenerator*>* inlines = string ? &_string_late_inlines : &_late_inlines;
+  for (int i = 0; i < inlines->length(); i++) {
+    CallGenerator* cg = inlines->at(i);
+    CallNode* call = cg->call_node();
+    if (shift > 0) {
+      inlines->at_put(i-shift, cg);
+    }
+    if (!useful.member(call)) {
+      shift++;
+    }
+  }
+  inlines->trunc_to(inlines->length()-shift);
+}
+
 // Disconnect all useless nodes by disconnecting those at the boundary.
 void Compile::remove_useless_nodes(Unique_Node_List &useful) {
   uint next = 0;
   while (next < useful.size()) {
     Node *n = useful.at(next++);

@@ -392,10 +408,13 @@
     Node* n = C->macro_node(i);
     if (!useful.member(n)) {
       remove_macro_node(n);
     }
   }
+  // clean up the late inline lists
+  remove_useless_late_inlines(useful, true);
+  remove_useless_late_inlines(useful, false);
   debug_only(verify_graph_edges(true/*check for no_dead_code*/);)
 }
 
 //------------------------------frame_size_in_words-----------------------------
 // frame_slots in units of words

@@ -609,10 +628,16 @@
 #ifndef PRODUCT
                   _trace_opto_output(TraceOptoOutput || method()->has_option("TraceOptoOutput")),
                   _printer(IdealGraphPrinter::printer()),
 #endif
                   _congraph(NULL),
+                  _late_inlines(comp_arena(), 2, 0, NULL),
+                  _string_late_inlines(comp_arena(), 2, 0, NULL),
+                  _late_inlines_pos(0),
+                  _number_of_mh_late_inlines(0),
+                  _inlining_progress(false),
+                  _inlining_incrementally(false),
                   _print_inlining_list(NULL),
                   _print_inlining(0) {
   C = this;
 
   CompileWrapper cw(this);

@@ -735,33 +760,17 @@
       // to whatever caller is dynamically above us on the stack.
       // This is done by a special, unique RethrowNode bound to root.
       rethrow_exceptions(kit.transfer_exceptions_into_jvms());
     }
 
-    if (!failing() && has_stringbuilder()) {
-      {
-        // remove useless nodes to make the usage analysis simpler
-        ResourceMark rm;
-        PhaseRemoveUseless pru(initial_gvn(), &for_igvn);
-      }
+    assert(IncrementalInline || (_late_inlines.length() == 0 && !has_mh_late_inlines()), "incremental inlining is off");
 
-      {
-        ResourceMark rm;
-        print_method("Before StringOpts", 3);
-        PhaseStringOpts pso(initial_gvn(), &for_igvn);
-        print_method("After StringOpts", 3);
+    if (_late_inlines.length() == 0 && !has_mh_late_inlines() && !failing() && has_stringbuilder()) {
+      string_inline(true);
       }
 
-      // now inline anything that we skipped the first time around
-      while (_late_inlines.length() > 0) {
-        CallGenerator* cg = _late_inlines.pop();
-        cg->do_late_inline();
         if (failing())  return;
-      }
-    }
-    assert(_late_inlines.length() == 0, "should have been processed");
-    dump_inlining();
 
     print_method("Before RemoveUseless", 3);
 
     // Remove clutter produced by parsing.
     if (!failing()) {

@@ -904,10 +913,13 @@
     _printer(NULL),
 #endif
     _dead_node_list(comp_arena()),
     _dead_node_count(0),
     _congraph(NULL),
+    _number_of_mh_late_inlines(0),
+    _inlining_progress(false),
+    _inlining_incrementally(false),
     _print_inlining_list(NULL),
     _print_inlining(0) {
   C = this;
 
 #ifndef PRODUCT

@@ -1758,10 +1770,136 @@
     igvn.replace_node(n, n->in(1));
   }
   assert(predicate_count()==0, "should be clean!");
 }
 
+// StringOpts and late inlining of string methods
+void Compile::string_inline(bool parse_time) {
+  {
+    // remove useless nodes to make the usage analysis simpler
+    ResourceMark rm;
+    PhaseRemoveUseless pru(initial_gvn(), for_igvn());
+  }
+  
+  {
+    ResourceMark rm;
+    print_method("Before StringOpts", 3);
+    PhaseStringOpts pso(initial_gvn(), for_igvn());
+    print_method("After StringOpts", 3);
+  }
+  
+  // now inline anything that we skipped the first time around
+  if (!parse_time) {
+    _late_inlines_pos = _late_inlines.length();
+  }
+
+  while (_string_late_inlines.length() > 0) {
+    CallGenerator* cg = _string_late_inlines.pop();
+
+    cg->do_late_inline();
+
+    if (failing())  return;
+  }
+  _string_late_inlines.trunc_to(0);
+}
+
+void Compile::incremental_inline_one(PhaseIterGVN& igvn) {
+  assert(IncrementalInline, "incremental inlining should be on");
+  PhaseGVN* gvn = initial_gvn();
+
+  clear_inlining_progress();
+  for_igvn()->clear();
+  gvn->update_with(&igvn);
+
+  int i = 0;
+
+  {
+    for (; i <_late_inlines.length() && !inlining_progress(); i++) {
+
+      CallGenerator* cg = _late_inlines.at(i);
+
+      _late_inlines_pos = i+1;
+
+      cg->do_late_inline();
+
+      if (failing())  return;
+    }
+    int j = 0;
+    for (; i < _late_inlines.length(); i++, j++) {
+      _late_inlines.at_put(j, _late_inlines.at(i));
+    }
+    _late_inlines.trunc_to(j);
+  
+    {
+      ResourceMark rm;
+      PhaseRemoveUseless pru(C->initial_gvn(), C->for_igvn());
+    }
+  }
+
+  igvn = PhaseIterGVN(gvn);
+}
+
+// Perform incremental inlining until bound on number of live nodes is reached
+void Compile::incremental_inline(PhaseIterGVN& igvn) {
+  PhaseGVN* gvn = initial_gvn();
+
+  set_inlining_incrementally();
+  set_inlining_progress();
+  uint low_live_nodes = 0;
+
+  while(inlining_progress() && _late_inlines.length() > 0) {
+
+    if (live_nodes() > (uint)LiveNodeCountInliningCutoff) {
+      if (low_live_nodes < (uint)LiveNodeCountInliningCutoff * 8 / 10) {
+        // PhaseIdealLoop is expensive so we only try it once we are
+        // out of loop and we only try it again if the previous helped
+        // got the number of nodes down significantly
+        PhaseIdealLoop ideal_loop( igvn, false, true );
+        if (failing())  return;
+        low_live_nodes = live_nodes();
+        _major_progress = true;
+      }
+
+      if (live_nodes() > (uint)LiveNodeCountInliningCutoff) {
+        break;
+      }
+    }
+
+    incremental_inline_one(igvn);
+
+    if (failing())  return;
+
+    igvn.optimize();
+
+    if (failing())  return;
+  }
+
+  assert( igvn._worklist.size() == 0, "should be done with igvn" );
+
+  if (_string_late_inlines.length() > 0) {
+    assert(has_stringbuilder(), "inconsistent");
+    for_igvn()->clear();
+    initial_gvn()->update_with(&igvn);
+
+    string_inline(false);
+
+    if (failing())  return;
+
+    {
+      ResourceMark rm;
+      PhaseRemoveUseless pru(initial_gvn(), for_igvn());
+    }
+
+    igvn = PhaseIterGVN(gvn);
+
+    igvn.optimize();
+  }
+
+  clear_inlining_incrementally();
+}
+
+
 //------------------------------Optimize---------------------------------------
 // Given a graph, optimize it.
 void Compile::Optimize() {
   TracePhase t1("optimizer", &_t_optimizer, true);
 

@@ -1780,20 +1918,28 @@
   print_method("After Parsing");
 
  {
   // Iterative Global Value Numbering, including ideal transforms
   // Initialize IterGVN with types and values from parse-time GVN
+
   PhaseIterGVN igvn(initial_gvn());
+
   {
     NOT_PRODUCT( TracePhase t2("iterGVN", &_t_iterGVN, TimeCompiler); )
     igvn.optimize();
   }
 
   print_method("Iter GVN 1", 2);
 
   if (failing())  return;
 
+  incremental_inline(igvn);
+
+  print_method("Incremental Inline", 2);
+
+  if (failing())  return;
+
   // Perform escape analysis
   if (_do_escape_analysis && ConnectionGraph::has_candidates(this)) {
     if (has_loops()) {
       // Cleanup graph (remove dead nodes).
       TracePhase t2("idealLoop", &_t_idealLoop, true);

@@ -1912,10 +2058,11 @@
     }
   }
 
  } // (End scope of igvn; run destructor if necessary for asserts.)
 
+ dump_inlining();
   // A method with only infinite loops has no edges entering loops from root
   {
     NOT_PRODUCT( TracePhase t2("graphReshape", &_t_graphReshaping, TimeCompiler); )
     if (final_graph_reshaping()) {
       assert(failing(), "must bail out w/ explicit message");

@@ -3360,10 +3507,32 @@
   }
 }
 
 void Compile::dump_inlining() {
   if (PrintInlining) {
+    // Print inlining message for candidates that we couldn't inline
+    // for lack of space or non constant receiver
+    for (int i = 0; i < _late_inlines.length(); i++) {
+      CallGenerator* cg = _late_inlines.at(i);
+      cg->print_inlining_late("live nodes > LiveNodeCountInliningCutoff");
+    }
+    Unique_Node_List useful;
+    useful.push(root());
+    for( uint next = 0; next < useful.size(); ++next ) {
+      Node *n  = useful.at(next);
+      if (n->isa_Call() != NULL && n->as_Call()->_cg != NULL && n->as_Call()->_cg->call_node() == n) {
+        CallNode* call = n->as_Call();
+        CallGenerator* cg = call->_cg;
+        cg->print_inlining_late("receiver not constant");
+      }
+      uint max = n->len();
+      for( uint i = 0; i < max; ++i ) {
+        Node *m = n->in(i);
+        if( m == NULL ) continue;
+        useful.push(m);
+      }
+    }
     for (int i = 0; i < _print_inlining_list->length(); i++) {
       tty->print(_print_inlining_list->at(i).ss()->as_string());
     }
   }
 }