回报后增量:它们是如何实现的?

时间:2013-03-17 11:22:49

标签: java c++ return increment evaluation

所以,我知道很多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?

3 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分: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

答案 2 :(得分: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 compiled native bytecode了解如何在OpenJDK 7中查看结果。