--- old/src/hotspot/share/opto/macro.cpp 2020-02-12 14:31:49.371522815 +0100 +++ new/src/hotspot/share/opto/macro.cpp 2020-02-12 14:31:49.083522819 +0100 @@ -1559,7 +1559,11 @@ call->init_req( TypeFunc::Memory , slow_mem ); // may gc ptrs call->init_req( TypeFunc::ReturnAdr, alloc->in(TypeFunc::ReturnAdr) ); call->init_req( TypeFunc::FramePtr, alloc->in(TypeFunc::FramePtr) ); - + if (alloc->_larval) { + // Tag the klass pointer to let the runtime know that this is a larval allocation + klass_node = transform_later(new CastP2XNode(NULL, klass_node)); + klass_node = transform_later(new OrXNode(klass_node, _igvn.MakeConX(1))); + } call->init_req(TypeFunc::Parms+0, klass_node); if (length != NULL) { call->init_req(TypeFunc::Parms+1, length); @@ -1595,29 +1599,16 @@ // result_phi_rawmem (unless we are only generating a slow call when // both memory projections are combined) if (!always_slow && _memproj_fallthrough != NULL) { - for (DUIterator_Fast imax, i = _memproj_fallthrough->fast_outs(imax); i < imax; i++) { - Node *use = _memproj_fallthrough->fast_out(i); - _igvn.rehash_node_delayed(use); - imax -= replace_input(use, _memproj_fallthrough, result_phi_rawmem); - // back up iterator - --i; - } + _igvn.replace_in_uses(_memproj_fallthrough, result_phi_rawmem); } // Now change uses of _memproj_catchall to use _memproj_fallthrough and delete // _memproj_catchall so we end up with a call that has only 1 memory projection. - if (_memproj_catchall != NULL ) { + if (_memproj_catchall != NULL) { if (_memproj_fallthrough == NULL) { _memproj_fallthrough = new ProjNode(call, TypeFunc::Memory); transform_later(_memproj_fallthrough); } - for (DUIterator_Fast imax, i = _memproj_catchall->fast_outs(imax); i < imax; i++) { - Node *use = _memproj_catchall->fast_out(i); - _igvn.rehash_node_delayed(use); - imax -= replace_input(use, _memproj_catchall, _memproj_fallthrough); - // back up iterator - --i; - } - assert(_memproj_catchall->outcnt() == 0, "all uses must be deleted"); + _igvn.replace_in_uses(_memproj_catchall, _memproj_fallthrough); _igvn.remove_dead_node(_memproj_catchall); } @@ -1627,29 +1618,16 @@ // (it is different from memory projections where both projections are // combined in such case). if (_ioproj_fallthrough != NULL) { - for (DUIterator_Fast imax, i = _ioproj_fallthrough->fast_outs(imax); i < imax; i++) { - Node *use = _ioproj_fallthrough->fast_out(i); - _igvn.rehash_node_delayed(use); - imax -= replace_input(use, _ioproj_fallthrough, result_phi_i_o); - // back up iterator - --i; - } + _igvn.replace_in_uses(_ioproj_fallthrough, result_phi_i_o); } // Now change uses of _ioproj_catchall to use _ioproj_fallthrough and delete // _ioproj_catchall so we end up with a call that has only 1 i_o projection. - if (_ioproj_catchall != NULL ) { + if (_ioproj_catchall != NULL) { if (_ioproj_fallthrough == NULL) { _ioproj_fallthrough = new ProjNode(call, TypeFunc::I_O); transform_later(_ioproj_fallthrough); } - for (DUIterator_Fast imax, i = _ioproj_catchall->fast_outs(imax); i < imax; i++) { - Node *use = _ioproj_catchall->fast_out(i); - _igvn.rehash_node_delayed(use); - imax -= replace_input(use, _ioproj_catchall, _ioproj_fallthrough); - // back up iterator - --i; - } - assert(_ioproj_catchall->outcnt() == 0, "all uses must be deleted"); + _igvn.replace_in_uses(_ioproj_catchall, _ioproj_fallthrough); _igvn.remove_dead_node(_ioproj_catchall); } --- old/src/hotspot/share/opto/runtime.cpp 2020-02-12 14:31:49.947522807 +0100 +++ new/src/hotspot/share/opto/runtime.cpp 2020-02-12 14:31:49.747522810 +0100 @@ -200,7 +200,13 @@ SharedRuntime::_new_instance_ctr++; // new instance requires GC #endif assert(check_compiled_frame(thread), "incorrect caller"); - + // Check if this is a larval buffer allocation (klass pointer is tagged) + bool is_larval = false; + if (is_set_nth_bit((intptr_t)klass, 0)) { + clear_nth_bit((intptr_t&)klass, 0); + assert(klass->is_value(), "must be value"); + is_larval = true; + } // These checks are cheap to make and support reflective allocation. int lh = klass->layout_helper(); if (Klass::layout_helper_needs_slow_path(lh) || !InstanceKlass::cast(klass)->is_initialized()) { @@ -214,7 +220,10 @@ if (!HAS_PENDING_EXCEPTION) { // Scavenge and allocate an instance. Handle holder(THREAD, klass->klass_holder()); // keep the klass alive - oop result = InstanceKlass::cast(klass)->allocate_instance(THREAD); + instanceOop result = InstanceKlass::cast(klass)->allocate_instance(THREAD); + if (is_larval) { + result->set_mark(result->mark().enter_larval_state()); + } thread->set_vm_result(result); // Pass oops back through thread local storage. Our apparent type to Java --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java 2020-02-12 14:31:50.499522799 +0100 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestIntrinsics.java 2020-02-12 14:31:50.287522802 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2020, 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 @@ -1031,4 +1031,26 @@ Class result = test55(Integer.class); Asserts.assertEQ(result, Integer.class); } + + // Same as test39 but Unsafe.putInt to buffer is not intrinsified/compiled + @DontCompile + public void test56_callee(MyValue1? v) { // Use ? here to make sure the argument is not scalarized (otherwise larval information is lost) + U.putInt(v, X_OFFSET, rI); + } + + @Test() + @Warmup(10000) // Fill up the TLAB to trigger slow path allocation + public MyValue1 test56(MyValue1 v) { + v = U.makePrivateBuffer(v); + test56_callee(v); + v = U.finishPrivateBuffer(v); + return v; + } + + @DontCompile + public void test56_verifier(boolean warmup) { + MyValue1 v = MyValue1.createWithFieldsInline(rI, rL); + MyValue1 res = test56(v.setX(v, 0)); + Asserts.assertEQ(res.hash(), v.hash()); + } }