JDK-8308023 - Exponential classfile blowup with nested try/finally

Jan Lahoda jan.lahoda at oracle.com
Fri Jul 28 08:16:50 UTC 2023


Hi,


I believe a few of us were looking at this some time ago. There is a 
range of potential solutions, some with a possibly lesser impact, but 
safer, some with potentially bigger impact, but possibly more dangerous.


Overall, I am personally a bit worried about adding conditional jumps 
that were not in the source code - these might affect way how the code 
performs (as Remi says). This might be more OK for deeply nested 
try-finally, which are probably already problematic, but possibly not 
for a simple/lightly nested try-finally. But having two ways to generate 
try-finally is somewhat problematic for javac.


There are potentially safer options, like for example, considering:

         try {
             a = 0;
         } finally {
             try {
                 b = 1;
             } finally {
                 c = 2;
             }
         }

this gets translated roughly as:

         try {
             a = 0;
         } catch (Throwable t0) {
             try {
                 b = 1;
             } catch (Throwable t1) {
                 c = 2;
                 throw t1;
             }
             c = 2;
             throw t0;
         }
         try {
             b = 1;
         } catch (Throwable t1) {
             c = 2;
             throw t1;
         }
         c = 2;


We probably could only have one copy of (by just re-wiring Exceptions 
table entries, and producing only one copy of the code):

         } catch (Throwable t1) {
             c = 2;
             throw t1;
         }

It may not seem like a saving a lot, but for deeply nested try-finally 
(in the simple form from this report), this actually saves a lot 
(because for deeper nesting, the shared snippets get bigger and bigger). 
I've had some prototype, but it has some problems (does not generate 
proper StackMaps, and allocating the variable for the exception is 
somewhat tricky).


Overall, given the usual complexity of doing anything in Gen, I believe 
the inclination was to see if we can wait until javac will use the 
ClassFile API to generate bytecode, and revisit then. This would at 
least help us generate proper StackMaps much more easier.


Also please note that the situation will get much more difficult when 
there are exits from inside of the try block - return, break, continue, 
yield, where break and continue instances can (in general) 
break/continue to more than one target. Some unifications are possible 
then at the cost of unconditional jump, which may be OK, but would still 
need a careful evaluation.


Overall, I suspect that whatever we do, we would need to run proper 
experiments to ensure there's no problem with performance of the new code.


Jan


On 28. 07. 23 0:51, Archie Cobbs wrote:
> On Thu, Jul 27, 2023 at 5:22 PM <forax at univ-mlv.fr> wrote:
>
>         Wait - are you saying the split-verifier has exponential
>         verification time in certain cases?
>
>         If so, how is that not just a stupid verifier bug? Not to
>         mention an easy way to DOS Java's widely heralded code
>         portability.
>
>
>     There are two verifiers, the old one that does not use
>     StackMapTable attributes has an exponential verification time if
>     JSR/RET are used.
>     The new one, the split-verifier, is linear but requires
>     StackMapTable attributes and to duplicated finally blocks.
>
>
> Got it. So the old verifier is not really relevant to this discussion, 
> because we're talking here about class files that don't use JSR/RET 
> (major version >= 50).
>
>
>         Isn't it a JIT's job to be aware of any such effects (if they
>         exist on some particular hardware) and deal with them??
>
>
>     It has nothing to do with a peculiar hardware, a JIT is an
>     optimizing bytecode to assembly compiler, like any optimizing
>     compiler, not all bytecode shapes are equal.
>
>     The crux of this issue is this question, Why do want a VM engineer
>     to spend time on a bytecode shape that no sane bytecode generators
>     will ever generate ?
>
>
> I don't really understand your point. It sounds to me like you're 
> saying "Doing anything other than what we're already doing would be 
> crazy" but without providing any specific justification.
>
> What I'm suggesting seems much more sane to me than what we're 
> currently doing. What's sane about exponential blowup and excessively 
> duplicated bytecode??
>
> While I'm sure you're right that there are JITs out there that have 
> evolved to work better (or worse) based on typical (or atypical) 
> bytecode patterns, that's not an a priori reason to throw your hands 
> up and say something could never work.
>
> In fact, I bet it would be faster without any JIT changes at all. 
> After all, you're going to have N code paths instead of 2^N!
>
> -Archie
>
> -- 
> Archie L. Cobbs
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20230728/245e5414/attachment-0001.htm>


More information about the compiler-dev mailing list