要求编译器输出汇编语言文件的方法因编译器而异,为此我们需要查询所用编译器的文档。本节我们来看看两种常用的C/C++编译器:GCC(GNU编译器套件)及微软的Visual C++。
要让GCC编译器输出汇编语言代码,请在启动编译器的命令行中指定“-S”选项。下面是启动GCC编译器的命令行示例:
GCC所用的“-S”选项其实并非让编译器生成汇编语言文件。GCC总会产生汇编输出文件,“-S”只是要求GCC在生成汇编语言文件后不再进行任何处理而已。GCC生成的汇编文件名同原C文件,如示例中的“ t1 ”,扩展名则为 .s 。通常编译时,GCC会在生成 .s 汇编文件后,又删除该 .s 汇编文件。
Visual C++编译器以命令行选项“-FA”指定输出汇编语言文件。汇编语言文件与MASM兼容。下面是用来吩咐Visual C++(VC++)生成汇编清单的典型命令:
我们举一个编译器生成汇编语言输出的例子,请看下列C语言程序:
下面几小节将给出Visual C++和GCC编译器对这段代码输出的汇编清单,以便我们对其汇编清单之间的差异有感性认识。
用Visual C++编译此文件的命令如下:
该命令将生成下列MASM汇编语言输出。
注意: 所输出的汇编语言语句确切含义并不重要,关键是要搞清Visual C++清单在语法上与接下来章节的Gas清单有哪些不同之处。
和Visual C++一样,GCC编译器也不会向汇编输出文件插入C源代码。GCC比较善解人意—其编译器总会输出汇编语言代码,而不是应用户要求才生成。由于不向输出文件中插入C源代码,编译器不必写这些数据,而汇编器也不必读这些数据,GCC就能够节约少许编译时间。下面是在PowerPC处理器上使用“gcc -O1 -S t1.c”命令得到的GCC输出:
不难看出,GCC的输出很简练。当然,由于这是PowerPC汇编语言,它与80x86平台上的Visual C++编译器输出没有可比性。
下面是GCC对源文件 t1.c 编译生成的x86-64汇编代码:
该例子表明,GCC为PowerPC发出的大块代码主要取决于机器架构,而非编译器。倘若我们将上述代码与其他编译器的输出进行比较,会发现它们都差不多。
以下代码为 tl.c 源文件在Raspberry Pi(运行的是32位Raspian)上经GCC编译成的ARMv6汇编语言代码:
注意这段源码中的@表示注释,Gas将忽略从@到行末的内容。
给定一个Swift源文件 main.swift ,你可以使用以下命令向Swift编译器请求输出汇编语言文件:
这将产生 result.asm 输出汇编语言文件。请考虑以下Swift源代码:
使用前面的命令行编译此文件,会生成相当长的汇编语言输出文件。以下是该段代码的主过程:
正如你所看到的,与C++相比,Swift并没有产生多好的代码。事实上,为了节省篇幅,此代码清单省略了数百行额外代码。
除非我们对汇编语言编程相当精通,否则分析汇编输出可不是一件容易的事。如果我们不是汇编语言程序员,能做得最好的事就是数数指令有多少条,并假设某个编译器选项或重组高级语言源代码的方式能生成的指令数越少,结果就越好。这种假设其实并不一贯正确,因为执行某些机器指令—特别是在80x86之类的CISC处理器上—要比其他指令花的时间多。80x86处理器上的某三四条指令序列,可能比完成同样操作的单条指令执行得还快。值得庆幸的是,编译器通常不会因为高级语言的源代码重组而改用这两种指令序列的另一种。因此在检查汇编输出时,一般不必担心发生这类问题。
请注意有些编译器会随着优化级别的改动,而生成不同的代码序列。这是由于某些优化设置使得编译器生成的程序代码较短,而另一些优化设置则使编译器生成运行较快的代码。于是,偏重生成较小可执行文件的优化设置也许选择单条指令,而非3条指令来完成某项工作—假定编译这3条指令会生成较多代码;而偏重速度的优化设置则可能选择较快执行的那个指令序列。
本节采用几种C/C++编译器作为示例。我们也要知道,其他语言的编译器也能生成汇编代码。可以查看编译器文档来找出产生汇编输出是否可行,以及用什么选项能够做到这一点。Visual C++等编译器还提供集成开发环境(integrated development environment,IDE),我们就不必再用命令行式的工具了。大部分编译器即便工作于集成开发环境,命令行方式照样能用,集成开发环境和命令行都可以指定汇编输出。具体如何实现,请查看编译器厂家的说明文档。