--- 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,
--- 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);
--- 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,
--- /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");
+ }
+ }
+}