8072016: Array copy may cause infinite cycle of deoptimization/compilation
Summary: Infinite deoptimization/recompilation cycles in case of arraycopy with tightly coupled allocation
Reviewed-by: kvn, vlivanov
--- a/hotspot/src/share/vm/opto/graphKit.cpp Fri Feb 06 13:50:44 2015 +0100
+++ b/hotspot/src/share/vm/opto/graphKit.cpp Mon Feb 09 15:10:58 2015 +0100
@@ -2819,8 +2819,12 @@
if (type != NULL) {
Deoptimization::DeoptReason class_reason = Deoptimization::Reason_speculate_class_check;
Deoptimization::DeoptReason null_reason = Deoptimization::Reason_speculate_null_check;
+ ciMethod* trap_method = (sfpt == NULL) ? method() : sfpt->jvms()->method();
+ int trap_bci = (sfpt == NULL) ? bci() : sfpt->jvms()->bci();
+
if (!too_many_traps(null_reason) && !too_many_recompiles(null_reason) &&
- !too_many_traps(class_reason) && !too_many_recompiles(class_reason)) {
+ !C->too_many_traps(trap_method, trap_bci, class_reason) &&
+ !C->too_many_recompiles(trap_method, trap_bci, class_reason)) {
Node* not_null_obj = NULL;
// not_null is true if we know the object is not null and
// there's no need for a null check
--- a/hotspot/src/share/vm/opto/library_call.cpp Fri Feb 06 13:50:44 2015 +0100
+++ b/hotspot/src/share/vm/opto/library_call.cpp Mon Feb 09 15:10:58 2015 +0100
@@ -4740,6 +4740,8 @@
// tightly_coupled_allocation()
AllocateArrayNode* alloc = tightly_coupled_allocation(dest, NULL);
+ ciMethod* trap_method = method();
+ int trap_bci = bci();
SafePointNode* sfpt = NULL;
if (alloc != NULL) {
// The JVM state for uncommon traps between the allocation and
@@ -4764,6 +4766,9 @@
sfpt->set_i_o(map()->i_o());
sfpt->set_memory(map()->memory());
+
+ trap_method = jvms->method();
+ trap_bci = jvms->bci();
}
bool validated = false;
@@ -4868,7 +4873,7 @@
}
}
- if (!too_many_traps(Deoptimization::Reason_intrinsic) && !src->is_top() && !dest->is_top()) {
+ if (!C->too_many_traps(trap_method, trap_bci, Deoptimization::Reason_intrinsic) && !src->is_top() && !dest->is_top()) {
// validate arguments: enables transformation the ArrayCopyNode
validated = true;
--- a/hotspot/test/compiler/arraycopy/TestArrayCopyNoInit.java Fri Feb 06 13:50:44 2015 +0100
+++ b/hotspot/test/compiler/arraycopy/TestArrayCopyNoInit.java Mon Feb 09 15:10:58 2015 +0100
@@ -29,8 +29,6 @@
*
*/
-import java.lang.invoke.*;
-
public class TestArrayCopyNoInit {
static int[] m1(int[] src) {
@@ -134,7 +132,7 @@
return dest;
}
- static public void main(String[] args) throws Throwable {
+ static public void main(String[] args) {
boolean success = true;
int[] src = new int[10];
TestArrayCopyNoInit[] src2 = new TestArrayCopyNoInit[10];
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/arraycopy/TestArrayCopyNoInitDeopt.java Mon Feb 09 15:10:58 2015 +0100
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2015, 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 8072016
+ * @summary Infinite deoptimization/recompilation cycles in case of arraycopy with tightly coupled allocation
+ * @library /testlibrary /../../test/lib /compiler/whitebox
+ * @build TestArrayCopyNoInitDeopt
+ * @run main ClassFileInstaller sun.hotspot.WhiteBox
+ * @run main ClassFileInstaller com.oracle.java.testlibrary.Platform
+ * @run main/othervm -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:TypeProfileLevel=020
+ * TestArrayCopyNoInitDeopt
+ *
+ */
+
+
+import sun.hotspot.WhiteBox;
+import sun.hotspot.code.NMethod;
+import com.oracle.java.testlibrary.Platform;
+import java.lang.reflect.*;
+
+public class TestArrayCopyNoInitDeopt {
+
+ public static int[] m1(Object src) {
+ if (src == null) return null;
+ int[] dest = new int[10];
+ try {
+ System.arraycopy(src, 0, dest, 0, 10);
+ } catch (ArrayStoreException npe) {
+ }
+ return dest;
+ }
+
+ static Object m2_src(Object src) {
+ return src;
+ }
+
+ public static int[] m2(Object src) {
+ if (src == null) return null;
+ src = m2_src(src);
+ int[] dest = new int[10];
+ try {
+ System.arraycopy(src, 0, dest, 0, 10);
+ } catch (ArrayStoreException npe) {
+ }
+ return dest;
+ }
+
+ private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+
+ static boolean deoptimize(Method method, Object src_obj) throws Exception {
+ for (int i = 0; i < 10; i++) {
+ method.invoke(null, src_obj);
+ if (!WHITE_BOX.isMethodCompiled(method)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static public void main(String[] args) throws Exception {
+ if (Platform.isServer()) {
+ int[] src = new int[10];
+ Object src_obj = new Object();
+ Method method_m1 = TestArrayCopyNoInitDeopt.class.getMethod("m1", Object.class);
+ Method method_m2 = TestArrayCopyNoInitDeopt.class.getMethod("m2", Object.class);
+
+ // Warm up
+ for (int i = 0; i < 20000; i++) {
+ m1(src);
+ }
+
+ // And make sure m1 is compiled by C2
+ WHITE_BOX.enqueueMethodForCompilation(method_m1, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+
+ if (!WHITE_BOX.isMethodCompiled(method_m1)) {
+ throw new RuntimeException("m1 not compiled");
+ }
+
+ // should deoptimize for type check
+ if (!deoptimize(method_m1, src_obj)) {
+ throw new RuntimeException("m1 not deoptimized");
+ }
+
+ WHITE_BOX.enqueueMethodForCompilation(method_m1, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+
+ if (!WHITE_BOX.isMethodCompiled(method_m1)) {
+ throw new RuntimeException("m1 not recompiled");
+ }
+
+ if (deoptimize(method_m1, src_obj)) {
+ throw new RuntimeException("m1 deoptimized again");
+ }
+
+ // Same test as above but with speculative types
+
+ // Warm up & make sure we collect type profiling
+ for (int i = 0; i < 20000; i++) {
+ m2(src);
+ }
+
+ // And make sure m2 is compiled by C2
+ WHITE_BOX.enqueueMethodForCompilation(method_m2, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+
+ if (!WHITE_BOX.isMethodCompiled(method_m2)) {
+ throw new RuntimeException("m2 not compiled");
+ }
+
+ // should deoptimize for speculative type check
+ if (!deoptimize(method_m2, src_obj)) {
+ throw new RuntimeException("m2 not deoptimized");
+ }
+
+ WHITE_BOX.enqueueMethodForCompilation(method_m2, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+
+ if (!WHITE_BOX.isMethodCompiled(method_m2)) {
+ throw new RuntimeException("m2 not recompiled");
+ }
+
+ // should deoptimize for actual type check
+ if (!deoptimize(method_m2, src_obj)) {
+ throw new RuntimeException("m2 not deoptimized");
+ }
+
+ WHITE_BOX.enqueueMethodForCompilation(method_m2, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);
+
+ if (!WHITE_BOX.isMethodCompiled(method_m2)) {
+ throw new RuntimeException("m2 not recompiled");
+ }
+
+ if (deoptimize(method_m2, src_obj)) {
+ throw new RuntimeException("m2 deoptimized again");
+ }
+ }
+ }
+}