8064703: crash running specjvm98's javac following 8060252
authorroland
Mon, 01 Dec 2014 11:59:56 +0100
changeset 27921 4b932655e504
parent 27919 f1c3cf5737dc
child 27922 5462454ce541
child 28036 0365e6e60e1a
8064703: crash running specjvm98's javac following 8060252 Summary: uncommon trap between arraycopy and initialization may leave array initialized Reviewed-by: kvn, vlivanov, goetz
hotspot/src/share/vm/opto/graphKit.cpp
hotspot/src/share/vm/opto/graphKit.hpp
hotspot/src/share/vm/opto/library_call.cpp
hotspot/test/compiler/arraycopy/TestArrayCopyNoInit.java
--- a/hotspot/src/share/vm/opto/graphKit.cpp	Tue Dec 02 12:24:31 2014 -0800
+++ b/hotspot/src/share/vm/opto/graphKit.cpp	Mon Dec 01 11:59:56 2014 +0100
@@ -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	Tue Dec 02 12:24:31 2014 -0800
+++ b/hotspot/src/share/vm/opto/graphKit.hpp	Mon Dec 01 11:59:56 2014 +0100
@@ -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	Tue Dec 02 12:24:31 2014 -0800
+++ b/hotspot/src/share/vm/opto/library_call.cpp	Mon Dec 01 11:59:56 2014 +0100
@@ -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	Mon Dec 01 11:59:56 2014 +0100
@@ -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");
+        }
+    }
+}