--- old/src/hotspot/share/opto/cfgnode.cpp 2018-07-03 11:32:54.105925245 +0200 +++ new/src/hotspot/share/opto/cfgnode.cpp 2018-07-03 11:32:53.837924413 +0200 @@ -1646,12 +1646,13 @@ if( phase->type_or_null(r) == Type::TOP ) // Dead code? return NULL; // No change - // If all inputs are value types, push the value type node down through the - // phi because value type nodes should be merged through their input values. + // If all inputs are value types of the same type, push the value type node down + // through the phi because value type nodes should be merged through their input values. if (req() > 2 && in(1) != NULL && in(1)->is_ValueTypeBase() && (can_reshape || in(1)->is_ValueType())) { int opcode = in(1)->Opcode(); uint i = 2; - for (; i < req() && in(i) && in(i)->is_ValueTypeBase(); i++) { + // Check if inputs are values of the same type + for (; i < req() && in(i) && in(i)->is_ValueTypeBase() && in(i)->cmp(*in(1)); i++) { assert(in(i)->Opcode() == opcode, "mixing pointers and values?"); } if (i == req()) { --- old/src/hotspot/share/opto/parse1.cpp 2018-07-03 11:32:54.597926770 +0200 +++ new/src/hotspot/share/opto/parse1.cpp 2018-07-03 11:32:54.329925939 +0200 @@ -1881,7 +1881,6 @@ // It is a bug if we create a phi which sees a garbage value on a live path. // Merging two value types? - assert(n->is_top() || m->is_ValueType() == n->is_ValueType(), "value types should only be merged with other value types"); if (phi != NULL && n->isa_ValueType()) { // Reload current state because it may have been updated by ensure_phi m = map()->in(j); --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java 2018-07-03 11:32:55.077928259 +0200 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestLWorld.java 2018-07-03 11:32:54.813927440 +0200 @@ -1882,4 +1882,47 @@ Test65Value vt = __MakeDefault Test65Value(); test66(vt); } + + // Merging value types of different types + @Test() + public Object test67(Object o, boolean b) { + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + return b ? vt : o; + } + + @DontCompile + public void test67_verifier(boolean warmup) { + test67(new Object(), false); + MyValue1 result = (MyValue1)test67(new Object(), true); + Asserts.assertEQ(result.hash(), hash()); + } + + @Test() + public Object test68(boolean b) { + MyValue1 vt = MyValue1.createWithFieldsInline(rI, rL); + return b ? vt : testValue2; + } + + @DontCompile + public void test68_verifier(boolean warmup) { + MyValue1 result1 = (MyValue1)test68(true); + Asserts.assertEQ(result1.hash(), hash()); + MyValue2 result2 = (MyValue2)test68(false); + Asserts.assertEQ(result2.hash(), testValue2.hash()); + } + + @Test() + public Object test69(boolean b) { + MyValue1 vt1 = MyValue1.createWithFieldsInline(rI, rL); + MyValue2 vt2 = MyValue2.createWithFieldsInline(rI, true); + return b ? vt1 : vt2; + } + + @DontCompile + public void test69_verifier(boolean warmup) { + MyValue1 result1 = (MyValue1)test69(true); + Asserts.assertEQ(result1.hash(), hash()); + MyValue2 result2 = (MyValue2)test69(false); + Asserts.assertEQ(result2.hash(), testValue2.hash()); + } }