While writing an answer to a question about JVM byte code offsets, I noticed something in the behavior of javac and the resulting class files that I can not explain:
When compiling a class like this
class FarJump
{
public static void main(String args[])
{
call(0, 1);
}
public static void call(int x, int y)
{
if (x < y)
{
y++;
y++;
// ... (10921 times - too much code to post here!)
y++;
y++;
}
System.out.println(y);
}
}
then the resulting byte code will contain the following if_icmpge
instruction:
public static void call(int, int);
Code:
0: iload_0
1: iload_1
2: if_icmpge 32768
5: iinc 1, 1
8: iinc 1, 1
...
According to the documentation of the jump instructions, the offset (which is 32768 in this case) is computed as follows:
If the comparison succeeds, the unsigned branchbyte1 and branchbyte2 are used to construct a signed 16-bit offset, where the offset is calculated to be (branchbyte1 << 8) | branchbyte2.
So the offset is said to be a signed 16 bit value. However, the maximum value that a signed 16 bit value can hold is 32767, and not 32768.
The resulting class file still seems to be valid, and can be executed normally.
I had a look at the bytecode checking in the OpenJDK, and it seems (to me) that this is only valid due to the parentheses being misplaced:
int jump = (((signed char)(code[offset+1])) << 8) + code[offset+2];
It will cast the first byte to signed char
. Then it will apply the shift, and add the second byte. I would have expected it to be
int jump = (((signed char)(code[offset+1]) << 8)) + code[offset+2];
or maybe even
int jump = (signed char)((code[offset+1]) << 8) + code[offset+2]);
but I'm not familiar with the type promotions and possible compiler-specific caveats of shifting signed and unsigned types, so I'm not sure whether there is a deeper meaning behind this cast...
So does a jump offset of 32768 comply to the specification? And does the jump computation code in the OpenJDK make any sense in this regard?
The argument to if_icmpge
is an offset, but javap shows the jump target as an absolute position. That is, javap should show a getstatic
at 32768:
and not 32770:
(i.e., 2 + 32768).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With