8033626: assert(ex_map->jvms()->same_calls_as(_exceptions->jvms())) failed: all collected exceptions must come from the same place
Reviewed-by: kvn, roland
--- a/hotspot/src/share/vm/opto/graphKit.cpp Tue Jun 10 08:53:22 2014 +0200
+++ b/hotspot/src/share/vm/opto/graphKit.cpp Tue Jun 10 10:00:59 2014 +0000
@@ -2513,7 +2513,7 @@
//------------------------------make_slow_call_ex------------------------------
// Make the exception handler hookups for the slow call
-void GraphKit::make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool separate_io_proj) {
+void GraphKit::make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool separate_io_proj, bool deoptimize) {
if (stopped()) return;
// Make a catch node with just two handlers: fall-through and catch-all
@@ -2527,11 +2527,17 @@
set_i_o(i_o);
if (excp != top()) {
- // Create an exception state also.
- // Use an exact type if the caller has specified a specific exception.
- const Type* ex_type = TypeOopPtr::make_from_klass_unique(ex_klass)->cast_to_ptr_type(TypePtr::NotNull);
- Node* ex_oop = new CreateExNode(ex_type, control(), i_o);
- add_exception_state(make_exception_state(_gvn.transform(ex_oop)));
+ if (deoptimize) {
+ // Deoptimize if an exception is caught. Don't construct exception state in this case.
+ uncommon_trap(Deoptimization::Reason_unhandled,
+ Deoptimization::Action_none);
+ } else {
+ // Create an exception state also.
+ // Use an exact type if the caller has specified a specific exception.
+ const Type* ex_type = TypeOopPtr::make_from_klass_unique(ex_klass)->cast_to_ptr_type(TypePtr::NotNull);
+ Node* ex_oop = new CreateExNode(ex_type, control(), i_o);
+ add_exception_state(make_exception_state(_gvn.transform(ex_oop)));
+ }
}
}
@@ -3352,7 +3358,8 @@
//---------------------------set_output_for_allocation-------------------------
Node* GraphKit::set_output_for_allocation(AllocateNode* alloc,
- const TypeOopPtr* oop_type) {
+ const TypeOopPtr* oop_type,
+ bool deoptimize_on_exception) {
int rawidx = Compile::AliasIdxRaw;
alloc->set_req( TypeFunc::FramePtr, frameptr() );
add_safepoint_edges(alloc);
@@ -3360,7 +3367,7 @@
set_control( _gvn.transform(new ProjNode(allocx, TypeFunc::Control) ) );
// create memory projection for i_o
set_memory ( _gvn.transform( new ProjNode(allocx, TypeFunc::Memory, true) ), rawidx );
- make_slow_call_ex(allocx, env()->Throwable_klass(), true);
+ make_slow_call_ex(allocx, env()->Throwable_klass(), true, deoptimize_on_exception);
// create a memory projection as for the normal control path
Node* malloc = _gvn.transform(new ProjNode(allocx, TypeFunc::Memory));
@@ -3438,9 +3445,11 @@
// The optional arguments are for specialized use by intrinsics:
// - If 'extra_slow_test' if not null is an extra condition for the slow-path.
// - If 'return_size_val', report the the total object size to the caller.
+// - deoptimize_on_exception controls how Java exceptions are handled (rethrow vs deoptimize)
Node* GraphKit::new_instance(Node* klass_node,
Node* extra_slow_test,
- Node* *return_size_val) {
+ Node* *return_size_val,
+ bool deoptimize_on_exception) {
// Compute size in doublewords
// The size is always an integral number of doublewords, represented
// as a positive bytewise size stored in the klass's layout_helper.
@@ -3508,7 +3517,7 @@
size, klass_node,
initial_slow_test);
- return set_output_for_allocation(alloc, oop_type);
+ return set_output_for_allocation(alloc, oop_type, deoptimize_on_exception);
}
//-------------------------------new_array-------------------------------------
@@ -3518,7 +3527,8 @@
Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable)
Node* length, // number of array elements
int nargs, // number of arguments to push back for uncommon trap
- Node* *return_size_val) {
+ Node* *return_size_val,
+ bool deoptimize_on_exception) {
jint layout_con = Klass::_lh_neutral_value;
Node* layout_val = get_layout_helper(klass_node, layout_con);
int layout_is_con = (layout_val == NULL);
@@ -3661,7 +3671,7 @@
ary_type = ary_type->is_aryptr()->cast_to_size(length_type);
}
- Node* javaoop = set_output_for_allocation(alloc, ary_type);
+ Node* javaoop = set_output_for_allocation(alloc, ary_type, deoptimize_on_exception);
// Cast length on remaining path to be as narrow as possible
if (map()->find_edge(length) >= 0) {
--- a/hotspot/src/share/vm/opto/graphKit.hpp Tue Jun 10 08:53:22 2014 +0200
+++ b/hotspot/src/share/vm/opto/graphKit.hpp Tue Jun 10 10:00:59 2014 +0000
@@ -807,7 +807,7 @@
// merge in all memory slices from new_mem, along the given path
void merge_memory(Node* new_mem, Node* region, int new_path);
- void make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool separate_io_proj);
+ void make_slow_call_ex(Node* call, ciInstanceKlass* ex_klass, bool separate_io_proj, bool deoptimize = false);
// Helper functions to build synchronizations
int next_monitor();
@@ -849,13 +849,16 @@
// implementation of object creation
Node* set_output_for_allocation(AllocateNode* alloc,
- const TypeOopPtr* oop_type);
+ const TypeOopPtr* oop_type,
+ bool deoptimize_on_exception=false);
Node* get_layout_helper(Node* klass_node, jint& constant_value);
Node* new_instance(Node* klass_node,
Node* slow_test = NULL,
- Node* *return_size_val = NULL);
+ Node* *return_size_val = NULL,
+ bool deoptimize_on_exception = false);
Node* new_array(Node* klass_node, Node* count_val, int nargs,
- Node* *return_size_val = NULL);
+ Node* *return_size_val = NULL,
+ bool deoptimize_on_exception = false);
// java.lang.String helpers
Node* load_String_offset(Node* ctrl, Node* str);
--- a/hotspot/src/share/vm/opto/library_call.cpp Tue Jun 10 08:53:22 2014 +0200
+++ b/hotspot/src/share/vm/opto/library_call.cpp Tue Jun 10 10:00:59 2014 +0000
@@ -4579,7 +4579,10 @@
// It's an instance, and it passed the slow-path tests.
PreserveJVMState pjvms(this);
Node* obj_size = NULL;
- Node* alloc_obj = new_instance(obj_klass, NULL, &obj_size);
+ // Need to deoptimize on exception from allocation since Object.clone intrinsic
+ // is reexecuted if deoptimization occurs and there could be problems when merging
+ // exception state between multiple Object.clone versions (reexecute=true vs reexecute=false).
+ Node* alloc_obj = new_instance(obj_klass, NULL, &obj_size, /*deoptimize_on_exception=*/true);
copy_to_clone(obj, alloc_obj, obj_size, false, !use_ReduceInitialCardMarks());
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/intrinsics/clone/TestObjectClone.java Tue Jun 10 10:00:59 2014 +0000
@@ -0,0 +1,80 @@
+/*
+ * 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 8033626
+ * @summary assert(ex_map->jvms()->same_calls_as(_exceptions->jvms())) failed: all collected exceptions must come from the same place
+ * @library /testlibrary
+ * @run main/othervm -XX:-TieredCompilation -Xbatch -XX:CompileOnly=TestObjectClone::f TestObjectClone
+ */
+import com.oracle.java.testlibrary.Asserts;
+
+public class TestObjectClone implements Cloneable {
+ static class A extends TestObjectClone {}
+ static class B extends TestObjectClone {
+ public B clone() {
+ return (B)TestObjectClone.b;
+ }
+ }
+ static class C extends TestObjectClone {
+ public C clone() {
+ return (C)TestObjectClone.c;
+ }
+ }
+ static class D extends TestObjectClone {
+ public D clone() {
+ return (D)TestObjectClone.d;
+ }
+ }
+ static TestObjectClone a = new A(), b = new B(), c = new C(), d = new D();
+
+ public static Object f(TestObjectClone o) throws CloneNotSupportedException {
+ // Polymorphic call site: >90% Object::clone / <10% other methods
+ return o.clone();
+ }
+
+ public static void main(String[] args) throws Exception {
+ TestObjectClone[] params1 = {a, a, a, a, a, a, a, a, a, a, a,
+ a, a, a, a, a, a, a, a, a, a, a,
+ a, a, a, a, a, a, a, a, a, a, a,
+ b, c, d};
+
+ for (int i = 0; i < 15000; i++) {
+ f(params1[i % params1.length]);
+ }
+
+ Asserts.assertTrue(f(a) != a);
+ Asserts.assertTrue(f(b) == b);
+ Asserts.assertTrue(f(c) == c);
+ Asserts.assertTrue(f(d) == d);
+
+ try {
+ f(null);
+ throw new AssertionError("");
+ } catch (NullPointerException e) { /* expected */ }
+
+ System.out.println("TEST PASSED");
+ }
+}