I’m writing a compiler that outputs JVM byte code, and using ASM 9.4 for the backend. This works fine, but I am puzzled about one particular quirk.
COMPUTE_FRAMES to automatically compute the stack frames. It is said that the tradeoff is that this (understandably) makes code generation take more time, but it seems to be fast enough.
What is not documented anywhere that I have been able to find, is that specifying
COMPUTE_FRAMES causes a lot of redundant instructions to be generated. Specifically, an
athrow at the end of each method, and in some cases, a bunch of long runs of
nops scattered through the method.
This does not seem to be doing any harm; my initial test cases are passing okay, and I suppose the JIT compiler won’t actually generate any machine code for the redundant instructions. But it is a little disconcerting. Why is it doing this? Am I doing anything wrong?
This is a sign that you have unreachable code in your generated bytecode. With stackmaps, every instruction not reachable by its preceding instruction must have a stack frame, even if not being the target of a branch. This is necessary to verify the code in a single linear pass.
But for unreachable code, there is no known initial state and it would be very challenging to invent a valid initial state for an arbitrary sequence of instructions. ASM’s solution is to replace the entire unreachable code with nop instructions, followed by a single
athrow statement. For this sequence, it’s possible to specify a valid initial stack frame; it’s a frame having a single throwable on the stack. This sequence also has no impact on the subsequent, potentially reachable code.
Since the code is unreachable, this replacement has no impact on the execution, as you already noticed in your test. And you’re right, the JIT compiler won’t generate any native code for it.
But to git rid of this disconcerting code, you’d have to revise your compiler code, to find out at which part it generates unreachable code.