--- old/src/hotspot/cpu/x86/interp_masm_x86.cpp 2018-05-21 11:51:59.427837514 +0200 +++ new/src/hotspot/cpu/x86/interp_masm_x86.cpp 2018-05-21 11:51:59.135832310 +0200 @@ -1254,6 +1254,10 @@ // Load (object->mark() | 1) into swap_reg %rax orptr(swap_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); + if (EnableValhalla && !UseBiasedLocking) { + // For slow path is_always_locked, using biased, which is never natural for !UseBiasLocking + andptr(swap_reg, ~markOopDesc::biased_lock_bit_in_place); + } // Save (object->mark() | 1) into BasicLock's displaced header movptr(Address(lock_reg, mark_offset), swap_reg); --- old/src/hotspot/cpu/x86/macroAssembler_x86.cpp 2018-05-21 11:52:00.155850486 +0200 +++ new/src/hotspot/cpu/x86/macroAssembler_x86.cpp 2018-05-21 11:51:59.835844784 +0200 @@ -3728,6 +3728,26 @@ } } +void MacroAssembler::test_oop_is_value(Register oop, Register temp, Label* is_value, Label* is_not_value) { + const int mask = Universe::oop_metadata_valuetype_mask(); + assert((is_value != NULL) || (is_not_value != NULL), "Need a label to jump to"); + assert((is_value == NULL) ^ (is_not_value == NULL), "Need one label"); +#ifdef _LP64 + if (UseCompressedClassPointers) { + movl(temp, Address(oop, oopDesc::klass_offset_in_bytes())); + } else +#endif + movptr(temp, Address(oop, oopDesc::klass_offset_in_bytes())); + + andl(temp, mask); + testl(temp, temp); + if (is_not_value != NULL) { + jcc(Assembler::zero, *is_not_value); + } else { + jcc(Assembler::notZero, *is_value); + } +} + void MacroAssembler::os_breakpoint() { // instead of directly emitting a breakpoint, call os:breakpoint for better debugability // (e.g., MSVC can't call ps() otherwise) --- old/src/hotspot/cpu/x86/macroAssembler_x86.hpp 2018-05-21 11:52:00.907863887 +0200 +++ new/src/hotspot/cpu/x86/macroAssembler_x86.hpp 2018-05-21 11:52:00.587858184 +0200 @@ -108,6 +108,8 @@ 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); + void test_oop_is_value(Register oop, Register temp, Label* is_value, Label* is_not_value); + // 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) { --- old/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp 2018-05-21 11:52:01.583875932 +0200 +++ new/src/hotspot/cpu/x86/sharedRuntime_x86_64.cpp 2018-05-21 11:52:01.283870587 +0200 @@ -2680,6 +2680,10 @@ // Load (object->mark() | 1) into swap_reg %rax __ orptr(swap_reg, Address(obj_reg, oopDesc::mark_offset_in_bytes())); + if (EnableValhalla && !UseBiasedLocking) { + // For slow path is_always_locked, using biased, which is never natural for !UseBiasLocking + __ andptr(swap_reg, ~markOopDesc::biased_lock_bit_in_place); + } // Save (object->mark() | 1) into BasicLock's displaced header __ movptr(Address(lock_reg, mark_word_offset), swap_reg); --- old/src/hotspot/share/classfile/classFileParser.cpp 2018-05-21 11:52:02.287888476 +0200 +++ new/src/hotspot/share/classfile/classFileParser.cpp 2018-05-21 11:52:02.007883487 +0200 @@ -4074,6 +4074,7 @@ if (is_value_type() && (!has_nonstatic_fields)) { // There are a number of fixes required throughout the type system and JIT throwValueTypeLimitation(THREAD_AND_LOCATION, "Value Types do not support zero instance size yet"); + return; } // Prepare list of oops for oop map generation. @@ -4531,7 +4532,7 @@ info->has_nonstatic_fields = has_nonstatic_fields; } -void ClassFileParser::set_precomputed_flags(InstanceKlass* ik) { +void ClassFileParser::set_precomputed_flags(InstanceKlass* ik, TRAPS) { assert(ik != NULL, "invariant"); const Klass* const super = ik->super(); @@ -4564,6 +4565,10 @@ // Check if this klass supports the java.lang.Cloneable interface if (SystemDictionary::Cloneable_klass_loaded()) { if (ik->is_subtype_of(SystemDictionary::Cloneable_klass())) { + if (ik->is_value()) { + throwValueTypeLimitation(THREAD_AND_LOCATION, "Value Types do not support Cloneable"); + return; + } ik->set_is_cloneable(); } } @@ -5740,7 +5745,7 @@ } // Fill in has_finalizer, has_vanilla_constructor, and layout_helper - set_precomputed_flags(ik); + set_precomputed_flags(ik, CHECK); // check if this class can access its super class check_super_class_access(ik, CHECK); --- old/src/hotspot/share/classfile/classFileParser.hpp 2018-05-21 11:52:03.043901948 +0200 +++ new/src/hotspot/share/classfile/classFileParser.hpp 2018-05-21 11:52:02.731896388 +0200 @@ -314,7 +314,7 @@ int runtime_invisible_annotations_length, TRAPS); - void set_precomputed_flags(InstanceKlass* k); + void set_precomputed_flags(InstanceKlass* k, TRAPS); // Format checker methods void classfile_parse_error(const char* msg, TRAPS) const; --- old/src/hotspot/share/classfile/systemDictionary.cpp 2018-05-21 11:52:03.683913351 +0200 +++ new/src/hotspot/share/classfile/systemDictionary.cpp 2018-05-21 11:52:03.415908576 +0200 @@ -2297,7 +2297,7 @@ // cleared if revocation occurs too often for this type // NOTE that we must only do this when the class is initally // defined, not each time it is referenced from a new class loader - if (k->class_loader() == class_loader()) { + if (k->class_loader() == class_loader() && !k->is_value()) { k->set_prototype_header(markOopDesc::biased_locking_prototype()); } } --- old/src/hotspot/share/gc/shared/collectedHeap.inline.hpp 2018-05-21 11:52:04.443926893 +0200 +++ new/src/hotspot/share/gc/shared/collectedHeap.inline.hpp 2018-05-21 11:52:04.103920835 +0200 @@ -60,12 +60,7 @@ oop obj = (oop)obj_ptr; assert(obj != NULL, "NULL object pointer"); - if (UseBiasedLocking && (klass != NULL)) { - obj->set_mark(klass->prototype_header()); - } else { - // May be bootstrapping - obj->set_mark(markOopDesc::prototype()); - } + obj->set_mark(Klass::default_prototype_header(klass)); } // Support for jvmti and dtrace --- old/src/hotspot/share/memory/universe.cpp 2018-05-21 11:52:05.131939152 +0200 +++ new/src/hotspot/share/memory/universe.cpp 2018-05-21 11:52:04.807933379 +0200 @@ -166,6 +166,7 @@ NarrowPtrStruct Universe::_narrow_oop = { NULL, 0, true }; NarrowPtrStruct Universe::_narrow_klass = { NULL, 0, true }; address Universe::_narrow_ptrs_base; +int Universe::_oop_metadata_valuetype_mask = KlassPtrValueTypeMask; void Universe::basic_type_classes_do(void f(Klass*)) { f(boolArrayKlassObj()); --- old/src/hotspot/share/memory/universe.hpp 2018-05-21 11:52:05.823951482 +0200 +++ new/src/hotspot/share/memory/universe.hpp 2018-05-21 11:52:05.511945923 +0200 @@ -195,6 +195,9 @@ static struct NarrowPtrStruct _narrow_klass; static address _narrow_ptrs_base; + // value type using klass alignment encoded as oop metadata + static int _oop_metadata_valuetype_mask; + // array of dummy objects used with +FullGCAlot debug_only(static objArrayOop _fullgc_alot_dummy_array;) // index of next entry to clear @@ -436,8 +439,13 @@ static void set_narrow_klass_shift(int shift) { assert(shift == 0 || shift == LogKlassAlignmentInBytes, "invalid shift for klass ptrs"); _narrow_klass._shift = shift; + if (shift == LogKlassAlignmentInBytes) { + _oop_metadata_valuetype_mask = 1; + } } + static int oop_metadata_valuetype_mask() { return _oop_metadata_valuetype_mask; } + // Reserve Java heap and determine CompressedOops mode static ReservedSpace reserve_heap(size_t heap_size, size_t alignment); --- old/src/hotspot/share/oops/arrayKlass.cpp 2018-05-21 11:52:06.495963456 +0200 +++ new/src/hotspot/share/oops/arrayKlass.cpp 2018-05-21 11:52:06.195958111 +0200 @@ -94,6 +94,7 @@ set_layout_helper(Klass::_lh_neutral_value); set_is_cloneable(); // All arrays are considered to be cloneable (See JLS 20.1.5) TRACE_INIT_ID(this); + assert(!ptr_is_value_type(this), "ArrayKlass encoded as value type"); } Symbol* ArrayKlass::create_element_klass_array_name(Klass* element_klass, TRAPS) { --- old/src/hotspot/share/oops/instanceKlass.cpp 2018-05-21 11:52:07.179975643 +0200 +++ new/src/hotspot/share/oops/instanceKlass.cpp 2018-05-21 11:52:06.879970298 +0200 @@ -171,7 +171,7 @@ ik = new (loader_data, size, THREAD) InstanceClassLoaderKlass(parser); } else if (parser.is_value_type()) { // value type - ik = new (loader_data, size, THREAD) ValueKlass(parser); + ik = new (loader_data, size, true, THREAD) ValueKlass(parser); } else { // normal ik = new (loader_data, size, THREAD) InstanceKlass(parser, InstanceKlass::_misc_kind_other); --- old/src/hotspot/share/oops/klass.cpp 2018-05-21 11:52:07.895988401 +0200 +++ new/src/hotspot/share/oops/klass.cpp 2018-05-21 11:52:07.583982841 +0200 @@ -170,8 +170,17 @@ return NULL; } -void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() { - return Metaspace::allocate(loader_data, word_size, MetaspaceObj::ClassType, THREAD); +void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, bool is_value, TRAPS) throw () { + // Pad size in case need adjust to even/odd klass ptr + uintptr_t addr = (uintptr_t) Metaspace::allocate(loader_data, word_size + (1 << LogKlassAlignment), MetaspaceObj::ClassType, THREAD); + // values are odd, otherwise make even (and vice versa) + if (is_value ^ (((addr & KlassPtrValueTypeMask) >> LogKlassAlignmentInBytes) != 0)) { + addr += (1 << LogKlassAlignmentInBytes); + } + assert(is_aligned(addr, (1 << LogKlassAlignmentInBytes)), "Klass base alignment incorrect"); + assert( is_value || ((addr & KlassPtrValueTypeMask) == 0), "Klass even alignment incorrect"); + assert(!is_value || ((addr & KlassPtrValueTypeMask) != 0), "Klass odd alignment incorrect"); + return (void*) addr; } // "Normal" instantiation is preceeded by a MetaspaceObj allocation --- old/src/hotspot/share/oops/klass.hpp 2018-05-21 11:52:08.588000730 +0200 +++ new/src/hotspot/share/oops/klass.hpp 2018-05-21 11:52:08.283995314 +0200 @@ -28,6 +28,7 @@ #include "gc/shared/specialized_oop_closures.hpp" #include "memory/iterator.hpp" #include "memory/memRegion.hpp" +#include "oops/markOop.hpp" #include "oops/metadata.hpp" #include "oops/oop.hpp" #include "oops/oopHandle.hpp" @@ -162,7 +163,10 @@ // Constructor Klass(); - void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw(); + void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, bool is_value, TRAPS) throw(); + void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() { + return operator new (size, loader_data, word_size, false, THREAD); + } public: enum DefaultsLookupMode { find_defaults, skip_defaults }; @@ -583,6 +587,9 @@ // prototype markOop. If biased locking is enabled it may further be // biasable and have an epoch. markOop prototype_header() const { return _prototype_header; } + static inline markOop default_prototype_header(Klass* k) { + return (k == NULL) ? markOopDesc::prototype() : k->prototype_header(); + } // NOTE: once instances of this klass are floating around in the // system, this header must only be updated at a safepoint. // NOTE 2: currently we only ever set the prototype header to the @@ -689,6 +696,9 @@ static Klass* decode_klass_not_null(narrowKlass v); static Klass* decode_klass(narrowKlass v); + + static bool decode_ptr_is_value_type(narrowKlass v); + static bool ptr_is_value_type(Klass* v); }; // Helper to convert the oop iterate macro suffixes into bool values that can be used by template functions. --- old/src/hotspot/share/oops/klass.inline.hpp 2018-05-21 11:52:09.248012489 +0200 +++ new/src/hotspot/share/oops/klass.inline.hpp 2018-05-21 11:52:08.932006859 +0200 @@ -30,8 +30,8 @@ #include "oops/markOop.hpp" inline void Klass::set_prototype_header(markOop header) { - assert(!header->has_bias_pattern() || is_instance_klass(), "biased locking currently only supported for Java instances"); - _prototype_header = header; + assert(!is_value() || header->is_always_locked(), "Unexpected prototype"); + _prototype_header = header; } inline bool Klass::is_null(Klass* obj) { return obj == NULL; } @@ -71,6 +71,14 @@ return is_null(v) ? (Klass*)NULL : decode_klass_not_null(v); } +inline bool Klass::decode_ptr_is_value_type(narrowKlass v) { + return (v & Universe::oop_metadata_valuetype_mask()) != 0; +} + +inline bool Klass::ptr_is_value_type(Klass* v) { + return ((uintptr_t)v & KlassPtrValueTypeMask) != 0; +} + template bool Klass::is_instanceof_or_null(T element) { if (oopDesc::is_null(element)) { --- old/src/hotspot/share/oops/markOop.hpp 2018-05-21 11:52:09.900024107 +0200 +++ new/src/hotspot/share/oops/markOop.hpp 2018-05-21 11:52:09.620019117 +0200 @@ -38,6 +38,7 @@ // -------- // hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object) +// "1" :23 epoch:2 age:4 biased_lock:1 lock:2 (biased always locked object) // size:32 ------------------------------------------>| (CMS free block) // PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object) // @@ -45,6 +46,7 @@ // -------- // unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object) +// "1" :54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased always locked object) // PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object) // size:64 ----------------------------------------------------->| (CMS free block) // @@ -96,6 +98,16 @@ // not valid at any other time // // We assume that stack/thread pointers have the lowest two bits cleared. +// +// Always locked: since displaced and monitor references require memory at a +// fixed address, and hash code can be displaced, an efficiently providing a +// *permanent lock* leaves us with specializing the biased pattern (even when +// biased locking isn't enabled). Since biased_lock_alignment for the thread +// reference doesn't use the lowest bit ("2 << thread_shift"), we can use +// this illegal thread pointer alignment to denote "always locked" pattern. +// +// [ |1| epoch | age | 1 | 01] permanently locked +// class BasicLock; class ObjectMonitor; @@ -124,7 +136,8 @@ age_shift = lock_bits + biased_lock_bits, cms_shift = age_shift + age_bits, hash_shift = cms_shift + cms_bits, - epoch_shift = hash_shift + epoch_shift = hash_shift, + thread_shift = epoch_shift + epoch_bits }; enum { lock_mask = right_n_bits(lock_bits), @@ -145,7 +158,7 @@ }; // Alignment of JavaThread pointers encoded in object header required by biased locking - enum { biased_lock_alignment = 2 << (epoch_shift + epoch_bits) + enum { biased_lock_alignment = 2 << thread_shift }; #ifdef _WIN64 @@ -159,7 +172,8 @@ unlocked_value = 1, monitor_value = 2, marked_value = 3, - biased_lock_pattern = 5 + biased_lock_pattern = 5, + always_locked_pattern = 1 << thread_shift | biased_lock_pattern }; enum { no_hash = 0 }; // no hash value assigned @@ -172,6 +186,12 @@ enum { max_bias_epoch = epoch_mask }; + static markOop always_locked_prototype() { + return markOop(always_locked_pattern); + } + + bool is_always_locked() const { return value() == always_locked_pattern; } + // Biased Locking accessors. // These must be checked by all code which calls into the // ObjectSynchronizer and other code. The biasing is not understood @@ -183,6 +203,7 @@ } JavaThread* biased_locker() const { assert(has_bias_pattern(), "should not call this otherwise"); + assert(!is_always_locked(), "invariant"); return (JavaThread*) ((intptr_t) (mask_bits(value(), ~(biased_lock_mask_in_place | age_mask_in_place | epoch_mask_in_place)))); } // Indicates that the mark has the bias bit set but that it has not @@ -200,6 +221,7 @@ markOop set_bias_epoch(int epoch) { assert(has_bias_pattern(), "should not call this otherwise"); assert((epoch & (~epoch_mask)) == 0, "epoch overflow"); + assert(!is_always_locked(), "Rebias needs to fail"); return markOop(mask_bits(value(), ~epoch_mask_in_place) | (epoch << epoch_shift)); } markOop incr_bias_epoch() { @@ -364,7 +386,7 @@ inline static markOop encode_pointer_as_mark(void* p) { return markOop(p)->set_marked(); } // Recover address of oop from encoded form used in mark - inline void* decode_pointer() { if (UseBiasedLocking && has_bias_pattern()) return NULL; return clear_lock_bits(); } + inline void* decode_pointer() { if (has_bias_pattern()) return NULL; return clear_lock_bits(); } // These markOops indicate cms free chunk blocks and not objects. // In 64 bit, the markOop is set to distinguish them from oops. --- old/src/hotspot/share/oops/oop.cpp 2018-05-21 11:52:10.580036222 +0200 +++ new/src/hotspot/share/oops/oop.cpp 2018-05-21 11:52:10.284030948 +0200 @@ -101,7 +101,7 @@ ResetNoHandleMark rnm; // Might be called from LEAF/QUICK ENTRY HandleMark hm(THREAD); Handle object(THREAD, this); - return ObjectSynchronizer::identity_hash_value_for(object); + return ObjectSynchronizer::FastHashCode(THREAD, object()); } // When String table needs to rehash --- old/src/hotspot/share/oops/oop.hpp 2018-05-21 11:52:11.232047838 +0200 +++ new/src/hotspot/share/oops/oop.hpp 2018-05-21 11:52:10.924042351 +0200 @@ -81,6 +81,9 @@ inline Klass** klass_addr(); inline narrowKlass* compressed_klass_addr(); + // oop only test (does not load klass) + inline bool klass_is_value_type(); + inline void set_klass(Klass* k); inline void release_set_klass(Klass* k); --- old/src/hotspot/share/oops/oop.inline.hpp 2018-05-21 11:52:11.832058528 +0200 +++ new/src/hotspot/share/oops/oop.inline.hpp 2018-05-21 11:52:11.564053753 +0200 @@ -96,10 +96,23 @@ return &_metadata._compressed_klass; } +// oop only test (does not load klass) +bool oopDesc::klass_is_value_type() { + if (UseCompressedClassPointers) { + return Klass::decode_ptr_is_value_type(_metadata._compressed_klass); + } else { + return Klass::ptr_is_value_type(_metadata._klass); + } +} + + #define CHECK_SET_KLASS(k) \ do { \ assert(Universe::is_bootstrapping() || k != NULL, "NULL Klass"); \ assert(Universe::is_bootstrapping() || k->is_klass(), "not a Klass"); \ + assert(!EnableValhalla || (k->is_value() && Klass::ptr_is_value_type(k)) \ + || (!k->is_value() && !Klass::ptr_is_value_type(k)), \ + "Klass value encoding"); \ } while (0) void oopDesc::set_klass(Klass* k) { --- old/src/hotspot/share/oops/valueKlass.cpp 2018-05-21 11:52:12.524070857 +0200 +++ new/src/hotspot/share/oops/valueKlass.cpp 2018-05-21 11:52:12.212065299 +0200 @@ -93,7 +93,9 @@ instanceOop ValueKlass::allocate_instance(TRAPS) { int size = size_helper(); // Query before forming handle. - return (instanceOop)CollectedHeap::obj_allocate(this, size, CHECK_NULL); + instanceOop oop = (instanceOop)CollectedHeap::obj_allocate(this, size, CHECK_NULL); + assert(oop->mark()->is_always_locked(), "Unlocked value type"); + return oop; } instanceOop ValueKlass::allocate_buffered_or_heap_instance(bool* in_heap, TRAPS) { @@ -618,3 +620,13 @@ } } } + +void ValueKlass::verify_on(outputStream* st) { + InstanceKlass::verify_on(st); + guarantee(prototype_header()->is_always_locked(), "Prototype header is not always locked"); +} + +void ValueKlass::oop_verify_on(oop obj, outputStream* st) { + InstanceKlass::oop_verify_on(obj, st); + guarantee(obj->mark()->is_always_locked(), "Header is not always locked"); +} --- old/src/hotspot/share/oops/valueKlass.hpp 2018-05-21 11:52:13.192082759 +0200 +++ new/src/hotspot/share/oops/valueKlass.hpp 2018-05-21 11:52:12.908077699 +0200 @@ -49,7 +49,8 @@ *((address*)adr_unpack_handler()) = NULL; assert(pack_handler() == NULL, "pack handler not null"); *((int*)adr_default_value_offset()) = 0; - + assert(Klass::ptr_is_value_type(this), "Value type klass ptr encoding"); + set_prototype_header(markOopDesc::always_locked_prototype()); } ValueKlassFixedBlock* valueklass_static_bloc() const { @@ -269,6 +270,11 @@ void deallocate_contents(ClassLoaderData* loader_data); static void cleanup(ValueKlass* ik) ; + + // Verification + void verify_on(outputStream* st); + void oop_verify_on(oop obj, outputStream* st); + }; #endif /* SHARE_VM_OOPS_VALUEKLASS_HPP */ --- old/src/hotspot/share/prims/jvm.cpp 2018-05-21 11:52:13.892095230 +0200 +++ new/src/hotspot/share/prims/jvm.cpp 2018-05-21 11:52:13.584089742 +0200 @@ -653,6 +653,7 @@ ResourceMark rm(THREAD); THROW_MSG_0(vmSymbols::java_lang_CloneNotSupportedException(), klass->external_name()); } + assert(!EnableValhalla || !obj->klass()->is_value(), "Clone disallowed on value type"); // Make shallow object copy const int size = obj->size(); --- old/src/hotspot/share/runtime/biasedLocking.cpp 2018-05-21 11:52:14.584107558 +0200 +++ new/src/hotspot/share/runtime/biasedLocking.cpp 2018-05-21 11:52:14.288102285 +0200 @@ -45,7 +45,9 @@ static GrowableArray* _preserved_mark_stack = NULL; static void enable_biased_locking(InstanceKlass* k) { - k->set_prototype_header(markOopDesc::biased_locking_prototype()); + if (!k->is_value()) { + k->set_prototype_header(markOopDesc::biased_locking_prototype()); + } } class VM_EnableBiasedLocking: public VM_Operation { --- old/src/hotspot/share/runtime/synchronizer.cpp 2018-05-21 11:52:15.224118960 +0200 +++ new/src/hotspot/share/runtime/synchronizer.cpp 2018-05-21 11:52:14.952114114 +0200 @@ -128,6 +128,19 @@ const oop, const ObjectSynchronizer::InflateCause); +#define CHECK_THROW_NOSYNC_IMSE(obj) \ + if ((obj)->mark()->is_always_locked()) { \ + ResourceMark rm(THREAD); \ + THROW_MSG(vmSymbols::java_lang_IllegalMonitorStateException(), obj->klass()->external_name()); \ + } + +#define CHECK_THROW_NOSYNC_IMSE_0(obj) \ + if ((obj)->mark()->is_always_locked()) { \ + ResourceMark rm(THREAD); \ + THROW_MSG_0(vmSymbols::java_lang_IllegalMonitorStateException(), obj->klass()->external_name()); \ + } + + #define CHAINMARKER (cast_to_oop(-1)) @@ -159,6 +172,7 @@ assert(((JavaThread *) self)->thread_state() == _thread_in_Java, "invariant"); NoSafepointVerifier nsv; if (obj == NULL) return false; // slow-path for invalid obj + assert(!EnableValhalla || !obj->klass()->is_value(), "monitor op on value type"); const markOop mark = obj->mark(); if (mark->has_locker() && self->is_lock_owned((address)mark->locker())) { @@ -209,6 +223,7 @@ assert(((JavaThread *) Self)->thread_state() == _thread_in_Java, "invariant"); NoSafepointVerifier nsv; if (obj == NULL) return false; // Need to throw NPE + assert(!EnableValhalla || !obj->klass()->is_value(), "monitor op on value type"); const markOop mark = obj->mark(); if (mark->has_monitor()) { @@ -264,6 +279,7 @@ void ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock, bool attempt_rebias, TRAPS) { + CHECK_THROW_NOSYNC_IMSE(obj); if (UseBiasedLocking) { if (!SafepointSynchronize::is_at_safepoint()) { BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD); @@ -282,6 +298,10 @@ void ObjectSynchronizer::fast_exit(oop object, BasicLock* lock, TRAPS) { markOop mark = object->mark(); + if (EnableValhalla && mark->is_always_locked()) { + return; + } + assert(!EnableValhalla || !object->klass()->is_value(), "monitor op on value type"); // We cannot check for Biased Locking if we are racing an inflation. assert(mark == markOopDesc::INFLATING() || !mark->has_bias_pattern(), "should not see bias pattern here"); @@ -338,6 +358,7 @@ // We don't need to use fast path here, because it must have been // failed in the interpreter/compiler code. void ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) { + CHECK_THROW_NOSYNC_IMSE(obj); markOop mark = obj->mark(); assert(!mark->has_bias_pattern(), "should not see bias pattern here"); @@ -390,6 +411,7 @@ // NOTE: must use heavy weight monitor to handle complete_exit/reenter() intptr_t ObjectSynchronizer::complete_exit(Handle obj, TRAPS) { TEVENT(complete_exit); + assert(!EnableValhalla || !obj->klass()->is_value(), "monitor op on value type"); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -405,6 +427,7 @@ // NOTE: must use heavy weight monitor to handle complete_exit/reenter() void ObjectSynchronizer::reenter(Handle obj, intptr_t recursion, TRAPS) { TEVENT(reenter); + assert(!EnableValhalla || !obj->klass()->is_value(), "monitor op on value type"); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -422,6 +445,7 @@ void ObjectSynchronizer::jni_enter(Handle obj, TRAPS) { // the current locking is from JNI instead of Java code TEVENT(jni_enter); + CHECK_THROW_NOSYNC_IMSE(obj); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -434,6 +458,7 @@ // NOTE: must use heavy weight monitor to handle jni monitor exit void ObjectSynchronizer::jni_exit(oop obj, Thread* THREAD) { TEVENT(jni_exit); + CHECK_THROW_NOSYNC_IMSE(obj); if (UseBiasedLocking) { Handle h_obj(THREAD, obj); BiasedLocking::revoke_and_rebias(h_obj, false, THREAD); @@ -478,6 +503,7 @@ // Wait/Notify/NotifyAll // NOTE: must use heavy weight monitor to handle wait() int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) { + CHECK_THROW_NOSYNC_IMSE_0(obj); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -501,6 +527,7 @@ } void ObjectSynchronizer::waitUninterruptibly(Handle obj, jlong millis, TRAPS) { + CHECK_THROW_NOSYNC_IMSE(obj); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -515,6 +542,7 @@ } void ObjectSynchronizer::notify(Handle obj, TRAPS) { + CHECK_THROW_NOSYNC_IMSE(obj); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -531,6 +559,7 @@ // NOTE: see comment of notify() void ObjectSynchronizer::notifyall(Handle obj, TRAPS) { + CHECK_THROW_NOSYNC_IMSE(obj); if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(obj, false, THREAD); assert(!obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -709,6 +738,14 @@ } intptr_t ObjectSynchronizer::FastHashCode(Thread * Self, oop obj) { + if (EnableValhalla && obj->klass()->is_value()) { + // Expected tooling to override hashCode for value type, just don't crash + if (log_is_enabled(Debug, monitorinflation)) { + ResourceMark rm; + log_debug(monitorinflation)("FastHashCode for value type: %s", obj->klass()->external_name()); + } + return obj->klass()->java_mirror()->identity_hash(); + } if (UseBiasedLocking) { // NOTE: many places throughout the JVM do not expect a safepoint // to be taken here, in particular most operations on perm gen @@ -813,15 +850,12 @@ return hash; } -// Deprecated -- use FastHashCode() instead. - -intptr_t ObjectSynchronizer::identity_hash_value_for(Handle obj) { - return FastHashCode(Thread::current(), obj()); -} - bool ObjectSynchronizer::current_thread_holds_lock(JavaThread* thread, Handle h_obj) { + if (EnableValhalla && h_obj->mark()->is_always_locked()) { + return false; + } if (UseBiasedLocking) { BiasedLocking::revoke_and_rebias(h_obj, false, thread); assert(!h_obj->mark()->has_bias_pattern(), "biases should be revoked by now"); @@ -1383,6 +1417,10 @@ assert(Universe::verify_in_progress() || !SafepointSynchronize::is_at_safepoint(), "invariant"); + if (EnableValhalla) { + guarantee(!object->klass()->is_value(), "Attempt to inflate value type"); + } + EventJavaMonitorInflate event; for (;;) { --- old/src/hotspot/share/runtime/synchronizer.hpp 2018-05-21 11:52:15.900131004 +0200 +++ new/src/hotspot/share/runtime/synchronizer.hpp 2018-05-21 11:52:15.624126087 +0200 @@ -119,7 +119,6 @@ // Returns the identity hash value for an oop // NOTE: It may cause monitor inflation - static intptr_t identity_hash_value_for(Handle obj); static intptr_t FastHashCode(Thread * Self, oop obj); // java.lang.Thread support --- old/src/hotspot/share/utilities/globalDefinitions.hpp 2018-05-21 11:52:16.584143189 +0200 +++ new/src/hotspot/share/utilities/globalDefinitions.hpp 2018-05-21 11:52:16.284137845 +0200 @@ -451,6 +451,7 @@ const int LogKlassAlignment = LogKlassAlignmentInBytes - LogHeapWordSize; const int KlassAlignmentInBytes = 1 << LogKlassAlignmentInBytes; const int KlassAlignment = KlassAlignmentInBytes / HeapWordSize; +const int KlassPtrValueTypeMask = (1 << (LogKlassAlignmentInBytes + 1)) - 1; // Maximal size of heap where unscaled compression can be used. Also upper bound // for heap placement: 4GB. --- old/test/hotspot/jtreg/runtime/valhalla/valuetypes/classfileparser/BadValueTypes.java 2018-05-21 11:52:17.292155803 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/classfileparser/BadValueTypes.java 2018-05-21 11:52:16.980150244 +0200 @@ -88,5 +88,7 @@ throw new RuntimeException( "Wrong ClassCircularityError: " + e.getMessage()); } } + + runTest("ValueCloneable", "Value Types do not support Cloneable"); } } --- old/test/hotspot/jtreg/runtime/valhalla/valuetypes/classfileparser/cfpTests.jcod 2018-05-21 11:52:18.012168630 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/classfileparser/cfpTests.jcod 2018-05-21 11:52:17.704163143 +0200 @@ -1395,3 +1395,89 @@ } // end SourceFile } // Attributes } // end class CircStaticB + + +// Test that a value type cannot be Cloneable. +// +// final __ByValue class ValueCloneable implements Cloneable { +// final int field; +// private ValueCloneable() { field = 0; } +//} + +class ValueCloneable { + 0xCAFEBABE; + 0; // minor version + 55; // version + [] { // Constant Pool + ; // first element is empty + Method #4 #14; // #1 + Field #3 #15; // #2 + class #16; // #3 + class #17; // #4 + class #18; // #5 + Utf8 "field"; // #6 + Utf8 "I"; // #7 + Utf8 ""; // #8 + Utf8 "()V"; // #9 + Utf8 "Code"; // #10 + Utf8 "LineNumberTable"; // #11 + Utf8 "SourceFile"; // #12 + Utf8 "ValueCloneable.java"; // #13 + NameAndType #8 #9; // #14 + NameAndType #6 #7; // #15 + Utf8 "ValueCloneable"; // #16 + Utf8 "java/lang/Object"; // #17 + Utf8 "java/lang/Cloneable"; // #18 + } // Constant Pool + + 0x0130; // access + #3;// this_cpx + #4;// super_cpx + + [] { // Interfaces + #5; + } // Interfaces + + [] { // fields + { // Member + 0x0010; // access + #6; // name_cpx + #7; // sig_cpx + [] { // Attributes + } // Attributes + } // Member + } // fields + + [] { // methods + { // Member + 0x0002; // access + #8; // name_cpx + #9; // sig_cpx + [] { // Attributes + Attr(#10) { // Code + 2; // max_stack + 1; // max_locals + Bytes[]{ + 0x2AB700012A03B500; + 0x02B1; + } + [] { // Traps + } // end Traps + [] { // Attributes + Attr(#11) { // LineNumberTable + [] { // LineNumberTable + 0 4; + } + } // end LineNumberTable + } // Attributes + } // end Code + } // Attributes + } // Member + } // methods + + [] { // Attributes + Attr(#12) { // SourceFile + #13; + } // end SourceFile + } // Attributes +} // end class ValueCloneable --- /dev/null 2018-05-09 12:22:01.004600734 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/ObjectMethods.java 2018-05-21 11:52:18.384175257 +0200 @@ -0,0 +1,235 @@ +/* + * 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 + * 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. + */ +package runtime.valhalla.valuetypes; + +import java.lang.invoke.*; + +import jdk.experimental.value.MethodHandleBuilder; + +/* + * @test ObjectMethods + * @summary Check object method implemented by the VM behave with value types + * @modules java.base/jdk.experimental.bytecode + * java.base/jdk.experimental.value + * @library /test/lib + * @compile -XDenableValueTypes ObjectMethods.java + * @run main/othervm -Xint -XX:+EnableValhalla -XX:+UseBiasedLocking -XX:+UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods + * @run main/othervm -Xint -XX:+EnableValhalla -XX:-UseBiasedLocking -XX:-UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods + * @run main/othervm -Xint -XX:+EnableValhalla -noverify runtime.valhalla.valuetypes.ObjectMethods noverify + */ + +public class ObjectMethods { + + public static void main(String[] args) { + testObjectMethods((args.length > 0 && args[0].equals("noverify"))); + } + + public static void testObjectMethods(boolean verifierDisabled) { + MyInt val = MyInt.create(7); + MyInt sameVal = MyInt.create(7); + + // Exercise all the Object native/VM methods... + + if (verifierDisabled) { // Just noverifier... + checkMonitorExit(val); + return; + } + + // getClass() + checkGetClass(val, MyInt.class); + + //hashCode()/identityHashCode() + checkHashCodes(val, sameVal.hashCode()); + + // clone() + checkNotCloneable(val); + + // synchronized + checkSynchronized(val); + + // wait/notify() + checkWait(val); + checkNotify(val); + + System.gc(); + } + + + static void checkGetClass(Object val, Class expectedClass) { + Class clazz = val.getClass(); + if (clazz == null) { + throw new RuntimeException("getClass return null"); + } else if (clazz != expectedClass) { + throw new RuntimeException("getClass (" + clazz + ") doesn't match " + expectedClass); + } + } + + // Just check we don't crash the VM + static void checkHashCodes(Object val, int expectedHashCode) { + if (val.hashCode() != expectedHashCode) { + throw new RuntimeException("Hash code mismatch value: " + val.hashCode() + + " expected: " + expectedHashCode); + } + } + + static void checkNotCloneable(MyInt val) { + boolean sawCnse = false; + try { + val.attemptClone(); + } catch (CloneNotSupportedException cnse) { + sawCnse = true; + } + if (!sawCnse) { + throw new RuntimeException("clone() did not fail"); + } + // Cloneable value type checked by "BadValueTypes" CFP tests + } + + static void checkSynchronized(Object val) { + boolean sawImse = false; + try { + synchronized (val) { + throw new IllegalStateException("Unreachable code, reached"); + } + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } + if (!sawImse) { + throw new RuntimeException("monitorenter did not fail"); + } + // synchronized method modifiers tested by "BadValueTypes" CFP tests + // jni monitor ops tested by "ValueWithJni" + } + + // Check we haven't broken the mismatched monitor block check... + static void checkMonitorExit(Object val) { + boolean sawImse = false; + try { + MethodHandleBuilder.loadCode(MethodHandles.lookup(), + "mismatchedMonitorExit", + MethodType.methodType(Void.TYPE, Object.class), + CODE->{ + CODE + .aload(0) + .monitorexit() + .return_(); + }).invokeExact(val); + throw new IllegalStateException("Unreachable code, reached"); + } catch (Throwable t) { + if (t instanceof IllegalMonitorStateException) { + sawImse = true; + } + else { + throw new RuntimeException(t); + } + } + if (!sawImse) { + throw new RuntimeException("monitorexit did not fail"); + } + } + + static void checkWait(Object val) { + boolean sawImse = false; + try { + val.wait(); + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } catch (InterruptedException intExc) { + throw new RuntimeException(intExc); + } + if (!sawImse) { + throw new RuntimeException("wait() did not fail"); + } + + sawImse = false; + try { + val.wait(1l); + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } catch (InterruptedException intExc) { + throw new RuntimeException(intExc); + } + if (!sawImse) { + throw new RuntimeException("wait() did not fail"); + } + + sawImse = false; + try { + val.wait(0l, 100); + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } catch (InterruptedException intExc) { + throw new RuntimeException(intExc); + } + if (!sawImse) { + throw new RuntimeException("wait() did not fail"); + } + } + + static void checkNotify(Object val) { + boolean sawImse = false; + try { + val.notify(); + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } + if (!sawImse) { + throw new RuntimeException("notify() did not fail"); + } + + sawImse = false; + try { + val.notifyAll(); + } catch (IllegalMonitorStateException imse) { + sawImse = true; + } + if (!sawImse) { + throw new RuntimeException("notifyAll() did not fail"); + } + } + + static final __ByValue class MyInt { + final int value; + private MyInt() { value = 0; } + public static MyInt create(int v) { + MyInt mi = __MakeDefault MyInt(); + mi = __WithField(mi.value, v); + return mi; + } + public Object attemptClone() throws CloneNotSupportedException { + try { // Check it is not possible to clone... + MethodHandles.Lookup lookup = MethodHandles.lookup(); + MethodHandle mh = lookup.findVirtual(getClass(), + "clone", + MethodType.methodType(Object.class)); + return mh.invokeExact(this); + } catch (Throwable t) { + if (t instanceof CloneNotSupportedException) { + throw (CloneNotSupportedException) t; + } + throw new RuntimeException(t); + } + } + } + +} --- /dev/null 2018-05-09 12:22:01.004600734 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/ValueWithJni.java 2018-05-21 11:52:19.052187157 +0200 @@ -0,0 +1,79 @@ + +/* + * 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 + * 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. + */ +package runtime.valhalla.valuetypes; + +/* @test + * @summary test JNI functions with values + * @run main/othervm/native -Xint -XX:+EnableValhalla runtime.valhalla.valuetypes.ValueWithJni + */ +public __ByValue final class ValueWithJni { + + static { + System.loadLibrary("ValueWithJni"); + } + + public static void main(String[] args) { + testJniMonitorOps(); + } + + final int x; + private ValueWithJni() { x = 0; } + + public native void doJniMonitorEnter(); + public native void doJniMonitorExit(); + + public static ValueWithJni createValueWithJni(int x) { + ValueWithJni v = __MakeDefault ValueWithJni(); + v = __WithField(v.x, x); + return v; + } + + public static void testJniMonitorOps() { + boolean sawImse = false; + try { + createValueWithJni(0).doJniMonitorEnter(); + } catch (Throwable t) { + sawImse = checkImse(t); + } + if (!sawImse) { + throw new RuntimeException("JNI MonitorEnter did not fail"); + } + sawImse = false; + try { + createValueWithJni(0).doJniMonitorExit(); + } catch (Throwable t) { + sawImse = checkImse(t); + } + if (!sawImse) { + throw new RuntimeException("JNI MonitorExit did not fail"); + } + } + + static boolean checkImse(Throwable t) { + if (t instanceof IllegalMonitorStateException) { + return true; + } + throw new RuntimeException(t); + } +} --- /dev/null 2018-05-09 12:22:01.004600734 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/libValueWithJni.c 2018-05-21 11:52:19.704198773 +0200 @@ -0,0 +1,34 @@ +/* + * 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 + * 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. + */ + +#include + +JNIEXPORT void JNICALL +Java_runtime_valhalla_valuetypes_ValueWithJni_doJniMonitorEnter(JNIEnv *env, jobject obj) { + (*env)->MonitorEnter(env, obj); +} + +JNIEXPORT void JNICALL +Java_runtime_valhalla_valuetypes_ValueWithJni_doJniMonitorExit(JNIEnv *env, jobject obj) { + (*env)->MonitorExit(env, obj); +}