8076188: Optimize arraycopy out for non escaping destination
authorroland
Tue, 12 May 2015 10:27:50 +0200
changeset 30629 b6e5ad2f18d5
parent 30628 3c15b4a3bf4d
child 30630 0c9eb2d41b48
child 30631 483c444f36f4
8076188: Optimize arraycopy out for non escaping destination Summary: if the destination of an arraycopy is non escaping, the arraycopy may be optimized out Reviewed-by: kvn, vlivanov
hotspot/src/share/vm/opto/arraycopynode.cpp
hotspot/src/share/vm/opto/arraycopynode.hpp
hotspot/src/share/vm/opto/callnode.cpp
hotspot/src/share/vm/opto/callnode.hpp
hotspot/src/share/vm/opto/escape.cpp
hotspot/src/share/vm/opto/escape.hpp
hotspot/src/share/vm/opto/gcm.cpp
hotspot/src/share/vm/opto/ifnode.cpp
hotspot/src/share/vm/opto/macro.cpp
hotspot/src/share/vm/opto/macroArrayCopy.cpp
hotspot/src/share/vm/opto/memnode.cpp
hotspot/src/share/vm/opto/memnode.hpp
hotspot/test/compiler/arraycopy/TestArrayCopyAsLoadsStores.java
hotspot/test/compiler/arraycopy/TestArrayCopyUtils.java
hotspot/test/compiler/arraycopy/TestEliminateArrayCopy.java
hotspot/test/compiler/arraycopy/TestInstanceCloneAsLoadsStores.java
hotspot/test/compiler/arraycopy/TestInstanceCloneUtils.java
--- a/hotspot/src/share/vm/opto/arraycopynode.cpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/arraycopynode.cpp	Tue May 12 10:27:50 2015 +0200
@@ -30,7 +30,9 @@
   : CallNode(arraycopy_type(), NULL, TypeRawPtr::BOTTOM),
     _alloc_tightly_coupled(alloc_tightly_coupled),
     _kind(None),
-    _arguments_validated(false) {
+    _arguments_validated(false),
+    _src_type(TypeOopPtr::BOTTOM),
+    _dest_type(TypeOopPtr::BOTTOM) {
   init_class_id(Class_ArrayCopy);
   init_flags(Flag_is_macro);
   C->add_macro_node(this);
@@ -595,3 +597,17 @@
 
   return mem;
 }
+
+bool ArrayCopyNode::may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase) {
+  const TypeOopPtr* dest_t = phase->type(in(ArrayCopyNode::Dest))->is_oopptr();
+  assert(!dest_t->is_known_instance() || _dest_type->is_known_instance(), "result of EA not recorded");
+  const TypeOopPtr* src_t = phase->type(in(ArrayCopyNode::Src))->is_oopptr();
+  assert(!src_t->is_known_instance() || _src_type->is_known_instance(), "result of EA not recorded");
+
+  if (_dest_type != TypeOopPtr::BOTTOM || t_oop->is_known_instance()) {
+    assert(_dest_type == TypeOopPtr::BOTTOM || _dest_type->is_known_instance(), "result of EA is known instance");
+    return t_oop->instance_id() == _dest_type->instance_id();
+  }
+
+  return CallNode::may_modify_arraycopy_helper(dest_t, t_oop, phase);
+}
--- a/hotspot/src/share/vm/opto/arraycopynode.hpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/arraycopynode.hpp	Tue May 12 10:27:50 2015 +0200
@@ -124,6 +124,10 @@
     ParmLimit
   };
 
+  // Results from escape analysis for non escaping inputs
+  const TypeOopPtr* _src_type;
+  const TypeOopPtr* _dest_type;
+
   static ArrayCopyNode* make(GraphKit* kit, bool may_throw,
                              Node* src, Node* src_offset,
                              Node* dest,  Node* dest_offset,
@@ -154,11 +158,12 @@
   virtual bool guaranteed_safepoint()  { return false; }
   virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
 
+  virtual bool may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase);
+
   bool is_alloc_tightly_coupled() const { return _alloc_tightly_coupled; }
 
 #ifndef PRODUCT
   virtual void dump_spec(outputStream *st) const;
 #endif
 };
-
 #endif // SHARE_VM_OPTO_ARRAYCOPYNODE_HPP
--- a/hotspot/src/share/vm/opto/callnode.cpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/callnode.cpp	Tue May 12 10:27:50 2015 +0200
@@ -797,11 +797,12 @@
       }
       cast = use;
     } else if (!use->is_Initialize() &&
-               !use->is_AddP()) {
+               !use->is_AddP() &&
+               use->Opcode() != Op_MemBarStoreStore) {
       // Expected uses are restricted to a CheckCastPP, an Initialize
-      // node, and AddP nodes. If we encounter any other use (a Phi
-      // node can be seen in rare cases) return this to prevent
-      // incorrect optimizations.
+      // node, a MemBarStoreStore (clone) and AddP nodes. If we
+      // encounter any other use (a Phi node can be seen in rare
+      // cases) return this to prevent incorrect optimizations.
       return this;
     }
   }
@@ -1006,6 +1007,14 @@
 
 
 //=============================================================================
+bool CallLeafNode::is_call_to_arraycopystub() const {
+  if (_name != NULL && strstr(_name, "arraycopy") != 0) {
+    return true;
+  }
+  return false;
+}
+
+
 #ifndef PRODUCT
 void CallLeafNode::dump_spec(outputStream *st) const {
   st->print("# ");
@@ -1875,3 +1884,72 @@
     log->tail(tag);
   }
 }
+
+bool CallNode::may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr *t_oop, PhaseTransform *phase) {
+  if (dest_t->is_known_instance() && t_oop->is_known_instance()) {
+    return dest_t->instance_id() == t_oop->instance_id();
+  }
+
+  if (dest_t->isa_instptr() && !dest_t->klass()->equals(phase->C->env()->Object_klass())) {
+    // clone
+    if (t_oop->isa_aryptr()) {
+      return false;
+    }
+    if (!t_oop->isa_instptr()) {
+      return true;
+    }
+    if (dest_t->klass()->is_subtype_of(t_oop->klass()) || t_oop->klass()->is_subtype_of(dest_t->klass())) {
+      return true;
+    }
+    // unrelated
+    return false;
+  }
+
+  if (dest_t->isa_aryptr()) {
+    // arraycopy or array clone
+    if (t_oop->isa_instptr()) {
+      return false;
+    }
+    if (!t_oop->isa_aryptr()) {
+      return true;
+    }
+
+    const Type* elem = dest_t->is_aryptr()->elem();
+    if (elem == Type::BOTTOM) {
+      // An array but we don't know what elements are
+      return true;
+    }
+
+    dest_t = dest_t->add_offset(Type::OffsetBot)->is_oopptr();
+    uint dest_alias = phase->C->get_alias_index(dest_t);
+    uint t_oop_alias = phase->C->get_alias_index(t_oop);
+
+    return dest_alias == t_oop_alias;
+  }
+
+  return true;
+}
+
+bool CallLeafNode::may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase) {
+  if (is_call_to_arraycopystub()) {
+    const TypeTuple* args = _tf->domain();
+    Node* dest = NULL;
+    // Stubs that can be called once an ArrayCopyNode is expanded have
+    // different signatures. Look for the second pointer argument,
+    // that is the destination of the copy.
+    for (uint i = TypeFunc::Parms, j = 0; i < args->cnt(); i++) {
+      if (args->field_at(i)->isa_ptr()) {
+        j++;
+        if (j == 2) {
+          dest = in(i);
+          break;
+        }
+      }
+    }
+    if (may_modify_arraycopy_helper(phase->type(dest)->is_oopptr(), t_oop, phase)) {
+      return true;
+    }
+    return false;
+  }
+  return CallNode::may_modify(t_oop, phase);
+}
--- a/hotspot/src/share/vm/opto/callnode.hpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/callnode.hpp	Tue May 12 10:27:50 2015 +0200
@@ -556,6 +556,10 @@
 // contain the functionality of a full scope chain of debug nodes.
 class CallNode : public SafePointNode {
   friend class VMStructs;
+
+protected:
+  bool may_modify_arraycopy_helper(const TypeOopPtr* dest_t, const TypeOopPtr *t_oop, PhaseTransform *phase);
+
 public:
   const TypeFunc *_tf;        // Function type
   address      _entry_point;  // Address of method being called
@@ -781,6 +785,8 @@
 #ifndef PRODUCT
   virtual void  dump_spec(outputStream *st) const;
 #endif
+  bool is_call_to_arraycopystub() const;
+  virtual bool may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase);
 };
 
 //------------------------------CallLeafNoFPNode-------------------------------
@@ -1082,5 +1088,4 @@
   JVMState* dbg_jvms() const { return NULL; }
 #endif
 };
-
 #endif // SHARE_VM_OPTO_CALLNODE_HPP
--- a/hotspot/src/share/vm/opto/escape.cpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/escape.cpp	Tue May 12 10:27:50 2015 +0200
@@ -28,6 +28,7 @@
 #include "libadt/vectset.hpp"
 #include "memory/allocation.hpp"
 #include "opto/c2compiler.hpp"
+#include "opto/arraycopynode.hpp"
 #include "opto/callnode.hpp"
 #include "opto/cfgnode.hpp"
 #include "opto/compile.hpp"
@@ -113,6 +114,7 @@
   GrowableArray<Node*> alloc_worklist;
   GrowableArray<Node*> ptr_cmp_worklist;
   GrowableArray<Node*> storestore_worklist;
+  GrowableArray<ArrayCopyNode*> arraycopy_worklist;
   GrowableArray<PointsToNode*>   ptnodes_worklist;
   GrowableArray<JavaObjectNode*> java_objects_worklist;
   GrowableArray<JavaObjectNode*> non_escaped_worklist;
@@ -173,6 +175,10 @@
       // Collect address nodes for graph verification.
       addp_worklist.append(n);
 #endif
+    } else if (n->is_ArrayCopy()) {
+      // Keep a list of ArrayCopy nodes so if one of its input is non
+      // escaping, we can record a unique type
+      arraycopy_worklist.append(n->as_ArrayCopy());
     }
     for (DUIterator_Fast imax, i = n->fast_outs(imax); i < imax; i++) {
       Node* m = n->fast_out(i);   // Get user
@@ -289,7 +295,7 @@
       C->AliasLevel() >= 3 && EliminateAllocations) {
     // Now use the escape information to create unique types for
     // scalar replaceable objects.
-    split_unique_types(alloc_worklist);
+    split_unique_types(alloc_worklist, arraycopy_worklist);
     if (C->failing())  return false;
     C->print_method(PHASE_AFTER_EA, 2);
 
@@ -333,7 +339,7 @@
 // Populate Connection Graph with PointsTo nodes and create simple
 // connection graph edges.
 void ConnectionGraph::add_node_to_connection_graph(Node *n, Unique_Node_List *delayed_worklist) {
-  assert(!_verify, "this method sould not be called for verification");
+  assert(!_verify, "this method should not be called for verification");
   PhaseGVN* igvn = _igvn;
   uint n_idx = n->_idx;
   PointsToNode* n_ptn = ptnode_adr(n_idx);
@@ -901,8 +907,7 @@
       // are still a few direct calls to the copy subroutines (See
       // PhaseStringOpts::copy_string())
       is_arraycopy = (call->Opcode() == Op_ArrayCopy) ||
-        (call->as_CallLeaf()->_name != NULL &&
-         strstr(call->as_CallLeaf()->_name, "arraycopy") != 0);
+        call->as_CallLeaf()->is_call_to_arraycopystub();
       // fall through
     case Op_CallLeaf: {
       // Stub calls, objects do not escape but they are not scale replaceable.
@@ -980,7 +985,17 @@
               !arg_is_arraycopy_dest) {
             continue;
           }
-          set_escape_state(arg_ptn, PointsToNode::ArgEscape);
+          PointsToNode::EscapeState es = PointsToNode::ArgEscape;
+          if (call->is_ArrayCopy()) {
+            ArrayCopyNode* ac = call->as_ArrayCopy();
+            if (ac->is_clonebasic() ||
+                ac->is_arraycopy_validated() ||
+                ac->is_copyof_validated() ||
+                ac->is_copyofrange_validated()) {
+              es = PointsToNode::NoEscape;
+            }
+          }
+          set_escape_state(arg_ptn, es);
           if (arg_is_arraycopy_dest) {
             Node* src = call->in(TypeFunc::Parms);
             if (src->is_AddP()) {
@@ -994,7 +1009,7 @@
               // as base since objects escape states are not related.
               // Only escape state of destination object's fields affects
               // escape state of fields in source object.
-              add_arraycopy(call, PointsToNode::ArgEscape, src_ptn, arg_ptn);
+              add_arraycopy(call, es, src_ptn, arg_ptn);
             }
           }
         }
@@ -1272,12 +1287,12 @@
         if ((e->escape_state() < field_es) &&
             e->is_Field() && ptn->is_JavaObject() &&
             e->as_Field()->is_oop()) {
-          // Change escape state of referenced fileds.
+          // Change escape state of referenced fields.
           set_escape_state(e, field_es);
-          es_changed = true;;
+          es_changed = true;
         } else if (e->escape_state() < es) {
           set_escape_state(e, es);
-          es_changed = true;;
+          es_changed = true;
         }
         if (es_changed) {
           escape_worklist.push(e);
@@ -1389,7 +1404,7 @@
           for (UseIterator k(arycp); k.has_next(); k.next()) {
             PointsToNode* abase = k.get();
             if (abase->arraycopy_dst() && abase != base) {
-              // Look for the same arracopy reference.
+              // Look for the same arraycopy reference.
               add_fields_to_worklist(field, abase);
             }
           }
@@ -1469,12 +1484,13 @@
   int new_edges = 0;
   Node* alloc = pta->ideal_node();
   if (init_val == phantom_obj) {
-    // Do nothing for Allocate nodes since its fields values are "known".
-    if (alloc->is_Allocate())
+    // Do nothing for Allocate nodes since its fields values are
+    // "known" unless they are initialized by arraycopy/clone.
+    if (alloc->is_Allocate() && !pta->arraycopy_dst())
       return 0;
-    assert(alloc->as_CallStaticJava(), "sanity");
+    assert(pta->arraycopy_dst() || alloc->as_CallStaticJava(), "sanity");
 #ifdef ASSERT
-    if (alloc->as_CallStaticJava()->method() == NULL) {
+    if (!pta->arraycopy_dst() && alloc->as_CallStaticJava()->method() == NULL) {
       const char* name = alloc->as_CallStaticJava()->_name;
       assert(strncmp(name, "_multianewarray", 15) == 0, "sanity");
     }
@@ -1623,11 +1639,12 @@
   //
   for (UseIterator i(jobj); i.has_next(); i.next()) {
     PointsToNode* use = i.get();
-    assert(!use->is_Arraycopy(), "sanity");
+    if (use->is_Arraycopy()) {
+      continue;
+    }
     if (use->is_Field()) {
       FieldNode* field = use->as_Field();
-      assert(field->is_oop() && field->scalar_replaceable() &&
-             field->fields_escape_state() == PointsToNode::NoEscape, "sanity");
+      assert(field->is_oop() && field->scalar_replaceable(), "sanity");
       if (field->offset() == Type::OffsetBot) {
         jobj->set_scalar_replaceable(false);
         return;
@@ -1660,6 +1677,10 @@
   }
 
   for (EdgeIterator j(jobj); j.has_next(); j.next()) {
+    if (j.get()->is_Arraycopy()) {
+      continue;
+    }
+
     // Non-escaping object node should point only to field nodes.
     FieldNode* field = j.get()->as_Field();
     int offset = field->as_Field()->offset();
@@ -2636,6 +2657,7 @@
       if (proj_in->is_Allocate() && proj_in->_idx == (uint)toop->instance_id()) {
         break;  // hit one of our sentinels
       } else if (proj_in->is_Call()) {
+        // ArrayCopy node processed here as well
         CallNode *call = proj_in->as_Call();
         if (!call->may_modify(toop, igvn)) {
           result = call->in(TypeFunc::Memory);
@@ -2648,6 +2670,15 @@
           result = proj_in->in(TypeFunc::Memory);
         }
       } else if (proj_in->is_MemBar()) {
+        if (proj_in->in(TypeFunc::Memory)->is_MergeMem() &&
+            proj_in->in(TypeFunc::Memory)->as_MergeMem()->in(Compile::AliasIdxRaw)->is_Proj() &&
+            proj_in->in(TypeFunc::Memory)->as_MergeMem()->in(Compile::AliasIdxRaw)->in(0)->is_ArrayCopy()) {
+          // clone
+          ArrayCopyNode* ac = proj_in->in(TypeFunc::Memory)->as_MergeMem()->in(Compile::AliasIdxRaw)->in(0)->as_ArrayCopy();
+          if (ac->may_modify(toop, igvn)) {
+            break;
+          }
+        }
         result = proj_in->in(TypeFunc::Memory);
       }
     } else if (result->is_MergeMem()) {
@@ -2724,7 +2755,7 @@
 //
 //  Phase 1:  Process possible allocations from alloc_worklist.  Create instance
 //            types for the CheckCastPP for allocations where possible.
-//            Propagate the the new types through users as follows:
+//            Propagate the new types through users as follows:
 //               casts and Phi:  push users on alloc_worklist
 //               AddP:  cast Base and Address inputs to the instance type
 //                      push any AddP users on alloc_worklist and push any memnode
@@ -2803,7 +2834,7 @@
 //    90  LoadP    _ 120  30   ... alias_index=6
 //   100  LoadP    _  80  20   ... alias_index=4
 //
-void ConnectionGraph::split_unique_types(GrowableArray<Node *>  &alloc_worklist) {
+void ConnectionGraph::split_unique_types(GrowableArray<Node *>  &alloc_worklist, GrowableArray<ArrayCopyNode*> &arraycopy_worklist) {
   GrowableArray<Node *>  memnode_worklist;
   GrowableArray<PhiNode *>  orig_phis;
   PhaseIterGVN  *igvn = _igvn;
@@ -2912,9 +2943,12 @@
       if (alloc->is_Allocate() && (t->isa_instptr() || t->isa_aryptr())) {
 
         // First, put on the worklist all Field edges from Connection Graph
-        // which is more accurate then putting immediate users from Ideal Graph.
+        // which is more accurate than putting immediate users from Ideal Graph.
         for (EdgeIterator e(ptn); e.has_next(); e.next()) {
           PointsToNode* tgt = e.get();
+          if (tgt->is_Arraycopy()) {
+            continue;
+          }
           Node* use = tgt->ideal_node();
           assert(tgt->is_Field() && use->is_AddP(),
                  "only AddP nodes are Field edges in CG");
@@ -3068,6 +3102,38 @@
     }
 
   }
+
+  // Go over all ArrayCopy nodes and if one of the inputs has a unique
+  // type, record it in the ArrayCopy node so we know what memory this
+  // node uses/modified.
+  for (int next = 0; next < arraycopy_worklist.length(); next++) {
+    ArrayCopyNode* ac = arraycopy_worklist.at(next);
+    Node* dest = ac->in(ArrayCopyNode::Dest);
+    if (dest->is_AddP()) {
+      dest = get_addp_base(dest);
+    }
+    JavaObjectNode* jobj = unique_java_object(dest);
+    if (jobj != NULL) {
+      Node *base = get_map(jobj->idx());
+      if (base != NULL) {
+        const TypeOopPtr *base_t = _igvn->type(base)->isa_oopptr();
+        ac->_dest_type = base_t;
+      }
+    }
+    Node* src = ac->in(ArrayCopyNode::Src);
+    if (src->is_AddP()) {
+      src = get_addp_base(src);
+    }
+    jobj = unique_java_object(src);
+    if (jobj != NULL) {
+      Node* base = get_map(jobj->idx());
+      if (base != NULL) {
+        const TypeOopPtr *base_t = _igvn->type(base)->isa_oopptr();
+        ac->_src_type = base_t;
+      }
+    }
+  }
+
   // New alias types were created in split_AddP().
   uint new_index_end = (uint) _compile->num_alias_types();
   assert(unique_old == _compile->unique(), "there should be no new ideal nodes after Phase 1");
--- a/hotspot/src/share/vm/opto/escape.hpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/escape.hpp	Tue May 12 10:27:50 2015 +0200
@@ -536,7 +536,7 @@
 
   // Propagate unique types created for unescaped allocated objects
   // through the graph
-  void split_unique_types(GrowableArray<Node *>  &alloc_worklist);
+  void split_unique_types(GrowableArray<Node *>  &alloc_worklist, GrowableArray<ArrayCopyNode*> &arraycopy_worklist);
 
   // Helper methods for unique types split.
   bool split_AddP(Node *addp, Node *base);
--- a/hotspot/src/share/vm/opto/gcm.cpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/gcm.cpp	Tue May 12 10:27:50 2015 +0200
@@ -483,7 +483,7 @@
 
   // Compute the alias index.  Loads and stores with different alias indices
   // do not need anti-dependence edges.
-  uint load_alias_idx = C->get_alias_index(load->adr_type());
+  int load_alias_idx = C->get_alias_index(load->adr_type());
 #ifdef ASSERT
   if (load_alias_idx == Compile::AliasIdxBot && C->AliasLevel() > 0 &&
       (PrintOpto || VerifyAliases ||
--- a/hotspot/src/share/vm/opto/ifnode.cpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/ifnode.cpp	Tue May 12 10:27:50 2015 +0200
@@ -973,21 +973,25 @@
           assert(init_n->Opcode() == Op_ConvI2L, "unexpected first node");
           Node* new_n = igvn->C->conv_I2X_index(igvn, l, array_size);
 
-          for (uint j = 2; j < stack.size(); j++) {
-            Node* n = stack.node_at(j);
-            Node* clone = n->clone();
-            int rep = clone->replace_edge(init_n, new_n);
+          // The type of the ConvI2L may be widen and so the new
+          // ConvI2L may not be better than an existing ConvI2L
+          if (new_n != init_n) {
+            for (uint j = 2; j < stack.size(); j++) {
+              Node* n = stack.node_at(j);
+              Node* clone = n->clone();
+              int rep = clone->replace_edge(init_n, new_n);
+              assert(rep > 0, "can't find expected node?");
+              clone = igvn->transform(clone);
+              init_n = n;
+              new_n = clone;
+            }
+            igvn->hash_delete(use);
+            int rep = use->replace_edge(init_n, new_n);
             assert(rep > 0, "can't find expected node?");
-            clone = igvn->transform(clone);
-            init_n = n;
-            new_n = clone;
-          }
-          igvn->hash_delete(use);
-          int rep = use->replace_edge(init_n, new_n);
-          assert(rep > 0, "can't find expected node?");
-          igvn->transform(use);
-          if (init_n->outcnt() == 0) {
-            igvn->_worklist.push(init_n);
+            igvn->transform(use);
+            if (init_n->outcnt() == 0) {
+              igvn->_worklist.push(init_n);
+            }
           }
         }
       } else if (use->in(0) == NULL && (igvn->type(use)->isa_long() ||
--- a/hotspot/src/share/vm/opto/macro.cpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/macro.cpp	Tue May 12 10:27:50 2015 +0200
@@ -26,6 +26,7 @@
 #include "compiler/compileLog.hpp"
 #include "libadt/vectset.hpp"
 #include "opto/addnode.hpp"
+#include "opto/arraycopynode.hpp"
 #include "opto/callnode.hpp"
 #include "opto/castnode.hpp"
 #include "opto/cfgnode.hpp"
@@ -613,7 +614,10 @@
         for (DUIterator_Fast kmax, k = use->fast_outs(kmax);
                                    k < kmax && can_eliminate; k++) {
           Node* n = use->fast_out(k);
-          if (!n->is_Store() && n->Opcode() != Op_CastP2X) {
+          if (!n->is_Store() && n->Opcode() != Op_CastP2X &&
+              !(n->is_ArrayCopy() &&
+                n->as_ArrayCopy()->is_clonebasic() &&
+                n->in(ArrayCopyNode::Dest) == use)) {
             DEBUG_ONLY(disq_node = n;)
             if (n->is_Load() || n->is_LoadStore()) {
               NOT_PRODUCT(fail_eliminate = "Field load";)
@@ -623,6 +627,12 @@
             can_eliminate = false;
           }
         }
+      } else if (use->is_ArrayCopy() &&
+                 (use->as_ArrayCopy()->is_arraycopy_validated() ||
+                  use->as_ArrayCopy()->is_copyof_validated() ||
+                  use->as_ArrayCopy()->is_copyofrange_validated()) &&
+                 use->in(ArrayCopyNode::Dest) == res) {
+        // ok to eliminate
       } else if (use->is_SafePoint()) {
         SafePointNode* sfpt = use->as_SafePoint();
         if (sfpt->is_Call() && sfpt->as_Call()->has_non_debug_use(res)) {
@@ -887,11 +897,49 @@
             }
 #endif
             _igvn.replace_node(n, n->in(MemNode::Memory));
+          } else if (n->is_ArrayCopy()) {
+            // Disconnect ArrayCopy node
+            ArrayCopyNode* ac = n->as_ArrayCopy();
+            assert(ac->is_clonebasic(), "unexpected array copy kind");
+            Node* ctl_proj = ac->proj_out(TypeFunc::Control);
+            Node* mem_proj = ac->proj_out(TypeFunc::Memory);
+            if (ctl_proj != NULL) {
+              _igvn.replace_node(ctl_proj, n->in(0));
+            }
+            if (mem_proj != NULL) {
+              _igvn.replace_node(mem_proj, n->in(TypeFunc::Memory));
+            }
           } else {
             eliminate_card_mark(n);
           }
           k -= (oc2 - use->outcnt());
         }
+      } else if (use->is_ArrayCopy()) {
+        // Disconnect ArrayCopy node
+        ArrayCopyNode* ac = use->as_ArrayCopy();
+        assert(ac->is_arraycopy_validated() ||
+               ac->is_copyof_validated() ||
+               ac->is_copyofrange_validated(), "unsupported");
+        CallProjections callprojs;
+        ac->extract_projections(&callprojs, true);
+
+        _igvn.replace_node(callprojs.fallthrough_ioproj, ac->in(TypeFunc::I_O));
+        _igvn.replace_node(callprojs.fallthrough_memproj, ac->in(TypeFunc::Memory));
+        _igvn.replace_node(callprojs.fallthrough_catchproj, ac->in(TypeFunc::Control));
+
+        // Set control to top. IGVN will remove the remaining projections
+        ac->set_req(0, top());
+        ac->replace_edge(res, top());
+
+        // Disconnect src right away: it can help find new
+        // opportunities for allocation elimination
+        Node* src = ac->in(ArrayCopyNode::Src);
+        ac->replace_edge(src, top());
+        if (src->outcnt() == 0) {
+          _igvn.remove_dead_node(src);
+        }
+
+        _igvn._worklist.push(ac);
       } else {
         eliminate_card_mark(use);
       }
--- a/hotspot/src/share/vm/opto/macroArrayCopy.cpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/macroArrayCopy.cpp	Tue May 12 10:27:50 2015 +0200
@@ -1097,8 +1097,15 @@
       assert(alloc != NULL, "expect alloc");
     }
 
+    const TypePtr* adr_type = _igvn.type(dest)->is_oopptr()->add_offset(Type::OffsetBot);
+    if (ac->_dest_type != TypeOopPtr::BOTTOM) {
+      adr_type = ac->_dest_type->add_offset(Type::OffsetBot)->is_ptr();
+    }
+    if (ac->_src_type != ac->_dest_type) {
+      adr_type = TypeRawPtr::BOTTOM;
+    }
     generate_arraycopy(ac, alloc, &ctrl, merge_mem, &io,
-                       TypeAryPtr::OOPS, T_OBJECT,
+                       adr_type, T_OBJECT,
                        src, src_offset, dest, dest_offset, length,
                        true, !ac->is_copyofrange());
 
@@ -1152,7 +1159,7 @@
   }
   // (2) src and dest arrays must have elements of the same BasicType
   // Figure out the size and type of the elements we will be copying.
-  BasicType src_elem  =  top_src->klass()->as_array_klass()->element_type()->basic_type();
+  BasicType src_elem  = top_src->klass()->as_array_klass()->element_type()->basic_type();
   BasicType dest_elem = top_dest->klass()->as_array_klass()->element_type()->basic_type();
   if (src_elem  == T_ARRAY)  src_elem  = T_OBJECT;
   if (dest_elem == T_ARRAY)  dest_elem = T_OBJECT;
@@ -1232,6 +1239,13 @@
   }
   // This is where the memory effects are placed:
   const TypePtr* adr_type = TypeAryPtr::get_array_body_type(dest_elem);
+  if (ac->_dest_type != TypeOopPtr::BOTTOM) {
+    adr_type = ac->_dest_type->add_offset(Type::OffsetBot)->is_ptr();
+  }
+  if (ac->_src_type != ac->_dest_type) {
+    adr_type = TypeRawPtr::BOTTOM;
+  }
+
   generate_arraycopy(ac, alloc, &ctrl, merge_mem, &io,
                      adr_type, dest_elem,
                      src, src_offset, dest, dest_offset, length,
--- a/hotspot/src/share/vm/opto/memnode.cpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/memnode.cpp	Tue May 12 10:27:50 2015 +0200
@@ -28,6 +28,7 @@
 #include "memory/allocation.inline.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "opto/addnode.hpp"
+#include "opto/arraycopynode.hpp"
 #include "opto/cfgnode.hpp"
 #include "opto/compile.hpp"
 #include "opto/connode.hpp"
@@ -107,6 +108,32 @@
 
 #endif
 
+static bool membar_for_arraycopy_helper(const TypeOopPtr *t_oop, MergeMemNode* mm, PhaseTransform *phase) {
+  if (mm->memory_at(Compile::AliasIdxRaw)->is_Proj()) {
+    Node* n = mm->memory_at(Compile::AliasIdxRaw)->in(0);
+    if ((n->is_ArrayCopy() && n->as_ArrayCopy()->may_modify(t_oop, phase)) ||
+        (n->is_CallLeaf() && n->as_CallLeaf()->may_modify(t_oop, phase))) {
+      return true;
+    }
+  }
+  return false;
+}
+
+static bool membar_for_arraycopy(const TypeOopPtr *t_oop, MemBarNode* mb, PhaseTransform *phase) {
+  Node* mem = mb->in(TypeFunc::Memory);
+  if (mem->is_MergeMem()) {
+    return membar_for_arraycopy_helper(t_oop, mem->as_MergeMem(), phase);
+  } else if (mem->is_Phi()) {
+    // after macro expansion of an ArrayCopyNode we may have a Phi
+    for (uint i = 1; i < mem->req(); i++) {
+      if (mem->in(i) != NULL && mem->in(i)->is_MergeMem() && membar_for_arraycopy_helper(t_oop, mem->in(i)->as_MergeMem(), phase)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 Node *MemNode::optimize_simple_memory_chain(Node *mchain, const TypeOopPtr *t_oop, Node *load, PhaseGVN *phase) {
   assert((t_oop != NULL), "sanity");
   bool is_instance = t_oop->is_known_instance_field();
@@ -129,6 +156,7 @@
       if (proj_in->is_Allocate() && proj_in->_idx == instance_id) {
         break;  // hit one of our sentinels
       } else if (proj_in->is_Call()) {
+        // ArrayCopyNodes processed here as well
         CallNode *call = proj_in->as_Call();
         if (!call->may_modify(t_oop, phase)) { // returns false for instances
           result = call->in(TypeFunc::Memory);
@@ -136,7 +164,7 @@
       } else if (proj_in->is_Initialize()) {
         AllocateNode* alloc = proj_in->as_Initialize()->allocation();
         // Stop if this is the initialization for the object instance which
-        // which contains this memory slice, otherwise skip over it.
+        // contains this memory slice, otherwise skip over it.
         if ((alloc == NULL) || (alloc->_idx == instance_id)) {
           break;
         }
@@ -150,6 +178,9 @@
           }
         }
       } else if (proj_in->is_MemBar()) {
+        if (membar_for_arraycopy(t_oop, proj_in->as_MemBar(), phase)) {
+          break;
+        }
         result = proj_in->in(TypeFunc::Memory);
       } else {
         assert(false, "unexpected projection");
@@ -477,6 +508,75 @@
 }
 
 
+// Find an arraycopy that must have set (can_see_stored_value=true) or
+// could have set (can_see_stored_value=false) the value for this load
+Node* LoadNode::find_previous_arraycopy(PhaseTransform* phase, Node* ld_alloc, Node*& mem, bool can_see_stored_value) const {
+  if (mem->is_Proj() && mem->in(0) != NULL && (mem->in(0)->Opcode() == Op_MemBarStoreStore ||
+                                               mem->in(0)->Opcode() == Op_MemBarCPUOrder)) {
+    Node* mb = mem->in(0);
+    if (mb->in(0) != NULL && mb->in(0)->is_Proj() &&
+        mb->in(0)->in(0) != NULL && mb->in(0)->in(0)->is_ArrayCopy()) {
+      ArrayCopyNode* ac = mb->in(0)->in(0)->as_ArrayCopy();
+      if (ac->is_clonebasic()) {
+        intptr_t offset;
+        AllocateNode* alloc = AllocateNode::Ideal_allocation(ac->in(ArrayCopyNode::Dest), phase, offset);
+        assert(alloc != NULL && alloc->initialization()->is_complete_with_arraycopy(), "broken allocation");
+        if (alloc == ld_alloc) {
+          return ac;
+        }
+      }
+    }
+  } else if (mem->is_Proj() && mem->in(0) != NULL && mem->in(0)->is_ArrayCopy()) {
+    ArrayCopyNode* ac = mem->in(0)->as_ArrayCopy();
+
+    if (ac->is_arraycopy_validated() ||
+        ac->is_copyof_validated() ||
+        ac->is_copyofrange_validated()) {
+      Node* ld_addp = in(MemNode::Address);
+      if (ld_addp->is_AddP()) {
+        Node* ld_base = ld_addp->in(AddPNode::Address);
+        Node* ld_offs = ld_addp->in(AddPNode::Offset);
+
+        Node* dest = ac->in(ArrayCopyNode::Dest);
+
+        if (dest == ld_base) {
+          Node* src_pos = ac->in(ArrayCopyNode::SrcPos);
+          Node* dest_pos = ac->in(ArrayCopyNode::DestPos);
+          Node* len = ac->in(ArrayCopyNode::Length);
+
+          const TypeInt *dest_pos_t = phase->type(dest_pos)->isa_int();
+          const TypeX *ld_offs_t = phase->type(ld_offs)->isa_intptr_t();
+          const TypeInt *len_t = phase->type(len)->isa_int();
+          const TypeAryPtr* ary_t = phase->type(dest)->isa_aryptr();
+
+          if (dest_pos_t != NULL && ld_offs_t != NULL && len_t != NULL && ary_t != NULL) {
+            BasicType ary_elem  = ary_t->klass()->as_array_klass()->element_type()->basic_type();
+            uint header = arrayOopDesc::base_offset_in_bytes(ary_elem);
+            uint elemsize = type2aelembytes(ary_elem);
+
+            intptr_t dest_pos_plus_len_lo = (((intptr_t)dest_pos_t->_lo) + len_t->_lo) * elemsize + header;
+            intptr_t dest_pos_plus_len_hi = (((intptr_t)dest_pos_t->_hi) + len_t->_hi) * elemsize + header;
+            intptr_t dest_pos_lo = ((intptr_t)dest_pos_t->_lo) * elemsize + header;
+            intptr_t dest_pos_hi = ((intptr_t)dest_pos_t->_hi) * elemsize + header;
+
+            if (can_see_stored_value) {
+              if (ld_offs_t->_lo >= dest_pos_hi && ld_offs_t->_hi < dest_pos_plus_len_lo) {
+                return ac;
+              }
+            } else {
+              if (ld_offs_t->_hi < dest_pos_lo || ld_offs_t->_lo >= dest_pos_plus_len_hi) {
+                mem = ac->in(TypeFunc::Memory);
+              }
+              return ac;
+            }
+          }
+        }
+      }
+    }
+  }
+  return NULL;
+}
+
 // The logic for reordering loads and stores uses four steps:
 // (a) Walk carefully past stores and initializations which we
 //     can prove are independent of this load.
@@ -510,6 +610,7 @@
   for (;;) {                // While we can dance past unrelated stores...
     if (--cnt < 0)  break;  // Caught in cycle or a complicated dance?
 
+    Node* prev = mem;
     if (mem->is_Store()) {
       Node* st_adr = mem->in(MemNode::Address);
       intptr_t st_offset = 0;
@@ -580,15 +681,26 @@
         return mem;         // let caller handle steps (c), (d)
       }
 
+    } else if (find_previous_arraycopy(phase, alloc, mem, false) != NULL) {
+      if (prev != mem) {
+        // Found an arraycopy but it doesn't affect that load
+        continue;
+      }
+      // Found an arraycopy that may affect that load
+      return mem;
     } else if (addr_t != NULL && addr_t->is_known_instance_field()) {
       // Can't use optimize_simple_memory_chain() since it needs PhaseGVN.
       if (mem->is_Proj() && mem->in(0)->is_Call()) {
+        // ArrayCopyNodes processed here as well.
         CallNode *call = mem->in(0)->as_Call();
         if (!call->may_modify(addr_t, phase)) {
           mem = call->in(TypeFunc::Memory);
           continue;         // (a) advance through independent call memory
         }
       } else if (mem->is_Proj() && mem->in(0)->is_MemBar()) {
+        if (membar_for_arraycopy(addr_t, mem->in(0)->as_MemBar(), phase)) {
+          break;
+        }
         mem = mem->in(0)->in(TypeFunc::Memory);
         continue;           // (a) advance through independent MemBar memory
       } else if (mem->is_ClearArray()) {
@@ -760,6 +872,66 @@
   return false;
 }
 
+// Is the value loaded previously stored by an arraycopy? If so return
+// a load node that reads from the source array so we may be able to
+// optimize out the ArrayCopy node later.
+Node* MemNode::can_see_arraycopy_value(Node* st, PhaseTransform* phase) const {
+  Node* ld_adr = in(MemNode::Address);
+  intptr_t ld_off = 0;
+  AllocateNode* ld_alloc = AllocateNode::Ideal_allocation(ld_adr, phase, ld_off);
+  Node* ac = find_previous_arraycopy(phase, ld_alloc, st, true);
+  if (ac != NULL) {
+    assert(ac->is_ArrayCopy(), "what kind of node can this be?");
+    assert(is_Load(), "only for loads");
+
+    if (ac->as_ArrayCopy()->is_clonebasic()) {
+      assert(ld_alloc != NULL, "need an alloc");
+      Node* ld = clone();
+      Node* addp = in(MemNode::Address)->clone();
+      assert(addp->is_AddP(), "address must be addp");
+      assert(addp->in(AddPNode::Base) == ac->in(ArrayCopyNode::Dest)->in(AddPNode::Base), "strange pattern");
+      assert(addp->in(AddPNode::Address) == ac->in(ArrayCopyNode::Dest)->in(AddPNode::Address), "strange pattern");
+      addp->set_req(AddPNode::Base, ac->in(ArrayCopyNode::Src)->in(AddPNode::Base));
+      addp->set_req(AddPNode::Address, ac->in(ArrayCopyNode::Src)->in(AddPNode::Address));
+      ld->set_req(MemNode::Address, phase->transform(addp));
+      if (in(0) != NULL) {
+        assert(ld_alloc->in(0) != NULL, "alloc must have control");
+        ld->set_req(0, ld_alloc->in(0));
+      }
+      return ld;
+    } else {
+      Node* ld = clone();
+      Node* addp = in(MemNode::Address)->clone();
+      assert(addp->in(AddPNode::Base) == addp->in(AddPNode::Address), "should be");
+      addp->set_req(AddPNode::Base, ac->in(ArrayCopyNode::Src));
+      addp->set_req(AddPNode::Address, ac->in(ArrayCopyNode::Src));
+
+      const TypeAryPtr* ary_t = phase->type(in(MemNode::Address))->isa_aryptr();
+      BasicType ary_elem  = ary_t->klass()->as_array_klass()->element_type()->basic_type();
+      uint header = arrayOopDesc::base_offset_in_bytes(ary_elem);
+      uint shift  = exact_log2(type2aelembytes(ary_elem));
+
+      Node* diff = phase->transform(new SubINode(ac->in(ArrayCopyNode::SrcPos), ac->in(ArrayCopyNode::DestPos)));
+#ifdef _LP64
+      diff = phase->transform(new ConvI2LNode(diff));
+#endif
+      diff = phase->transform(new LShiftXNode(diff, phase->intcon(shift)));
+
+      Node* offset = phase->transform(new AddXNode(addp->in(AddPNode::Offset), diff));
+      addp->set_req(AddPNode::Offset, offset);
+      ld->set_req(MemNode::Address, phase->transform(addp));
+
+      if (in(0) != NULL) {
+        assert(ac->in(0) != NULL, "alloc must have control");
+        ld->set_req(0, ac->in(0));
+      }
+      return ld;
+    }
+  }
+  return NULL;
+}
+
+
 //---------------------------can_see_stored_value------------------------------
 // This routine exists to make sure this set of tests is done the same
 // everywhere.  We need to make a coordinated change: first LoadNode::Ideal
@@ -793,6 +965,7 @@
           opc == Op_MemBarRelease ||
           opc == Op_StoreFence ||
           opc == Op_MemBarReleaseLock ||
+          opc == Op_MemBarStoreStore ||
           opc == Op_MemBarCPUOrder) {
         Node* mem = current->in(0)->in(TypeFunc::Memory);
         if (mem->is_MergeMem()) {
@@ -863,8 +1036,9 @@
       if ((alloc != NULL) && (alloc == ld_alloc)) {
         // examine a captured store value
         st = init->find_captured_store(ld_off, memory_size(), phase);
-        if (st != NULL)
+        if (st != NULL) {
           continue;             // take one more trip around
+        }
       }
     }
 
@@ -1335,6 +1509,29 @@
     }
   }
 
+  // Is there a dominating load that loads the same value?  Leave
+  // anything that is not a load of a field/array element (like
+  // barriers etc.) alone
+  if (in(0) != NULL && adr_type() != TypeRawPtr::BOTTOM && can_reshape) {
+    for (DUIterator_Fast imax, i = mem->fast_outs(imax); i < imax; i++) {
+      Node *use = mem->fast_out(i);
+      if (use != this &&
+          use->Opcode() == Opcode() &&
+          use->in(0) != NULL &&
+          use->in(0) != in(0) &&
+          use->in(Address) == in(Address)) {
+        Node* ctl = in(0);
+        for (int i = 0; i < 10 && ctl != NULL; i++) {
+          ctl = IfNode::up_one_dom(ctl);
+          if (ctl == use->in(0)) {
+            set_req(0, use->in(0));
+            return this;
+          }
+        }
+      }
+    }
+  }
+
   // Check for prior store with a different base or offset; make Load
   // independent.  Skip through any number of them.  Bail out if the stores
   // are in an endless dead cycle and report no progress.  This is a key
@@ -1348,6 +1545,12 @@
   // the alias index stuff.  So instead, peek through Stores and IFF we can
   // fold up, do so.
   Node* prev_mem = find_previous_store(phase);
+  if (prev_mem != NULL) {
+    Node* value = can_see_arraycopy_value(prev_mem, phase);
+    if (value != NULL) {
+      return value;
+    }
+  }
   // Steps (a), (b):  Walk past independent stores to find an exact match.
   if (prev_mem != NULL && prev_mem != in(MemNode::Memory)) {
     // (c) See if we can fold up on the spot, but don't fold up here.
@@ -2529,7 +2732,6 @@
 
 //=============================================================================
 //-------------------------------adr_type--------------------------------------
-// Do we Match on this edge index or not?  Do not match memory
 const TypePtr* ClearArrayNode::adr_type() const {
   Node *adr = in(3);
   if (adr == NULL)  return NULL; // node is dead
--- a/hotspot/src/share/vm/opto/memnode.hpp	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/src/share/vm/opto/memnode.hpp	Tue May 12 10:27:50 2015 +0200
@@ -72,6 +72,8 @@
     debug_only(_adr_type=at; adr_type();)
   }
 
+  virtual Node* find_previous_arraycopy(PhaseTransform* phase, Node* ld_alloc, Node*& mem, bool can_see_stored_value) const { return NULL; }
+
 public:
   // Helpers for the optimizer.  Documented in memnode.cpp.
   static bool detect_ptr_independence(Node* p1, AllocateNode* a1,
@@ -124,6 +126,7 @@
   // Can this node (load or store) accurately see a stored value in
   // the given memory state?  (The state may or may not be in(Memory).)
   Node* can_see_stored_value(Node* st, PhaseTransform* phase) const;
+  Node* can_see_arraycopy_value(Node* st, PhaseTransform* phase) const;
 
 #ifndef PRODUCT
   static void dump_adr_type(const Node* mem, const TypePtr* adr_type, outputStream *st);
@@ -147,6 +150,8 @@
   // Should LoadNode::Ideal() attempt to remove control edges?
   virtual bool can_remove_control() const;
   const Type* const _type;      // What kind of value is loaded?
+
+  virtual Node* find_previous_arraycopy(PhaseTransform* phase, Node* ld_alloc, Node*& mem, bool can_see_stored_value) const;
 public:
 
   LoadNode(Node *c, Node *mem, Node *adr, const TypePtr* at, const Type *rt, MemOrd mo)
--- a/hotspot/test/compiler/arraycopy/TestArrayCopyAsLoadsStores.java	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/test/compiler/arraycopy/TestArrayCopyAsLoadsStores.java	Tue May 12 10:27:50 2015 +0200
@@ -25,50 +25,15 @@
  * @test
  * @bug 6912521
  * @summary small array copy as loads/stores
+ * @compile TestArrayCopyAsLoadsStores.java TestArrayCopyUtils.java
  * @run main/othervm -ea -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestArrayCopyAsLoadsStores::m* -XX:TypeProfileLevel=200 TestArrayCopyAsLoadsStores
  * @run main/othervm -ea -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestArrayCopyAsLoadsStores::m* -XX:+IgnoreUnrecognizedVMOptions -XX:+StressArrayCopyMacroNode -XX:TypeProfileLevel=200 TestArrayCopyAsLoadsStores
  *
  */
 
-import java.lang.annotation.*;
-import java.lang.reflect.*;
 import java.util.*;
 
-public class TestArrayCopyAsLoadsStores {
-
-    public enum ArraySrc {
-        SMALL,
-        LARGE,
-        ZERO
-    }
-
-    public enum ArrayDst {
-        NONE,
-        NEW,
-        SRC
-    }
-
-    static class A {
-    }
-
-    static class B extends A {
-    }
-
-    static final A[] small_a_src = new A[5];
-    static final A[] large_a_src = new A[10];
-    static final A[] zero_a_src = new A[0];
-    static final int[] small_int_src = new int[5];
-    static final int[] large_int_src = new int[10];
-    static final int[] zero_int_src = new int[0];
-    static final Object[] small_object_src = new Object[5];
-    static Object src;
-
-    @Retention(RetentionPolicy.RUNTIME)
-    @interface Args {
-        ArraySrc src();
-        ArrayDst dst() default ArrayDst.NONE;
-        int[] extra_args() default {};
-    }
+public class TestArrayCopyAsLoadsStores extends TestArrayCopyUtils {
 
     // array clone should be compiled as loads/stores
     @Args(src=ArraySrc.SMALL)
@@ -349,166 +314,7 @@
         return false;
     }
 
-    final HashMap<String,Method> tests = new HashMap<>();
-    {
-        for (Method m : this.getClass().getDeclaredMethods()) {
-            if (m.getName().matches("m[0-9]+(_check)?")) {
-                assert(Modifier.isStatic(m.getModifiers())) : m;
-                tests.put(m.getName(), m);
-            }
-        }
-    }
-
-    boolean success = true;
-
-    void doTest(String name) throws Exception {
-        Method m = tests.get(name);
-        Method m_check = tests.get(name + "_check");
-        Class[] paramTypes = m.getParameterTypes();
-        Object[] params = new Object[paramTypes.length];
-        Class retType = m.getReturnType();
-        boolean isIntArray = (retType.isPrimitive() && !retType.equals(Void.TYPE)) ||
-            (retType.equals(Void.TYPE) && paramTypes[0].getComponentType().isPrimitive()) ||
-            (retType.isArray() && retType.getComponentType().isPrimitive());
-
-        Args args = m.getAnnotation(Args.class);
-
-        Object src = null;
-        switch(args.src()) {
-        case SMALL: {
-            if (isIntArray) {
-                src = small_int_src;
-            } else {
-                src = small_a_src;
-            }
-            break;
-        }
-        case LARGE: {
-            if (isIntArray) {
-                src = large_int_src;
-            } else {
-                src = large_a_src;
-            }
-            break;
-        }
-        case ZERO: {
-            if (isIntArray) {
-                src = zero_int_src;
-            } else {
-                src = zero_a_src;
-            }
-            break;
-        }
-        }
-
-        for (int i = 0; i < 20000; i++) {
-            boolean failure = false;
-
-            int p = 0;
-
-            if (params.length > 0) {
-                if (isIntArray) {
-                    params[0] = ((int[])src).clone();
-                } else {
-                    params[0] = ((A[])src).clone();
-                }
-                p++;
-            }
-
-            if (params.length > 1) {
-                switch(args.dst()) {
-                case NEW: {
-                    if (isIntArray) {
-                        params[1] = new int[((int[])params[0]).length];
-                    } else {
-                        params[1] = new A[((A[])params[0]).length];
-                    }
-                    p++;
-                    break;
-                }
-                case SRC: {
-                    params[1] = params[0];
-                    p++;
-                    break;
-                }
-                case NONE: break;
-                }
-            }
-
-            for (int j = 0; j < args.extra_args().length; j++) {
-                params[p+j] = args.extra_args()[j];
-            }
-
-            Object res = m.invoke(null, params);
-
-            if (retType.isPrimitive() && !retType.equals(Void.TYPE)) {
-                int s = (int)res;
-                int sum = 0;
-                int[] int_res = (int[])src;
-                for (int j = 0; j < int_res.length; j++) {
-                    sum += int_res[j];
-                }
-                failure = (s != sum);
-                if (failure) {
-                    System.out.println("Test " + name + " failed: result = " + s + " != " + sum);
-                }
-            } else {
-                Object dest = null;
-                if (!retType.equals(Void.TYPE)) {
-                    dest = res;
-                } else {
-                    dest = params[1];
-                }
-
-                if (m_check != null) {
-                    failure = (boolean)m_check.invoke(null,  new Object[] { src, dest });
-                } else {
-                    if (isIntArray) {
-                        int[] int_res = (int[])src;
-                        int[] int_dest = (int[])dest;
-                        for (int j = 0; j < int_res.length; j++) {
-                            if (int_res[j] != int_dest[j]) {
-                                System.out.println("Test " + name + " failed for " + j + " src[" + j +"]=" + int_res[j] + ", dest[" + j + "]=" + int_dest[j]);
-                                failure = true;
-                            }
-                        }
-                    } else {
-                        Object[] object_res = (Object[])src;
-                        Object[] object_dest = (Object[])dest;
-                        for (int j = 0; j < object_res.length; j++) {
-                            if (object_res[j] != object_dest[j]) {
-                                System.out.println("Test " + name + " failed for " + j + " src[" + j +"]=" + object_res[j] + ", dest[" + j + "]=" + object_dest[j]);
-                                failure = true;
-                            }
-                        }
-                    }
-                }
-            }
-
-            if (failure) {
-                success = false;
-                break;
-            }
-        }
-    }
-
     public static void main(String[] args) throws Exception {
-        for (int i = 0; i < small_a_src.length; i++) {
-            small_a_src[i] = new A();
-        }
-
-        for (int i = 0; i < small_int_src.length; i++) {
-            small_int_src[i] = i;
-        }
-
-        for (int i = 0; i < large_int_src.length; i++) {
-            large_int_src[i] = i;
-        }
-
-        for (int i = 0; i < 5; i++) {
-            small_object_src[i] = new Object();
-        }
-
         TestArrayCopyAsLoadsStores test = new TestArrayCopyAsLoadsStores();
 
         test.doTest("m1");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/arraycopy/TestArrayCopyUtils.java	Tue May 12 10:27:50 2015 +0200
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.util.*;
+
+abstract class TestArrayCopyUtils {
+    public enum ArraySrc {
+        SMALL,
+        LARGE,
+        ZERO
+    }
+
+    public enum ArrayDst {
+        NONE,
+        NEW,
+        SRC
+    }
+
+    static class A {
+    }
+
+    static class B extends A {
+    }
+
+    static final A[] small_a_src = new A[5];
+    static final A[] large_a_src = new A[10];
+    static final A[] zero_a_src = new A[0];
+    static final int[] small_int_src = new int[5];
+    static final int[] large_int_src = new int[10];
+    static final int[] zero_int_src = new int[0];
+    static final Object[] small_object_src = new Object[5];
+    static Object src;
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @interface Args {
+        ArraySrc src();
+        ArrayDst dst() default ArrayDst.NONE;
+        int[] extra_args() default {};
+    }
+
+    final HashMap<String,Method> tests = new HashMap<>();
+    {
+        for (Method m : this.getClass().getDeclaredMethods()) {
+            if (m.getName().matches("m[0-9]+(_check)?")) {
+                assert(Modifier.isStatic(m.getModifiers())) : m;
+                tests.put(m.getName(), m);
+            }
+        }
+    }
+
+    boolean success = true;
+
+    void doTest(String name) throws Exception {
+        Method m = tests.get(name);
+        Method m_check = tests.get(name + "_check");
+        Class[] paramTypes = m.getParameterTypes();
+        Object[] params = new Object[paramTypes.length];
+        Class retType = m.getReturnType();
+        boolean isIntArray = (retType.isPrimitive() && !retType.equals(Void.TYPE)) ||
+            (retType.equals(Void.TYPE) && paramTypes[0].getComponentType().isPrimitive()) ||
+            (retType.isArray() && retType.getComponentType().isPrimitive());
+
+        Args args = m.getAnnotation(Args.class);
+
+        Object src = null;
+        switch(args.src()) {
+        case SMALL: {
+            if (isIntArray) {
+                src = small_int_src;
+            } else {
+                src = small_a_src;
+            }
+            break;
+        }
+        case LARGE: {
+            if (isIntArray) {
+                src = large_int_src;
+            } else {
+                src = large_a_src;
+            }
+            break;
+        }
+        case ZERO: {
+            if (isIntArray) {
+                src = zero_int_src;
+            } else {
+                src = zero_a_src;
+            }
+            break;
+        }
+        }
+
+        for (int i = 0; i < 20000; i++) {
+            boolean failure = false;
+
+            int p = 0;
+
+            if (params.length > 0) {
+                if (isIntArray) {
+                    params[0] = ((int[])src).clone();
+                } else {
+                    params[0] = ((A[])src).clone();
+                }
+                p++;
+            }
+
+            if (params.length > 1) {
+                switch(args.dst()) {
+                case NEW: {
+                    if (isIntArray) {
+                        params[1] = new int[((int[])params[0]).length];
+                    } else {
+                        params[1] = new A[((A[])params[0]).length];
+                    }
+                    p++;
+                    break;
+                }
+                case SRC: {
+                    params[1] = params[0];
+                    p++;
+                    break;
+                }
+                case NONE: break;
+                }
+            }
+
+            for (int j = 0; j < args.extra_args().length; j++) {
+                params[p+j] = args.extra_args()[j];
+            }
+
+            Object res = m.invoke(null, params);
+
+            if (retType.isPrimitive() && !retType.equals(Void.TYPE)) {
+                int s = (int)res;
+                int sum = 0;
+                int[] int_res = (int[])src;
+                for (int j = 0; j < int_res.length; j++) {
+                    sum += int_res[j];
+                }
+                failure = (s != sum);
+                if (failure) {
+                    System.out.println("Test " + name + " failed: result = " + s + " != " + sum);
+                }
+            } else {
+                Object dest = null;
+                if (!retType.equals(Void.TYPE)) {
+                    dest = res;
+                } else {
+                    dest = params[1];
+                }
+
+                if (m_check != null) {
+                    failure = (boolean)m_check.invoke(null,  new Object[] { src, dest });
+                } else {
+                    if (isIntArray) {
+                        int[] int_res = (int[])src;
+                        int[] int_dest = (int[])dest;
+                        for (int j = 0; j < int_res.length; j++) {
+                            if (int_res[j] != int_dest[j]) {
+                                System.out.println("Test " + name + " failed for " + j + " src[" + j +"]=" + int_res[j] + ", dest[" + j + "]=" + int_dest[j]);
+                                failure = true;
+                            }
+                        }
+                    } else {
+                        Object[] object_res = (Object[])src;
+                        Object[] object_dest = (Object[])dest;
+                        for (int j = 0; j < object_res.length; j++) {
+                            if (object_res[j] != object_dest[j]) {
+                                System.out.println("Test " + name + " failed for " + j + " src[" + j +"]=" + object_res[j] + ", dest[" + j + "]=" + object_dest[j]);
+                                failure = true;
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (failure) {
+                success = false;
+                break;
+            }
+        }
+    }
+
+    TestArrayCopyUtils() {
+        for (int i = 0; i < small_a_src.length; i++) {
+            small_a_src[i] = new A();
+        }
+
+        for (int i = 0; i < small_int_src.length; i++) {
+            small_int_src[i] = i;
+        }
+
+        for (int i = 0; i < large_int_src.length; i++) {
+            large_int_src[i] = i;
+        }
+
+        for (int i = 0; i < 5; i++) {
+            small_object_src[i] = new Object();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/arraycopy/TestEliminateArrayCopy.java	Tue May 12 10:27:50 2015 +0200
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8076188
+ * @summary arraycopy to non escaping destination may be eliminated
+ * @compile TestEliminateArrayCopy.java TestArrayCopyUtils.java
+ * @run main/othervm -ea -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestEliminateArrayCopy*::m* TestEliminateArrayCopy
+ *
+ */
+
+public class TestEliminateArrayCopy {
+
+    static class CloneTests extends TestInstanceCloneUtils {
+        // object allocation and ArrayCopyNode should be eliminated
+        static void m1(E src) throws CloneNotSupportedException {
+            src.clone();
+        }
+
+        // both object allocations and ArrayCopyNode should be eliminated
+        static void m2(Object dummy) throws CloneNotSupportedException {
+            E src = new E(false);
+            src.clone();
+        }
+
+        // object allocation and ArrayCopyNode should be eliminated. Fields should be loaded from src.
+        static int m3(E src) throws CloneNotSupportedException {
+            E dest = (E)src.clone();
+            return dest.i1 + dest.i2 + dest.i3 + dest.i4 + dest.i5 +
+                dest.i6 + dest.i7 + dest.i8 + dest.i9;
+        }
+    }
+
+    static class ArrayCopyTests extends TestArrayCopyUtils {
+
+        // object allocation and ArrayCopyNode should be eliminated.
+        @Args(src=ArraySrc.LARGE)
+        static int m1() throws CloneNotSupportedException {
+            int[] array_clone = (int[])large_int_src.clone();
+            return array_clone[0] + array_clone[1] + array_clone[2] +
+                array_clone[3] + array_clone[4] + array_clone[5] +
+                array_clone[6] + array_clone[7] + array_clone[8] +
+                array_clone[9];
+        }
+
+        // object allocation and ArrayCopyNode should be eliminated.
+        @Args(src=ArraySrc.LARGE)
+        static int m2() {
+            int[] dest = new int[10];
+            System.arraycopy(large_int_src, 0, dest, 0, 10);
+            return dest[0] + dest[1] + dest[2] + dest[3] + dest[4] +
+                dest[5] + dest[6] + dest[7] + dest[8] + dest[9];
+        }
+
+        // object allocations and ArrayCopyNodes should be eliminated.
+        @Args(src=ArraySrc.LARGE)
+        static int m3() {
+            int[] dest1 = new int[10];
+            System.arraycopy(large_int_src, 0, dest1, 0, 10);
+
+            int[] dest2 = new int[10];
+            System.arraycopy(dest1, 0, dest2, 0, 10);
+
+            return dest2[0] + dest2[1] + dest2[2] + dest2[3] + dest2[4] +
+                dest2[5] + dest2[6] + dest2[7] + dest2[8] + dest2[9];
+        }
+
+        static class m4_class {
+            Object f;
+        }
+
+        static void m4_helper() {}
+
+        // allocations eliminated and arraycopy optimized out
+        @Args(src=ArraySrc.LARGE)
+        static int m4() {
+            int[] dest = new int[10];
+            m4_class o = new m4_class();
+            o.f = dest;
+            m4_helper();
+            System.arraycopy(large_int_src, 0, o.f, 0, 10);
+            return dest[0] + dest[1] + dest[2] + dest[3] + dest[4] +
+                dest[5] + dest[6] + dest[7] + dest[8] + dest[9];
+        }
+
+        static void m5_helper() {}
+
+        // Small copy cannot be converted to loads/stores because
+        // allocation is not close enough to arraycopy but arraycopy
+        // itself can be eliminated
+        @Args(src=ArraySrc.SMALL, dst=ArrayDst.NEW)
+        static void m5(A[] src, A[] dest) {
+            A[] temp = new A[5];
+            m5_helper();
+            System.arraycopy(src, 0, temp, 0, 5);
+            dest[0] = temp[0];
+            dest[1] = temp[1];
+            dest[2] = temp[2];
+            dest[3] = temp[3];
+            dest[4] = temp[4];
+        }
+
+        // object allocation and ArrayCopyNode should be eliminated.
+        @Args(src=ArraySrc.LARGE)
+        static int m6(int [] src) {
+            int res = src[0] + src[1] + src[2] + src[3] + src[4] +
+                src[5] + src[6] + src[7] + src[8] + src[9];
+
+            int[] dest = new int[10];
+
+            System.arraycopy(src, 0, dest, 0, 10);
+
+            res += dest[0] + dest[1] + dest[2] + dest[3] + dest[4] +
+                dest[5] + dest[6] + dest[7] + dest[8] + dest[9];
+            return res/2;
+        }
+
+        @Args(src=ArraySrc.LARGE)
+        static int m7() {
+            int[] dest = new int[10];
+            dest[0] = large_int_src[8];
+            dest[1] = large_int_src[9];
+            System.arraycopy(large_int_src, 0, dest, 2, 8);
+            return dest[0] + dest[1] + dest[2] + dest[3] + dest[4] +
+                dest[5] + dest[6] + dest[7] + dest[8] + dest[9];
+        }
+    }
+
+    // test that OptimizePtrCompare still works
+    static final Object[] m1_array = new Object[10];
+    static boolean m1_array_null_element = false;
+    static void m1(int i) {
+        Object[] array_clone = (Object[])m1_array.clone();
+        if (array_clone[i] == null) {
+            m1_array_null_element = true;
+        }
+    }
+
+    static public void main(String[] args) throws Exception {
+        CloneTests clone_tests = new CloneTests();
+
+        clone_tests.doTest(clone_tests.e, "m1");
+        clone_tests.doTest(null, "m2");
+        clone_tests.doTest(clone_tests.e, "m3");
+
+        ArrayCopyTests ac_tests = new ArrayCopyTests();
+
+        ac_tests.doTest("m1");
+        ac_tests.doTest("m2");
+        ac_tests.doTest("m3");
+        ac_tests.doTest("m4");
+        ac_tests.doTest("m5");
+        ac_tests.doTest("m6");
+        ac_tests.doTest("m7");
+
+        if (!clone_tests.success || !ac_tests.success) {
+            throw new RuntimeException("some tests failed");
+        }
+
+        // Make sure both branches of the if in m1() appear taken
+        for (int i = 0; i < 7000; i++) {
+            m1(0);
+        }
+        m1_array[0] = new Object();
+        for (int i = 0; i < 20000; i++) {
+            m1(0);
+        }
+        m1_array_null_element = false;
+        m1(0);
+        if (m1_array_null_element) {
+            throw new RuntimeException("OptimizePtrCompare test failed");
+        }
+    }
+}
--- a/hotspot/test/compiler/arraycopy/TestInstanceCloneAsLoadsStores.java	Mon May 11 09:44:07 2015 +0200
+++ b/hotspot/test/compiler/arraycopy/TestInstanceCloneAsLoadsStores.java	Tue May 12 10:27:50 2015 +0200
@@ -25,200 +25,13 @@
  * @test
  * @bug 6700100
  * @summary small instance clone as loads/stores
+ * @compile TestInstanceCloneAsLoadsStores.java TestInstanceCloneUtils.java
  * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestInstanceCloneAsLoadsStores::m* TestInstanceCloneAsLoadsStores
  * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,TestInstanceCloneAsLoadsStores::m* -XX:+IgnoreUnrecognizedVMOptions -XX:+StressArrayCopyMacroNode TestInstanceCloneAsLoadsStores
  *
  */
 
-import java.lang.reflect.*;
-import java.util.*;
-
-public class TestInstanceCloneAsLoadsStores {
-    static class Base implements Cloneable {
-        void initialize(Class c, int i) {
-            for (Field f : c.getDeclaredFields()) {
-                setVal(f, i);
-                i++;
-            }
-            if (c != Base.class) {
-                initialize(c.getSuperclass(), i);
-            }
-        }
-
-        Base() {
-            initialize(getClass(), 0);
-        }
-
-        void setVal(Field f, int i) {
-            try {
-                if (f.getType() == int.class) {
-                    f.setInt(this, i);
-                    return;
-                } else if (f.getType() == short.class) {
-                    f.setShort(this, (short)i);
-                    return;
-                } else if (f.getType() == byte.class) {
-                    f.setByte(this, (byte)i);
-                    return;
-                } else if (f.getType() == long.class) {
-                    f.setLong(this, i);
-                    return;
-                }
-            } catch(IllegalAccessException iae) {
-                throw new RuntimeException("Getting fields failed");
-            }
-            throw new RuntimeException("unexpected field type");
-        }
-
-        int getVal(Field f) {
-            try {
-                if (f.getType() == int.class) {
-                    return f.getInt(this);
-                } else if (f.getType() == short.class) {
-                    return (int)f.getShort(this);
-                } else if (f.getType() == byte.class) {
-                    return (int)f.getByte(this);
-                } else if (f.getType() == long.class) {
-                    return (int)f.getLong(this);
-                }
-            } catch(IllegalAccessException iae) {
-                throw new RuntimeException("Setting fields failed");
-            }
-            throw new RuntimeException("unexpected field type");
-        }
-
-        boolean fields_equal(Class c, Base o) {
-            for (Field f : c.getDeclaredFields()) {
-                if (getVal(f) != o.getVal(f)) {
-                    return false;
-                }
-            }
-            if (c != Base.class) {
-                return fields_equal(c.getSuperclass(), o);
-            }
-            return true;
-        }
-
-        public boolean equals(Object obj) {
-            return fields_equal(getClass(), (Base)obj);
-        }
-
-        String print_fields(Class c, String s) {
-            for (Field f : c.getDeclaredFields()) {
-                if (s != "") {
-                    s += "\n";
-                }
-                s = s + f + " = " + getVal(f);
-            }
-            if (c != Base.class) {
-                return print_fields(c.getSuperclass(), s);
-            }
-            return s;
-        }
-
-        public String toString() {
-            return print_fields(getClass(), "");
-        }
-
-        int fields_sum(Class c, int s) {
-            for (Field f : c.getDeclaredFields()) {
-                s += getVal(f);
-            }
-            if (c != Base.class) {
-                return fields_sum(c.getSuperclass(), s);
-            }
-            return s;
-        }
-
-        public int sum() {
-            return fields_sum(getClass(), 0);
-        }
-
-    }
-
-    static class A extends Base {
-        int i1;
-        int i2;
-        int i3;
-        int i4;
-        int i5;
-
-        public Object clone() throws CloneNotSupportedException {
-            return super.clone();
-        }
-    }
-
-    static class B extends A {
-        int i6;
-    }
-
-    static final class D extends Base {
-        byte  i1;
-        short i2;
-        long  i3;
-        int   i4;
-        int   i5;
-
-        public Object clone() throws CloneNotSupportedException {
-            return super.clone();
-        }
-    }
-
-    static final class E extends Base {
-        int i1;
-        int i2;
-        int i3;
-        int i4;
-        int i5;
-        int i6;
-        int i7;
-        int i8;
-        int i9;
-
-        public Object clone() throws CloneNotSupportedException {
-            return super.clone();
-        }
-    }
-
-    static final class F extends Base {
-        public Object clone() throws CloneNotSupportedException {
-            return super.clone();
-        }
-    }
-
-    static class G extends Base {
-        int i1;
-        int i2;
-        int i3;
-
-        public Object myclone() throws CloneNotSupportedException {
-            return clone();
-        }
-    }
-
-    static class H extends G {
-        int i4;
-        int i5;
-
-        public Object clone() throws CloneNotSupportedException {
-            return super.clone();
-        }
-    }
-
-    static class J extends Base  {
-        int i1;
-        int i2;
-        int i3;
-
-        public Object myclone() throws CloneNotSupportedException {
-            return clone();
-        }
-    }
-
-    static class K extends J {
-        int i4;
-        int i5;
-    }
+public class TestInstanceCloneAsLoadsStores extends TestInstanceCloneUtils {
 
     // Should be compiled as loads/stores
     static Object m1(D src) throws CloneNotSupportedException {
@@ -269,62 +82,10 @@
         return (J)src.myclone();
     }
 
-    final HashMap<String,Method> tests = new HashMap<>();
-    {
-        for (Method m : this.getClass().getDeclaredMethods()) {
-            if (m.getName().matches("m[0-9]+")) {
-                assert(Modifier.isStatic(m.getModifiers())) : m;
-                tests.put(m.getName(), m);
-            }
-        }
-    }
-
-    boolean success = true;
-
-    void doTest(Base src, String name) throws Exception {
-        Method m = tests.get(name);
-
-        for (int i = 0; i < 20000; i++) {
-            boolean failure = false;
-            Base res = null;
-            int s = 0;
-            if (m.getReturnType().isPrimitive()) {
-                s = (int)m.invoke(null, src);
-                failure = (s != src.sum());
-            } else {
-                res = (Base)m.invoke(null, src);
-                failure = !res.equals(src);
-            }
-            if (failure) {
-                System.out.println("Test " + name + " failed");
-                System.out.println("source: ");
-                System.out.println(src);
-                System.out.println("result: ");
-                if (m.getReturnType().isPrimitive()) {
-                    System.out.println(s);
-                } else {
-                    System.out.println(res);
-                }
-                success = false;
-                break;
-            }
-        }
-    }
-
     public static void main(String[] args) throws Exception {
 
         TestInstanceCloneAsLoadsStores test = new TestInstanceCloneAsLoadsStores();
 
-        A a = new A();
-        B b = new B();
-        D d = new D();
-        E e = new E();
-        F f = new F();
-        G g = new G();
-        H h = new H();
-        J j = new J();
-        K k = new K();
-
         test.doTest(d, "m1");
         test.doTest(d, "m2");
         test.doTest(e, "m3");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/arraycopy/TestInstanceCloneUtils.java	Tue May 12 10:27:50 2015 +0200
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.reflect.*;
+import java.util.*;
+
+abstract class TestInstanceCloneUtils {
+    static class Base implements Cloneable {
+        void initialize(Class c, int i) {
+            for (Field f : c.getDeclaredFields()) {
+                setVal(f, i);
+                i++;
+            }
+            if (c != Base.class) {
+                initialize(c.getSuperclass(), i);
+            }
+        }
+
+        Base(boolean initialize) {
+            if (initialize) {
+                initialize(getClass(), 0);
+            }
+        }
+
+        void setVal(Field f, int i) {
+            try {
+                if (f.getType() == int.class) {
+                    f.setInt(this, i);
+                    return;
+                } else if (f.getType() == short.class) {
+                    f.setShort(this, (short)i);
+                    return;
+                } else if (f.getType() == byte.class) {
+                    f.setByte(this, (byte)i);
+                    return;
+                } else if (f.getType() == long.class) {
+                    f.setLong(this, i);
+                    return;
+                }
+            } catch(IllegalAccessException iae) {
+                throw new RuntimeException("Getting fields failed");
+            }
+            throw new RuntimeException("unexpected field type");
+        }
+
+        int getVal(Field f) {
+            try {
+                if (f.getType() == int.class) {
+                    return f.getInt(this);
+                } else if (f.getType() == short.class) {
+                    return (int)f.getShort(this);
+                } else if (f.getType() == byte.class) {
+                    return (int)f.getByte(this);
+                } else if (f.getType() == long.class) {
+                    return (int)f.getLong(this);
+                }
+            } catch(IllegalAccessException iae) {
+                throw new RuntimeException("Setting fields failed");
+            }
+            throw new RuntimeException("unexpected field type");
+        }
+
+        boolean fields_equal(Class c, Base o) {
+            for (Field f : c.getDeclaredFields()) {
+                if (getVal(f) != o.getVal(f)) {
+                    return false;
+                }
+            }
+            if (c != Base.class) {
+                return fields_equal(c.getSuperclass(), o);
+            }
+            return true;
+        }
+
+        public boolean equals(Object obj) {
+            return fields_equal(getClass(), (Base)obj);
+        }
+
+        String print_fields(Class c, String s) {
+            for (Field f : c.getDeclaredFields()) {
+                if (s != "") {
+                    s += "\n";
+                }
+                s = s + f + " = " + getVal(f);
+            }
+            if (c != Base.class) {
+                return print_fields(c.getSuperclass(), s);
+            }
+            return s;
+        }
+
+        public String toString() {
+            return print_fields(getClass(), "");
+        }
+
+        int fields_sum(Class c, int s) {
+            for (Field f : c.getDeclaredFields()) {
+                s += getVal(f);
+            }
+            if (c != Base.class) {
+                return fields_sum(c.getSuperclass(), s);
+            }
+            return s;
+        }
+
+        public int sum() {
+            return fields_sum(getClass(), 0);
+        }
+
+    }
+
+    static class A extends Base {
+        int i1;
+        int i2;
+        int i3;
+        int i4;
+        int i5;
+
+        A(boolean initialize) {
+            super(initialize);
+        }
+
+        public Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+    }
+
+    static class B extends A {
+        int i6;
+
+        B(boolean initialize) {
+            super(initialize);
+        }
+    }
+
+    static final class D extends Base {
+        byte  i1;
+        short i2;
+        long  i3;
+        int   i4;
+        int   i5;
+
+        D(boolean initialize) {
+            super(initialize);
+        }
+
+        public Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+    }
+
+    static final class E extends Base {
+        int i1;
+        int i2;
+        int i3;
+        int i4;
+        int i5;
+        int i6;
+        int i7;
+        int i8;
+        int i9;
+
+        E(boolean initialize) {
+            super(initialize);
+        }
+
+        public Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+    }
+
+    static final class F extends Base {
+        F(boolean initialize) {
+            super(initialize);
+        }
+
+        public Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+    }
+
+    static class G extends Base {
+        int i1;
+        int i2;
+        int i3;
+
+        G(boolean initialize) {
+            super(initialize);
+        }
+
+        public Object myclone() throws CloneNotSupportedException {
+            return clone();
+        }
+    }
+
+    static class H extends G {
+        int i4;
+        int i5;
+
+        H(boolean initialize) {
+            super(initialize);
+        }
+
+        public Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+    }
+
+    static class J extends Base  {
+        int i1;
+        int i2;
+        int i3;
+
+        J(boolean initialize) {
+            super(initialize);
+        }
+
+        public Object myclone() throws CloneNotSupportedException {
+            return clone();
+        }
+    }
+
+    static class K extends J {
+        int i4;
+        int i5;
+
+        K(boolean initialize) {
+            super(initialize);
+        }
+
+    }
+
+    static final A a = new A(true);
+    static final B b = new B(true);
+    static final D d = new D(true);
+    static final E e = new E(true);
+    static final F f = new F(true);
+    static final G g = new G(true);
+    static final H h = new H(true);
+    static final J j = new J(true);
+    static final K k = new K(true);
+
+    final HashMap<String,Method> tests = new HashMap<>();
+    {
+        for (Method m : this.getClass().getDeclaredMethods()) {
+            if (m.getName().matches("m[0-9]+")) {
+                assert(Modifier.isStatic(m.getModifiers())) : m;
+                tests.put(m.getName(), m);
+            }
+        }
+    }
+
+    boolean success = true;
+
+    void doTest(Base src, String name) throws Exception {
+        Method m = tests.get(name);
+
+        for (int i = 0; i < 20000; i++) {
+            boolean failure = false;
+            Base res = null;
+            int s = 0;
+            Class retType = m.getReturnType();
+            if (retType.isPrimitive()) {
+                if (!retType.equals(Void.TYPE)) {
+                    s = (int)m.invoke(null, src);
+                    failure = (s != src.sum());
+                } else {
+                    m.invoke(null, src);
+                }
+            } else {
+                res = (Base)m.invoke(null, src);
+                failure = !res.equals(src);
+            }
+            if (failure) {
+                System.out.println("Test " + name + " failed");
+                System.out.println("source: ");
+                System.out.println(src);
+                System.out.println("result: ");
+                if (m.getReturnType().isPrimitive()) {
+                    System.out.println(s);
+                } else {
+                    System.out.println(res);
+                }
+                success = false;
+                break;
+            }
+        }
+    }
+
+}