changeset 49449:a07d36b876f6 lworld

Initial lworld array support runtime/interpreter
author dsimms
date Fri, 23 Mar 2018 10:23:09 +0100
parents 872071254bc9
children e008cad90214
files src/hotspot/cpu/x86/macroAssembler_x86.cpp src/hotspot/cpu/x86/macroAssembler_x86.hpp src/hotspot/cpu/x86/templateTable_x86.cpp src/hotspot/share/interpreter/interpreterRuntime.cpp src/hotspot/share/interpreter/interpreterRuntime.hpp src/hotspot/share/interpreter/templateTable.cpp src/hotspot/share/oops/objArrayKlass.cpp src/hotspot/share/oops/valueArrayKlass.cpp src/hotspot/share/oops/valueArrayKlass.hpp src/hotspot/share/oops/valueKlass.cpp test/hotspot/jtreg/runtime/valhalla/valuetypes/ValueTypeArray.java
diffstat 11 files changed, 550 insertions(+), 145 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/cpu/x86/macroAssembler_x86.cpp	Thu Mar 22 13:35:14 2018 -0400
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.cpp	Fri Mar 23 10:23:09 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, 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
@@ -3657,6 +3657,12 @@
   }
 }
 
+void MacroAssembler::test_klass_is_value(Register klass, Register temp_reg, Label& is_value) {
+  movl(temp_reg, Address(klass, Klass::access_flags_offset()));
+  testl(temp_reg, JVM_ACC_VALUE);
+  jcc(Assembler::notZero, is_value);
+}
+
 void MacroAssembler::test_field_is_flattenable(Register flags, Register temp_reg, Label& is_flattenable) {
   movl(temp_reg, flags);
   shrl(temp_reg, ConstantPoolCacheEntry::is_flattenable_field_shift);
@@ -3681,6 +3687,21 @@
   jcc(Assembler::notZero, is_flattened);
 }
 
+void MacroAssembler::test_flat_array_klass(Register klass, Register temp_reg,
+                                           Label& is_flat_array) {
+  movl(temp_reg, Address(klass, Klass::layout_helper_offset()));
+  sarl(temp_reg, Klass::_lh_array_tag_shift);
+  cmpl(temp_reg, Klass::_lh_array_tag_vt_value);
+  jcc(Assembler::equal, is_flat_array);
+}
+
+
+void MacroAssembler::test_flat_array_oop(Register oop, Register temp_reg,
+                                         Label& is_flat_array) {
+  load_klass(temp_reg, oop);
+  test_flat_array_klass(temp_reg, temp_reg, is_flat_array);
+}
+
 void MacroAssembler::test_value_is_not_buffered(Register value, Register temp_reg, Label& not_buffered) {
   ExternalAddress VTBuffer_top(VTBuffer::top_addr());
   ExternalAddress VTBuffer_end(VTBuffer::end_addr());
--- a/src/hotspot/cpu/x86/macroAssembler_x86.hpp	Thu Mar 22 13:35:14 2018 -0400
+++ b/src/hotspot/cpu/x86/macroAssembler_x86.hpp	Fri Mar 23 10:23:09 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, 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
@@ -97,11 +97,17 @@
   void null_check(Register reg, int offset = -1);
   static bool needs_explicit_null_check(intptr_t offset);
 
+  void test_klass_is_value(Register klass, Register temp_reg, Label& is_value);
+
   void test_field_is_flattenable(Register flags, Register temp_reg, Label& is_flattenable);
   void test_field_is_not_flattenable(Register flags, Register temp_reg, Label& notFlattenable);
   void test_field_is_flattened(Register flags, Register temp_reg, Label& is_flattened);
   void test_value_is_not_buffered(Register value, Register temp_reg, Label& not_buffered);
 
+  // Check klass/oops is flat value type array (oop->_klass->_layout_helper & vt_bit)
+  void test_flat_array_klass(Register klass, Register temp_reg, Label& is_flat_array);
+  void test_flat_array_oop(Register oop, Register temp_reg, Label& is_flat_array);
+
   // Required platform-specific helpers for Label::patch_instructions.
   // They _shadow_ the declarations in AbstractAssembler, which are undefined.
   void pd_patch_instruction(address branch, address target) {
--- a/src/hotspot/cpu/x86/templateTable_x86.cpp	Thu Mar 22 13:35:14 2018 -0400
+++ b/src/hotspot/cpu/x86/templateTable_x86.cpp	Fri Mar 23 10:23:09 2018 +0100
@@ -875,12 +875,26 @@
 
 void TemplateTable::aaload() {
   transition(itos, atos);
-  // rax: index
-  // rdx: array
-  index_check(rdx, rax); // kills rbx
-  __ load_heap_oop(rax, Address(rdx, rax,
-                                UseCompressedOops ? Address::times_4 : Address::times_ptr,
-                                arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
+
+  Register array = rcx;
+  Register index = rax;
+
+  index_check(array, index); // kills rbx
+  if (EnableValhalla) {
+    Label is_flat_array, done;
+    __ test_flat_array_oop(array, rbx, is_flat_array);
+    __ load_heap_oop(rax, Address(array, index,
+                                     UseCompressedOops ? Address::times_4 : Address::times_ptr,
+                                     arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
+    __ jmp(done);
+    __ bind(is_flat_array);
+    __ call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::value_array_load), array, index);
+    __ bind(done);
+  } else {
+    __ load_heap_oop(rax, Address(array, index,
+                                     UseCompressedOops ? Address::times_4 : Address::times_ptr,
+                                     arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
+  }
 }
 
 void TemplateTable::baload() {
@@ -1153,7 +1167,7 @@
 }
 
 void TemplateTable::aastore() {
-  Label is_null, ok_is_subtype, done;
+  Label is_null, is_flat_array, ok_is_subtype, done;
   transition(vtos, vtos);
   // stack: ..., array, index, value
   __ movptr(rax, at_tos());    // value
@@ -1165,14 +1179,20 @@
                           arrayOopDesc::base_offset_in_bytes(T_OBJECT));
 
   index_check_without_pop(rdx, rcx);     // kills rbx
+
   __ testptr(rax, rax);
   __ jcc(Assembler::zero, is_null);
 
+  // Move array class to rdi
+  __ load_klass(rdi, rdx);
+  if (EnableValhalla) {
+    __ test_flat_array_klass(rdi, rbx, is_flat_array);
+  }
+
   // Move subklass into rbx
   __ load_klass(rbx, rax);
   // Move superklass into rax
-  __ load_klass(rax, rdx);
-  __ movptr(rax, Address(rax,
+  __ movptr(rax, Address(rdi,
                          ObjArrayKlass::element_klass_offset()));
   // Compress array + index*oopSize + 12 into a single register.  Frees rcx.
   __ lea(rdx, element_address);
@@ -1190,6 +1210,14 @@
 
   // Get the value we will store
   __ movptr(rax, at_tos());
+  if (ValueTypesBufferMaxMemory > 0) {
+    Label is_on_heap;
+    __ test_value_is_not_buffered(rax, rbx, is_on_heap);
+    __ push(rdx); // save precomputed element address, and convert buffer oop to heap oop
+    __ call_VM(rax, CAST_FROM_FN_PTR(address, InterpreterRuntime::value_heap_copy), rax);
+    __ pop(rdx);
+    __ bind(is_on_heap);
+  }
   // Now store using the appropriate barrier
   do_oop_store(_masm, Address(rdx, 0), rax, _bs->kind(), true);
   __ jmp(done);
@@ -1197,33 +1225,56 @@
   // Have a NULL in rax, rdx=array, ecx=index.  Store NULL at ary[idx]
   __ bind(is_null);
   __ profile_null_seen(rbx);
-
+  if (EnableValhalla) {
+    Label is_null_into_value_array_npe, store_null;
+
+    __ load_klass(rdi, rdx);
+    // No way to store null in flat array
+    __ test_flat_array_klass(rdi, rbx, is_null_into_value_array_npe);
+
+    // Use case for storing values in objArray where element_klass is specifically
+    // a value type because they could not be flattened "for reasons",
+    // these need to have the same semantics as flat arrays, i.e. NPE
+    __ movptr(rdi, Address(rdi, ObjArrayKlass::element_klass_offset()));
+    __ test_klass_is_value(rdi, rdi, is_null_into_value_array_npe);
+    __ jmp(store_null);
+
+    __ bind(is_null_into_value_array_npe);
+    __ jump(ExternalAddress(Interpreter::_throw_NullPointerException_entry));
+
+    __ bind(store_null);
+  }
   // Store a NULL
   do_oop_store(_masm, element_address, noreg, _bs->kind(), true);
-
+  __ jmp(done);
+
+  if (EnableValhalla) {
+    Label is_type_ok;
+    __ bind(is_flat_array); // Store non-null value to flat
+
+    // Simplistic type check...
+
+    // Profile the not-null value's klass.
+    __ load_klass(rbx, rax);
+    __ profile_typecheck(rcx, rbx, rax); // blows rcx, and rax
+    // Move superklass into rax
+    __ movptr(rax, Address(rdi, ArrayKlass::element_klass_offset()));
+    __ cmpptr(rax, rbx); // flat value array needs exact type match
+    __ jccb(Assembler::equal, is_type_ok);
+
+    __ profile_typecheck_failed(rcx);
+    __ jump(ExternalAddress(Interpreter::_throw_ArrayStoreException_entry));
+
+    __ bind(is_type_ok);
+    __ movptr(rax, at_tos());  // value
+    __ movl(rcx, at_tos_p1()); // index
+    __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::value_array_store), rax, rdx, rcx);
+  }
   // Pop stack arguments
   __ bind(done);
   __ addptr(rsp, 3 * Interpreter::stackElementSize);
 }
 
-// This code has to be merged with aastore
-//void TemplateTable::vastore() {
-//  transition(vtos, vtos);
-//
-//  Register value = rcx;
-//  Register index = rbx;
-//  Register array = rax;
-//
-//  // stack: ..., array, index, value
-//  __ pop_ptr(value);
-//  __ pop_i(index);
-//  __ pop_ptr(array);
-//
-//  index_check_without_pop(array, index);
-//
-//  __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::value_array_store), array, index, value);
-//}
-
 void TemplateTable::bastore() {
   transition(itos, vtos);
   __ pop_i(rbx);
--- a/src/hotspot/share/interpreter/interpreterRuntime.cpp	Thu Mar 22 13:35:14 2018 -0400
+++ b/src/hotspot/share/interpreter/interpreterRuntime.cpp	Fri Mar 23 10:23:09 2018 +0100
@@ -322,7 +322,7 @@
         // copy must be created because a field must never point to a TLVB allocated value
         Handle voop_h = Handle(THREAD, voop);
         ValueKlass* field_vk = ValueKlass::cast(voop->klass());
-        assert(field_vk == vklass->get_value_field_klass(field_index), "Sanity check");
+        assert(!cp_entry->is_flattenable() || field_vk == vklass->get_value_field_klass(field_index), "Sanity check");
         instanceOop field_copy = field_vk->allocate_instance(CHECK_((type2size[field_type]) * AbstractInterpreter::stackElementSize));
         Handle field_copy_h = Handle(THREAD, field_copy);
         field_vk->value_store(field_vk->data_for_oop(voop_h()), field_vk->data_for_oop(field_copy_h()), true, false);
@@ -493,58 +493,31 @@
 
 IRT_ENTRY(void, InterpreterRuntime::value_array_load(JavaThread* thread, arrayOopDesc* array, int index))
   Klass* klass = array->klass();
-  assert(klass->is_valueArray_klass() || klass->is_objArray_klass(), "expected value or object array oop");
+  assert(klass->is_valueArray_klass(), "expected value or object array oop");
 
-  if (klass->is_objArray_klass()) {
-    thread->set_vm_result(((objArrayOop) array)->obj_at(index));
-  } else {
-    ValueArrayKlass* vaklass = ValueArrayKlass::cast(klass);
-    ValueKlass* vklass = vaklass->element_klass();
-    arrayHandle ah(THREAD, array);
-    bool in_heap;
-    instanceOop value_holder = vklass->allocate_buffered_or_heap_instance(&in_heap, CHECK);
-    void* src = ((valueArrayOop)ah())->value_at_addr(index, vaklass->layout_helper());
-    vklass->value_store(src, vklass->data_for_oop(value_holder),
-                          vaklass->element_byte_size(), in_heap, false);
-    thread->set_vm_result(value_holder);
-  }
+  ValueArrayKlass* vaklass = ValueArrayKlass::cast(klass);
+  ValueKlass* vklass = vaklass->element_klass();
+  arrayHandle ah(THREAD, array);
+  bool in_heap;
+  instanceOop value_holder = vklass->allocate_buffered_or_heap_instance(&in_heap, CHECK);
+  void* src = ((valueArrayOop)ah())->value_at_addr(index, vaklass->layout_helper());
+  vklass->value_store(src, vklass->data_for_oop(value_holder),
+                        vaklass->element_byte_size(), in_heap, false);
+  thread->set_vm_result(value_holder);
 IRT_END
 
-IRT_ENTRY(void, InterpreterRuntime::value_array_store(JavaThread* thread, arrayOopDesc* array, int index, void* val))
+IRT_ENTRY(void, InterpreterRuntime::value_array_store(JavaThread* thread, void* val, arrayOopDesc* array, int index))
+  assert(val != NULL, "can't store null into flat array");
   Klass* klass = array->klass();
-  assert(klass->is_valueArray_klass() || klass->is_objArray_klass(), "expected value or object array oop");
-  Handle array_h(THREAD, array);
+  assert(klass->is_valueArray_klass(), "expected value array");
+  assert(ArrayKlass::cast(klass)->element_klass() == ((oop)val)->klass(), "Store type incorrect");
 
-  if (ArrayKlass::cast(klass)->element_klass() != ((oop)val)->klass()) {
-    THROW(vmSymbols::java_lang_ArrayStoreException());
-  }
-  if (klass->is_objArray_klass()) {
-    if(VTBuffer::is_in_vt_buffer(val)) {
-      // A Java heap allocated copy must be made because an array cannot
-      // reference a thread-local buffered value
-      Handle val_h(THREAD, (oop)val);
-      ObjArrayKlass* aklass = ObjArrayKlass::cast(klass);
-      Klass* eklass = aklass->element_klass();
-      assert(eklass->is_value(), "Sanity check");
-      assert(eklass == ((oop)val)->klass(), "Sanity check");
-      ValueKlass* vklass = ValueKlass::cast(eklass);
-      // allocate heap instance
-      instanceOop res = vklass->allocate_instance(CHECK);
-      Handle res_h(THREAD, res);
-      // copy value
-      vklass->value_store(((char*)(oopDesc*)val_h()) + vklass->first_field_offset(),
-                            ((char*)(oopDesc*)res_h()) + vklass->first_field_offset(),true, false);
-      val = res_h();
-    }
-    ((objArrayOop) array_h())->obj_at_put(index, (oop)val);
-  } else {
-    valueArrayOop varray = (valueArrayOop)array;
-    ValueArrayKlass* vaklass = ValueArrayKlass::cast(klass);
-    ValueKlass* vklass = vaklass->element_klass();
-    const int lh = vaklass->layout_helper();
-    vklass->value_store(vklass->data_for_oop((oop)val), varray->value_at_addr(index, lh),
-                        vaklass->element_byte_size(), true, false);
-  }
+  valueArrayOop varray = (valueArrayOop)array;
+  ValueArrayKlass* vaklass = ValueArrayKlass::cast(klass);
+  ValueKlass* vklass = vaklass->element_klass();
+  const int lh = vaklass->layout_helper();
+  vklass->value_store(vklass->data_for_oop((oop)val), varray->value_at_addr(index, lh),
+                      vaklass->element_byte_size(), true, false);
 IRT_END
 
 IRT_ENTRY(void, InterpreterRuntime::multianewarray(JavaThread* thread, jint* first_size_address))
@@ -578,6 +551,16 @@
   thread->set_vm_result(obj);
 IRT_END
 
+IRT_ENTRY(void, InterpreterRuntime::value_heap_copy(JavaThread* thread, oopDesc* value))
+  assert(VTBuffer::is_in_vt_buffer(value), "Must only be called for buffered values");
+  ValueKlass* vk = ValueKlass::cast(value->klass());
+  Handle val_h(THREAD, value);
+  instanceOop obj = vk->allocate_instance(CHECK);
+  Handle obj_h(THREAD, obj);
+  vk->value_store(vk->data_for_oop(val_h()), vk->data_for_oop(obj_h()), true, false);
+  thread->set_vm_result(obj_h());
+IRT_END
+
 IRT_LEAF(void, InterpreterRuntime::recycle_vtbuffer(void* alloc_ptr))
   JavaThread* thread = (JavaThread*)Thread::current();
   VTBuffer::recycle_vtbuffer(thread, alloc_ptr);
--- a/src/hotspot/share/interpreter/interpreterRuntime.hpp	Thu Mar 22 13:35:14 2018 -0400
+++ b/src/hotspot/share/interpreter/interpreterRuntime.hpp	Fri Mar 23 10:23:09 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, 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
@@ -128,10 +128,10 @@
   static void    return_value_step2(oopDesc* obj, void* alloc_ptr);
   static void    check_areturn(JavaThread* thread, oopDesc* obj);
   static void    fix_frame_vt_alloc_ptr(JavaThread* thread);
+  static void    value_heap_copy(JavaThread* thread, oopDesc* value);
 
-  // vaload/vastore
   static void value_array_load(JavaThread* thread, arrayOopDesc* array, int index);
-  static void value_array_store(JavaThread* thread, arrayOopDesc* array, int index, void* val);
+  static void value_array_store(JavaThread* thread, void* val, arrayOopDesc* array, int index);
 
   // Quicken instance-of and check-cast bytecodes
   static void    quicken_io_cc(JavaThread* thread);
--- a/src/hotspot/share/interpreter/templateTable.cpp	Thu Mar 22 13:35:14 2018 -0400
+++ b/src/hotspot/share/interpreter/templateTable.cpp	Fri Mar 23 10:23:09 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2018, 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
@@ -309,7 +309,7 @@
   def(Bytecodes::_laload              , ____|____|____|____, itos, ltos, laload              ,  _           );
   def(Bytecodes::_faload              , ____|____|____|____, itos, ftos, faload              ,  _           );
   def(Bytecodes::_daload              , ____|____|____|____, itos, dtos, daload              ,  _           );
-  def(Bytecodes::_aaload              , ____|____|____|____, itos, atos, aaload              ,  _           );
+  def(Bytecodes::_aaload              , ____|____|clvm|____, itos, atos, aaload              ,  _           );
   def(Bytecodes::_baload              , ____|____|____|____, itos, itos, baload              ,  _           );
   def(Bytecodes::_caload              , ____|____|____|____, itos, itos, caload              ,  _           );
   def(Bytecodes::_saload              , ____|____|____|____, itos, itos, saload              ,  _           );
--- a/src/hotspot/share/oops/objArrayKlass.cpp	Thu Mar 22 13:35:14 2018 -0400
+++ b/src/hotspot/share/oops/objArrayKlass.cpp	Fri Mar 23 10:23:09 2018 +0100
@@ -226,6 +226,11 @@
                                int dst_pos, int length, TRAPS) {
   assert(s->is_objArray(), "must be obj array");
 
+  if (d->is_valueArray()) {
+    ValueArrayKlass::cast(d->klass())->copy_array(s, src_pos, d, dst_pos, length, THREAD);
+    return;
+  }
+
   if (!d->is_objArray()) {
     THROW(vmSymbols::java_lang_ArrayStoreException());
   }
@@ -336,11 +341,11 @@
 }
 
 bool ObjArrayKlass::compute_is_subtype_of(Klass* k) {
-  if (!k->is_objArray_klass())
+  if (k->is_valueArray_klass() || k->is_objArray_klass()) {
+    return element_klass()->is_subtype_of(ArrayKlass::cast(k)->element_klass());
+  } else {
     return ArrayKlass::compute_is_subtype_of(k);
-
-  ObjArrayKlass* oak = ObjArrayKlass::cast(k);
-  return element_klass()->is_subtype_of(oak->element_klass());
+  }
 }
 
 void ObjArrayKlass::initialize(TRAPS) {
--- a/src/hotspot/share/oops/valueArrayKlass.cpp	Thu Mar 22 13:35:14 2018 -0400
+++ b/src/hotspot/share/oops/valueArrayKlass.cpp	Fri Mar 23 10:23:09 2018 +0100
@@ -83,6 +83,8 @@
   assert(ValueArrayFlatten, "Flatten array required");
   assert(ValueKlass::cast(element_klass)->is_atomic() || (!ValueArrayAtomicAccess), "Atomic by-default");
 
+  Klass* super_klass = SystemDictionary::Object_klass()->array_klass_or_null();
+  guarantee(super_klass != NULL, "No super");
   ClassLoaderData* loader_data = element_klass->class_loader_data();
   int size = ArrayKlass::static_size(ValueArrayKlass::header_size());
   ValueArrayKlass* vak = new (loader_data, size, THREAD) ValueArrayKlass(element_klass, name);
@@ -90,7 +92,7 @@
     return NULL;
   }
   loader_data->add_class(vak);
-  complete_create_array_klass(vak, vak->super(), vak->module(), CHECK_NULL);
+  complete_create_array_klass(vak, super_klass, vak->module(), CHECK_NULL);
   return vak;
 }
 
@@ -170,10 +172,11 @@
 
 void ValueArrayKlass::copy_array(arrayOop s, int src_pos,
                                  arrayOop d, int dst_pos, int length, TRAPS) {
-  assert(s->is_valueArray(), "must be value array");
+
+  assert(s->is_objArray() || s->is_valueArray(), "must be obj or value array");
 
    // Check destination
-   if (!d->is_valueArray() || element_klass() != ValueArrayKlass::cast(d->klass())->element_klass()) {
+   if ((!d->is_valueArray()) && (!d->is_objArray())) {
      THROW(vmSymbols::java_lang_ArrayStoreException());
    }
 
@@ -190,22 +193,84 @@
    if (length == 0)
      return;
 
-   valueArrayOop sa = valueArrayOop(s);
-   valueArrayOop da = valueArrayOop(d);
-   address src = (address) sa->value_at_addr(src_pos, layout_helper());
-   address dst = (address) da->value_at_addr(dst_pos, layout_helper());
-   if (contains_oops()) {
-     int elem_incr = 1 << log2_element_size();
-     address src_end = src + (length << log2_element_size());
-     while (src < src_end) {
-       element_klass()->value_store(src, dst, element_byte_size(), true, false);
-       src += elem_incr;
-       dst += elem_incr;
+   ArrayKlass* sk = ArrayKlass::cast(s->klass());
+   ArrayKlass* dk = ArrayKlass::cast(d->klass());
+   Klass* d_elem_klass = dk->element_klass();
+   Klass* s_elem_klass = sk->element_klass();
+   /**** CMH: compare and contrast impl, re-factor once we find edge cases... ****/
+
+   if (sk->is_valueArray_klass()) {
+     assert(sk == this, "Unexpected call to copy_array");
+     // Check subtype, all src homogeneous, so just once
+     if (!s_elem_klass->is_subtype_of(d_elem_klass)) {
+       THROW(vmSymbols::java_lang_ArrayStoreException());
      }
-   } else {
-     // we are basically a type array...don't bother limiting element copy
-     // it would have to be a lot wasted space to be worth value_store() calls, need a setting here ?
-     Copy::conjoint_memory_atomic(src, dst, (size_t)length << log2_element_size());
+
+     valueArrayOop sa = valueArrayOop(s);
+     ValueKlass* s_elem_vklass = element_klass();
+
+     // valueArray-to-valueArray
+     if (dk->is_valueArray_klass()) {
+       // element types MUST be exact, subtype check would be dangerous
+       if (dk != this) {
+         THROW(vmSymbols::java_lang_ArrayStoreException());
+       }
+
+       valueArrayOop da = valueArrayOop(d);
+       address dst = (address) da->value_at_addr(dst_pos, layout_helper());
+       address src = (address) sa->value_at_addr(src_pos, layout_helper());
+       if (contains_oops()) {
+         int elem_incr = 1 << log2_element_size();
+         address src_end = src + (length << log2_element_size());
+         while (src < src_end) {
+           s_elem_vklass->value_store(src, dst, element_byte_size(), true, false);
+           src += elem_incr;
+           dst += elem_incr;
+         }
+       } else {
+         // we are basically a type array...don't bother limiting element copy
+         // it would have to be a lot wasted space to be worth value_store() calls, need a setting here ?
+         Copy::conjoint_memory_atomic(src, dst, (size_t)length << log2_element_size());
+       }
+     }
+     else { // valueArray-to-objArray
+       assert(dk->is_objArray_klass(), "Expected objArray here");
+       // Need to allocate each new src elem payload -> dst oop
+       objArrayHandle dh(THREAD, (objArrayOop)d);
+       valueArrayHandle sh(THREAD, sa);
+       int dst_end = dst_pos + length;
+       while (dst_pos < dst_end) {
+         oop o = s_elem_vklass->allocate_instance(CHECK);
+         s_elem_vklass->value_store(sh->value_at_addr(src_pos, layout_helper()),
+                                             s_elem_vklass->data_for_oop(o), true, true);
+         dh->obj_at_put(dst_pos, o);
+         dst_pos++;
+         src_pos++;
+       }
+     }
+   } else { // objArray-to-valueArray
+     assert(d->is_valueArray(), "objArray copy to valueArray expected");
+
+     ValueKlass* d_elem_vklass = ValueKlass::cast(d_elem_klass);
+     valueArrayOop da = valueArrayOop(d);
+     objArrayOop sa = objArrayOop(s);
+
+     int src_end = src_pos + length;
+     int delem_incr = 1 << dk->log2_element_size();
+     address dst = (address) da->value_at_addr(dst_pos, layout_helper());
+     while (src_pos < src_end) {
+       oop se = sa->obj_at(src_pos);
+       if (se == NULL) {
+         THROW(vmSymbols::java_lang_NullPointerException());
+       }
+       // Check exact type per element
+       if (se->klass() != d_elem_klass) {
+         THROW(vmSymbols::java_lang_ArrayStoreException());
+       }
+       d_elem_vklass->value_store(d_elem_vklass->data_for_oop(se), dst, true, false);
+       dst += delem_incr;
+       src_pos++;
+     }
    }
 }
 
@@ -255,16 +320,52 @@
 }
 
 ModuleEntry* ValueArrayKlass::module() const {
-  assert(element_klass() != NULL, "ObjArrayKlass returned unexpected NULL bottom_klass");
+  assert(element_klass() != NULL, "ValueArrayKlass returned unexpected NULL bottom_klass");
   // The array is defined in the module of its bottom class
   return element_klass()->module();
 }
 
 PackageEntry* ValueArrayKlass::package() const {
-  assert(element_klass() != NULL, "ObjArrayKlass returned unexpected NULL bottom_klass");
+  assert(element_klass() != NULL, "ValuerrayKlass returned unexpected NULL bottom_klass");
   return element_klass()->package();
 }
 
+bool ValueArrayKlass::can_be_primary_super_slow() const {
+    return true;
+}
+
+GrowableArray<Klass*>* ValueArrayKlass::compute_secondary_supers(int num_extra_slots) {
+  // interfaces = { cloneable_klass, serializable_klass, elemSuper[], ... };
+  Array<Klass*>* elem_supers = element_klass()->secondary_supers();
+  int num_elem_supers = elem_supers == NULL ? 0 : elem_supers->length();
+  int num_secondaries = num_extra_slots + 2 + num_elem_supers;
+  if (num_secondaries == 2) {
+    // Must share this for correct bootstrapping!
+    set_secondary_supers(Universe::the_array_interfaces_array());
+    return NULL;
+  } else {
+    GrowableArray<Klass*>* secondaries = new GrowableArray<Klass*>(num_elem_supers+2);
+    secondaries->push(SystemDictionary::Cloneable_klass());
+    secondaries->push(SystemDictionary::Serializable_klass());
+    for (int i = 0; i < num_elem_supers; i++) {
+      Klass* elem_super = (Klass*) elem_supers->at(i);
+      Klass* array_super = elem_super->array_klass_or_null();
+      assert(array_super != NULL, "must already have been created");
+      secondaries->push(array_super);
+    }
+    return secondaries;
+  }
+}
+
+bool ValueArrayKlass::compute_is_subtype_of(Klass* k) {
+  if (k->is_valueArray_klass() || k->is_objArray_klass()) {
+    return element_klass()->is_subtype_of(ArrayKlass::cast(k)->element_klass());
+  } else {
+    return ArrayKlass::compute_is_subtype_of(k);
+  }
+}
+
+
 void ValueArrayKlass::print_on(outputStream* st) const {
 #ifndef PRODUCT
   assert(!is_objArray_klass(), "Unimplemented");
--- a/src/hotspot/share/oops/valueArrayKlass.hpp	Thu Mar 22 13:35:14 2018 -0400
+++ b/src/hotspot/share/oops/valueArrayKlass.hpp	Fri Mar 23 10:23:09 2018 +0100
@@ -68,6 +68,10 @@
   ModuleEntry* module() const;
   PackageEntry* package() const;
 
+  bool can_be_primary_super_slow() const;
+  GrowableArray<Klass*>* compute_secondary_supers(int num_extra_slots);
+  bool compute_is_subtype_of(Klass* k);
+
   int element_byte_size() const { return 1 << layout_helper_log2_element_size(_layout_helper); }
 
   bool is_valueArray_klass_slow() const { return true; }
@@ -115,8 +119,6 @@
   void oop_pc_update_pointers(oop obj, ParCompactionManager* cm);
 #endif
 
-  CMH("Oop iterators. Don't have embedded oops yet, so CMH...")
-
  private:
   template <bool nv, typename OopClosureType>
   inline void oop_oop_iterate(oop obj, OopClosureType* closure);
--- a/src/hotspot/share/oops/valueKlass.cpp	Thu Mar 22 13:35:14 2018 -0400
+++ b/src/hotspot/share/oops/valueKlass.cpp	Fri Mar 23 10:23:09 2018 +0100
@@ -68,7 +68,9 @@
   int last_offset  = 0; // find the last offset, add basic type size
   int last_tsz     = 0;
   for (JavaFieldStream fs(this); !fs.done(); fs.next()) {
-    if (fs.offset() > last_offset) {
+    if (fs.access_flags().is_static()) {
+      continue;
+    } else if (fs.offset() > last_offset) {
       BasicType type = fs.field_descriptor().field_type();
       if (is_java_primitive(type)) {
         last_tsz = type2aelembytes(type);
--- a/test/hotspot/jtreg/runtime/valhalla/valuetypes/ValueTypeArray.java	Thu Mar 22 13:35:14 2018 -0400
+++ b/test/hotspot/jtreg/runtime/valhalla/valuetypes/ValueTypeArray.java	Fri Mar 23 10:23:09 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -23,7 +23,10 @@
 
 package runtime.valhalla.valuetypes;
 
+import java.lang.reflect.Array;
 import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
 
 import static jdk.test.lib.Asserts.*;
 
@@ -47,18 +50,18 @@
     }
 
     public void run() {
-        // Class.forName does not support loading of DVT
-        // - should call ValueType::arrayTypeClass instead?
-        // testClassForName();
+        testClassForName();
         testSimplePointArray();
         testLong8Array();
-        // embedded oops not yet supported
-        //testMixedPersonArray();
+        testMixedPersonArray();
         testMultiDimPointArray();
-        // Some design issues, ignore for now
-        //testAtomicArray();
-        //testArrayCopy();
+        testComposition();
+
+        testSanityCheckcasts();
+        testObjectArrayOfValues();
+
         testReflectArray();
+        testUtilArrays();
     }
 
     void testClassForName() {
@@ -80,6 +83,18 @@
     }
 
     void testSimplePointArray() {
+        Point[] defaultPoint = new Point[1];
+        Point p = defaultPoint[0];
+        assertEquals(p.x, 0, "invalid default loaded from array");
+        assertEquals(p.y, 0, "invalid default loaded from array");
+        boolean gotNpe = false;
+        try {
+            defaultPoint[0] = (Point) getNull();
+        } catch (NullPointerException npe) {
+            gotNpe = true;
+        }
+        assertTrue(gotNpe, "Expected NullPointerException");
+
         Point[] points = createSimplePointArray();
         checkSimplePointArray(points);
         System.gc(); // check that VTs survive GC
@@ -94,9 +109,14 @@
     static Point[] createSimplePointArray() {
         Point[] ps = new Point[2];
         assertEquals(ps.length, 2, "Length");
-        System.out.println(ps);
+        ps.toString();
         ps[0] = Point.createPoint(1, 2);
         ps[1] = Point.createPoint(3, 4);
+        boolean sawOob = false;
+        try {
+            ps[2] = Point.createPoint(0, 0);
+        } catch (ArrayIndexOutOfBoundsException aioobe) { sawOob = true; }
+        assertTrue(sawOob, "Didn't see AIOOBE");
         System.gc(); // check that VTs survive GC
         return ps;
     }
@@ -111,7 +131,7 @@
     void testLong8Array() {
         Long8Value[] values = new Long8Value[3];
         assertEquals(values.length, 3, "length");
-        System.out.println(values);
+        values.toString();
         Long8Value value = values[1];
         long zl = 0;
         Long8Value.check(value, zl, zl, zl, zl, zl, zl, zl, zl);
@@ -129,7 +149,7 @@
         Person[] people = new Person[3];
 
         people[0] = Person.create(1, "First", "Last");
-        assertEquals(people[0].getId(), 1L, "Invalid Id");
+        assertEquals(people[0].getId(), 1, "Invalid Id person");
         assertEquals(people[0].getFirstName(), "First", "Invalid First Name");
         assertEquals(people[0].getLastName(), "Last", "Invalid Last Name");
 
@@ -138,7 +158,7 @@
 
         Person[] peopleCopy = new Person[people.length];
         System.arraycopy(people, 0, peopleCopy, 0, people.length);
-        assertEquals(peopleCopy[2].getId(), 3L, "Invalid Id");
+        assertEquals(peopleCopy[2].getId(), 3, "Invalid Id");
         assertEquals(peopleCopy[2].getFirstName(), "Bob", "Invalid First Name");
         assertEquals(peopleCopy[2].getLastName(), "Dobalina", "Invalid Last Name");
     }
@@ -154,39 +174,21 @@
         assertEquals(defaultPoint.y, 0, "invalid point x value");
     }
 
-
-    @SuppressWarnings("unchecked")
-    void testArrayCopy() {
-        // Test copy atomic vs relax combos...
-        int testLength = 3;
-        Long8Value long8Value = Long8Value.create(1, 2, 3, 4, 5, 6, 7, 8);
-        Long8Value long8ValueZero = Long8Value.create(0, 0, 0, 0, 0, 0, 0, 0);
-        Long8Value[] relaxedValues = new Long8Value[testLength];
-        for (int i = 0; i < testLength; i++) {
-            relaxedValues[i] = long8Value;
-        }
-
-        // relaxed -> relaxed
-        Long8Value[] relaxedValues2 = new Long8Value[testLength];
-        System.arraycopy(relaxedValues, 0, relaxedValues2, 0, testLength);
-        Long8Value.check(relaxedValues2[testLength-1], 1, 2, 3, 4, 5, 6, 7, 8);
-    }
-
     void testReflectArray() {
         // Check the java.lang.reflect.Array.newInstance methods...
         Class<?> cls = (Class<?>) Point[].class;
-        Point[][] array = (Point[][]) java.lang.reflect.Array.newInstance(cls, 1);
+        Point[][] array = (Point[][]) Array.newInstance(cls, 1);
         assertEquals(array.length, 1, "Incorrect length");
         assertTrue(array[0] == null, "Expected NULL");
 
-        Point[][][] array3 = (Point[][][]) java.lang.reflect.Array.newInstance(cls, 1, 2);
+        Point[][][] array3 = (Point[][][]) Array.newInstance(cls, 1, 2);
         assertEquals(array3.length, 1, "Incorrect length");
         assertEquals(array3[0].length, 2, "Incorrect length");
         assertTrue(array3[0][0] == null, "Expected NULL");
 
         // Now create ObjArrays of ValueArray...
         cls = (Class<?>) Point.class;
-        array = (Point[][]) java.lang.reflect.Array.newInstance(cls, 1, 2);
+        array = (Point[][]) Array.newInstance(cls, 1, 2);
         assertEquals(array.length, 1, "Incorrect length");
         assertEquals(array[0].length, 2, "Incorrect length");
         Point p = array[0][1];
@@ -194,4 +196,236 @@
         assertEquals(x, 0, "Bad Point Value");
     }
 
+    static final __ByValue class MyInt implements Comparable<MyInt> {
+        final int value;
+
+        private MyInt() { value = 0; }
+        public int getValue() { return value; }
+        public String toString() { return "MyInt: " + getValue(); }
+        public int compareTo(MyInt that) { return Integer.compare(this.getValue(), that.getValue()); }
+        public boolean equals(Object o) {
+            if (o instanceof MyInt) {
+                return this.getValue() == ((MyInt) o).getValue();
+            }
+            return false;
+        }
+
+        public static MyInt create(int v) {
+            MyInt mi = __MakeDefault MyInt();
+            mi = __WithField(mi.value, v);
+            return mi;
+        }
+
+        public static final MyInt MIN = MyInt.create(Integer.MIN_VALUE);
+        public static final MyInt ZERO = MyInt.create(0);
+        public static final MyInt MAX = MyInt.create(Integer.MAX_VALUE);
+    }
+
+    void testSanityCheckcasts() {
+        MyInt[] myInts = new MyInt[1];
+        assertTrue(myInts instanceof Object[]);
+        assertTrue(myInts instanceof Comparable[]);
+
+        Object arrObj = Array.newInstance(MyInt.class, 1);
+        assertTrue(arrObj instanceof Object[], "Not Object array");
+        assertTrue(arrObj instanceof Comparable[], "Not Comparable array");
+        assertTrue(arrObj instanceof MyInt[], "Not MyInt array");
+
+        Object[] arr = (Object[]) arrObj;
+        assertTrue(arr instanceof Comparable[], "Not Comparable array");
+        assertTrue(arr instanceof MyInt[], "Not MyInt array");
+        Comparable[] comparables = (Comparable[])arr;
+        MyInt[] myIntArr = (MyInt[]) arr;
+    }
+
+    void testUtilArrays() {
+        // Sanity check j.u.Arrays
+        MyInt[] myInts = new MyInt[] { MyInt.MAX, MyInt.MIN };
+        // Sanity sort another copy
+        MyInt[] copyMyInts = Arrays.copyOf(myInts, myInts.length + 1);
+        checkArrayElementsEqual(copyMyInts, new MyInt[] { myInts[0], myInts[1], MyInt.ZERO});
+
+        Arrays.sort(copyMyInts);
+        checkArrayElementsEqual(copyMyInts, new MyInt[] { MyInt.MIN, MyInt.ZERO, MyInt.MAX });
+
+        List myIntList = Arrays.asList(copyMyInts);
+        checkArrayElementsEqual(copyMyInts, myIntList.toArray(new MyInt[copyMyInts.length]));
+        // This next line needs testMixedLayoutArrays to work
+        checkArrayElementsEqual(copyMyInts, myIntList.toArray());
+
+        // Sanity check j.u.ArrayList
+        ArrayList<MyInt> aList = new ArrayList<MyInt>(Arrays.asList(copyMyInts));
+        assertTrue(aList.indexOf(MyInt.MIN) == 0, "Bad Index");
+        assertTrue(aList.indexOf(MyInt.ZERO) == 1, "Bad Index");
+        assertTrue(aList.indexOf(MyInt.MAX) == 2, "Bad Index");
+
+        aList.remove(2);
+        aList.add(MyInt.create(5));
+
+        // Interesting:
+        //aList.add((MyInt)getNull());
+
+        // javac currently generating "java/util/Objects.requireNonNull
+        // should checkcast treat null against Value class as CCE ?
+        // Then in the end, ArrayList.elementData is Object[], (that's why remove works)
+        // why can't I write add(null) then ?
+    }
+
+    void testObjectArrayOfValues() {
+        testSanityObjectArrays();
+        testMixedLayoutArrays();
+    }
+
+    void testSanityObjectArrays() {
+        Object[] objects = new Object[2];
+        assertTrue(objects[0] == null && objects[1] == null, "Not null ?");
+
+        objects[0] = MyInt.create(1);
+        objects[1] = Integer.valueOf(2);
+        assertTrue(objects[0].equals(MyInt.create(1)), "Bad Value");
+        assertTrue(objects[1].equals(Integer.valueOf(2)), "Bad Object");
+
+        Comparable[] copyComparables = new Comparable[objects.length];
+        System.arraycopy(objects, 0, copyComparables, 0, objects.length);
+        checkArrayElementsEqual(objects, copyComparables);
+
+        objects[0] = null;
+        objects[1] = null;
+        assertTrue(objects[0] == null && objects[1] == null, "Not null ?");
+
+        Comparable[] comparables = new Comparable[2];
+        assertTrue(comparables[0] == null && comparables[1] == null, "Not null ?");
+        comparables[0] = MyInt.create(3);
+        comparables[1] = Integer.valueOf(4);
+        assertTrue(comparables[0].equals(MyInt.create(3)), "Bad Value");
+        assertTrue(comparables[1].equals(Integer.valueOf(4)), "Bad Object");
+
+        Object[] copyObjects = new Object[2];
+        System.arraycopy(comparables, 0, copyObjects, 0, comparables.length);
+        checkArrayElementsEqual(comparables, copyObjects);
+
+        comparables[0] = null;
+        comparables[1] = null;
+        assertTrue(comparables[0] == null && comparables[1] == null, "Not null ?");
+    }
+
+    void testMixedLayoutArrays() {
+        Object[] objArray = new Object[3];
+        Comparable[] compArray = new Comparable[3];
+        MyInt[] valArray = new MyInt[] { MyInt.MIN, MyInt.ZERO, MyInt.MAX };
+
+        arrayCopy(valArray, 0, objArray, 0, 3);
+        checkArrayElementsEqual(valArray, objArray);
+        arrayCopy(valArray, 0, objArray, 0, 3);
+
+        objArray = new Object[3];
+        System.arraycopy(valArray, 0, objArray, 0, 3);
+        checkArrayElementsEqual(valArray, objArray);
+
+        System.arraycopy(valArray, 0, compArray, 0, 3);
+        checkArrayElementsEqual(valArray, compArray);
+
+        valArray = new MyInt[] { MyInt.ZERO, MyInt.ZERO, MyInt.ZERO };
+        System.arraycopy(compArray, 0, valArray, 0, 3);
+        checkArrayElementsEqual(valArray, compArray);
+
+        valArray = new MyInt[] { MyInt.ZERO, MyInt.ZERO, MyInt.ZERO };
+        System.arraycopy(objArray, 0, valArray, 0, 3);
+        checkArrayElementsEqual(valArray, objArray);
+
+        // Sanity check dst == src
+        System.arraycopy(valArray, 0, valArray, 0, 3);
+        checkArrayElementsEqual(valArray, objArray);
+
+        objArray[0] = "Not a value object";
+        try {
+            System.arraycopy(objArray, 0, valArray, 0, 3);
+            throw new RuntimeException("Expected ArrayStoreException");
+        } catch (ArrayStoreException ase) {}
+    }
+
+    static final __ByValue class MyPoint {
+        final __Flattenable MyInt x;
+        final               MyInt y;
+
+        private MyPoint() {
+            x = MyInt.ZERO;
+            y = x;
+        }
+        public boolean equals(Object that) {
+            if (that instanceof MyPoint) {
+                MyPoint thatPoint = (MyPoint) that;
+                return x.equals(thatPoint.x) && java.util.Objects.equals(y, thatPoint.y);
+            }
+            return false;
+        }
+        static MyPoint create(int x) {
+            MyPoint mp = __MakeDefault MyPoint();
+            mp = __WithField(mp.x, MyInt.create(x));
+            return mp;
+        }
+        static MyPoint create(int x, int y) {
+            MyPoint mp = __MakeDefault MyPoint();
+            mp = __WithField(mp.x, MyInt.create(x));
+            mp = __WithField(mp.y, MyInt.create(y));
+            return mp;
+        }
+        static final MyPoint ORIGIN = create(0);
+    }
+
+    void testComposition() {
+        // Test array operations with compostion of values, check element payload is correct...
+        MyPoint a = MyPoint.create(1, 2);
+        MyPoint b = MyPoint.create(7, 21);
+        MyPoint c = MyPoint.create(Integer.MAX_VALUE, Integer.MIN_VALUE);
+
+        MyPoint[] pts = new MyPoint[3];
+        if (!pts[0].equals(MyPoint.ORIGIN)) {
+            throw new RuntimeException("Equals failed: " + pts[0] + " vs " + MyPoint.ORIGIN);
+        }
+        pts = new MyPoint[] { a, b, c };
+        checkArrayElementsEqual(pts, new Object[] { a, b, c});
+        Object[] oarr = new Object[3];
+
+        arrayCopy(pts, 0, oarr, 0, 3);
+        checkArrayElementsEqual(pts, oarr);
+
+        oarr = new Object[3];
+        System.arraycopy(pts, 0, oarr, 0, 3);
+        checkArrayElementsEqual(pts, oarr);
+
+        System.arraycopy(oarr, 0, pts, 0, 3);
+        checkArrayElementsEqual(pts, oarr);
+
+        oarr = new Object[3];
+        try {
+            System.arraycopy(oarr, 0, pts, 0, 3);
+            throw new RuntimeException("Expected NPE");
+        }
+        catch (NullPointerException npe) {}
+
+        oarr = new Object[3];
+        oarr[0] = new Object();
+        try {
+            System.arraycopy(oarr, 0, pts, 0, 3);
+            throw new RuntimeException("Expected ASE");
+        }
+        catch (ArrayStoreException ase) {}
+    }
+
+    void checkArrayElementsEqual(Object[] arr1, Object[] arr2) {
+        assertTrue(arr1.length == arr2.length, "Bad length");
+        for (int i = 0; i < arr1.length; i++) {
+            assertTrue(java.util.Objects.equals(arr1[i], arr2[i]), "Element " + i + " not equal");
+        }
+    }
+
+    void arrayCopy(Object[] src, int srcPos, Object[] dst, int dstPos, int length) {
+        for (int i = 0; i < length ; i++) {
+            dst[dstPos++] = src[srcPos++];
+        }
+    }
+
+    Object getNull() { return null; }
+
 }