--- old/src/hotspot/cpu/x86/macroAssembler_x86.cpp 2018-07-04 16:48:42.033121669 +0200 +++ new/src/hotspot/cpu/x86/macroAssembler_x86.cpp 2018-07-04 16:48:41.541121675 +0200 @@ -1760,7 +1760,11 @@ jccb(Assembler::notZero, IsInflated); // Attempt stack-locking ... - orptr (tmpReg, markOopDesc::unlocked_value); + orptr(tmpReg, markOopDesc::unlocked_value); + if (EnableValhalla && !UseBiasedLocking) { + // Mask always_locked bit such that we go to the slow path if object is a value type + andptr(tmpReg, ~markOopDesc::biased_lock_bit_in_place); + } movptr(Address(boxReg, 0), tmpReg); // Anticipate successful CAS if (os::is_MP()) { lock(); --- old/src/hotspot/share/oops/klass.inline.hpp 2018-07-04 16:48:44.005121641 +0200 +++ new/src/hotspot/share/oops/klass.inline.hpp 2018-07-04 16:48:43.077121654 +0200 @@ -31,7 +31,7 @@ inline void Klass::set_prototype_header(markOop header) { assert(!is_value() || header->is_always_locked(), "Unexpected prototype"); - _prototype_header = header; + _prototype_header = header; } inline bool Klass::is_null(Klass* obj) { return obj == NULL; } --- old/src/hotspot/share/opto/callnode.cpp 2018-07-04 16:48:45.145121626 +0200 +++ new/src/hotspot/share/opto/callnode.cpp 2018-07-04 16:48:44.701121632 +0200 @@ -849,6 +849,7 @@ } bool CallNode::has_debug_use(Node *n) { + assert(jvms() != NULL, "jvms should not be null"); for (uint i = jvms()->debug_start(); i < jvms()->debug_end(); i++) { Node *arg = in(i); if (arg == n) { --- old/src/hotspot/share/opto/graphKit.cpp 2018-07-04 16:48:45.857121616 +0200 +++ new/src/hotspot/share/opto/graphKit.cpp 2018-07-04 16:48:45.477121621 +0200 @@ -1772,13 +1772,21 @@ arg = vt->allocate(this)->get_oop(); } } else if (t->is_valuetypeptr()) { - // Constant null passed for a value type argument - assert(arg->bottom_type()->remove_speculative() == TypePtr::NULL_PTR, "Anything other than null?"); ciMethod* declared_method = method()->get_method_at_bci(bci()); - int arg_size = declared_method->signature()->arg_size_for_bc(java_bc()); - inc_sp(arg_size); // restore arguments - uncommon_trap(Deoptimization::Reason_null_check, Deoptimization::Action_none); - return; + if (arg->is_ValueTypePtr()) { + // We are calling an Object method with a value type receiver + ValueTypePtrNode* vt = arg->isa_ValueTypePtr(); + assert(i == TypeFunc::Parms && !declared_method->is_static(), "argument must be receiver"); + assert(vt->is_allocated(&gvn()), "argument must be allocated"); + arg = vt->get_oop(); + } else { + // Constant null passed for a value type argument + assert(arg->bottom_type()->remove_speculative() == TypePtr::NULL_PTR, "Anything other than null?"); + int arg_size = declared_method->signature()->arg_size_for_bc(java_bc()); + inc_sp(arg_size); // restore arguments + uncommon_trap(Deoptimization::Reason_null_check, Deoptimization::Action_none); + return; + } } call->init_req(idx, arg); idx++; @@ -3411,6 +3419,14 @@ assert(dead_locals_are_killed(), "should kill locals before sync. point"); + // We cannot lock on a value type + if (obj->is_ValueTypeBase()) { + inc_sp(1); + uncommon_trap(Deoptimization::Reason_class_check, Deoptimization::Action_none); + dec_sp(1); + return NULL; + } + // Box the stack location Node* box = _gvn.transform(new BoxLockNode(next_monitor())); Node* mem = reset_memory(); @@ -3478,6 +3494,7 @@ map()->pop_monitor(); // Kill monitor from debug info return; } + assert(!obj->is_ValueTypeBase(), "should not unlock on value type"); // Memory barrier to avoid floating things down past the locked region insert_mem_bar(Op_MemBarReleaseLock); --- old/src/hotspot/share/opto/macro.cpp 2018-07-04 16:48:46.685121604 +0200 +++ new/src/hotspot/share/opto/macro.cpp 2018-07-04 16:48:46.265121610 +0200 @@ -1804,16 +1804,15 @@ // Helper for PhaseMacroExpand::expand_allocate_common. // Initializes the newly-allocated storage. -Node* -PhaseMacroExpand::initialize_object(AllocateNode* alloc, - Node* control, Node* rawmem, Node* object, - Node* klass_node, Node* length, - Node* size_in_bytes) { +Node* PhaseMacroExpand::initialize_object(AllocateNode* alloc, + Node* control, Node* rawmem, Node* object, + Node* klass_node, Node* length, + Node* size_in_bytes) { InitializeNode* init = alloc->initialization(); // Store the klass & mark bits Node* mark_node = NULL; // For now only enable fast locking for non-array types - if (UseBiasedLocking && (length == NULL)) { + if ((EnableValhalla || UseBiasedLocking) && length == NULL) { mark_node = make_load(control, rawmem, klass_node, in_bytes(Klass::prototype_header_offset()), TypeRawPtr::BOTTOM, T_ADDRESS); } else { mark_node = makecon(TypeRawPtr::make((address)markOopDesc::prototype())); @@ -2729,13 +2728,7 @@ transform_later(slowpath_false); Node* rawmem = new StorePNode(slowpath_false, mem, top_adr, TypeRawPtr::BOTTOM, new_top, MemNode::unordered); transform_later(rawmem); - Node* mark_node = NULL; - // For now only enable fast locking for non-array types - if (UseBiasedLocking) { - mark_node = make_load(slowpath_false, rawmem, klass_node, in_bytes(Klass::prototype_header_offset()), TypeRawPtr::BOTTOM, T_ADDRESS); - } else { - mark_node = makecon(TypeRawPtr::make((address)markOopDesc::prototype())); - } + Node* mark_node = mark_node = makecon(TypeRawPtr::make((address)markOopDesc::always_locked_prototype())); rawmem = make_store(slowpath_false, rawmem, old_top, oopDesc::mark_offset_in_bytes(), mark_node, T_ADDRESS); rawmem = make_store(slowpath_false, rawmem, old_top, oopDesc::klass_offset_in_bytes(), klass_node, T_METADATA); if (UseCompressedClassPointers) { --- old/src/hotspot/share/runtime/basicLock.cpp 2018-07-04 16:48:47.913121587 +0200 +++ new/src/hotspot/share/runtime/basicLock.cpp 2018-07-04 16:48:47.185121597 +0200 @@ -61,6 +61,10 @@ // There are some subtle concurrency issues, however, and since the benefit is // is small (given the support for inflated fast-path locking in the fast_lock, etc) // we'll leave that optimization for another time. + if (obj->is_value()) { + // Ignore value types + return; + } if (displaced_header()->is_neutral()) { ObjectSynchronizer::inflate_helper(obj); --- old/src/hotspot/share/runtime/deoptimization.cpp 2018-07-04 16:48:49.233121569 +0200 +++ new/src/hotspot/share/runtime/deoptimization.cpp 2018-07-04 16:48:48.693121577 +0200 @@ -1319,7 +1319,7 @@ Thread* thread = Thread::current(); for (int i = 0; i < monitors->length(); i++) { MonitorInfo* mon_info = monitors->at(i); - if (!mon_info->eliminated() && mon_info->owner() != NULL) { + if (!mon_info->eliminated() && mon_info->owner() != NULL && !mon_info->owner()->is_value()) { objects_to_revoke->append(Handle(thread, mon_info->owner())); } } --- old/src/hotspot/share/runtime/sharedRuntime.cpp 2018-07-04 16:48:50.261121555 +0200 +++ new/src/hotspot/share/runtime/sharedRuntime.cpp 2018-07-04 16:48:49.737121562 +0200 @@ -2037,7 +2037,8 @@ // on AARCH64 and ARM until JDK-8153107 is resolved. if (ARM_ONLY((SyncFlags & 256) != 0 &&) AARCH64_ONLY((SyncFlags & 256) != 0 &&) - !SafepointSynchronize::is_synchronizing()) { + !SafepointSynchronize::is_synchronizing() && + (!EnableValhalla || !_obj->klass()->is_value())) { // Only try quick_enter() if we're not trying to reach a safepoint // so that the calling thread reaches the safepoint more quickly. if (ObjectSynchronizer::quick_enter(_obj, thread, lock)) return; @@ -2054,17 +2055,23 @@ Handle h_obj(THREAD, obj); if (UseBiasedLocking) { // Retry fast entry if bias is revoked to avoid unnecessary inflation - ObjectSynchronizer::fast_enter(h_obj, lock, true, CHECK); + ObjectSynchronizer::fast_enter(h_obj, lock, true, THREAD); } else { - ObjectSynchronizer::slow_enter(h_obj, lock, CHECK); + ObjectSynchronizer::slow_enter(h_obj, lock, THREAD); + } + if (HAS_PENDING_EXCEPTION) { + // Deoptimize the (compiled) caller frame + assert(EnableValhalla && h_obj()->klass()->is_value(), "must be a value type"); + RegisterMap reg_map(thread); + frame caller_frame = thread->last_frame().sender(®_map); + Deoptimization::deoptimize_frame(thread, caller_frame.id()); } - assert(!HAS_PENDING_EXCEPTION, "Should have no exception here"); JRT_BLOCK_END JRT_END // Handles the uncommon cases of monitor unlocking in compiled code JRT_LEAF(void, SharedRuntime::complete_monitor_unlocking_C(oopDesc* _obj, BasicLock* lock, JavaThread * THREAD)) - oop obj(_obj); + oop obj(_obj); assert(JavaThread::current() == THREAD, "invariant"); // I'm not convinced we need the code contained by MIGHT_HAVE_PENDING anymore // testing was unable to ever fire the assert that guarded it so I have removed it. --- old/src/hotspot/share/runtime/vframeArray.cpp 2018-07-04 16:48:51.149121543 +0200 +++ new/src/hotspot/share/runtime/vframeArray.cpp 2018-07-04 16:48:50.693121549 +0200 @@ -58,9 +58,8 @@ } void vframeArrayElement::fill_in(compiledVFrame* vf, bool realloc_failures) { - -// Copy the information from the compiled vframe to the -// interpreter frame we will be creating to replace vf + // Copy the information from the compiled vframe to the + // interpreter frame we will be creating to replace vf _method = vf->method(); _bci = vf->raw_bci(); @@ -90,7 +89,7 @@ if (monitor->owner_is_scalar_replaced()) { dest->set_obj(NULL); } else { - assert(monitor->owner() == NULL || (!monitor->owner()->is_unlocked() && !monitor->owner()->has_bias_pattern()), "object must be null or locked, and unbiased"); + assert(monitor->owner() == NULL || monitor->owner()->is_value() || (!monitor->owner()->is_unlocked() && !monitor->owner()->has_bias_pattern()), "object must be null or locked, and unbiased"); dest->set_obj(monitor->owner()); monitor->lock()->move_to(monitor->owner(), dest->lock()); } @@ -192,6 +191,15 @@ pc = Interpreter::deopt_reexecute_entry(method(), bcp); } else { bcp = method()->bcp_from(bci()); + if (EnableValhalla && thread->deopt_compiled_method()->is_compiled_by_c2() && + *bcp == Bytecodes::_monitorenter && exec_mode == Deoptimization::Unpack_exception) { + // For monitorenter, the slow path of C2 compiled code will call SharedRuntime::complete_monitor_locking_C + // which installs an exception and deoptimize the compiled caller if the object is a value type. + // Increment bci to place it in the proper exception handler range (right after monitorenter). + int len = Bytecodes::length_for(Bytecodes::_monitorenter); + int next_bci = bci() + len; + bcp = method()->bcp_from(next_bci); + } pc = Interpreter::deopt_continue_after_entry(method(), bcp, callee_parameters, is_top_frame); use_next_mdp = true; } @@ -199,7 +207,8 @@ // Monitorenter and pending exceptions: // - // For Compiler2, there should be no pending exception when deoptimizing at monitorenter + // Monitorenter on a value type will throw an exception in the runtime and deoptimize (handled above). + // Otherwise, for Compiler2, there should be no pending exception when deoptimizing at monitorenter // because there is no safepoint at the null pointer check (it is either handled explicitly // or prior to the monitorenter) and asynchronous exceptions are not made "pending" by the // runtime interface for the slow case (see JRT_ENTRY_FOR_MONITORENTER). If an asynchronous --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java 2018-07-04 16:48:51.913121532 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java 2018-07-04 16:48:51.549121537 +0200 @@ -121,7 +121,7 @@ Asserts.assertTrue(test4(), "test4 failed"); } - // TODO re-enable once Object method support is implemented +// Disabled until 8206274 is fixed /* // Test toString() method @Test(failOn = ALLOC + STORE + LOAD) @@ -137,17 +137,17 @@ */ // Test hashCode() method - // @Test() - // public int test6(MyValue1 v) { - // return v.hashCode(); - // } - - // @DontCompile - // public void test6_verifier(boolean warmup) { - // MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); - // int res = test6(v); - // Asserts.assertEQ(res, v.hashCode()); - // } + @Test() + public int test6(MyValue1 v) { + return v.hashCode(); + } + + @DontCompile + public void test6_verifier(boolean warmup) { + MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); + int res = test6(v); + Asserts.assertEQ(res, v.hashCode()); + } // Test default value type array creation via reflection @Test() @@ -262,17 +262,17 @@ } // Test hashCode() method - // @Test() - // public int test15(Object v) { - // return v.hashCode(); - // } - - // @DontCompile - // public void test15_verifier(boolean warmup) { - // MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); - // int res = test15(v); - // Asserts.assertEQ(res, v.hashCode()); - // } + @Test() + public int test15(Object v) { + return v.hashCode(); + } + + @DontCompile + public void test15_verifier(boolean warmup) { + MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); + int res = test15(v); + Asserts.assertEQ(res, v.hashCode()); + } @Test() public int test16(Object v) { @@ -309,4 +309,35 @@ int res = test18(v); Asserts.assertEQ(res, System.identityHashCode(v)); } + + // hashCode() and toString() with different value types + @Test() + public int test19(MyValue1 vt1, MyValue1 vt2, boolean b) { + MyValue1 res = b ? vt1 : vt2; + return res.hashCode(); + } + + @DontCompile + public void test19_verifier(boolean warmup) { + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + int res = test19(vt, vt, true); + Asserts.assertEQ(res, vt.hashCode()); + res = test19(vt, vt, false); + Asserts.assertEQ(res, vt.hashCode()); + } + + @Test() + public String test20(MyValue1 vt1, MyValue1 vt2, boolean b) { + MyValue1 res = b ? vt1 : vt2; + return res.toString(); + } + + @DontCompile + public void test20_verifier(boolean warmup) { + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + String res = test20(vt, vt, true); + Asserts.assertEQ(res, vt.toString()); + res = test20(vt, vt, false); + Asserts.assertEQ(res, vt.toString()); + } } --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java 2018-07-04 16:48:52.997121517 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java 2018-07-04 16:48:52.597121523 +0200 @@ -46,17 +46,17 @@ * -XX:ValueFieldMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1 * compiler.valhalla.valuetypes.TestLWorld * @run main/othervm/timeout=120 -Xbootclasspath/a:. -ea -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-UseCompressedOops + * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-UseCompressedOops -XX:-UseOptoBiasInlining * -XX:+EnableValhalla -XX:-ValueTypePassFieldsAsArgs -XX:-ValueTypeReturnedAsFields -XX:+ValueArrayFlatten * -XX:ValueFieldMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatSize=-1 -XX:ValueArrayElemMaxFlatOops=-1 * compiler.valhalla.valuetypes.TestLWorld * @run main/othervm/timeout=120 -Xbootclasspath/a:. -ea -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-UseCompressedOops + * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:-UseCompressedOops -XX:-UseBiasedLocking * -XX:+EnableValhalla -XX:+ValueTypePassFieldsAsArgs -XX:+ValueTypeReturnedAsFields -XX:-ValueArrayFlatten * -XX:ValueFieldMaxFlatSize=0 -XX:ValueArrayElemMaxFlatSize=0 -XX:ValueArrayElemMaxFlatOops=0 * -DVerifyIR=false compiler.valhalla.valuetypes.TestLWorld * @run main/othervm/timeout=120 -Xbootclasspath/a:. -ea -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:+AlwaysIncrementalInline + * -XX:+UnlockExperimentalVMOptions -XX:+WhiteBoxAPI -XX:+AlwaysIncrementalInline -XX:-UseBiasedLocking * -XX:+EnableValhalla -XX:-ValueTypePassFieldsAsArgs -XX:-ValueTypeReturnedAsFields -XX:+ValueArrayFlatten * -XX:ValueFieldMaxFlatSize=0 -XX:ValueArrayElemMaxFlatSize=0 -XX:ValueArrayElemMaxFlatOops=0 * -XX:-MonomorphicArrayCheck @@ -860,7 +860,7 @@ @DontCompile public void test26_verifier(boolean warmup) { - interfaceField1 = valueField1; + objectField1 = valueField1; MyInterface result = null; result = test26(0); Asserts.assertEQ(((MyObject)result).x, rI); @@ -1925,4 +1925,168 @@ MyValue2 result2 = (MyValue2)test69(false); Asserts.assertEQ(result2.hash(), testValue2.hash()); } + + // Test synchronization on value types + @Test() + public void test70(Object vt) { + synchronized (vt) { + throw new RuntimeException("test70 failed: synchronization on value type should not succeed"); + } + } + + @DontCompile + public void test70_verifier(boolean warmup) { + try { + test70(testValue1); + throw new RuntimeException("test70 failed: no exception thrown"); + } catch (IllegalMonitorStateException ex) { + // Expected + } + } + + @ForceInline + public void test71_inline(Object vt) { + synchronized (vt) { + throw new RuntimeException("test71 failed: synchronization on value type should not succeed"); + } + } + + @Test() + public void test71(MyValue1 vt) { + test71_inline(vt); + } + + @DontCompile + public void test71_verifier(boolean warmup) { + try { + test71(testValue1); + throw new RuntimeException("test71 failed: no exception thrown"); + } catch (IllegalMonitorStateException ex) { + // Expected + } + } + + @ForceInline + public void test72_inline(Object vt) { + synchronized (vt) { + throw new RuntimeException("test72 failed: synchronization on value type should not succeed"); + } + } + + @Test() + public void test72() { + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + test72_inline(vt); + } + + @DontCompile + public void test72_verifier(boolean warmup) { + try { + test72(); + throw new RuntimeException("test72 failed: no exception thrown"); + } catch (IllegalMonitorStateException ex) { + // Expected + } + } + + @Test() + public void test73(Object o, boolean b) { + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + Object sync = b ? vt : o; + synchronized (sync) { + if (b) { + throw new RuntimeException("test73 failed: synchronization on value type should not succeed"); + } + } + } + + @DontCompile + public void test73_verifier(boolean warmup) { + test73(new Object(), false); + try { + test73(new Object(), true); + throw new RuntimeException("test73 failed: no exception thrown"); + } catch (IllegalMonitorStateException ex) { + // Expected + } + } + + @Test() + public void test74(boolean b) { + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + Object sync = b ? vt : testValue2; + synchronized (sync) { + throw new RuntimeException("test74 failed: synchronization on value type should not succeed"); + } + } + + @DontCompile + public void test74_verifier(boolean warmup) { + try { + test74(false); + throw new RuntimeException("test74 failed: no exception thrown"); + } catch (IllegalMonitorStateException ex) { + // Expected + } + try { + test74(true); + throw new RuntimeException("test74 failed: no exception thrown"); + } catch (IllegalMonitorStateException ex) { + // Expected + } + } + + // Test catching the IllegalMonitorStateException in compiled code + @Test() + public void test75(Object vt) { + boolean thrown = false; + try { + synchronized (vt) { + throw new RuntimeException("test75 failed: no exception thrown"); + } + } catch (IllegalMonitorStateException ex) { + thrown = true; + } + if (!thrown) { + throw new RuntimeException("test75 failed: no exception thrown"); + } + } + + @DontCompile + public void test75_verifier(boolean warmup) { + test75(testValue1); + } + + @Test() + public void test76(Object o) { + try { + synchronized (o) { } + } catch (IllegalMonitorStateException ex) { + // Expected + return; + } + throw new RuntimeException("test76 failed: no exception thrown"); + } + + @DontCompile + public void test76_verifier(boolean warmup) { + test76(testValue1); + } + + // Test synchronization without any instructions in the synchronized block + @Test() + public void test77(Object o) { + synchronized (o) { } + } + + @DontCompile + public void test77_verifier(boolean warmup) { + try { + test77(testValue1); + } catch (IllegalMonitorStateException ex) { + // Expected + return; + } + throw new RuntimeException("test77 failed: no exception thrown"); + } } --- old/test/hotspot/jtreg/runtime/valhalla/valuetypes/ObjectMethods.java 2018-07-04 16:48:54.217121500 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/ObjectMethods.java 2018-07-04 16:48:53.753121507 +0200 @@ -36,6 +36,9 @@ * @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 + * @run main/othervm -Xcomp -XX:+EnableValhalla -XX:+UseBiasedLocking -XX:+UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods + * @run main/othervm -Xcomp -XX:+EnableValhalla -XX:-UseBiasedLocking -XX:-UseCompressedClassPointers runtime.valhalla.valuetypes.ObjectMethods + * @run main/othervm -Xcomp -XX:+EnableValhalla -noverify runtime.valhalla.valuetypes.ObjectMethods noverify */ public class ObjectMethods { @@ -138,8 +141,7 @@ } catch (Throwable t) { if (t instanceof IllegalMonitorStateException) { sawImse = true; - } - else { + } else { throw new RuntimeException(t); } }