编译器将您的代码分成非常简单的指令,然后以它认为最佳的方式重新组合和排列它们。
编码
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有关的所有操作都是无效代码:从未使用过。而且我的重读也可以消除:
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
然后再次注意,我被阅读了两次,因此消除其中之一:
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. 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 ++给您的结果。
请注意,在两个优化路径中,重要的顺序依赖性都得以保留,只要不删除指令就什么都不做即可。