# HG changeset patch # User roland # Date 1417692125 0 # Node ID 5462454ce541f14abfa39e65b67082a35d4ba800 # Parent b34081fac06c9530a51e32aceb8f7b7e60f76205# Parent 4b932655e504ee4041e88241c3c86920df3e471e Merge diff -r b34081fac06c -r 5462454ce541 hotspot/src/share/vm/opto/graphKit.cpp --- a/hotspot/src/share/vm/opto/graphKit.cpp Thu Dec 04 09:52:15 2014 +0100 +++ b/hotspot/src/share/vm/opto/graphKit.cpp Thu Dec 04 11:22:05 2014 +0000 @@ -2809,7 +2809,8 @@ */ Node* GraphKit::maybe_cast_profiled_obj(Node* obj, ciKlass* type, - bool not_null) { + bool not_null, + SafePointNode* sfpt) { // type == NULL if profiling tells us this object is always null if (type != NULL) { Deoptimization::DeoptReason class_reason = Deoptimization::Reason_speculate_class_check; @@ -2831,7 +2832,13 @@ ciKlass* exact_kls = type; Node* slow_ctl = type_check_receiver(exact_obj, exact_kls, 1.0, &exact_obj); - { + if (sfpt != NULL) { + GraphKit kit(sfpt->jvms()); + PreserveJVMState pjvms(&kit); + kit.set_control(slow_ctl); + kit.uncommon_trap(class_reason, + Deoptimization::Action_maybe_recompile); + } else { PreserveJVMState pjvms(this); set_control(slow_ctl); uncommon_trap(class_reason, diff -r b34081fac06c -r 5462454ce541 hotspot/src/share/vm/opto/graphKit.hpp --- a/hotspot/src/share/vm/opto/graphKit.hpp Thu Dec 04 09:52:15 2014 +0100 +++ b/hotspot/src/share/vm/opto/graphKit.hpp Thu Dec 04 11:22:05 2014 +0000 @@ -418,7 +418,8 @@ // Cast obj to type and emit guard unless we had too many traps here already Node* maybe_cast_profiled_obj(Node* obj, ciKlass* type, - bool not_null = false); + bool not_null = false, + SafePointNode* sfpt = NULL); // Cast obj to not-null on this path Node* cast_not_null(Node* obj, bool do_replace_in_map = true); diff -r b34081fac06c -r 5462454ce541 hotspot/src/share/vm/opto/library_call.cpp --- a/hotspot/src/share/vm/opto/library_call.cpp Thu Dec 04 09:52:15 2014 +0100 +++ b/hotspot/src/share/vm/opto/library_call.cpp Thu Dec 04 11:22:05 2014 +0000 @@ -4697,10 +4697,6 @@ Node* dest_offset = argument(3); // type: int Node* length = argument(4); // type: int - // Check for allocation before we add nodes that would confuse - // tightly_coupled_allocation() - AllocateArrayNode* alloc = tightly_coupled_allocation(dest, NULL); - // The following tests must be performed // (1) src and dest are arrays. // (2) src and dest arrays must have elements of the same BasicType @@ -4717,6 +4713,36 @@ src = null_check(src, T_ARRAY); dest = null_check(dest, T_ARRAY); + // Check for allocation before we add nodes that would confuse + // tightly_coupled_allocation() + AllocateArrayNode* alloc = tightly_coupled_allocation(dest, NULL); + + SafePointNode* sfpt = NULL; + if (alloc != NULL) { + // The JVM state for uncommon traps between the allocation and + // arraycopy is set to the state before the allocation: if the + // initialization is performed by the array copy, we don't want to + // go back to the interpreter with an unitialized array. + JVMState* old_jvms = alloc->jvms(); + JVMState* jvms = old_jvms->clone_shallow(C); + uint size = alloc->req(); + sfpt = new SafePointNode(size, jvms); + jvms->set_map(sfpt); + for (uint i = 0; i < size; i++) { + sfpt->init_req(i, alloc->in(i)); + } + // re-push array length for deoptimization + sfpt->ins_req(jvms->stkoff() + jvms->sp(), alloc->in(AllocateNode::ALength)); + jvms->set_sp(jvms->sp()+1); + jvms->set_monoff(jvms->monoff()+1); + jvms->set_scloff(jvms->scloff()+1); + jvms->set_endoff(jvms->endoff()+1); + jvms->set_should_reexecute(true); + + sfpt->set_i_o(map()->i_o()); + sfpt->set_memory(map()->memory()); + } + bool notest = false; const Type* src_type = _gvn.type(src); @@ -4762,14 +4788,14 @@ if (could_have_src && could_have_dest) { // This is going to pay off so emit the required guards if (!has_src) { - src = maybe_cast_profiled_obj(src, src_k); + src = maybe_cast_profiled_obj(src, src_k, true, sfpt); src_type = _gvn.type(src); top_src = src_type->isa_aryptr(); has_src = (top_src != NULL && top_src->klass() != NULL); src_spec = true; } if (!has_dest) { - dest = maybe_cast_profiled_obj(dest, dest_k); + dest = maybe_cast_profiled_obj(dest, dest_k, true); dest_type = _gvn.type(dest); top_dest = dest_type->isa_aryptr(); has_dest = (top_dest != NULL && top_dest->klass() != NULL); @@ -4810,10 +4836,10 @@ if (could_have_src && could_have_dest) { // If we can have both exact types, emit the missing guards if (could_have_src && !src_spec) { - src = maybe_cast_profiled_obj(src, src_k); + src = maybe_cast_profiled_obj(src, src_k, true, sfpt); } if (could_have_dest && !dest_spec) { - dest = maybe_cast_profiled_obj(dest, dest_k); + dest = maybe_cast_profiled_obj(dest, dest_k, true); } } } @@ -4855,13 +4881,28 @@ Node* not_subtype_ctrl = gen_subtype_check(src_klass, dest_klass); if (not_subtype_ctrl != top()) { - PreserveJVMState pjvms(this); - set_control(not_subtype_ctrl); - uncommon_trap(Deoptimization::Reason_intrinsic, - Deoptimization::Action_make_not_entrant); - assert(stopped(), "Should be stopped"); + if (sfpt != NULL) { + GraphKit kit(sfpt->jvms()); + PreserveJVMState pjvms(&kit); + kit.set_control(not_subtype_ctrl); + kit.uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_make_not_entrant); + assert(kit.stopped(), "Should be stopped"); + } else { + PreserveJVMState pjvms(this); + set_control(not_subtype_ctrl); + uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_make_not_entrant); + assert(stopped(), "Should be stopped"); + } } - { + if (sfpt != NULL) { + GraphKit kit(sfpt->jvms()); + kit.set_control(_gvn.transform(slow_region)); + kit.uncommon_trap(Deoptimization::Reason_intrinsic, + Deoptimization::Action_make_not_entrant); + assert(kit.stopped(), "Should be stopped"); + } else { PreserveJVMState pjvms(this); set_control(_gvn.transform(slow_region)); uncommon_trap(Deoptimization::Reason_intrinsic, diff -r b34081fac06c -r 5462454ce541 hotspot/test/compiler/arraycopy/TestArrayCopyNoInit.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/arraycopy/TestArrayCopyNoInit.java Thu Dec 04 11:22:05 2014 +0000 @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2014, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8064703 + * @summary Deoptimization between array allocation and arraycopy may result in non initialized array + * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:TypeProfileLevel=020 TestArrayCopyNoInit + * + */ + +import java.lang.invoke.*; + +public class TestArrayCopyNoInit { + + static int[] m1(int[] src) { + int[] dest = new int[10]; + try { + System.arraycopy(src, 0, dest, 0, 10); + } catch (NullPointerException npe) { + } + return dest; + } + + static int[] m2(Object src, boolean flag) { + Class tmp = src.getClass(); + if (flag) { + return null; + } + int[] dest = new int[10]; + try { + System.arraycopy(src, 0, dest, 0, 10); + } catch (ArrayStoreException npe) { + } + return dest; + } + + static int[] m3(int[] src, int src_offset) { + int tmp = src[0]; + int[] dest = new int[10]; + try { + System.arraycopy(src, src_offset, dest, 0, 10); + } catch (IndexOutOfBoundsException npe) { + } + return dest; + } + + static int[] m4(int[] src, int length) { + int tmp = src[0]; + int[] dest = new int[10]; + try { + System.arraycopy(src, 0, dest, 0, length); + } catch (IndexOutOfBoundsException npe) { + } + return dest; + } + + static TestArrayCopyNoInit[] m5(Object[] src) { + Object tmp = src[0]; + TestArrayCopyNoInit[] dest = new TestArrayCopyNoInit[10]; + System.arraycopy(src, 0, dest, 0, 0); + return dest; + } + + static class A { + } + + static class B extends A { + } + + static class C extends B { + } + + static class D extends C { + } + + static class E extends D { + } + + static class F extends E { + } + + static class G extends F { + } + + static class H extends G { + } + + static class I extends H { + } + + static H[] m6(Object[] src) { + Object tmp = src[0]; + H[] dest = new H[10]; + System.arraycopy(src, 0, dest, 0, 0); + return dest; + } + + static Object m7_src(Object src) { + return src; + } + + static int[] m7(Object src, boolean flag) { + Class tmp = src.getClass(); + if (flag) { + return null; + } + src = m7_src(src); + int[] dest = new int[10]; + try { + System.arraycopy(src, 0, dest, 0, 10); + } catch (ArrayStoreException npe) { + } + return dest; + } + + static public void main(String[] args) throws Throwable { + boolean success = true; + int[] src = new int[10]; + TestArrayCopyNoInit[] src2 = new TestArrayCopyNoInit[10]; + int[] res = null; + TestArrayCopyNoInit[] res2 = null; + Object src_obj = new Object(); + + for (int i = 0; i < 20000; i++) { + m1(src); + } + + res = m1(null); + for (int i = 0; i < res.length; i++) { + if (res[i] != 0) { + success = false; + System.out.println("Uninitialized array following NPE"); + break; + } + } + + for (int i = 0; i < 20000; i++) { + if ((i%2) == 0) { + m2(src, false); + } else { + m2(src_obj, true); + } + } + res = m2(src_obj, false); + for (int i = 0; i < res.length; i++) { + if (res[i] != 0) { + success = false; + System.out.println("Uninitialized array following failed array check"); + break; + } + } + + for (int i = 0; i < 20000; i++) { + m3(src, 0); + } + res = m3(src, -1); + for (int i = 0; i < res.length; i++) { + if (res[i] != 0) { + success = false; + System.out.println("Uninitialized array following failed src offset check"); + break; + } + } + + for (int i = 0; i < 20000; i++) { + m4(src, 0); + } + res = m4(src, -1); + for (int i = 0; i < res.length; i++) { + if (res[i] != 0) { + success = false; + System.out.println("Uninitialized array following failed length check"); + break; + } + } + + for (int i = 0; i < 20000; i++) { + m5(src2); + } + res2 = m5(new Object[10]); + for (int i = 0; i < res2.length; i++) { + if (res2[i] != null) { + success = false; + System.out.println("Uninitialized array following failed type check"); + break; + } + } + + H[] src3 = new H[10]; + I b = new I(); + for (int i = 0; i < 20000; i++) { + m6(src3); + } + H[] res3 = m6(new Object[10]); + for (int i = 0; i < res3.length; i++) { + if (res3[i] != null) { + success = false; + System.out.println("Uninitialized array following failed full type check"); + break; + } + } + + for (int i = 0; i < 20000; i++) { + if ((i%2) == 0) { + m7(src, false); + } else { + m7(src_obj, true); + } + } + res = m7(src_obj, false); + for (int i = 0; i < res.length; i++) { + if (res[i] != 0) { + success = false; + System.out.println("Uninitialized array following failed type check with return value profiling"); + break; + } + } + + if (!success) { + throw new RuntimeException("Some tests failed"); + } + } +}