编译器发展的历史
编译器是将便于人们书写、阅读和维护的高级计算机语言翻译成计算机能够识别和运行的低级机器语言的程序。编译器把源程序作为输入,并把它翻译成目标语言的等价程序。源程序一般是高级语言,如Pascal和C++,而目标语言是汇编语言或目标机器的目标代码,有时称为机器码。
现代编译器的主要工作流程如下:
源代码)→预处理器)→编译器)→汇编器)→目标代码)→链接器)→可执行文件。
目录[隐藏]
1的工作原理
2种编译器类型
3预处理程序(预处理器)
4编译器前端(frontend)
5编译器后端(backend)
6编译语言和解释语言的比较
7历史
8另见
操作原理
翻译是从源代码(通常是高级语言)到可由计算机或虚拟机直接执行的目标代码(通常是低级语言或机器语言)。但是也有从低级语言到高级语言的编译器,那些用来从高级语言生成的低级语言代码重新生成高级语言代码的编译器也叫反编译程序。也有从一种高级语言生成另一种高级语言的编译器,或者生成需要进一步处理的中间代码的编译器(也称为级联)。
典型的编译器输出是一个由机器码组成的目标文件,机器码包含入口点的名称和地址以及外部调用(对不在这个目标文件中的函数调用)。一组目标文件不需要由同一个编译器生成,但是使用的编译器必须采用相同的输出格式,可以链接在一起生成用户可以直接执行的可执行程序。
编译器类型
编译器可以生成目标代码,在与编译器本身所在的计算机和操作系统(平台)相同的环境中运行。这个编译器也被称为“本地”编译器。此外,编译器还可以生成在其他平台上运行的目标代码。这种编译器也被称为交叉编译器。交叉编译器在生成新的硬件平台方面非常有用。“源到源编译器”是指使用高级语言作为输入,输出也是高级语言的编译器。例如,自动并行化编译器通常将高级语言作为输入,转换其中的代码,并用并行代码注释(如OpenMP)或语言构造(如FORTRAN的DOALL指令)对其进行注释。
预处理器(预处理器)
该功能是通过替换预定义的程序段来补充源程序。
编译器前端
前端主要负责解析输入的源程序,词法分析器和语法分析器协同工作。词法分析器负责找出源程序中的'(Token ',语法分析器根据预先定义的语法把这些零散的单词组装成有意义的表达式、语句、函数等等。比如“a = b+ c;”前端词法分析器看到“a,=,b,+,c;”根据定义好的语法,解析器先把它们组装成表达式“b+c”,再组装成语句“a = b+c”。前端还负责语义检查,比如检测操作中涉及的变量是否为同一类型,以及简单的错误处理。最终的结果通常是抽象语法树(AST),以便后端可以进一步优化和处理它。
编译器后端
编译器后端主要负责分析、优化中间表示代码生成机器码。
一般来说,所有的编译器分析、优化和修改都可以分为两类:过程内或过程间。很明显,函数之间的分析和优化更加精确,但是完成的时间更长。
编译器分析的对象是前端生成并传输的中间代码。现代优化编译器通常使用几层中间代码来表示程序。高级IR接近于输入源程序的格式,它依赖于语言并包含更多的全局信息和源程序的结构。中间层的中能级IR与输入语言无关,低层的低能级IR类似于机器语言。不同的分析,优化发生在最合适的中间代码层。
常见的编译分析包括函数调用树、控制流图,以及变量定义-使用、使用-定义链(define-use/use-define或u-d/d-u链)、变量别名分析、指针分析和数据依赖分析。
上述程序分析结果是编译器优化和编译器改造的先决条件。常见的优化和创新包括:函数内联、死代码消除、标准化循环结构、循环展开、循环合并、循环融合、数组填充等等。优化变形的目的是减少代码长度,提高内存和缓存的利用率,减少读写磁盘和访问网络数据的频率。更高级的优化甚至可以将串行代码变成并行多线程代码。
机器代码的生成是将优化后的中间代码转化为机器指令的过程。现代编译器主要采用生成汇编代码的策略,而不是直接生成二进制目标代码。即使在代码生成阶段,高级编译器仍然要做大量的分析、优化和变形工作。比如如何分配寄存器,如何选择合适的机器指令选择,如何将几个代码组合成一句话等等。
编译语言和解释语言的比较
很多人把高级编程语言分为两类:编译语言和解释语言。然而,实际上这些语言大部分都可以由编译器和解释器共同实现,分类实际上反映了该语言的通用实现方法。(然而,一些解释性语言很难用编译器实现。例如允许在线代码更改的解释语言。)
历史
20世纪50年代,IBM的约翰·巴科斯领导一个研究小组开发FORTRAN语言及其编译器。但由于当时人们对编译理论了解不多,开发工作变得复杂而艰巨。与此同时,诺姆·乔姆斯基开始了对自然语言结构的研究。他的发现最终使得编译器的结构变得极其简单,甚至带有一些自动化。乔姆斯基的研究导致根据语言语法的难易程度和识别语言所需的算法对语言进行分类。正如现在所说的乔姆斯基层次结构,它包括四个层次的语法:0型语法、1型语法、2型语法和3型语法,每一个都是前者的特例。类型2语法(或上下文无关语法)已被证明是编程语言中最有用的,今天它代表了编程语言结构的标准方式。分析问题(一种有效的上下文无关语法识别算法)是在20世纪60年代和70年代研究的,它完美地解决了这个问题。现在是编译原理的标准部分。
有限自动机和正则表达式与上下文无关文法密切相关,它们对应于乔姆斯基的3类文法。对它们的研究几乎与乔姆斯基的研究同时开始,引入了文字代表编程语言的符号化方式。
人们于是深化了生成有效目标代码的方法,也就是最初的编译器,一直沿用至今。人们通常称之为优化技术,但它只是提高了它的有效性,因为它从未真正获得优化的目标代码,所以它实际上应该被称为代码改进技术。
当分析问题变得容易理解时,人们就花大量时间开发程序,研究这部分编译器的自动构造。起初,这些程序被称为编译器-编译器,但更准确地说,它们应该被称为解析器生成器,因为它们只能自动处理部分编译。这些程序中最著名的是YACC(另一个编译器-编译器),它是斯蒂夫·约翰森在1975为Unix系统编写的。同样,有限状态自动机的研究开发了一个工具,叫做扫描器生成器,其中Lex(与Yacc同时由Mike Lesk为Unix系统开发)是最好的。
在20世纪70年代末和80年代初,大量的项目集中于编译器其他部分的生成自动化,包括代码生成。这些尝试并没有取得太大的成功,可能是因为操作过于复杂,人们对其了解不多。
编译器设计的最新发展包括:第一,编译器包括了具有更复杂算法的应用程序,用于推断或简化程序中的信息;这与更复杂的编程语言的开发相结合。其中,有一个典型的函数式语言编译的统一Hindley-Milner类型检查算法。其次,编译器越来越成为基于窗口的交互式开发环境(IDE)的一部分,它包括编辑器、链接器、调试器和项目管理器。这样的IDE标准不多,但是开发标准的窗口环境已经成为方向。另一方面,虽然近年来在编译原理领域做了大量的研究,但基本的编译器设计原理在过去的20年中并没有太大的变化,它正在迅速成为计算机科学课程中的中心环节。
在20世纪90年代,作为GNU项目或其他开源项目的一部分,许多免费编译器和编译器开发工具被开发出来。这些工具可以用来编译所有的计算机编程语言。其中一些被认为是高质量的,对现代编译理论感兴趣的人可以很容易地获得他们的免费源代码。
1999左右,SGI公布了一个工业化并行优化编译器Pro64的源代码,该编译器被全球多个编译器研究小组作为研究平台,命名为Open64。Open64具有良好的设计结构、全面的分析和优化,是编译器高级研究的理想平台。
编译器是一种特殊的程序,它可以把用特定编程语言编写的程序变成机器可以运行的机器代码。当我们编写程序时,我们使用的环境是文本编辑器。这时,我的程序把这个程序叫做源程序。之后程序员就可以运行相应的编译器,通过指定要编译的文件名,就可以将相应的源文件转换成机器码(通过一个复杂的过程)。
编译器工作方法
首先,编译器进行语法分析,也就是分离那些字符串。然后进行语义分析,即明确语法分析所分析的各个语法单位的意义。最后生成目标文件,也叫obj文件。最终的可执行代码可以通过链接器的链接生成。有时候我们需要把多个文件生成的目标文件链接起来,生成最终的代码。我们称这个过程为交联。