changeset 9627:c02a7038ce96

Reworked C2 Shenandoah optimizer. Fixes some methods that couldn't be compiled, a SEGV caused by implicit null checks, and improves performance.
author rkennke
date Tue, 29 Sep 2015 16:51:31 +0200
parents fd59c4412e00
children 82e2de7b5401
files src/cpu/x86/vm/x86_64.ad src/share/vm/opto/graphKit.cpp src/share/vm/opto/phaseX.cpp src/share/vm/opto/phaseX.hpp src/share/vm/opto/shenandoahSupport.cpp src/share/vm/opto/shenandoahSupport.hpp
diffstat 6 files changed, 362 insertions(+), 224 deletions(-) [+]
line wrap: on
line diff
--- a/src/cpu/x86/vm/x86_64.ad	Fri Sep 25 16:45:39 2015 +0200
+++ b/src/cpu/x86/vm/x86_64.ad	Tue Sep 29 16:51:31 2015 +0200
@@ -6404,28 +6404,6 @@
 %}
 
 instruct shenandoahRB(rRegP dst, rRegP src, rFlagsReg cr) %{
-  predicate(n->bottom_type()->make_ptr()->ptr() != TypePtr::NotNull);
-  match(Set dst (ShenandoahReadBarrier src));
-  effect(DEF dst, USE src, KILL cr);
-  ins_cost(300); // XXX
-  format %{ "shenandoah_rb $dst,$src" %}
-  ins_encode %{
-    Register s = $src$$Register;
-    Register d = $dst$$Register;
-    Label is_null;
-    if (s != d) {
-      __ movptr(d, 0);
-    }
-    __ testptr(s, s);
-    __ jcc(Assembler::zero, is_null);
-    __ movptr(d, Address(s, -8));
-    __ bind(is_null);
-  %}
-  ins_pipe(ialu_reg_mem);
-%}
-
-instruct shenandoahRBNotNull(rRegP dst, rRegP src, rFlagsReg cr) %{
-  predicate(n->bottom_type()->make_ptr()->ptr() == TypePtr::NotNull);
   match(Set dst (ShenandoahReadBarrier src));
   effect(DEF dst, USE src);
   ins_cost(125); // XXX
@@ -6439,32 +6417,6 @@
 %}
 
 instruct shenandoahWB(rax_RegP dst, rdi_RegP src, rFlagsReg cr) %{
-  predicate(n->bottom_type()->make_ptr()->ptr() != TypePtr::NotNull);
-  match(Set dst (ShenandoahWriteBarrier src));
-  effect(DEF dst, USE_KILL src, KILL cr);
-  ins_cost(125); // XXX
-  format %{ "shenandoah_wb $dst,$src" %}
-  ins_encode %{
-    Label done;
-    Register s = $src$$Register;
-    Register d = $dst$$Register;
-    assert(s == rdi, "need rdi");
-    assert(d == rax, "result in rax");
-    __ movptr(d, 0);
-    __ testptr(s, s);
-    __ jcc(Assembler::zero, done);
-    Address evacuation_in_progress = Address(r15_thread, in_bytes(JavaThread::evacuation_in_progress_offset()));
-    __ cmpb(evacuation_in_progress, 0);
-    __ movptr(d, Address(s, -8));
-    __ jcc(Assembler::equal, done);
-    __ call(RuntimeAddress(CAST_FROM_FN_PTR(address, StubRoutines::x86::shenandoah_wb())));
-    __ bind(done);
-  %}
-  ins_pipe(pipe_slow);
-%}
-
-instruct shenandoahWBNotNull(rax_RegP dst, rdi_RegP src, rFlagsReg cr) %{
-  predicate(n->bottom_type()->make_ptr()->ptr() == TypePtr::NotNull);
   match(Set dst (ShenandoahWriteBarrier src));
   effect(DEF dst, USE_KILL src, KILL cr);
   ins_cost(300); // XXX
--- a/src/share/vm/opto/graphKit.cpp	Fri Sep 25 16:45:39 2015 +0200
+++ b/src/share/vm/opto/graphKit.cpp	Tue Sep 29 16:51:31 2015 +0200
@@ -4435,17 +4435,43 @@
     const TypePtr* adr_type = obj_type->is_ptr()->add_offset(-8);
     Node* mem = use_mem ? memory(adr_type) : immutable_memory();
 
-    if (! ShenandoahBarrierNode::needs_barrier(&_gvn, obj, mem)) {
+    if (! ShenandoahBarrierNode::needs_barrier(&_gvn, NULL, obj, mem)) {
       // We know it is null, no barrier needed.
       return obj;
     }
 
 
-    Node* ctrl = use_ctrl ? control() : NULL;
-    ShenandoahReadBarrierNode* rb = new ShenandoahReadBarrierNode(ctrl, mem, obj);
-    Node* n = _gvn.transform(rb);
-    record_for_igvn(n);
-    return n;
+    if (obj_type->meet(TypePtr::NULL_PTR) == obj_type->remove_speculative()) {
+
+      // We don't know if it's null or not. Need null-check.
+      enum { _not_null_path = 1, _null_path, PATH_LIMIT };
+      RegionNode* region = new RegionNode(PATH_LIMIT);
+      Node*       phi    = new PhiNode(region, obj_type);
+      Node* null_ctrl = top();
+      Node* not_null_obj = null_check_oop(obj, &null_ctrl);
+
+      region->init_req(_null_path, null_ctrl);
+      phi   ->init_req(_null_path, obj);
+
+      Node* ctrl = use_ctrl ? control() : NULL;
+      ShenandoahReadBarrierNode* rb = new ShenandoahReadBarrierNode(ctrl, mem, not_null_obj);
+      Node* n = _gvn.transform(rb);
+
+      region->init_req(_not_null_path, control());
+      phi   ->init_req(_not_null_path, n);
+
+      set_control(_gvn.transform(region));
+      record_for_igvn(region);
+      return _gvn.transform(phi);
+
+    } else {
+      // We know it is not null. Simple barrier is sufficient.
+      Node* ctrl = use_ctrl ? control() : NULL;
+      ShenandoahReadBarrierNode* rb = new ShenandoahReadBarrierNode(ctrl, mem, obj);
+      Node* n = _gvn.transform(rb);
+      record_for_igvn(n);
+      return n;
+    }
 
   } else {
     return obj;
@@ -4456,22 +4482,60 @@
 
   if (UseShenandoahGC && ShenandoahWriteBarrier) {
     
-    if (! ShenandoahBarrierNode::needs_barrier(&_gvn, obj, NULL)) {
+    if (! ShenandoahBarrierNode::needs_barrier(&_gvn, NULL, obj, NULL)) {
       return obj;
     }
     const Type* obj_type = obj->bottom_type();
     const TypePtr* adr_type = obj_type->is_ptr()->add_offset(-8);
-    // tty->print("wb for: "); obj->dump();
-    ShenandoahWriteBarrierNode* wb = new ShenandoahWriteBarrierNode(NULL, immutable_memory(), obj);
-    // tty->print("........"); wb->dump();
-    Node* n = _gvn.transform(wb);
-    if (wb == n) {
-      Node* proj = _gvn.transform(new ShenandoahWBMemProjNode(wb));
-      set_memory(proj, adr_type);
+    // tty->print_cr("memory at:");
+    // adr_type->dump();
+    // tty->print_cr("\n");
+    // memory(adr_type)->dump();
+    if (obj_type->meet(TypePtr::NULL_PTR) == obj_type->remove_speculative()) {
+      // We don't know if it's null or not. Need null-check.
+      enum { _not_null_path = 1, _null_path, PATH_LIMIT };
+      RegionNode* region = new RegionNode(PATH_LIMIT);
+      Node*       phi    = new PhiNode(region, obj_type);
+      Node*    memphi    = PhiNode::make(region, memory(adr_type), Type::MEMORY, C->alias_type(adr_type)->adr_type());
+
+      Node* prev_mem = memory(adr_type);
+      Node* null_ctrl = top();
+      Node* not_null_obj = null_check_oop(obj, &null_ctrl);
+
+      region->init_req(_null_path, null_ctrl);
+      phi   ->init_req(_null_path, null());
+      memphi->init_req(_null_path, prev_mem);
+
+      ShenandoahWriteBarrierNode* wb = new ShenandoahWriteBarrierNode(NULL, memory(adr_type), not_null_obj);
+      Node* n = _gvn.transform(wb);
+      if (n == wb) { // New barrier needs memory projection.
+	Node* proj = _gvn.transform(new ShenandoahWBMemProjNode(n));
+	set_memory(proj, adr_type);
+      }
+
+      region->init_req(_not_null_path, control());
+      phi   ->init_req(_not_null_path, n);
+      memphi->init_req(_not_null_path, memory(adr_type));
+
+      set_control(_gvn.transform(region));
+      record_for_igvn(region);
+      set_memory(_gvn.transform(memphi), adr_type);
+
+      Node* res_val = _gvn.transform(phi);
+      // replace_in_map(obj, res_val);
+      return res_val;
+    } else {
+      // We know it is not null. Simple barrier is sufficient.
+      ShenandoahWriteBarrierNode* wb = new ShenandoahWriteBarrierNode(NULL, memory(adr_type), obj);
+      Node* n = _gvn.transform(wb);
+      if (n == wb) {
+	Node* proj = _gvn.transform(new ShenandoahWBMemProjNode(wb));
+	set_memory(proj, adr_type);
+      }
+      // replace_in_map(obj, n);
+      record_for_igvn(n);
+      return n;
     }
-    record_for_igvn(n);
-    replace_in_map(obj, n);
-    return n;
 
   } else {
     return obj;
--- a/src/share/vm/opto/phaseX.cpp	Fri Sep 25 16:45:39 2015 +0200
+++ b/src/share/vm/opto/phaseX.cpp	Tue Sep 29 16:51:31 2015 +0200
@@ -1224,7 +1224,7 @@
     Node* con = makecon(t);     // Make a constant
     add_users_to_worklist(k);
     if (k->Opcode() == Op_ShenandoahWriteBarrier) {
-      con = k->as_ShenandoahBarrier()->kill_mem_proj(this, con);
+      assert(con->is_top(), "can only replace barrier with top");
     }
     subsume_node(k, con);       // Everybody using k now uses con
     return con;
@@ -1241,12 +1241,9 @@
 
   // Global Value Numbering
   i = hash_find_insert(k);      // Check for pre-existing node
-  if (i && (i != k) && k->Opcode() != Op_ShenandoahWriteBarrier) {
+  if (i && (i != k)) {
     // Return the pre-existing node if it isn't dead
     NOT_PRODUCT(set_progress();)
-    if (k->Opcode() == Op_ShenandoahWriteBarrier) {
-      i = k->as_ShenandoahBarrier()->kill_mem_proj(this, i);
-    }
     add_users_to_worklist(k);
     subsume_node(k, i);       // Everybody using k now uses i
     return i;
--- a/src/share/vm/opto/phaseX.hpp	Fri Sep 25 16:45:39 2015 +0200
+++ b/src/share/vm/opto/phaseX.hpp	Tue Sep 29 16:51:31 2015 +0200
@@ -190,6 +190,8 @@
   // _nodes is used in varying ways by subclasses, which define local accessors
 
 public:
+  virtual PhaseIterGVN *is_IterGVN() { return 0; }
+
   // Get a previously recorded type for the node n.
   // This type must already have been recorded.
   // If you want the type of a very new (untransformed) node,
--- a/src/share/vm/opto/shenandoahSupport.cpp	Fri Sep 25 16:45:39 2015 +0200
+++ b/src/share/vm/opto/shenandoahSupport.cpp	Tue Sep 29 16:51:31 2015 +0200
@@ -12,69 +12,21 @@
   }
 }
 
-Node* ShenandoahBarrierNode::kill_mem_proj(PhaseTransform* phase, Node* n) {
-  Node* mem_proj = find_out_with(Op_ShenandoahWBMemProj);
-  if (mem_proj != NULL) {
-    Node* in_mem = in(Memory);
-    for (DUIterator j = mem_proj->outs(); mem_proj->has_out(j); j++) {
-      Node* o = mem_proj->out(j);
-      phase->igvn_rehash_node_delayed(o);
-      o->replace_edge(mem_proj, in_mem);
-      --j;
-    }
-
-    phase->igvn_rehash_node_delayed(mem_proj);
-    mem_proj->set_req(0, Compile::current()->top());
-  }
-  assert(! has_out_with(Op_ShenandoahWBMemProj), "only one memory projection");
-  // tty->print("replace: "); dump();
-  // tty->print("with:    "); n->dump();
-#ifdef ASSERT
-  if (Opcode() == Op_ShenandoahWriteBarrier) {
-    // tty->print("killed mem-proj for: "); dump();
-    if (_idx == 7140) {
-      // tty->print_cr("break here");
-    }
-  }
-  if (n->is_ShenandoahBarrier()) {
-    // assert(n->as_ShenandoahBarrier()->num_mem_projs() <= 1, "0 or 1 memory proj");
-    n->as_ShenandoahBarrier()->check_invariants();
-  }
-#endif
-  return n;
+bool ShenandoahBarrierNode::needs_barrier(PhaseTransform* phase, Node* orig, Node* n, Node* rb_mem) {
+  Unique_Node_List visited;
+  return needs_barrier_impl(phase, orig, n, rb_mem, visited);
 }
 
-bool ShenandoahBarrierNode::phi_needs_barrier(PhaseTransform* phase, Node* phi, Node* rb_mem, GrowableArray<Node*>* phistack) {
-  assert(phi->is_Phi(), "expect phi");
-  if (phistack->contains(phi)) {
-    // If we came back to the same phi, then it's a loop in which we don't need a barrier.
-    // Unless we need on another path.
-    return false;
+bool ShenandoahBarrierNode::needs_barrier_impl(PhaseTransform* phase, Node* orig, Node* n, Node* rb_mem, Unique_Node_List &visited) {
+
+  if (visited.member(n)) {
+    return false; // Been there.
   }
-  bool need_barrier = false;
-  phistack->push(phi);
-  for (uint i = 1; i < phi->req() && ! need_barrier; i++) {
-    Node* input = phi->in(i);
-    if (input == NULL) {
-      need_barrier = true; // Phi not complete yet?
-    } else if (needs_barrier_impl(phase, input, rb_mem, phistack)) {
-      need_barrier = true;
-    }
-  }
-  phistack->pop();
-  return need_barrier;
-}
+  visited.push(n);
 
-bool ShenandoahBarrierNode::needs_barrier(PhaseTransform* phase, Node* n, Node* rb_mem) {
-  Arena* a = phase->arena();
-  GrowableArray<Node*>* phistack = new (a) GrowableArray<Node*>(a, 4, 0, NULL);
-  return needs_barrier_impl(phase, n, rb_mem, phistack);
-}
-
-bool ShenandoahBarrierNode::needs_barrier_impl(PhaseTransform* phase, Node* n, Node* rb_mem, GrowableArray<Node*>* phistack) {
   if (n->is_Allocate()) {
     // tty->print_cr("killed barrier for newly allocated object");
-    return false;;
+    return false;
   }
 
   if (n->is_CallJava()) {
@@ -92,20 +44,29 @@
   }
 
   if (n->is_CheckCastPP() || n->is_ConstraintCast()) {
-    return needs_barrier_impl(phase, n->in(1), rb_mem, phistack);
+    return needs_barrier_impl(phase, orig, n->in(1), rb_mem, visited);
   }
   if (n->is_Parm()) {
     return true;
   }
   if (n->is_Proj()) {
-    return needs_barrier_impl(phase, n->in(0), rb_mem, phistack);
+    return needs_barrier_impl(phase, orig, n->in(0), rb_mem, visited);
   }
   if (n->is_Phi()) {
-    return phi_needs_barrier(phase, n, rb_mem, phistack);
+    bool need_barrier = false;
+    for (uint i = 1; i < n->req() && ! need_barrier; i++) {
+      Node* input = n->in(i);
+      if (input == NULL) {
+	need_barrier = true; // Phi not complete yet?
+      } else if (needs_barrier_impl(phase, orig, input, rb_mem, visited)) {
+	need_barrier = true;
+      }
+    }
+    return need_barrier;
   }
   if (n->is_CMove()) {
-    return needs_barrier_impl(phase, n->in(CMoveNode::IfFalse), rb_mem, phistack) ||
-           needs_barrier_impl(phase, n->in(CMoveNode::IfTrue ), rb_mem, phistack);
+    return needs_barrier_impl(phase, orig, n->in(CMoveNode::IfFalse), rb_mem, visited) ||
+           needs_barrier_impl(phase, orig, n->in(CMoveNode::IfTrue ), rb_mem, visited);
   }
   if (n->Opcode() == Op_CreateEx) {
     return true;
@@ -167,17 +128,33 @@
 
 Node* ShenandoahReadBarrierNode::Ideal(PhaseGVN *phase, bool can_reshape) {
 
-  if (true || ! can_reshape) {
+  if (! can_reshape) {
     return NULL;
   }
 
+  // if (true) return NULL;
+
+  if (in(Memory) == phase->C->immutable_memory()) return NULL;
+
+  // If memory input is a MergeMem, take the appropriate slice out of it.
+  Node* mem_in = in(Memory);
+  if (mem_in->isa_MergeMem()) {
+    const TypePtr* adr_type = bottom_type()->is_ptr()->add_offset(-8);
+    uint alias_idx = phase->C->get_alias_index(adr_type);
+    mem_in = mem_in->as_MergeMem()->memory_at(alias_idx);
+    set_req(Memory, mem_in);
+    return this;
+  }
+
   Node* input = in(Memory);
   if (input->Opcode() == Op_ShenandoahWBMemProj) {
     input = input->in(0);
+    if (input->is_top()) return NULL; // Dead path.
     assert(input->Opcode() == Op_ShenandoahWriteBarrier, "expect write barrier");
     const Type* in_type = phase->type(input);
     const Type* this_type = phase->type(this);
     if (is_independent(in_type, this_type)) {
+      phase->igvn_rehash_node_delayed(input);
       set_req(Memory, input->in(Memory));
       return this;
     }
@@ -185,45 +162,59 @@
   return NULL;
 }
 
-uint ShenandoahWriteBarrierNode::count_rb_users(Node* n, GrowableArray<Node*>* phistack) {
-  uint count = 0;
-  for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax; j++) {
+bool ShenandoahBarrierNode::has_barrier_users(Node* n, Unique_Node_List &visited) {
+  if (visited.member(n)) {
+    return false;
+  }
+  visited.push(n);
+
+  bool has_users = false;
+  for (DUIterator_Fast jmax, j = n->fast_outs(jmax); j < jmax && ! has_users; j++) {
     Node* o = n->fast_out(j);
     if (o->Opcode() == Op_ShenandoahReadBarrier ||
 	o->Opcode() == Op_ShenandoahWriteBarrier) {
       // tty->print("counting barrier"); o->dump();
-      count++;
+      has_users = true;
     } else if (o->isa_Phi()) {
-      if (! phistack->contains(o)) {
-	phistack->push(o);
-	count += count_rb_users(o, phistack);
-	phistack->pop();
-      }
+      has_users = has_barrier_users(o, visited);
+    } else if (o->Opcode() == Op_MergeMem) {
+      // Not a user. ?
     } else {
-      // tty->print("not counting: "); o->dump();
+      // tty->print_cr("unknown user"); o->dump();
+      ShouldNotReachHere();
     }
   }
-  return count;
+  return has_users;
 }
 
 Node* ShenandoahWriteBarrierNode::Ideal(PhaseGVN *phase, bool can_reshape) {
 
-  if (true || ! can_reshape) return NULL;
+  if (! can_reshape) return NULL;
 
   if (in(Memory) == phase->C->immutable_memory()) return NULL;
 
+  Node* mem_in = in(Memory);
+  if (mem_in->isa_MergeMem()) {
+    const TypePtr* adr_type = bottom_type()->is_ptr()->add_offset(-8);
+    uint alias_idx = phase->C->get_alias_index(adr_type);
+    mem_in = mem_in->as_MergeMem()->memory_at(alias_idx);
+    set_req(Memory, mem_in);
+    return this;
+  }
+
   Node* mem_proj = find_out_with(Op_ShenandoahWBMemProj);
   if (mem_proj == NULL) {
-    // tty->print_cr("no mem proj: kill input mem");
+    // tty->print_cr("no mem proj: kill input mem"); dump();
     set_req(Memory, phase->C->immutable_memory());
     return this;
   }
 
-  Arena* a = phase->arena();
-  GrowableArray<Node*>* phistack = new (a) GrowableArray<Node*>(a, 4, 0, NULL);
-  uint num_users = count_rb_users(mem_proj, phistack);
-  if (num_users == 0) {
-    // tty->print_cr("no users of mem projection. kill input mem");
+  if (true) return NULL;
+
+  Unique_Node_List visited;
+  if (! has_barrier_users(mem_proj, visited)) {
+    // tty->print_cr("no users of mem projection. kill input mem"); dump();
+    phase->igvn_rehash_node_delayed(in(Memory));
     set_req(Memory, phase->C->immutable_memory());
     
     // tty->print_cr("reshaped wb: ");
@@ -233,35 +224,137 @@
   // tty->print_cr("leave mem proj alone");
   return NULL;
 }
-
-Node* ShenandoahBarrierNode::Identity(PhaseTransform* phase) {
+/*
+bool ShenandoahBarrierNode::dominates_control_impl(PhaseTransform* phase,
+						   Node* c1,
+						   Node* c2,
+						   Node* current,
+						   Unique_Node_List & visited) {
+  if (current == NULL) {
+    return true;
+  } else if (visited.member(current)) {
+    return true;
+  }
+  visited.push(current);
 
-#ifdef ASSERT
-  // check_invariants();
-#endif
-
-  if (Opcode() == Op_ShenandoahWriteBarrier) return this;
+  current->dump();
+  ShouldNotReachHere();
+}
+*/
 
-  Node* n = in(ValueIn);
-  Node* rb_mem = Opcode() == Op_ShenandoahReadBarrier ? in(Memory) : NULL;
-  if (! needs_barrier(phase, n, rb_mem)) {
-    if (Opcode() == Op_ShenandoahWriteBarrier) {
-      if (n->Opcode() == Op_ShenandoahWriteBarrier) {
-	return n;
-      } else {
+bool ShenandoahBarrierNode::dominates_control(PhaseTransform* phase,
+					      Node* c1,
+					      Node* c2) {
+  if (c1 == c2) {
+    return true;
+  }
+  if (c1 == NULL) {
+    return true;
+  }
 #ifdef ASSERT
-	tty->print("could not elide wb: "); dump();
-	tty->print("with..............: "); n->dump();
+  tty->print("c1: "); c1->dump(2);
+  tty->print("c2: "); c2->dump(2);
 #endif
-	return this;
-      }
-    } else {
-      return n;
-    }
+  ShouldNotReachHere();
+  return false;
+}
+
+bool ShenandoahBarrierNode::dominates_memory_impl(PhaseTransform* phase,
+						  Node* b1,
+						  Node* b2,
+						  Node* current,
+						  Unique_Node_List &visited) {
+  /*
+  tty->print_cr("phistack:");
+  for (int x = 0; x < phistack->length(); x++) {
+    tty->print("-->");
+    phistack->at(x)->dump();
+  }
+  */
+  if (current == NULL) {
+    return false;
+  } else if (visited.member(current)) {
+    // We have already seen it.
+    return true;
   }
 
-  if (Opcode() == Op_ShenandoahWriteBarrier) return this;
+  visited.push(current);
 
+  if (current == b1) {
+    // tty->print_cr("current == b1: "); current->dump();
+    return true;
+  } else if (current == b2) {
+    // tty->print_cr("current == b2: "); current->dump();
+    return false;
+  } else if (current == phase->C->immutable_memory()) {
+    // tty->print_cr("current == immutable_memory: "); current->dump();
+    return false;
+  } else if (current->isa_Phi()) {
+    // tty->print_cr("current == phi: "); current->dump();
+    bool dominates = true;
+    for (uint i = 1; i < current->req() && dominates == true; i++) {
+      Node* in = current->in(i);
+      dominates = dominates && dominates_memory_impl(phase, b1, b2, in, visited);
+    }
+    return dominates;
+  } else if (current->Opcode() == Op_ShenandoahWriteBarrier) {
+    // tty->print_cr("current == wb: "); current->dump();
+    // Follow through memory input.
+    Node* in = current->in(Memory);
+    return dominates_memory_impl(phase, b1, b2, in, visited);
+  } else if (current->Opcode() == Op_ShenandoahWBMemProj) {
+    // tty->print_cr("current == wb-memproj: "); current->dump();
+    // Follow through memory input.
+    Node* in = current->in(0);
+    return dominates_memory_impl(phase, b1, b2, in, visited);
+  } else if (current->is_top()) {
+    return false; // Dead path
+  } else if (current->is_Proj()) {
+    // tty->print_cr("current == proj: "); current->dump();
+    return dominates_memory_impl(phase, b1, b2, current->in(0), visited);
+  } else if (current->is_Call()) {
+    // tty->print_cr("current == call: "); current->dump();
+    return dominates_memory_impl(phase, b1, b2, current->in(TypeFunc::Memory), visited);
+  } else if (current->is_MemBar()) {
+    // tty->print_cr("current == membar: "); current->dump();
+    return dominates_memory_impl(phase, b1, b2, current->in(TypeFunc::Memory), visited);
+  } else if (current->is_MergeMem()) {
+    // tty->print_cr("current == mergemem: "); current->dump();
+    const TypePtr* adr_type = phase->type(b2)->is_ptr()->add_offset(-8);
+    uint alias_idx = phase->C->get_alias_index(adr_type);
+    Node* mem_in = current->as_MergeMem()->memory_at(alias_idx);
+    return dominates_memory_impl(phase, b1, b2, current->in(TypeFunc::Memory), visited);
+  } else {
+    // tty->print_cr("what else can we see here:");
+#ifdef ASSERT
+    current->dump();
+#endif
+    ShouldNotReachHere();
+    return false;
+  }
+}
+
+/**
+ * Determines if b1 dominates b2 through memory inputs. It returns true if:
+ * - b1 can be reached by following each branch in b2's memory input (through phis, etc)
+ * - or we get back to b2 (i.e. through a loop) without seeing b1
+ * In all other cases, (in particular, if we reach immutable_memory without having seen b1)
+ * we return false.
+ */
+bool ShenandoahBarrierNode::dominates_memory(PhaseTransform* phase, Node* b1, Node* b2) {
+  Unique_Node_List visited;
+  return dominates_memory_impl(phase, b1->in(Memory), b2, b2->in(Memory), visited);
+}
+
+Node* ShenandoahBarrierNode::Identity_impl(PhaseTransform* phase) {
+  Node* n = in(ValueIn);
+
+  Node* rb_mem = Opcode() == Op_ShenandoahReadBarrier ? in(Memory) : NULL;
+  if (! needs_barrier(phase, this, n, rb_mem)) {
+    return n;
+  }
+
+  // tty->print_cr("find sibling for: "); dump(2);
   // Try to find a write barrier sibling with identical inputs that we can fold into.
   for (DUIterator i = n->outs(); n->has_out(i); i++) {
     Node* sibling = n->out(i);
@@ -276,58 +369,52 @@
     if (sibling->Opcode() != Op_ShenandoahWriteBarrier) {
       continue;
     }
+    /*
     if (sibling->outcnt() == 0) {
       // Some dead node.
       continue;
     }
-#ifdef ASSERT
-    sibling->as_ShenandoahBarrier()->check_invariants();
-#endif
-    // Match any free-floating write barrier.
-    if (sibling->Opcode() == Op_ShenandoahWriteBarrier &&
-	sibling->in(Control) == NULL &&
-	sibling->in(Memory)  == Compile::current()->immutable_memory() &&
-	sibling->in(ValueIn)  == in(ValueIn)) {
-      return kill_mem_proj(phase, sibling);
-    }
+    */
+    assert(sibling->in(ValueIn) == in(ValueIn), "sanity");
+    assert(sibling->Opcode() == Op_ShenandoahWriteBarrier, "sanity");
+    // tty->print_cr("candidate: "); sibling->dump();
+
+    if (dominates_control(phase, sibling->in(Control), in(Control)) &&
+    	dominates_memory(phase, sibling, this)) {
 
-    // Identical barriers. Match both read&write to write barriers. Read-only matches in
-    // ShenandoahReadBarrier::Identity.
-    if (sibling->Opcode() == Op_ShenandoahWriteBarrier &&
-	sibling->in(Control) == in(Control) &&
-	sibling->in(Memory)  == in(Memory) &&
-	sibling->in(ValueIn)  == in(ValueIn)) {
-      return kill_mem_proj(phase, sibling);
+      /*
+      tty->print_cr("matched barrier:");
+      sibling->dump();
+      tty->print_cr("for: ");
+      dump();
+      */
+      return sibling;
     }
 
-    if (sibling->Opcode() == Op_ShenandoahWriteBarrier) {
-      if (sibling->in(ValueIn) == in(ValueIn)) {
-    	// Hot candidate. Check control and memory.
-    	if (sibling->in(Control) == in(Control)) {
-    	  // This can only work if our memory feeds from the write barrier we want
-    	  // to fold into.
-    	  Node* in_mem = in(Memory);
-    	  if (in_mem->Opcode() == Op_ShenandoahWBMemProj) {
-    	    if (in_mem->in(0) == sibling) {
-    	      // Gotcha!
-    	      // tty->print("found sibling: ");
-    	      // sibling->dump();
-    	      // tty->print("to replace this node: ");
-    	      // dump();
-	      return kill_mem_proj(phase, sibling);
-    	    }
-    	  }
-    	}
-    	/*
-    	  tty->print_cr("couldn't match candidate:");
-    	  sibling->dump(2);
-    	  tty->print_cr("to replace this:");
-    	  dump(2);
-    	*/
-      }
+    /*
+    tty->print_cr("couldn't match candidate:");
+    sibling->dump(2);
+    */
+  }
+  /*
+  tty->print_cr("couldn't match barrier to any:");
+  dump();
+  */
+  return this;
+}
+
+Node* ShenandoahBarrierNode::Identity(PhaseTransform* phase) {
+
+  Node* replacement = Identity_impl(phase);
+  if (replacement != this) {
+    // If we have a memory projection, we first need to make it go away.
+    Node* mem_proj = find_out_with(Op_ShenandoahWBMemProj);
+    if (mem_proj != NULL) {
+      phase->igvn_rehash_node_delayed(mem_proj);
+      return this;
     }
   }
-  return this;
+  return replacement;
 }
 
 Node* ShenandoahReadBarrierNode::Identity(PhaseTransform* phase) {
@@ -376,7 +463,7 @@
 
   Node* input = in(ValueIn);
   const Type* type = phase->type(input);
-  return type->remove_speculative();
+  return type;
 }
 
 #ifdef ASSERT
@@ -402,3 +489,32 @@
   // }
 }
 #endif
+
+Node* ShenandoahWBMemProjNode::Identity(PhaseTransform* phase) {
+
+  Node* wb = in(0);
+  if (wb->is_top()) return phase->C->top(); // Dead path.
+
+  assert(wb->Opcode() == Op_ShenandoahWriteBarrier, "expect write barrier");
+  if (wb->as_ShenandoahBarrier()->Identity_impl(phase) != wb) {
+    // If the parent write barrier would go away, make this mem proj go away first.
+    // Poke parent to give it a chance to go away too.
+    phase->igvn_rehash_node_delayed(wb);
+    return wb->in(ShenandoahBarrierNode::Memory);
+  }
+
+  // We can't do the below unless the graph is fully constructed.
+  if (! phase->is_IterGVN()) {
+    return this;
+  }
+
+  // If the mem projection has no barrier users, it's not needed anymore.
+  Unique_Node_List visited;
+  if (! ShenandoahWriteBarrierNode::has_barrier_users(this, visited)) {
+    // tty->print_cr("mem proj has no users. kill"); dump();
+    phase->igvn_rehash_node_delayed(wb);
+    return wb->in(ShenandoahBarrierNode::Memory);
+  }
+
+  return this;
+}
--- a/src/share/vm/opto/shenandoahSupport.hpp	Fri Sep 25 16:45:39 2015 +0200
+++ b/src/share/vm/opto/shenandoahSupport.hpp	Tue Sep 29 16:51:31 2015 +0200
@@ -13,8 +13,6 @@
 class ShenandoahBarrierNode : public TypeNode {
 public:
 
-  Node* kill_mem_proj(PhaseTransform* phase, Node* n);
-
 public:
   enum { Control,
          Memory,
@@ -48,6 +46,8 @@
   virtual uint size_of() const { return sizeof(*this); }
 
   virtual Node* Identity(PhaseTransform* phase);
+  Node* Identity_impl(PhaseTransform* phase);
+
   virtual const Type* Value(PhaseTransform* phase) const;
   virtual bool depends_only_on_test() const {
     return true;
@@ -57,11 +57,17 @@
   uint num_mem_projs();
 #endif
 
-  static bool needs_barrier(PhaseTransform* phase, Node* n, Node* rb_mem);
+  static bool needs_barrier(PhaseTransform* phase, Node* orig, Node* n, Node* rb_mem);
+
+  static bool has_barrier_users(Node* n, Unique_Node_List &visited);
 
 private:
-  static bool phi_needs_barrier(PhaseTransform* phase, Node* phi, Node* rb_mem, GrowableArray<Node*>* phistack);
-  static bool needs_barrier_impl(PhaseTransform* phase, Node* n, Node* rb_mem, GrowableArray<Node*>* phistack);
+  static bool needs_barrier_impl(PhaseTransform* phase, Node* orig, Node* n, Node* rb_mem, Unique_Node_List &visited);
+
+
+  bool dominates_control(PhaseTransform* phase, Node* c1, Node* c2);
+  bool dominates_memory(PhaseTransform* phase, Node* b1, Node* b2);
+  bool dominates_memory_impl(PhaseTransform* phase, Node* b1, Node* b2, Node* current, Unique_Node_List &visisted);
 };
 
 class ShenandoahReadBarrierNode : public ShenandoahBarrierNode {
@@ -73,6 +79,7 @@
   virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
   virtual Node* Identity(PhaseTransform* phase);
   virtual int Opcode() const;
+
 private:
   bool is_independent(const Type* in_type, const Type* this_type) const;
 
@@ -92,8 +99,6 @@
   //   if (i == MemNode::Memory) { assert(n == Compiler::current()->immutable_memory(), "set only immutable mem on wb"); }
   //   Node::set_req(i, n);
   // }
-private:
-  static uint count_rb_users(Node* n, GrowableArray<Node*>* phistack);
 };
 
 class ShenandoahWBMemProjNode : public ProjNode {
@@ -105,6 +110,8 @@
     in(0)->as_ShenandoahBarrier()->check_invariants();
 #endif
   }
+  virtual Node* Identity(PhaseTransform* phase);
+
   virtual int Opcode() const;
   virtual bool      is_CFG() const  { return false; }
   virtual const Type *bottom_type() const {return Type::MEMORY;}