--- old/src/hotspot/share/opto/valuetypenode.cpp 2018-01-12 16:34:05.176955662 +0100 +++ new/src/hotspot/share/opto/valuetypenode.cpp 2018-01-12 16:34:04.984955665 +0100 @@ -471,6 +471,7 @@ Node* ValueTypeNode::load_default_oop(PhaseGVN& gvn, ciValueKlass* vk) { // Load the default oop from the java mirror of the given ValueKlass + assert(!vk->is__Value(), "__Value has no default oop"); const TypeInstPtr* tip = TypeInstPtr::make(vk->java_mirror()); Node* base = gvn.makecon(tip); Node* adr = gvn.transform(new AddPNode(base, base, gvn.MakeConX(vk->default_value_offset()))); @@ -493,7 +494,24 @@ } vt->set_field_value(i, value); } - return gvn.transform(vt)->as_ValueType(); + vt = gvn.transform(vt)->as_ValueType(); + assert(vt->is_default(gvn), "must be the default value type"); + return vt; +} + + +bool ValueTypeNode::is_default(PhaseGVN& gvn) const { + if (value_klass()->is__Value()) { + return false; + } + for (uint i = 0; i < field_count(); ++i) { + Node* value = field_value(i); + if (!gvn.type(value)->is_zero_type() && + !(value->is_ValueType() && value->as_ValueType()->is_default(gvn))) { + return false; + } + } + return true; } ValueTypeNode* ValueTypeNode::make_from_oop(PhaseGVN& gvn, Node*& ctl, Node* mem, Node* oop, ciValueKlass* vk, bool null_check, bool buffer_check) { @@ -724,6 +742,16 @@ } Node* ValueTypeNode::Ideal(PhaseGVN* phase, bool can_reshape) { + Node* oop = get_oop(); + + if (is_default(*phase) && !oop->is_Load() && + !(oop->is_DecodeN() && oop->in(1)->is_Load())) { + // Use the pre-allocated oop for default value types + Node* oop = load_default_oop(*phase, value_klass()); + set_oop(oop); + return this; + } + if (!is_allocated(phase) && !value_klass()->is_bufferable()) { // Save base oop if fields are loaded from memory and the value // type is not buffered (in this case we should not use the oop). @@ -737,6 +765,20 @@ if (can_reshape) { PhaseIterGVN* igvn = phase->is_IterGVN(); + + if (is_default(*phase)) { + // Search for allocations of the default value type + for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { + AllocateNode* alloc = fast_out(i)->isa_Allocate(); + if (alloc != NULL && alloc->result_cast() != NULL && alloc->in(AllocateNode::ValueNode) == this) { + // Replace allocation be pre-allocated oop + Node* res = alloc->result_cast(); + Node* oop = load_default_oop(*phase, value_klass()); + igvn->replace_node(res, oop); + } + } + } + if (is_allocated(igvn)) { // Value type is heap allocated, search for safepoint uses for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { @@ -760,6 +802,7 @@ for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { AllocateNode* alloc = fast_out(i)->isa_Allocate(); if (alloc != NULL && alloc->result_cast() != NULL && alloc->in(AllocateNode::ValueNode) == this) { + assert(!is_default(*igvn), "default value type allocation"); Node* res_dom = NULL; if (is_allocated(igvn)) { // The value type is already allocated but still connected to an AllocateNode. --- old/src/hotspot/share/opto/valuetypenode.hpp 2018-01-12 16:34:05.836955653 +0100 +++ new/src/hotspot/share/opto/valuetypenode.hpp 2018-01-12 16:34:05.644955656 +0100 @@ -106,6 +106,9 @@ // Checks if the value type is loaded from memory and if so returns the oop Node* is_loaded(PhaseGVN* phase, ciValueKlass* vk = NULL, Node* base = NULL, int holder_offset = 0); + // Checks if the value type fields are all set to default values + bool is_default(PhaseGVN& gvn) const; + const TypeValueTypePtr* value_type_ptr() const { return TypeValueTypePtr::make(TypePtr::BotPTR, value_klass()); } ciValueKlass* value_klass() const { return type()->is_valuetype()->value_klass(); } --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestBasicFunctionality.java 2018-01-12 16:34:06.384955646 +0100 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestBasicFunctionality.java 2018-01-12 16:34:06.188955648 +0100 @@ -427,7 +427,7 @@ // trap: verify that deoptimization causes the value type to be // correctly allocated. @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE) - @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {2}, failOn = LOAD) + @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD) public long test20(boolean deopt) { MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); MyValue2[] va = new MyValue2[3]; @@ -729,4 +729,69 @@ test34(true, MyValue1.createWithFieldsInline(rI, rL)); test34(false, MyValue1.createWithFieldsInline(rI, rL)); } + + // Verify that the default value type is never allocated. + // C2 code should load and use the default oop from the java mirror. + @Test(failOn = ALLOC + ALLOCA + LOAD + STORE + LOOP + TRAP) + public MyValue3 test35(MyValue3[] va) { + // Explicitly create default value + MyValue3 vt = MyValue3.createDefault(); + va[0] = vt; + staticVal3 = vt; + vt.verify(vt); + + // Load default value from uninitialized value array + MyValue3[] dva = new MyValue3[1]; + staticVal3_copy = dva[0]; + va[1] = dva[0]; + dva[0].verify(dva[0]); + return vt; + } + + @DontCompile + public void test35_verifier(boolean warmup) { + MyValue3 vt = MyValue3.createDefault(); + MyValue3[] va = new MyValue3[2]; + va[0] = MyValue3.create(); + va[1] = MyValue3.create(); + MyValue3 res = test35(va); + res.verify(vt); + staticVal3.verify(vt); + staticVal3_copy.verify(vt); + va[0].verify(vt); + va[1].verify(vt); + } + + // Same as above but manually initialize value type fields to default. + @Test(failOn = ALLOC + ALLOCA + LOAD + STORE + LOOP + TRAP) + public MyValue3 test36(MyValue3 vt, MyValue3[] va) { + vt = MyValue3.setC(vt, (char)0); + vt = MyValue3.setBB(vt, (byte)0); + vt = MyValue3.setS(vt, (short)0); + vt = MyValue3.setI(vt, 0); + vt = MyValue3.setL(vt, 0); + vt = MyValue3.setO(vt, null); + vt = MyValue3.setF1(vt, 0); + vt = MyValue3.setF2(vt, 0); + vt = MyValue3.setF3(vt, 0); + vt = MyValue3.setF4(vt, 0); + vt = MyValue3.setF5(vt, 0); + vt = MyValue3.setF6(vt, 0); + vt = MyValue3.setV1(vt, MyValue3Inline.createDefault()); + va[0] = vt; + staticVal3 = vt; + vt.verify(vt); + return vt; + } + + @DontCompile + public void test36_verifier(boolean warmup) { + MyValue3 vt = MyValue3.createDefault(); + MyValue3[] va = new MyValue3[1]; + va[0] = MyValue3.create(); + MyValue3 res = test36(va[0], va); + res.verify(vt); + staticVal3.verify(vt); + va[0].verify(vt); + } }