Java 为一元not表达式生成JVM字节码

Java 为一元not表达式生成JVM字节码,java,compiler-construction,jvm,javac,bytecode,Java,Compiler Construction,Jvm,Javac,Bytecode,假设您正在编写Java(或Java的子集)编译器,并且希望为一元not表达式生成字节码,!E。您已通过类型检查,因此您知道E具有类型布尔值,即它将1或0推送到操作数堆栈上 一种方法是(在Jasmin语法中): i、 e.如果堆栈上有0,则按1,否则按0。另一种方法是,利用boolean只是一个int值1或0,即!E=(E+1)%2并生成 E iconst_1 iadd iconst_2 irem 使用一个比另一个有优势吗?或者完全是别的什么?我曾经试图编写一个Java反编译器,所以我过去知道j

假设您正在编写Java(或Java的子集)编译器,并且希望为一元not表达式生成字节码,
!E
。您已通过类型检查,因此您知道
E
具有类型
布尔值
,即它将
1
0
推送到操作数堆栈上

一种方法是(在Jasmin语法中):

i、 e.如果堆栈上有0,则按1,否则按0。另一种方法是,利用
boolean
只是一个
int
1
0
,即
!E=(E+1)%2
并生成

E
iconst_1
iadd
iconst_2
irem

使用一个比另一个有优势吗?或者完全是别的什么?

我曾经试图编写一个Java反编译器,所以我过去知道javac生成了什么代码。我记得javac1.0.x使用了
!E=E?false:true
而JavaC1.1使用了
!E=E^1(按位异或)。

我听说模运算可能非常慢。我没有它的来源,但考虑到加法比除法简单得多,这是有道理的。同样,在这种情况下,可能会出现程序计数器跳得太多的问题,在这种情况下,if/else方法将无法很好地工作


话虽如此,我怀疑尼尔的
E^1
是最快的,但这只是一种预感。你所要做的就是把数字通过一个逻辑电路,你就完成了!只有一个操作,而不是少数操作。

我不指望下面的定义在字节码级别上保持正确

true == 1
在二进制级别(几乎与语言无关),布尔值通常定义为

false == 0
true != 0
javac编译器显然也遵循这个定义(我所看到的javac字节码中的所有检查总是对零进行检查,从不对一进行检查)

对布尔值使用这个定义是有意义的,而不是只将1视为true,C也以这种方式定义它(true只是!=0,而不仅仅是1),在汇编代码中,这个约定也是常用的。因此,java也使用了这个定义,这使得它可以在不进行任何特殊转换的情况下将java布尔值传递给其他代码


我怀疑您的第一个代码示例(使用ifeq)是正确实现布尔型not运算符的唯一方法。如果布尔值未严格表示为0/1,则^1方法(带1的异或)将失败。任何其他int值都会导致表达式工作不正确。

我没有想到
^1
;那肯定比第二个好。你知道从前者切换到后者的基本原理吗,或者为什么没有首先使用
^1
?实际上我只是尝试了
javac1.6.0_26
,它生成了第一个(除了
ifne
而不是
ifeq
),所以我猜他们又切换回来了。我想我仍然在想一个和另一个的好处。这只是一个粗略的猜测,但也许他们将这些微优化转移到了JIT,而JIT可能就是它们的所在。JVM规范说:“Java虚拟机使用1表示true,0表示false对布尔数组组件进行编码。当编译器将Java编程语言布尔值映射到Java虚拟机类型int的值时,编译器必须使用相同的编码。“@isbadawi,它描述了JVM如何对布尔数组进行编码,以及Java编译器在进行编码时必须如何将布尔编码为int。它与这个问题没有直接关系。@EJP我觉得它有点模棱两可。第一句是关于布尔数组的,第二句是关于布尔值的。不过我可能错了。@isdabawi我发现JVM规范中的措辞非常混乱。首先,它对oraclevm如何将boolean[]实现为byte[]做了一个注释(但我认为这个注释不是规范的一部分)。然后,最后两句话讨论了java编译器在将boolean[]实现为int[]时应该做些什么——同样,这并没有说明VM必须如何实现boolean。事实上,我看不到任何东西表明虚拟机必须以某种方式实现它,它只给出了如何解释编译器可能为布尔[]生成的某些结构的约束。我看起来并不困惑。“Java虚拟机对布尔数组组件进行编码,使用1表示真,0表示假”这句话清楚地说明了JVM。因此,这意味着即使特定的实现在引擎盖下使用字节数组,这也不能显示出来,即从布尔数组读取必须产生零或一,而不能产生其他结果。有趣的是,我试验过的最新版本没有实现“非零表示真”规则,而是在使用非零或非一的int值作为布尔值时截断除最低位以外的所有位。^1的计算强度肯定比div/rem小得多,条件分支的成本也有些高(因为在大多数当前的CPU体系结构中,它们可能会导致分支预测失误惩罚)。更大的问题是,假设用于实现布尔值的int值严格限制为0/1是否有效。我相信Java是有效的。在任何情况下,如果您不知道,您可以执行
(E&1)^1
。(&1)^1将0x2转换为0x1(例如)。true==!true并不是人们所期望的。我不知道有任何简单的表达式可以将int的逻辑或所有32位折叠为单个位。布尔值受到严格限制,或者^-方法无法使用。
false == 0
true != 0