Prepared by: | simonis on Mon Nov 9 17:25:28 CET 2015 |
---|---|
Workspace: | /share/OpenJDK/jdk9-hs-comp/hotspot |
Compare against: | http://hg.openjdk.java.net/jdk9/hs-comp/hotspot |
Compare against version: | 9164 |
Summary of changes: | 443 lines changed: 427 ins; 1 del; 15 mod; 9775 unchg |
Changeset: | hotspot.changeset |
Bug id: | JDK-8141551 : C2 can not handle returns with inccompatible interface arrays |
Author comments: | C2 can not handle returns with incompatible interface arraysGiven the following interfaces and classes: public static interface I1 { public String getName(); } public static interface I2 { public String getName(); } public static class I2C implements I2 { public String getName() { return "I2";} } public static class I21C implements I2, I1 { public String getName() { return "I2 and I1";} } public static class Helper { public static I2 createI2Array0() { return new I2C(); } public static I2[] createI2Array1() { return new I2C[] { new I2C() }; } } we can write the following "pseudo" Java code: public class MeetIncompatibleInterfaceArrays0ASM { public static I1 run() { return Helper.createI2Array0(); // returns I2 } public static void test() { I1 i1 = run(); System.out.println(i1.getName()); } } public class MeetIncompatibleInterfaceArrays1ASM { public static I1[] run() { return Helper.createI2Array1(); // returns I2[] } public static void test() { I1[] i1 = run(); System.out.println(i1[0].getName()); } }
Notice that this is not legal Java code. We would have to use a cast in public static I1[] run() { return (I1[])Helper.createI2Array1(); // returns I2[] }
But in pure bytecode, the public static I1[] run(); Code: 0: invokestatic #16 // Method Helper.createI2Array1:()[LI2; 3: areturn
C2 can compile if (!_exits.control()->is_top() && _gvn.type(ret_phi)->empty()) { // In case of concurrent class loading, the type we set for the // ret_phi in build_exits() may have been too optimistic and the // ret_phi may be top now. #ifdef ASSERT { MutexLockerEx ml(Compile_lock, Mutex::_no_safepoint_check_flag); assert(ret_type->isa_ptr() && C->env()->system_dictionary_modification_counter_changed(), "return value must be well defined"); } #endif C->record_failure(C2Compiler::retry_class_loading_during_parsing()); }
If the compiler didn't find a valid return type for a method, it simply assumes that this could only happen because of concurrent class loading and retries the compilation (by setting the return value to This could should be fixed to something like: if (!_exits.control()->is_top() && _gvn.type(ret_phi)->empty()) { // In case of concurrent class loading, the type we set for the // ret_phi in build_exits() may have been too optimistic and the // ret_phi may be top now. // Otherwise, we've encountered an error and have to mark the method as // not compilable. Just using an assertion instead would be dangerous // as this could lead to an infinite compile loop in non-debug builds. { MutexLockerEx ml(Compile_lock, Mutex::_no_safepoint_check_flag); if (C->env()->system_dictionary_modification_counter_changed()) { C->record_failure(C2Compiler::retry_class_loading_during_parsing()); } else { C->record_method_not_compilable("Can't determine return type."); } } return; } That is, in the case where no concurrent class loading happened, we simply record the method as not compilable in order to prevent an infinite compile loop on that method. Of course that only fixes the implications of C2's inability to correctly compile the respective method. But it will also help if other unexpected problems occur and it is always better to happily run with an uncompiled method instead of crashing while trying to compile it. However a real fix should also improve C2'S type analysis in such a way to allow the compilation of such strange methods. This can be done as follows:
C2 already has code which handles anomalies in its type system due to the different handling of interfaces and classes. During the type computation of a Phi-Node in
Unfortunately this only partially fixes the problem. There's another code in
Finally, meeting interface with class types in the way described above can result in unsymmetric type lattices (see Cliff Click's blog at [1][2][3] for a nice description of the implications). They would be detected and objected in debug builds by special verification code in
This change also contains a regression test which tries to exercise the offending Java bytecode code in various variations like interpreted, C1, C2, with and without inlining and with arrays of different dimensions. I've also verified the proposed code by running the JCK vm and lang test in mixed mode, with and without tiered compilation and with [2] http://www.cliffc.org/blog/2012/02/27/too-much-theory-part-2 [3] http://www.cliffc.org/blog/2012/03/24/too-much-theory-part-3 |
Legend: |
Modified file Deleted file New file |
Cdiffs
Udiffs
Sdiffs
Frames
Old
New
-----
Raw
src/share/vm/opto/cfgnode.cpp
rev 9165 : 8141551: C2 can not handle returns with inccompatible interface arrays31 lines changed: 22 ins; 0 del; 9 mod; 2275 unchg
Cdiffs
Udiffs
Sdiffs
Frames
Old
New
-----
Raw
src/share/vm/opto/parse1.cpp
rev 9165 : 8141551: C2 can not handle returns with inccompatible interface arrays30 lines changed: 26 ins; 1 del; 3 mod; 2328 unchg
Cdiffs
Udiffs
Sdiffs
Frames
Old
New
-----
Raw
src/share/vm/opto/type.cpp
rev 9165 : 8141551: C2 can not handle returns with inccompatible interface arrays29 lines changed: 26 ins; 0 del; 3 mod; 5172 unchg
------ ------ ------
------
---
New
-----
Raw
test/compiler/types/TestMeetIncompatibleInterfaceArrays.java
rev 9165 : 8141551: C2 can not handle returns with inccompatible interface arrays353 lines changed: 353 ins; 0 del; 0 mod; 0 unchg
This code review page was prepared using /share/OpenJDK/webrev/webrev.ksh (vers 25.14-hg+openjdk.java.net).