# HG changeset patch # User thartmann # Date 1535091443 -7200 # Node ID 1e332d63bd964720f1be1feac2efcbc0cb74b68b # Parent fcf2fdd96a33be0627ee8aeb611dd3b7936ff77b 8209833: C2 compilation fails with "assert(ex_map->jvms()->same_calls_as(_exceptions->jvms())) failed: all collected exceptions must come from the same place" Summary: Deoptimize if exception is thrown in _clone intrinsic. Reviewed-by: kvn diff -r fcf2fdd96a33 -r 1e332d63bd96 src/hotspot/share/opto/graphKit.cpp --- a/src/hotspot/share/opto/graphKit.cpp Thu Aug 23 16:47:53 2018 -0700 +++ b/src/hotspot/share/opto/graphKit.cpp Fri Aug 24 08:17:23 2018 +0200 @@ -1772,7 +1772,7 @@ //return xcall; // no need, caller already has it } -Node* GraphKit::set_results_for_java_call(CallJavaNode* call, bool separate_io_proj) { +Node* GraphKit::set_results_for_java_call(CallJavaNode* call, bool separate_io_proj, bool deoptimize) { if (stopped()) return top(); // maybe the call folded up? // Capture the return value, if any. @@ -1785,7 +1785,7 @@ // Note: Since any out-of-line call can produce an exception, // we always insert an I_O projection from the call into the result. - make_slow_call_ex(call, env()->Throwable_klass(), separate_io_proj); + make_slow_call_ex(call, env()->Throwable_klass(), separate_io_proj, deoptimize); if (separate_io_proj) { // The caller requested separate projections be used by the fall @@ -2571,7 +2571,7 @@ Deoptimization::Action_none); } else { // Create an exception state also. - // Use an exact type if the caller has specified a specific exception. + // Use an exact type if the caller has 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))); diff -r fcf2fdd96a33 -r 1e332d63bd96 src/hotspot/share/opto/graphKit.hpp --- a/src/hotspot/share/opto/graphKit.hpp Thu Aug 23 16:47:53 2018 -0700 +++ b/src/hotspot/share/opto/graphKit.hpp Fri Aug 24 08:17:23 2018 +0200 @@ -693,7 +693,7 @@ // Finish up a java call that was started by set_edges_for_java_call. // Call add_exception on any throw arising from the call. // Return the call result (transformed). - Node* set_results_for_java_call(CallJavaNode* call, bool separate_io_proj = false); + Node* set_results_for_java_call(CallJavaNode* call, bool separate_io_proj = false, bool deoptimize = false); // Similar to set_edges_for_java_call, but simplified for runtime calls. void set_predefined_output_for_runtime_call(Node* call) { diff -r fcf2fdd96a33 -r 1e332d63bd96 src/hotspot/share/opto/library_call.cpp --- a/src/hotspot/share/opto/library_call.cpp Thu Aug 23 16:47:53 2018 -0700 +++ b/src/hotspot/share/opto/library_call.cpp Fri Aug 24 08:17:23 2018 +0200 @@ -4386,7 +4386,8 @@ if (!stopped()) { PreserveJVMState pjvms(this); CallJavaNode* slow_call = generate_method_call(vmIntrinsics::_clone, is_virtual); - Node* slow_result = set_results_for_java_call(slow_call); + // We need to deoptimize on exception (see comment above) + Node* slow_result = set_results_for_java_call(slow_call, false, /* deoptimize */ true); // this->control() comes from set_results_for_java_call result_reg->init_req(_slow_path, control()); result_val->init_req(_slow_path, slow_result); diff -r fcf2fdd96a33 -r 1e332d63bd96 test/hotspot/jtreg/compiler/intrinsics/object/TestClone.java --- a/test/hotspot/jtreg/compiler/intrinsics/object/TestClone.java Thu Aug 23 16:47:53 2018 -0700 +++ b/test/hotspot/jtreg/compiler/intrinsics/object/TestClone.java Fri Aug 24 08:17:23 2018 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2018, 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 @@ -29,7 +29,7 @@ * @library /test/lib * * @run main/othervm -XX:-TieredCompilation -Xbatch - * -XX:CompileCommand=compileonly,compiler.intrinsics.object.TestClone::f + * -XX:CompileCommand=compileonly,compiler.intrinsics.object.TestClone::test* * compiler.intrinsics.object.TestClone */ @@ -37,6 +37,43 @@ import jdk.test.lib.Asserts; +abstract class MyAbstract { + + public Object myClone1() throws CloneNotSupportedException { + return this.clone(); + } + + public Object myClone2() throws CloneNotSupportedException { + return this.clone(); + } + + public Object myClone3() throws CloneNotSupportedException { + return this.clone(); + } +} + +class MyClass1 extends MyAbstract { + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } +} + +class MyClass2 extends MyAbstract { + +} + +class MyClass3 extends MyAbstract implements Cloneable { + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } +} + +class MyClass4 extends MyAbstract implements Cloneable { + +} + public class TestClone implements Cloneable { static class A extends TestClone {} static class B extends TestClone { @@ -56,29 +93,70 @@ } static TestClone a = new A(), b = new B(), c = new C(), d = new D(); - public static Object f(TestClone o) throws CloneNotSupportedException { + public static Object test1(TestClone o) throws CloneNotSupportedException { // Polymorphic call site: >90% Object::clone / <10% other methods return o.clone(); } + public static void test2(MyAbstract obj, boolean shouldThrow) throws Exception { + try { + obj.myClone1(); + } catch (Exception e) { + return; // Expected + } + Asserts.assertFalse(shouldThrow, "No exception thrown"); + } + + public static void test3(MyAbstract obj, boolean shouldThrow) throws Exception { + try { + obj.myClone2(); + } catch (Exception e) { + return; // Expected + } + Asserts.assertFalse(shouldThrow, "No exception thrown"); + } + + public static void test4(MyAbstract obj, boolean shouldThrow) throws Exception { + try { + obj.myClone3(); + } catch (Exception e) { + return; // Expected + } + Asserts.assertFalse(shouldThrow, "No exception thrown"); + } + public static void main(String[] args) throws Exception { TestClone[] 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}; + MyClass1 obj1 = new MyClass1(); + MyClass2 obj2 = new MyClass2(); + MyClass3 obj3 = new MyClass3(); + MyClass4 obj4 = new MyClass4(); + for (int i = 0; i < 15000; i++) { - f(params1[i % params1.length]); + test1(params1[i % params1.length]); + + test2(obj1, true); + test2(obj2, true); + + test3(obj3, false); + test3(obj2, true); + + test4(obj3, false); + test4(obj4, false); } - Asserts.assertTrue(f(a) != a); - Asserts.assertTrue(f(b) == b); - Asserts.assertTrue(f(c) == c); - Asserts.assertTrue(f(d) == d); + Asserts.assertTrue(test1(a) != a); + Asserts.assertTrue(test1(b) == b); + Asserts.assertTrue(test1(c) == c); + Asserts.assertTrue(test1(d) == d); try { - f(null); - throw new AssertionError(""); + test1(null); + throw new AssertionError("No exception thrown"); } catch (NullPointerException e) { /* expected */ } System.out.println("TEST PASSED");