--- old/src/hotspot/share/oops/klass.inline.hpp 2018-07-09 14:55:50.926895872 +0200 +++ new/src/hotspot/share/oops/klass.inline.hpp 2018-07-09 14:55:50.638895876 +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-09 14:55:51.414895865 +0200 +++ new/src/hotspot/share/opto/callnode.cpp 2018-07-09 14:55:51.146895869 +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-09 14:55:51.934895858 +0200 +++ new/src/hotspot/share/opto/graphKit.cpp 2018-07-09 14:55:51.646895862 +0200 @@ -1397,6 +1397,7 @@ //------------------------------cast_not_null---------------------------------- // Cast obj to not-null on this path Node* GraphKit::cast_not_null(Node* obj, bool do_replace_in_map) { + assert(!obj->is_ValueType(), "should not cast value type"); const Type *t = _gvn.type(obj); const Type *t_not_null = t->join_speculative(TypePtr::NOTNULL); // Object is already not-null? @@ -1772,13 +1773,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++; @@ -3274,13 +3283,34 @@ return res; } +// Deoptimize if 'obj' is a value type +void GraphKit::gen_value_type_guard(Node* obj, int nargs) { + assert(EnableValhalla, "should only be used if value types are enabled"); + Node* bol = NULL; + if (obj->is_ValueTypeBase()) { + bol = intcon(0); + } else { + Node* kls = load_object_klass(obj); + Node* flags_addr = basic_plus_adr(kls, in_bytes(Klass::access_flags_offset())); + Node* flags = make_load(NULL, flags_addr, TypeInt::INT, T_INT, MemNode::unordered); + Node* is_value = _gvn.transform(new AndINode(flags, intcon(JVM_ACC_VALUE))); + Node* cmp = _gvn.transform(new CmpINode(is_value, intcon(0))); + bol = _gvn.transform(new BoolNode(cmp, BoolTest::eq)); + } + { BuildCutout unless(this, bol, PROB_MAX); + inc_sp(nargs); + uncommon_trap(Deoptimization::Reason_class_check, + Deoptimization::Action_none); + } +} + // Deoptimize if 'ary' is flattened or if 'obj' is null and 'ary' is a value type array void GraphKit::gen_value_type_array_guard(Node* ary, Node* obj, Node* elem_klass) { assert(EnableValhalla, "should only be used if value types are enabled"); if (elem_klass == NULL) { // Load array element klass Node* kls = load_object_klass(ary); - Node* k_adr = basic_plus_adr(kls, kls, in_bytes(ArrayKlass::element_klass_offset())); + Node* k_adr = basic_plus_adr(kls, in_bytes(ArrayKlass::element_klass_offset())); elem_klass = _gvn.transform(LoadKlassNode::make(_gvn, NULL, immutable_memory(), k_adr, TypeInstPtr::KLASS)); } // Check if element is a value type @@ -3326,7 +3356,7 @@ if (ValueArrayFlatten) { // Cannot statically determine if array is flattened, emit runtime check Node* kls = load_object_klass(ary); - Node* lhp = basic_plus_adr(kls, kls, in_bytes(Klass::layout_helper_offset())); + Node* lhp = basic_plus_adr(kls, in_bytes(Klass::layout_helper_offset())); Node* layout_val = make_load(NULL, lhp, TypeInt::INT, T_INT, MemNode::unordered); layout_val = _gvn.transform(new RShiftINode(layout_val, intcon(Klass::_lh_array_tag_shift))); Node* cmp = _gvn.transform(new CmpINode(layout_val, intcon(Klass::_lh_array_tag_vt_value))); @@ -3406,6 +3436,13 @@ if( !GenerateSynchronizationCode ) return NULL; // Not locking things? + + // We cannot lock on a value type + const TypeOopPtr* objptr = _gvn.type(obj)->make_oopptr(); + if (objptr->can_be_value_type()) { + gen_value_type_guard(obj, 1); + } + if (stopped()) // Dead monitor? return NULL; @@ -3478,6 +3515,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/graphKit.hpp 2018-07-09 14:55:52.474895851 +0200 +++ new/src/hotspot/share/opto/graphKit.hpp 2018-07-09 14:55:52.202895854 +0200 @@ -823,6 +823,7 @@ Node* gen_checkcast( Node *subobj, Node* superkls, Node* *failure_control = NULL ); + void gen_value_type_guard(Node* obj, int nargs = 0); void gen_value_type_array_guard(Node* ary, Node* obj, Node* elem_klass = NULL); void gen_flattened_array_guard(Node* ary, int nargs = 0); --- old/src/hotspot/share/opto/macro.cpp 2018-07-09 14:55:53.014895843 +0200 +++ new/src/hotspot/share/opto/macro.cpp 2018-07-09 14:55:52.746895847 +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/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java 2018-07-09 14:55:53.690895834 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java 2018-07-09 14:55:53.326895839 +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-09 14:55:54.458895823 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java 2018-07-09 14:55:54.170895827 +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/compiler/valhalla/valuetypes/ValueTypeTest.java 2018-07-09 14:55:55.246895812 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/ValueTypeTest.java 2018-07-09 14:55:54.954895816 +0200 @@ -101,7 +101,7 @@ // Pre-defined settings private static final List defaultFlags = Arrays.asList( "-XX:-BackgroundCompilation", "-XX:CICompilerCount=1", - "-XX:+PrintCompilation", "-XX:+PrintInlining", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", + "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly", "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,java.lang.invoke.*::*", "-XX:CompileCommand=compileonly,java.lang.Long::sum", --- old/test/hotspot/jtreg/runtime/valhalla/valuetypes/ObjectMethods.java 2018-07-09 14:55:55.858895804 +0200 +++ new/test/hotspot/jtreg/runtime/valhalla/valuetypes/ObjectMethods.java 2018-07-09 14:55:55.530895808 +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); } }