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に最適化が適用されます。

したがって、たとえばfreturnsinput * 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次第です。OpenJDK7で結果を確認する方法については、 JavaJITでコンパイルされたネイティブバイトコードの逆アセンブルを参照してください。

于 2013-03-17T11:46:01.553 に答える