コンパイラーはコードを受け取り、それを非常に単純な命令に分割してから、最適と思われる方法でそれらを再結合して配置します。
コード
int i = 1;
int x = ++i + ++i;
次の手順で構成されています。
1. store 1 in i
2. read i as tmp1
3. add 1 to tmp1
4. store tmp1 in i
5. read i as tmp2
6. read i as tmp3
7. add 1 to tmp3
8. store tmp3 in i
9. read i as tmp4
10. add tmp2 and tmp4, as tmp5
11. store tmp5 in x
しかし、これは私が書いたように番号付きのリストであるにもかかわらず、ここにはいくつかの順序の依存関係しかありません:1-> 2-> 3-> 4-> 5-> 10-> 11および1-> 6-> 7- > 8-> 9-> 10-> 11は相対的な順序を維持する必要があります。それ以外は、コンパイラは自由に並べ替えることができ、おそらく冗長性を排除できます。
たとえば、次のようにリストを注文できます。
1. store 1 in i
2. read i as tmp1
6. read i as tmp3
3. add 1 to tmp1
7. add 1 to tmp3
4. store tmp1 in i
8. store tmp3 in i
5. read i as tmp2
9. read i as tmp4
10. add tmp2 and tmp4, as tmp5
11. store tmp5 in x
なぜコンパイラはこれを行うことができますか?増分の副作用に対する順序付けがないためです。しかし、コンパイラーは単純化できるようになりました。たとえば、4にはデッドストアがあり、値はすぐに上書きされます。また、tmp2とtmp4は実際には同じものです。
1. store 1 in i
2. read i as tmp1
6. read i as tmp3
3. add 1 to tmp1
7. add 1 to tmp3
8. store tmp3 in i
5. read i as tmp2
10. add tmp2 and tmp2, as tmp5
11. store tmp5 in x
そして今、tmp1に関係するすべてはデッドコードです:それは決して使われません。そして、iの再読も排除することができます:
1. store 1 in i
6. read i as tmp3
7. add 1 to tmp3
8. store tmp3 in i
10. add tmp3 and tmp3, as tmp5
11. store tmp5 in x
ほら、このコードはずっと短いです。オプティマイザーは満足しています。私は一度だけインクリメントされたので、プログラマーはそうではありません。おっと。
代わりにコンパイラーが実行できる他のことを見てみましょう。元のバージョンに戻りましょう。
1. store 1 in i
2. read i as tmp1
3. add 1 to tmp1
4. store tmp1 in i
5. read i as tmp2
6. read i as tmp3
7. add 1 to tmp3
8. store tmp3 in i
9. read i as tmp4
10. add tmp2 and tmp4, as tmp5
11. store tmp5 in x
コンパイラは次のように並べ替えることができます。
1. store 1 in i
2. read i as tmp1
3. add 1 to tmp1
4. store tmp1 in i
6. read i as tmp3
7. add 1 to tmp3
8. store tmp3 in i
5. read i as tmp2
9. read i as tmp4
10. add tmp2 and tmp4, as tmp5
11. store tmp5 in x
次に、iが2回読み取られていることに再度注意して、そのうちの1つを削除します。
1. store 1 in i
2. read i as tmp1
3. add 1 to tmp1
4. store tmp1 in i
6. read i as tmp3
7. add 1 to tmp3
8. store tmp3 in i
5. read i as tmp2
10. add tmp2 and tmp2, as tmp5
11. store tmp5 in x
それは素晴らしいことですが、さらに進むことができます。tmp1を再利用できます。
1. store 1 in i
2. read i as tmp1
3. add 1 to tmp1
4. store tmp1 in i
6. read i as tmp1
7. add 1 to tmp1
8. store tmp1 in i
5. read i as tmp2
10. add tmp2 and tmp2, as tmp5
11. store tmp5 in x
次に、6でiの再読み取りを排除できます。
1. store 1 in i
2. read i as tmp1
3. add 1 to tmp1
4. store tmp1 in i
7. add 1 to tmp1
8. store tmp1 in i
5. read i as tmp2
10. add tmp2 and tmp2, as tmp5
11. store tmp5 in x
今4は死んだ店です:
1. store 1 in i
2. read i as tmp1
3. add 1 to tmp1
7. add 1 to tmp1
8. store tmp1 in i
5. read i as tmp2
10. add tmp2 and tmp2, as tmp5
11. store tmp5 in x
これで、3と7を1つの命令にマージできます。
1. store 1 in i
2. read i as tmp1
3+7. add 2 to tmp1
8. store tmp1 in i
5. read i as tmp2
10. add tmp2 and tmp2, as tmp5
11. store tmp5 in x
最後の一時的なものを排除します。
1. store 1 in i
2. read i as tmp1
3+7. add 2 to tmp1
8. store tmp1 in i
10. add tmp1 and tmp1, as tmp5
11. store tmp5 in x
そして今、あなたはVisual C ++があなたに与えている結果を得る。
どちらの最適化パスでも、何もしないために命令が削除されない限り、重要な順序の依存関係が保持されていることに注意してください。