< prev index next >

src/hotspot/share/opto/loopUnswitch.cpp

Print this page

        

@@ -22,10 +22,12 @@
  *
  */
 
 #include "precompiled.hpp"
 #include "memory/allocation.inline.hpp"
+#include "opto/mulnode.hpp"
+#include "opto/addnode.hpp"
 #include "opto/connode.hpp"
 #include "opto/convertnode.hpp"
 #include "opto/loopnode.hpp"
 #include "opto/opaquenode.hpp"
 #include "opto/rootnode.hpp"

@@ -49,10 +51,50 @@
 //                                 endloop
 //                               endif
 //
 // Note: the "else" clause may be empty
 
+static bool is_flattened_array_check(Node* iff, PhaseTransform* phase) {
+  if (iff->Opcode() != Op_If) {
+    return false;
+  }
+  Node* bol = iff->in(1);
+  if (!bol->is_Bool() || bol->as_Bool()->_test._test != BoolTest::ne) {
+    return false;
+  }
+  Node* cmp = bol->in(1);
+  if (cmp->Opcode() != Op_CmpI) {
+    return false;
+  }
+  Node* cmp_in1 = cmp->in(1);
+  Node* cmp_in2 = cmp->in(2);
+  if ((unsigned int)cmp_in2->find_int_con(0) != Klass::_lh_array_tag_vt_value) {
+    return false;
+  }
+  if (cmp_in1->Opcode() != Op_RShiftI) {
+    return false;
+  }
+  Node* shift_in1 = cmp_in1->in(1);
+  Node* shift_in2 = cmp_in1->in(2);
+  if ((unsigned int)shift_in2->find_int_con(0) != Klass::_lh_array_tag_shift) {
+    return false;
+  }
+  if (shift_in1->Opcode() != Op_LoadI) {
+    return false;
+  }
+  intptr_t offset;
+  Node* addr = AddPNode::Ideal_base_and_offset(shift_in1->in(MemNode::Address), phase, offset);
+  if (addr == NULL || offset != in_bytes(Klass::layout_helper_offset())) {
+    return false;
+  }
+  if (!phase->type(addr)->isa_klassptr()) {
+    return false;
+  }
+  
+  return true;
+}
+
 //------------------------------policy_unswitching-----------------------------
 // Return TRUE or FALSE if the loop should be unswitched
 // (ie. clone loop with an invariant test that does not exit the loop)
 bool IdealLoopTree::policy_unswitching( PhaseIdealLoop *phase ) const {
   if (!LoopUnswitching) {

@@ -72,21 +114,27 @@
 
   LoopNode* head = _head->as_Loop();
   if (head->unswitch_count() + 1 > head->unswitch_max()) {
     return false;
   }
-  if (phase->find_unswitching_candidate(this) == NULL) {
+
+  if (head->is_flattened_arrays()) {
+    return false;
+  }
+  
+  Node_List flattened_checks;
+  if (phase->find_unswitching_candidate(this, flattened_checks) == NULL && flattened_checks.size() == 0) {
     return false;
   }
 
   // Too speculative if running low on nodes.
   return phase->may_require_nodes(est_loop_clone_sz(3, _body.size()));
 }
 
 //------------------------------find_unswitching_candidate-----------------------------
 // Find candidate "if" for unswitching
-IfNode* PhaseIdealLoop::find_unswitching_candidate(const IdealLoopTree *loop) const {
+IfNode* PhaseIdealLoop::find_unswitching_candidate(const IdealLoopTree *loop, Node_List& flattened_checks) const {
 
   // Find first invariant test that doesn't exit the loop
   LoopNode *head = loop->_head->as_Loop();
   IfNode* unswitch_iff = NULL;
   Node* n = head->in(LoopNode::LoopBackControl);

@@ -107,10 +155,24 @@
         }
       }
     }
     n = n_dom;
   }
+  
+  if (unswitch_iff == NULL || is_flattened_array_check(unswitch_iff, &_igvn)) {
+    // collect all flattened array checks
+    for (uint i = 0; i < loop->_body.size(); i++) {
+      Node* n = loop->_body.at(i);
+      if (is_flattened_array_check(n, &_igvn) &&
+          loop->is_invariant(n->in(1)) &&
+          !loop->is_loop_exit(n)) {
+        flattened_checks.push(n);
+      }
+    }
+    unswitch_iff = NULL;
+  }
+  
   return unswitch_iff;
 }
 
 //------------------------------do_unswitching-----------------------------
 // Clone loop with an invariant test (that does not exit) and

@@ -119,12 +181,16 @@
 void PhaseIdealLoop::do_unswitching (IdealLoopTree *loop, Node_List &old_new) {
 
   // Find first invariant test that doesn't exit the loop
   LoopNode *head = loop->_head->as_Loop();
 
-  IfNode* unswitch_iff = find_unswitching_candidate((const IdealLoopTree *)loop);
-  assert(unswitch_iff != NULL, "should be at least one");
+  Node_List flattened_checks;
+  IfNode* unswitch_iff = find_unswitching_candidate((const IdealLoopTree *)loop, flattened_checks);
+  assert(unswitch_iff != NULL || flattened_checks.size() > 0, "should be at least one");
+  if (unswitch_iff == NULL) {
+    unswitch_iff = flattened_checks.at(0)->as_If();
+  }
 
 #ifndef PRODUCT
   if (TraceLoopOpts) {
     tty->print("Unswitch   %d ", head->unswitch_count()+1);
     loop->dump_head();

@@ -166,17 +232,49 @@
   // Increment unswitch count
   LoopNode* head_clone = old_new[head->_idx]->as_Loop();
   int nct = head->unswitch_count() + 1;
   head->set_unswitch_count(nct);
   head_clone->set_unswitch_count(nct);
+  head_clone->mark_flattened_arrays();
 
   // Add test to new "if" outside of loop
   IfNode* invar_iff   = proj_true->in(0)->as_If();
   Node* invar_iff_c   = invar_iff->in(0);
+  invar_iff->_prob    = unswitch_iff->_prob;
+  if (flattened_checks.size() > 0) {
+    // Flattened array checks are used in
+    // Parse::array_store()/Parse::array_load() to switch between a
+    // legacy object array access and a flattened value array
+    // access. We want the performance impact on legacy accesses to be
+    // as small as possible so we make 2 copies of the loops: a fast
+    // one where all accesses are known to be legacy, a slow one where
+    // some accesses are to flattened arrays. Flattened array checks
+    // can be removed from the first one but not from the second one
+    // as it can have a mix of flattened/legacy accesses.
+    BoolNode* bol       = unswitch_iff->in(1)->clone()->as_Bool();
+    register_new_node(bol, invar_iff->in(0));
+    Node* cmp = bol->in(1)->clone();
+    register_new_node(cmp, invar_iff->in(0));
+    bol->set_req(1, cmp);
+    Node* in1 = NULL;
+    for (uint i = 0; i < flattened_checks.size(); i++) {
+      Node* v = flattened_checks.at(i)->in(1)->in(1)->in(1);
+      v = new AndINode(v, _igvn.intcon(Klass::_lh_array_tag_vt_value));
+      register_new_node(v, invar_iff->in(0));
+      if (in1 == NULL) {
+        in1 = v;
+      } else {
+        in1 = new OrINode(in1, v);
+        register_new_node(in1, invar_iff->in(0));
+      }
+    }
+    cmp->set_req(1, in1);
+    invar_iff->set_req(1, bol);
+  } else {
   BoolNode* bol       = unswitch_iff->in(1)->as_Bool();
   invar_iff->set_req(1, bol);
-  invar_iff->_prob    = unswitch_iff->_prob;
+  }
 
   ProjNode* proj_false = invar_iff->proj_out(0)->as_Proj();
 
   // Hoist invariant casts out of each loop to the appropriate
   // control projection.

@@ -203,17 +301,25 @@
       Node* use_clone = old_new[use->_idx];
       _igvn.replace_input_of(use_clone, 1, nuse);
     }
   }
 
+  IfNode* unswitch_iff_clone = old_new[unswitch_iff->_idx]->as_If();
+  if (flattened_checks.size() > 0) {
+    for (uint i = 0; i < flattened_checks.size(); i++) {
+      IfNode* iff = flattened_checks.at(i)->as_If();
+      _igvn.rehash_node_delayed(iff);
+      short_circuit_if(iff, proj_true);
+    }
+  } else {
   // Hardwire the control paths in the loops into if(true) and if(false)
   _igvn.rehash_node_delayed(unswitch_iff);
   short_circuit_if(unswitch_iff, proj_true);
 
-  IfNode* unswitch_iff_clone = old_new[unswitch_iff->_idx]->as_If();
   _igvn.rehash_node_delayed(unswitch_iff_clone);
   short_circuit_if(unswitch_iff_clone, proj_false);
+  }
 
   // Reoptimize loops
   loop->record_for_igvn();
   for(int i = loop->_body.size() - 1; i >= 0 ; i--) {
     Node *n = loop->_body[i];
< prev index next >