如何将JVM跳转指令的偏移量设置为32768?

在写一个关于JVM字节代码偏移的问题的答案时 ,我注意到javac的行为以及我无法解释的结果类文件:

编译这样的类时

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); } } 

然后生成的字节代码将包含以下if_icmpge指令:

 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 ... 

根据跳转指令的文档,偏移量(在这种情况下为32768)计算如下:

如果比较成功,则使用无符号branchbyte1branchbyte2构造带符号的16位偏移量,其中偏移量计算为(branchbyte1 << 8)| branchbyte2

因此,偏移被称为带符号的 16位值。 但是,有符号 16位值可以容纳的最大值是32767,而不是32768。

生成的类文件似乎仍然有效,并且可以正常执行。

我看一下OpenJDK中的字节码检查,看起来(对我而言)这只是因为括号错位而有效:

 int jump = (((signed char)(code[offset+1])) << 8) + code[offset+2]; 

它会将第一个字节转换为signed char然后它将应用移位,并添加第二个字节。 我原以为是

 int jump = (((signed char)(code[offset+1]) << 8)) + code[offset+2]; 

或者甚至是

 int jump = (signed char)((code[offset+1]) << 8) + code[offset+2]); 

但是我不熟悉类型促销和可能的特定于编译器的转换有符号和无符号类型的注意事项,所以我不确定这个转换背后是否有更深层的含义……

那么32768的跳跃偏移是否符合规范? 在这方面,OpenJDK中的跳转计算代码是否有意义?

if_icmpge的参数是偏移量,但javap将跳转目标显示为绝对位置。 也就是说,javap应该在32768:处显示getstatic 32768:而不是32770:即2 + 32768)。

我写了一个简单的scala代码来生成进一步挖掘的代码。 对于各种跳转指令,将对偏移进行签名以支持向后和向前跳转。

如果偏移量小于等于0x7FFF,我看到goto指令,如果偏移量大于0x7FFF,我看到goto_w指令。

因此,Java中的方法限制为65535字节,因为LineNumberTable,LocalVariableTable,exception_table …并且限制为65535字节。 JVM根据需要使用goto / goto_w指令跳转带符号的16/32偏移量。