Community
Participate
Working Groups
The attached .java file and .class output file (from Sun's javac for version 5) is a file that will not compile on the Eclipse compiler with the -1.5 flag set or the -inlineJSR flag set. The _jspService method inlines to around 130K bytes of code. This file - the Java source is programmatically generated - is from a PMR from Oracle (PMR successfully dealt with). 1) Sun in their 1.5 compiler will generate jsr bytecodes (complete with the 1.5 version tag) if they fail to inline the jsr's. 2) This file will not copmile under the Sun Java6 javac early access compiler. The problem is that the finally clause of just under 500 bytes is inlined about 270 times. The format of the file is that there are multiple return points scattered throughout the source code and each of these becomes an inline point. Is it possible for the compiler to generate code that goto's a common jsr followed by return point reducing the number of inline locations. I suspect this pattern may show up elsewhere with other customers and it appears that Sun currently can't handle it for Java6, so it would be an advantage for us to be able to compile the class without jsr's. I will attach enough files (inlinetest.zip) to compile the class with the following command line: D:\test2>javac -cp ascontrol.jar;oc4j-internal.jar;ojsp.jar;servlet.jar;uix2.jar _topology.java
There were errors when trying to attach the sample code - A .zip of the source file and supporting libraries for compilation have been forwarded to Olivier Thomann.
Actually, using regular branch to subroutine (and back, using some tableswitch to map back to proper location) is not doable due to some classfile limitations. e.g. for code: public class X { public static void main(String[] args) { new X().foo(args); } int foo(String[] args){ try { if (args == null) return 0; if (args.length == 0) return args.length; } finally { System.out.print("finally"); } System.out.println(); return 2; } } I could generate: // Method descriptor #20 ([Ljava/lang/String;)I // Stack: 3, Locals: 5 int foo(String[] args); 0 aload_1 [args] 1 ifnonnull 11 4 iconst_0 5 istore_2 6 goto 39 9 iconst_0 10 ireturn 11 aload_1 [args] 12 arraylength 13 ifne 80 16 aload_1 [args] 17 arraylength 18 istore 4 20 iconst_1 21 istore_2 22 goto 39 25 iload 4 27 ireturn 28 goto 80 31 astore_3 32 iconst_2 33 istore_2 34 goto 39 37 aload_3 38 athrow 39 getstatic System.out : PrintStream [23] 42 ldc <String "finally"> [29] 44 invokevirtual PrintStream.print(String) : void [31] 47 iload_2 48 tableswitch default: 85 case 0: 9 case 1: 25 case 2: 37 case 3: 85 80 iconst_4 81 istore_2 82 goto 39 85 getstatic System.out : PrintStream [23] 88 invokevirtual PrintStream.println() : void [37] 91 iconst_2 92 ireturn Exception Table: [pc: 0, pc: 9] -> 31 when : any [pc: 11, pc: 25] -> 31 when : any [pc: 28, pc: 31] -> 31 when : any [pc: 80, pc: 85] -> 31 when : any Line numbers: [pc: 0, line: 7] [pc: 11, line: 8] [pc: 31, line: 9] [pc: 37, line: 11] [pc: 39, line: 10] [pc: 47, line: 11] [pc: 85, line: 12] [pc: 91, line: 13] Local variable table: [pc: 0, pc: 93] local: this index: 0 type: X [pc: 0, pc: 93] local: args index: 1 type: String[] But this is illegal from verifier standpoint: java.lang.VerifyError: (class: X, method: foo signature: ([Ljava/lang/String;)I) Accessing value from uninitialized register 4 at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:164) at Compile.main(Compile.java:108)
We can hack to issue JSR bytecodes as a fallback plan, but in Java7 these are forbidden. So this would only be a temporary workaround.
Philippe - I understand that some inlining situations would lead to inline bloat always. The sample I sent has multiple "return" places leading to excessive inlining. My suggestion is, in the presence of multiple returns, to replace the returns with goto's to a single return point and inline there. The line number table for the return source code lines would be the goto bytecodes, not the return bytecode. This is a solution to a limited subset of the inlining problems.
Also it wouldn't work in situation where some non-constant value is returned. So it would only improve situation for return instruction in void methods or return constant. In other circumstances, it would break bytecode verification, as the returned value must be cached in a variable which is treated as unitialized from bytecode verifier standpoint.
Different approach, which could benefit this usecase. Simply branch into similar subroutine sequences (either inlining finally or not). e.g. public class X { public static void main(String[] args) { System.out.println(new X().foo(args)); } String foo(String[] args) { try { if (args == null) return "KO"; switch(args.length) { case 0: return "OK"; case 1: return "KO"; case 3: return "OK"; default: return "KO"; } } finally { System.out.print("FINALLY:"); } } } // Method descriptor #26 ([Ljava/lang/String;)Ljava/lang/String; // Stack: 2, Locals: 3 String foo(String[] args); 0 aload_1 [args] 1 ifnonnull 15 4 getstatic System.out : PrintStream [16] 7 ldc <String "FINALLY:"> [35] 9 invokevirtual PrintStream.print(String) : void [37] 12 ldc <String "KO"> [40] 14 areturn 15 aload_1 [args] 16 arraylength 17 tableswitch default: 65 case 0: 48 case 1: 59 case 2: 65 case 3: 62 48 getstatic System.out : PrintStream [16] 51 ldc <String "FINALLY:"> [35] 53 invokevirtual PrintStream.print(String) : void [37] 56 ldc <String "OK"> [42] 58 areturn 59 goto 4 62 goto 48 65 goto 4 68 astore_2 69 getstatic System.out : PrintStream [16] 72 ldc <String "FINALLY:"> [35] 74 invokevirtual PrintStream.print(String) : void [37] 77 aload_2 78 athrow Exception Table: [pc: 0, pc: 4] -> 68 when : any [pc: 15, pc: 48] -> 68 when : any [pc: 59, pc: 68] -> 68 when : any Reuse occurs as soon as the sequence is the strictly the same: - same break/continue target label - return from void method - same constant being returned Other situations are not optimizable, since the returned value needs to be cached before finally runs, and thus each sequence may denote a different cached value.
WITHOUT optimization, the last test case would produce: // Method descriptor #26 ([Ljava/lang/String;)Ljava/lang/String; // Stack: 2, Locals: 3 String foo(String[] args); 0 aload_1 [args] 1 ifnonnull 15 4 getstatic System.out : PrintStream [16] 7 ldc <String "FINALLY:"> [35] 9 invokevirtual PrintStream.print(String) : void [37] 12 ldc <String "KO"> [40] 14 areturn 15 aload_1 [args] 16 arraylength 17 tableswitch default: 81 case 0: 48 case 1: 59 case 2: 81 case 3: 70 48 getstatic System.out : PrintStream [16] 51 ldc <String "FINALLY:"> [35] 53 invokevirtual PrintStream.print(String) : void [37] 56 ldc <String "OK"> [42] 58 areturn 59 getstatic System.out : PrintStream [16] 62 ldc <String "FINALLY:"> [35] 64 invokevirtual PrintStream.print(String) : void [37] 67 ldc <String "KO"> [40] 69 areturn 70 getstatic System.out : PrintStream [16] 73 ldc <String "FINALLY:"> [35] 75 invokevirtual PrintStream.print(String) : void [37] 78 ldc <String "OK"> [42] 80 areturn 81 getstatic System.out : PrintStream [16] 84 ldc <String "FINALLY:"> [35] 86 invokevirtual PrintStream.print(String) : void [37] 89 ldc <String "KO"> [40] 91 areturn 92 astore_2 93 getstatic System.out : PrintStream [16] 96 ldc <String "FINALLY:"> [35] 98 invokevirtual PrintStream.print(String) : void [37] 101 aload_2 102 athrow Exception Table: [pc: 0, pc: 4] -> 92 when : any [pc: 15, pc: 48] -> 92 when : any
In presence of multiple scopes with different local variables, the optimization is causing grief. e.g. public class X { public void save() { int a = 3; try { Object warnings = null; try { Object contexts = null; try { System.out.println(warnings); return; } catch (NullPointerException npe) { System.out.println(contexts); return; } } catch (Exception e) { return; } } catch (Exception e) { int dummy1 = 11; System.out.println(dummy1); int dummy2 = 12; System.out.println(dummy2); return; } finally { int b = 4; System.out.println("#save -> " + b + a); } } public static void main(String[] args) { new X().save(); } } produces // Method descriptor #6 ()V // Stack: 4, Locals: 7 public void save(); 0 iconst_3 1 istore_1 [a] 2 aconst_null 3 astore_2 [warnings] 4 aconst_null 5 astore_3 [contexts] 6 getstatic System.out : PrintStream [15] 9 aload_2 [warnings] 10 invokevirtual PrintStream.println(Object) : void [21] SUBROUTINE 13 iconst_4 14 istore 6 [b] 16 getstatic System.out : PrintStream [15] 19 new StringBuilder [27] 22 dup 23 ldc <String "#save -> "> [29] 25 invokespecial StringBuilder(String) [31] 28 iload 6 [b] 30 invokevirtual StringBuilder.append(int) : StringBuilder [34] 33 iload_1 [a] 34 invokevirtual StringBuilder.append(int) : StringBuilder [34] 37 invokevirtual StringBuilder.toString() : String [38] 40 invokevirtual PrintStream.println(String) : void [42] 43 return CATCH NULL POINTER EXCEPTION 44 astore 4 [npe] 46 getstatic System.out : PrintStream [15] 49 aload_3 [contexts] 50 invokevirtual PrintStream.println(Object) : void [21] 53 goto 13 CATCH EXCEPTION 56 astore_3 [e] 57 goto 13 CATCH EXCEPTION 60 astore_2 [e] 61 bipush 11 63 istore_3 [dummy1] 64 getstatic System.out : PrintStream [15] 67 iload_3 [dummy1] 68 invokevirtual PrintStream.println(int) : void [44] 71 bipush 12 73 istore 4 [dummy2] 75 getstatic System.out : PrintStream [15] 78 iload 4 [dummy2] 80 invokevirtual PrintStream.println(int) : void [44] 83 goto 13 CATCH ANY 86 astore 5 88 iconst_4 89 istore 6 [b] 91 getstatic System.out : PrintStream [15] 94 new StringBuilder [27] 97 dup 98 ldc <String "#save -> "> [29] 100 invokespecial StringBuilder(String) [31] 103 iload 6 [b] 105 invokevirtual StringBuilder.append(int) : StringBuilder [34] 108 iload_1 [a] 109 invokevirtual StringBuilder.append(int) : StringBuilder [34] 112 invokevirtual StringBuilder.toString() : String [38] 115 invokevirtual PrintStream.println(String) : void [42] 118 aload 5 120 athrow Exception Table: [pc: 6, pc: 44] -> 44 when : java.lang.NullPointerException [pc: 4, pc: 56] -> 56 when : java.lang.Exception [pc: 2, pc: 13] -> 60 when : java.lang.Exception [pc: 43, pc: 60] -> 60 when : java.lang.Exception [pc: 2, pc: 13] -> 86 when : any [pc: 44, pc: 86] -> 86 when : any Line numbers: [pc: 0, line: 3] [pc: 2, line: 5] [pc: 4, line: 7] [pc: 6, line: 9] [pc: 13, line: 25] [pc: 16, line: 26] [pc: 43, line: 10] [pc: 44, line: 11] [pc: 46, line: 12] [pc: 53, line: 13] [pc: 56, line: 15] [pc: 57, line: 16] [pc: 60, line: 18] [pc: 61, line: 19] [pc: 64, line: 20] [pc: 71, line: 21] [pc: 75, line: 22] [pc: 83, line: 23] [pc: 86, line: 24] [pc: 88, line: 25] [pc: 91, line: 26] [pc: 118, line: 27] and verifyError: java.lang.VerifyError: (class: X, method: save signature: ()V) Register 3 contains wrong type at java.lang.Class.forName0(Native Method) at java.lang.Class.forName(Class.java:164) at Compile.main(Compile.java:108)
The problem is that when entering subroutine (pc:13), register 3 may contain various local variable contents, which are not tolerated by bytecode verifier (even though the register is not used within the shared block).
Problem solved. Issues where related to miscomputed exception handler ranges. Optimization is also performed for "return null" (not a constant). And optimization is performing independantly from inlineJSR mode, so it can slightly reduce code size in presence of JSR patterns: 10 jsr <finally> areturn ... jsr <finally> areturn becomes: 10 jsr <finally> areturn ... goto 10 Added GenericTypeTest#test040-043. Also tuned existing tests which got affected by reuse and exception handler changes.
Verified for 3.2 M6 using build I20060328-0010