可能是一个奇怪的问题。
一个编写C ++编译器(或任何非VM语言)的人:是否需要能够读写原始机器语言?这是如何运作的?
编辑:我专门指的是编译为机器代码的编译器,而不是其他某种编程语言。
可能是一个奇怪的问题。
一个编写C ++编译器(或任何非VM语言)的人:是否需要能够读写原始机器语言?这是如何运作的?
编辑:我专门指的是编译为机器代码的编译器,而不是其他某种编程语言。
Answers:
一点都不。编译器完全有可能(甚至通常更可取)发出汇编代码。然后,汇编器负责创建实际的机器代码。
顺便说一句,区分非VM实现与VM实现是没有用的。
对于初学者来说,使用VM或对机器代码进行预编译只是实现一种语言的不同方式。在大多数情况下,可以使用任何一种策略来实现一种语言。实际上,我不得不使用一次C ++ 解释器。
同样,像JVM这样的许多VM都具有二进制机器代码和一些汇编程序,就像普通的体系结构一样。
LLVM(由Clang编译器使用)在这里值得特别提及:它定义了一个VM,其指令可以表示为字节码,文本汇编或数据结构,这样可以很容易地从编译器发出指令。因此,尽管它对于调试(了解您的工作)很有用,但您甚至不必了解汇编语言,而只需了解LLVM API。
LLVM的好处是它的VM只是一个抽象,字节码通常不被解释,而是透明地JITted。因此,完全有可能编写一种经过有效编译的语言,而无需了解您的CPU指令集。
否。您的问题的关键是编译是一个极为广泛的术语。编译可以从任何一种语言转换为任何一种语言。汇编/机器代码只是众多编译目标语言之一。例如,Java和.NET语言(例如C#,F#和VB.NET)都编译为某种中间代码,而不是机器特定的代码。然后是否可以在VM上运行都无关紧要,该语言仍会编译。还可以选择编译为其他语言,例如C。C实际上是非常流行的编译目标,许多工具都可以实现。最后,您可以使用一些工具或库来完成为您生成机器代码的艰苦工作。例如,LLVM可以减少创建独立编译器所需的工作。
另外,您的编辑没有任何意义。这就像在问“每个工程师都需要了解发动机的工作原理吗?我是在问工程师在发动机上工作。” 如果您正在处理发出机器代码的程序或库,那么您必须了解它。关键是,在编写编译器时不必执行此类操作。许多人在您之前完成了此操作,因此您需要有充分的理由再次执行此操作。
传统上,编译器包含三个部分:词法分析,解析和代码生成。词法分析将程序的文本分为语言关键字,名称和值。解析会分析来自词法分析的标记如何在语言的语法正确声明中进行组合。代码生成采用解析器生成的数据结构,并将其转换为机器代码或其他某种表示形式。如今,词法分析和解析可以合并为一个步骤。
显然,编写代码生成器的人员必须非常深入地了解目标机器代码,包括指令集,处理器管线和缓存行为。否则,编译器生成的程序将很慢且效率低下。他们很可能能够读写以八进制或十六进制数字表示的机器代码,但是它们通常会编写函数来生成机器代码,并在内部引用机器指令表。从理论上讲,编写词法分析器和解析器的人可能对机器代码的生成一无所知。实际上,某些现代编译器允许您插入自己的代码生成例程,这些例程可能会为词法分析器和解析器编写器从未听说过的某些CPU发出机器代码。
但是,实际上,编译器作者在每个步骤中对不同的处理器体系结构都了解很多,这有助于他们设计代码生成步骤所需的数据结构。
你不需要启动你的输入和输出语言的语义的详细知识,但是你最好完成与两者的一个精美的详细知识,否则你的编译器会unusably马车。因此,如果您的输入是C ++,而您的输出是某些特定的机器语言,那么您最终将需要了解两者的语义。
以下是将C ++编译为机器代码的一些细微之处:(就在我的头上,我确定还有很多我忘记的地方。)
大小int
是多少?基于机器的自然指针大小,各种算术运算大小的ALU的性能以及现有编译器为机器所做的选择,此处的“正确”选择是一门艺术。机器是否甚至具有64位算术?如果不是这样,则将32位整数相加应转换为一条指令,而将64位整数相加应转换为进行64位加法的函数调用。机器是否具有8位和16位加法运算,或者您必须模拟具有32位运算和掩码的运算(例如DEC Alpha 21064)?
机器上其他编译器,库和语言使用的调用约定是什么?参数是从右到左还是从左到右被压入堆栈?有些参数进入寄存器,而其他参数进入堆栈吗?整数和浮点数是否位于不同的寄存器空间中?寄存器分配的参数是否需要在varargs调用中进行特殊处理?哪些寄存器保存了呼叫者,哪些保存了被呼叫者?您可以执行叶子调用优化吗?
机器的每个换档指令有什么作用?如果您要求将64位整数移位65位,结果是什么?(在许多机器上,结果与移位1位相同,在其他机器上,结果为“ 0”。)
机器的内存一致性语义是什么?C ++ 11具有定义非常明确的内存语义,在某些情况下对某些优化设置了限制,但在其他情况下允许进行优化。如果您正在编译一种没有明确定义的内存语义的语言(例如C ++ 11之前的每个版本的C / C ++以及许多其他命令式语言),那么您将必须不断学习内存语义,并且通常您将需要发明与您的机器语义最匹配的内存语义。