正如第4章讨论的那样,当今的编译器大部分都产生目标码输出,供链接器读取并处理,从而生成可执行程序。由于目标码文件通常包括人不可读的二进制数据,许多编译器也提供了选项来生成代码相应的汇编语言清单。启用这个选项后,就能分析编译器的输出,必要的话就调整高级语言代码,以得到更合适的机器码。事实上,只要我们有特定的编译器,又透彻了解其优化原理,那么写出的高级语言源程序经编译后,得到的机器码几乎能与最好的手写汇编语言源代码相媲美。尽管我们不能指望这种优化对各种编译器都行得通,但所用编译器可在其他处理器上运行时,卓越编程的诀窍能让我们针对这些处理器写出同样不错的代码(或许效率低一些)。有些代码需要尽量高效地运行于某一类机器,又在其他CPU上可用,这时可以找到一个好的解决方案。
注意: 要记住,检查编译器的输出将导致无法移植所进行的优化。也就是说,检查编译器的输出后,我们可能会修改高级语言源代码,以便使编译器生成更有效的输出。而这些优化措施对另一种编译器未必管用。
能否生成汇编语言输出与具体编译器有关。有些编译器默认就会这么做,例如GCC就总是产生汇编语言文件,尽管编译完成后就将其删除。然而多数编译器必须明确指定,才会生成汇编语言清单。有些编译器生成的汇编语言清单能够用汇编器运行,由汇编器生成目标代码。有些编译器只生成含有汇编注释的文件,这种“汇编代码”在语法上不能被任何现有的汇编器认可。从本书的出发点来看,现有汇编器能否处理编译器的汇编语言输出代码(简称“汇编输出”)并不碍事,我们只要能够看懂输出,能够确定如何修改高级语言代码,以便生成更好的目标代码就行了。
那些能够产生汇编语言输出的编译器,其汇编代码的可读性也是良莠不齐的。有些编译器将高级语言源代码作为注释插入到汇编输出中,因而汇编指令与高级语言代码对照起来很方便。而其他编译器如GCC发送的是纯汇编语言代码,除非我们对特定CPU的汇编语言如数家珍,否则分析其输出将相当困难。
还有一个问题也许会影响到编译器的可读性,那就是我们选择的优化级别。假如禁用所有的优化措施,确定哪些汇编语言指令对应哪条高级语言代码会比较容易。可惜关掉优化功能后,大部分编译器只会生成低质量的代码。如果通过观察编译器的汇编输出,选择更适当的高级语言序列,那么所用优化级别必须与生成应用的产品级代码一致。不能以某个优化级别调整程序得到更好的汇编代码后,却在产品代码中使用另一个优化级别;否则就是画蛇添足,会平添许多麻烦。更糟的是,提高优化级别后,先前手工实现的优化可能反而妨碍编译器发挥作用。
在对编译器指定较高级别的优化时,编译器通常将变换代码在汇编输出文件中的位置,彻底消除某处代码,还会进行其他代码转换,这些做法可能搞乱高级语言代码与汇编输出的对应关系。尽管如此,稍经训练,我们就能找出高级语言的给定语句对应着哪些机器指令。