为何编译器生成汇编代码?


19

汇编语言将汇编语言转换为机器语言。为什么编译器会将高级语言转换为汇编语言?它不能直接从高级语言转换为机器代码吗?

Answers:


22

编译器产生汇编而不是适当的机器代码的其他原因是:

  • 汇编程序使用的符号地址而不是硬编码的机器地址使代码重定位更加容易。
  • 链接代码可能涉及安全检查,例如类型检查,而使用符号名称则更容易。
  • 通过更改汇编器而不是代码生成器,可以更轻松地适应机器代码中的微小更改。

为什么汇编语言如此高效,尽管它也是用英语编写的,并且处理器如何理解它?
CODERSAM

3
@CODERSAM汇编是一种正式语言,而不是自然语言。它非常接近于机器语言。因此,翻译不会导致效率低下。
马丁·伯杰

当您说“非常接近机器语言”时,这是什么意思?我真的很困惑!
2013年

2
@CODERSAM的确切含义很复杂,但是有点像代数中的同态。翻译时,说“ add eax,#2”(它是x86程序集),可以直接将其翻译为d7f5(或其他任何操作码),而无需查看上下文,而无需添加任何其他内容。程序集没有抽象。
Martin Berger 2013年

1
“程序集没有抽象” —我要说的是标签名称已经是抽象(从偏移量开始)。此外,上下文确实起到了作用:如add eax,2可转换为83 c0 0266 83 c0 02,根据最新的一样发生指令use16
Ruslan

15

编译器通常确实将高级代码直接转换为机器语言,但是可以以模块化的方式进行构建,以便一个后端发出机器代码,而另一端发出机器代码(例如GCC)。代码生成阶段生成“代码”,它是机器代码的某种内部表示形式,然后必须将其转换为可用格式,例如机器语言或汇编代码。


另外,如果源中可能包含某些汇编代码,则无论如何都必须有一种机制来转换该内联汇编。
保罗·克莱顿

为什么汇编语言如此高效,尽管它也是用英语编写的,并且处理器如何理解它?
CODERSAM

1
汇编语言是机器代码的“英语”描述。
Yuval Filmus 2013年

11

从历史上看,许多著名的编译器直接输出机器代码。但是,这样做有些困难。通常,试图确认编译器工作正常的人会发现检查汇编代码输出比机器代码更容易。此外,有可能(并且在历史上很普遍)使用单遍C或Pascal编译器来生成汇编语言文件,然后可以使用两遍汇编器对其进行处理。直接生成代码将需要使用两次通过的C或Pascal编译器,或者一次通过的编译器,然后使用某种方式对正向跳转地址进行反向修补[如果运行时环境使启动程序的大小可用于固定点 编译器可以在代码末尾编写补丁列表,并让启动代码在运行时应用这些补丁;这种方法会使每个补丁点的可执行文件大小增加大约四个字节,但会提高程序生成速度。

如果目标是使编译器快速运行,则直接代码生成可以很好地工作。但是,对于大多数项目而言,如今生成汇编语言代码并对其进行汇编的成本确实不是主要问题。通常,让编译器以可以与其他编译器生成的代码很好地交互的形式生成代码,这是一个足够大的好处,足以证明增加编译时间是合理的。


1

即使使用相同指令集的平台也可能具有不同的可重定位目标文件格式。我可以想到“ a.out”(早期的UNIX),OMF,MZ(MS-DOS EXE),NE(16位Windows),COFF(UNIX系统V),Mach-O(OS X和iOS)以及ELF(Linux和其他),以及它们的变体,例如32位Windows上的XCOFF(AIX),ECOFF(SGI)和基于COFF的可移植可执行文件(PE)。产生汇编语言的编译器不需要太多有关目标文件格式的知识,从而使汇编器和链接器可以将这些知识封装在单独的进程中。

另请参见堆栈溢出上的OMF和COFF之间的差异


1

通常,编译器在内部使用指令序列进行工作。每条指令将由一个数据结构表示,该数据结构表示其操作名称,操作数等。当操作数是地址时,这些地址通常是符号引用,而不是具体值。

输出汇编器相对简单。这几乎是采用编译器的内部数据结构并将其转储为特定格式的文本文件的问题。汇编器输出也相对易于阅读,这在您需要检查编译器正在执行的操作时很有用。

输出二进制目标文件的工作明显更多。编译器编写者需要了解所有指令的编码方式(在某些CPUS上可能微不足道),他们需要将一些符号引用转换为程序计数器的相对地址,而另一些符号引用则转换为二进制目标文件中的某种形式的元数据。 。他们需要以高度系统特定的格式写出所有内容。

是的,您绝对可以使编译器直接输出二进制对象,而无需将汇编程序写为中间步骤。像软件开发中的许多事情一样,问题是减少编译时间是否值得进行额外的开发和维护工作。

我最熟悉的编译器(freepascal)可以在所有平台上输出汇编程序,但只能直接在平台的子集上输出二进制对象。


1

除正常的可重定位代码外,编译器还应能够生成汇编器输出,这对程序员有利。

有一次我只是在LSI-11机器上的Unix System V上运行的C程序中找不到错误。似乎没有任何作用。最终,在无奈之下,我让Protable C编译器排除了其翻译的汇编器版本。我终于找到了错误!编译器分配的寄存器比机器中存在的更多!(编译器在仅具有寄存器R0至R7的机器上分配了寄存器R0至R8。)我设法解决了编译器中的错误,并使程序正常工作。

具有汇编器输出的另一个好处是尝试使用使用不同参数传递协议的“标准”库。后来的C编译器允许我使用参数设置协议(“ pascal”将使编译器按照给定的顺序添加参数,而不是C标准的相反顺序)。

另一个好处是允许程序员查看其编译器正在执行的艰巨工作。一个简单的C语句大约需要44条机器指令。从内存中加载值,然后迅速将其丢弃。等等等等...

我个人认为拥有编译器而不是可重定位的对象模块确实很愚蠢。在编译程序时,编译器会收集有关程序的大量信息。它通常将所有这些信息存储在一个符号表中。删除汇编代码后,它将引发所有此信息表。然后,汇编器检查已排除的代码,并重新收集编译器已经拥有的一些信息。但是,汇编器对For语句或While语句的If语句一无所知。因此,所有这些信息都丢失了。然后,汇编器生成可重定位的目标模块,而编译器则没有。

为什么???

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.