编程语言编译器首先转化为汇编还是直接转换为机器代码?
我主要感兴趣的是广泛使用的编译器,比如gcc。 但是如果不同的编译器做了不同的事情,我也想知道这一点。
以gcc为例,它是用C编写的一个简短的程序直接编译成机器码 ,还是先把它转换成可读的程序集,然后用(内置的)汇编程序把汇编程序翻译成二进制, 机器码 – 一系列的指令给CPU?
使用汇编代码创build一个二进制可执行文件是一个非常昂贵的操作 或者这是一个相对简单快捷的事情吗?
(假设我们只处理x86系列处理器,所有程序都是为Linux编写的。)
对于此事的任何帮助和想法,我将非常感激。 谢谢!
gcc实际上会生成汇编程序并使用汇编程序进行汇编。 不是所有的编译器都这样做 – MS编译器直接生成目标代码,尽pipe你可以让它们生成汇编输出。 将汇编程序翻译成目标代码是一个相当简单的过程,至less与编译相比。
某些编译器会生成其他高级语言代码作为其输出 – 例如, cfront ,第一个C ++编译器生成C作为其输出,然后由C编译器编译。
请注意,直接编译或汇编都不会生成可执行文件。 这是由链接器完成的,该链接器采用编译/汇编生成的各种目标代码文件,parsing它们包含的所有名称并生成最终的可执行二进制文件。
几乎所有的编译器,包括gcc,都会生成汇编代码,因为它更容易编译和debugging编译器。 主要的例外通常是即时编译器或交互式编译器,作者不希望性能开销或分叉整个过程来运行汇编程序。 一些有趣的例子包括
-
新泽西州的标准ML,交互式运行,即时编译每一个expression。
-
tinycc编译器的devise足够快,可以在100毫秒的时间内编译,加载和运行C脚本,因此不需要调用汇编器和链接器的开销。
这些案件的共同之处在于对“即时”反应的渴望。 汇编程序和连接程序很快,但对于交互式响应还不够好。 然而。
还有一大类语言,比如Smalltalk,Java和Lua ,它们被编译成字节码,而不是汇编代码,但是它们的实现可能稍后将字节码直接翻译成机器代码而没有汇编程序的好处。
(脚注:在20世纪90年代初,Mary Fernandez和我写了New Jersey Machine Code Toolkit , 代码在线,生成C库,编译器编写者可以绕过标准的汇编器和连接器。在生成a.out
时她的优化链接器的速度如果你不写入磁盘,加速甚至更大……)
编译器通常将源代码parsing为抽象语法树(AST),然后parsing为某种中间语言。 只有这样,通常在经过一些优化之后,才会发出目标语言。
关于gcc,它可以编译到各种各样的目标。 我不知道x86是不是编译成汇编语言,但是我确实给了你一些关于汇编语言的意见,而且你也问过了。
根据反向工程软件介绍 (Mike Perry和Nasko Oskov)的第2章 ,gcc和cl.exe(MSVC ++的后端编译器)都有-S开关,可用来输出每个编译器生成的程序集。
您也可以在详细模式下运行gcc( gcc -v
)来获取它执行的命令列表,以查看它在后台执行的操作。
GCC编译为汇编程序。 其他一些编译器不。 例如,LLVM-GCC编译成LLVM-assembly或LLVM-bytecode,然后编译成机器码。 几乎所有的编译器都有某种内部表示,LLVM-GCC使用LLVM,而IIRC,GCC使用了一种叫做GIMPLE的东西。
Visual C ++有一个开关来输出汇编代码,所以我认为它会在输出机器代码之前生成汇编代码。
在大多数多程编译器中,汇编语言是在代码生成步骤中生成的。 这允许您一次编写词法分析器,语法和语义阶段,然后使用单个汇编程序后端生成可执行代码。 这在交叉编译器中使用了很多,例如为一系列不同的cpu生成的C编译器。
几乎每一个编译器都有某种forms的这种隐含的或明确的步骤。
编译有很多阶段。 抽象地说,前端读取源代码,将其分解为令牌,最后分解为一个分析树。
后端负责首先生成一个连续的代码,如三个地址代码,例如:
码:
x = y + z + w
成:
reg1 = y + z x = reg1 + w
然后对其进行优化,将其翻译成汇编,最后转化为机器语言。 所有步骤都仔细分层,以便在需要时可以更换其中的一个
你可能有兴趣听这个播客: GCC的内部
没有一个答案澄清了ASSEMBLER是二进制代码和机器相关符号代码之间的第一层抽象的事实。 编译器是机器相关符号代码和机器独立符号代码之间的第二层抽象。
如果编译器直接将代码转换为二进制代码,按照定义,它将被称为汇编器而不是编译器。
比较恰当的说一个编译器使用中间码(可能是汇编语言,也可能不是汇编语言),例如Java使用字节码作为中间码,字节码是Java虚拟机(JVM)的汇编程序。
编辑:你可能想知道为什么一个汇编程序总是生产机器相关代码,为什么编译器能够生产机器独立代码。 答案很简单。 汇编程序是机器代码的直接映射,因此汇编语言总是与机器相关的。 相反,我们可以为不同的机器编写多个版本的编译器。 因此,为了独立于机器运行我们的代码,我们必须编译相同的代码,但是在为该机器编写的编译器版本上。
Java编译器编译为Java字节码(二进制格式),然后使用虚拟机(jvm)运行。
虽然这可能看起来很慢,但是它可以更快,因为JVM可以利用以后的CPU指令和新的优化。 一个C ++编译器不会这样做 – 你必须在编译时指定目标指令集。
尽pipe所有的编译器都没有将源代码转换为中间级代码,但是在几个编译器中有一个将源代码转换为机器级代码的桥梁