0

所以,我知道许多 C 风格的语言都有递减 ( --) 和递增 ( ++) 运算符,并允许在评估整个表达式之前或之后发生突变。

当退货发生后增量时会发生什么?我问的不是行为,而是执行。

给定虚拟机(例如 JavaScript/JVM)或物理机(例如编译的 C++),生成的操作码是否类似于以下内容?(假设基于堆栈的参数/返回。)

int x = 4, y = 8;
return f(++a) + y++;

变成这样:(也许?)

LOAD 4 A
LOAD 8 B

INC A
PUSH A
CALL F
POP INTO C
ADD C BY B
INC B
RET C

如果是这样,当表达式变得复杂时,这些语言中的此类操作如何决定在哪里嵌入增量,甚至可能有点 Lispish?

4

3 回答 3

3

Java 运行时远离您的源代码,甚至远离字节码。一旦方法被 JIT 编译,生成的机器代码就会被积极优化。但更重要的是,这种优化的细节远远超出了任何规范。如果您对它感兴趣,您可能会深入研究像 HotSpot 这样的实现,但您从中学到的一切都将特定于平台、版本、内部版本号、JVM 启动参数,甚至 JVM 的单独运行。

于 2013-03-17T11:28:22.430 回答
2

一旦代码被优化(通过 C++ 编译器或 JIT),我会期望类似于以下内容:

资源:

int x = 4, y = 8;
return f(++x) + y++;

指示:

PUSH 5
CALL f
POP INTO A
ADD 8 to A
RET A

我列出的说明是正确的(除非我犯了一些愚蠢的错误),并且它们只需要我知道优化器能够进行的代码转换。基本上,不计算未使用的结果,并且在优化时计算具有已知结果的操作。

但是,在特定架构上很有可能有一种更有效的方法来做到这一点,在这种情况下,优化器很有可能会知道并使用它。优化器经常超出我的预期,因为我不是专业的汇编程序员。在这种情况下,调用约定很可能在函数f和这段代码的情况下为返回值规定了相同的位置。所以可能不需要任何 POP——返回寄存器/堆栈位置可以只添加 8 个。此外,如果函数f是内联的,那么优化将在内联之后应用

因此,例如,如果f返回input * 2,则整个函数可能会优化为:

RET 18
于 2013-03-17T11:42:50.333 回答
1

您可以准确地看到编译器使用javap. 例如:

int x = 4, y = 8;
return f(++x) + y++;

被编译成这个字节码序列:

0:  iconst_4
1:  istore_1
2:  bipush  8
4:  istore_2
5:  aload_0
6:  iinc    1, 1
9:  iload_1
10: invokevirtual   #2; //Method f:(I)I
13: iload_2
14: iinc    2, 1
17: iadd
18: ireturn

当然,如何将其转换为汇编程序取决于 JVM - 请参阅Disassemble Java JIT 编译的本机字节码,了解如何在 OpenJDK 7 中查看结果。

于 2013-03-17T11:46:01.553 回答