编译器前后端的划分
‘壹’ 编译器前端开发
编译器粗略分为词法分析,语法分析,类型检查,中间代码生成,代码优化,目标代码生成,目标代码优化。把中间代码生成及之前阶段划分问编译器的前端,那么后端与前端是独立的。后端只需要一种中间代码表示,可以是三地址代码或四元式等,而这些都与前端生成的方式无关。也就是不论你前端是用fortran还是c/c++,只要生成了中间代码表示就可以了,后端是不管你是用哪种语言生成的。
‘贰’ 一个编译器至少包含三个部分的进程是什么
一个典型的编译程序通常包含8个组成部分,它们是词法分析程序、语法分析程序、语义分析程序、中间代码生成程序、中间代码优化程序、目标代码生成程序、表格管理程序和错误处理程序。
(1) 编译程序:如果源语言为高级语言,目标语言为某台计算机上的汇编语言或机器语
言,则此翻译程序称为编译程序。
(2) 源程序:源语言编写的程序称为源程序。
(3) 目标程序:目标语言书写的程序称为目标程序。
(4) 编译程序的前端:它由这样一些阶段组成:这些阶段的工作主要依赖于源语言而与
目标机无关。通常前端包括词法分析、语法分析、语义分析和中间代码生成这些阶
段,某些优化工作也可在前端做,也包括与前端每个阶段相关的出错处理工作和符
号表管理等工作。
(5) 后端:指那些依赖于目标机而一般不依赖源语言,只与中间代码有关的那些阶段,
即目标代码生成,以及相关出错处理和符号表操作。
(6) 遍:是对源程序或其等价的中间语言程序从头到尾扫视并完成规定任务的过程。
词法分析程序:输人源程序,拼单词、检查单词和分析单词,输出单词的机内表达形式。
语法分析程序:检查源程序中存在的形式语法错误,输出错误处理信息。
语义分析程序:进行语义检查和分析语义信息,并把分析的结果保存到各类语义信息表中。
中间代码生成程序:按照语义规则,将语法分析程序分析出的语法单位转换成一定形式的中间语言代码,如三元式或四元式。
中间代码优化程序:为了产生高质量的目标代码,对中间代码进行等价变换处理
‘叁’ 编译器的发展史
编译器编译器,是将便于人编写,阅读,维护的高级计算机语言翻译为计算机能识别,运行的低级机器语言的程序。编译器将源程序(Sourcenbsp;program)作为输入,翻译产生使用目标语言(Targetnbsp;language)的等价程序。源程序一般为高级语言(High-levelnbsp;language),如Pascal,C++等,而目标语言则是汇编语言或目标机器的目标代码(Objectnbsp;code),有时也称作机器代码(Machinenbsp;code)。一个现代编译器的主要工作流程如下:源程序(sourcenbsp;code)→预处理器(preprocessor)→编译器(compiler)→汇编程序(assembler)→目标程序(objectnbsp;code)→连接器(链接器,Linker)→可执行程序(executables)nbsp;目录nbsp;[隐藏]1nbsp;工作原理nbsp;2nbsp;编译器种类nbsp;3nbsp;预处理器(preprocessor)nbsp;4nbsp;编译器前端(frontend)nbsp;5nbsp;编译器后端(backend)nbsp;6nbsp;编译语言与解释语言对比nbsp;7nbsp;历史nbsp;8nbsp;参见nbsp;工作原理翻译是从源代码(通常为高级语言)到能直接被计算机或虚拟机执行的目标代码(通常为低级语言或机器言)。然而,也存在从低级语言到高级语言的编译器,这类编译器中用来从由高级语言生成的低级语言代码重新生成高级语言代码的又被叫做反编译器。也有从一种高级语言生成另一种高级语言的编译器,或者生成一种需要进一步处理的的中间代码的编译器(又叫级联)。典型的编译器输出是由包含入口点的名字和地址以及外部调用(到不在这个目标文件中的函数调用)的机器代码所组成的目标文件。一组目标文件,不必是同一编译器产生,但使用的编译器必需采用同样的输出格式,可以链接在一起并生成可以由用户直接执行的可执行程序。编译器种类编译器可以生成用来在与编译器本身所在的计算机和操作系统(平台)相同的环境下运行的目标代码,这种编译器又叫做“本地”编译器。另外,编译器也可以生成用来在其它平台上运行的目标代码,这种编译器又叫做交叉编译器。交叉编译器在生成新的硬件平台时非常有用。“源码到源码编译器”是指用一种高级语言作为输入,输出也是高级语言的编译器。例如:nbsp;自动并行化编译器经常采用一种高级语言作为输入,转换其中的代码,并用并行代码注释对它进行注释(如OpenMP)或者用语言构造进行注释(如FORTRAN的DOALL指令)。预处理器(preprocessor)作用是通过代入预定义等程序段将源程序补充完整。编译器前端(frontend)前端主要负责解析(parse)输入的源程序,由词法分析器和语法分析器协同工作。词法分析器负责把源程序中的‘单词’(Token)找出来,语法分析器把这些分散的单词按预先定义好的语法组装成有意义的表达式,语句nbsp;,函数等等。nbsp;例如“anbsp;=nbsp;bnbsp;+nbsp;c;”前端词法分析器看到的是“a,nbsp;=,nbsp;bnbsp;,nbsp;+,nbsp;c;”,语法分析器按定义的语法,先把他们组装成表达式“bnbsp;+nbsp;c”,再组装成“anbsp;=nbsp;bnbsp;+nbsp;c”的语句。nbsp;前端还负责语义(semanticnbsp;checking)的检查,例如检测参与运算的变量是否是同一类型的,简单的错误处理。最终的结果常常是一个抽象的语法树(abstractnbsp;syntaxnbsp;tree,或nbsp;AST),这样后端可以在此基础上进一步优化,处理。编译器后端(backend)编译器后端主要负责分析,优化中间代码(Intermediatenbsp;representation)以及生成机器代码(Codenbsp;Generation)。一般说来所有的编译器分析,优化,变型都可以分成两大类:nbsp;函数内(intraproceral)还是函数之间(interproceral)进行。很明显,函数间的分析,优化更准确,但需要更长的时间来完成。编译器分析(compilernbsp;analysis)的对象是前端生成并传递过来的中间代码,现代的优化型编译器(optimizingnbsp;compiler)常常用好几种层次的中间代码来表示程序,高层的中间代码(highnbsp;levelnbsp;IR)接近输入的源程序的格式,与输入语言相关(languagenbsp;dependent),包含更多的全局性的信息,和源程序的结构;中层的中间代码(middle
‘肆’ 交叉编译器的分类
编译器可以生成用来在与编译器本身所在的计算机和操作系统(平台)相同的环境下运行的目标代码,这种编译器又叫做“本地”编译器。另外,编译器也可以生成用来在其它平台上运行的目标代码,这种编译器又叫做交叉编译器。交叉编译器在生成新的硬件平台时非常有用。“源码到源码编译器”是指用一种高阶语言作为输入,输出也是高阶语言的编译器。例如: 自动并行化编译器经常采用一种高阶语言作为输入,转换其中的代码,并用并行代码注释对它进行注释(如OpenMP)或者用语言构造进行注释(如FORTRAN的DOALL指令)。
预处理器(preprocessor)
作用是通过代入预定义等程序段将源程序补充完整。
编译器前端(frontend)
前端主要负责解析(parse)输入的源代码,由语法分析器和语意分析器协同工作。语法分析器负责把源代码中的‘单词’(Token)找出来,语意分析器把这些分散的单词按预先定义好的语法组装成有意义的表达式,语句 ,函数等等。 例如“a = b + c;”前端语法分析器看到的是“a, =, b , +, c;”,语意分析器按定义的语法,先把他们组装成表达式“b + c”,再组装成“a = b + c”的语句。 前端还负责语义(semantic checking)的检查,例如检测参与运算的变量是否是同一类型的,简单的错误处理。最终的结果常常是一个抽象的语法树(abstract syntax tree,或 AST),这样后端可以在此基础上进一步优化和处理。
编译器后端(backend)
编译器后端主要负责分析,优化中间代码(Intermediate representation)以及生成机器代码(Code Generation)。
一般说来所有的编译器分析,优化,变型都可以分成两大类:函数内(intraproceral)还是函数之间(interproceral)进行。很明显,函数间的分析,优化更准确,但需要更长的时间来完成。
‘伍’ 编译器的发展史
编译器
编译器,是将便于人编写,阅读,维护的高级计算机语言翻译为计算机能识别,运行的低级机器语言的程序。编译器将源程序(Source program)作为输入,翻译产生使用目标语言(Target language)的等价程序。源程序一般为高级语言(High-level language),如Pascal,C++等,而目标语言则是汇编语言或目标机器的目标代码(Object code),有时也称作机器代码(Machine code)。
一个现代编译器的主要工作流程如下:
源程序(source code)→预处理器(preprocessor)→编译器(compiler)→汇编程序(assembler)→目标程序(object code)→连接器(链接器,Linker)→可执行程序(executables)
目录 [隐藏]
1 工作原理
2 编译器种类
3 预处理器(preprocessor)
4 编译器前端(frontend)
5 编译器后端(backend)
6 编译语言与解释语言对比
7 历史
8 参见
工作原理
翻译是从源代码(通常为高级语言)到能直接被计算机或虚拟机执行的目标代码(通常为低级语言或机器言)。然而,也存在从低级语言到高级语言的编译器,这类编译器中用来从由高级语言生成的低级语言代码重新生成高级语言代码的又被叫做反编译器。也有从一种高级语言生成另一种高级语言的编译器,或者生成一种需要进一步处理的的中间代码的编译器(又叫级联)。
典型的编译器输出是由包含入口点的名字和地址以及外部调用(到不在这个目标文件中的函数调用)的机器代码所组成的目标文件。一组目标文件,不必是同一编译器产生,但使用的编译器必需采用同样的输出格式,可以链接在一起并生成可以由用户直接执行的可执行程序。
编译器种类
编译器可以生成用来在与编译器本身所在的计算机和操作系统(平台)相同的环境下运行的目标代码,这种编译器又叫做“本地”编译器。另外,编译器也可以生成用来在其它平台上运行的目标代码,这种编译器又叫做交叉编译器。交叉编译器在生成新的硬件平台时非常有用。“源码到源码编译器”是指用一种高级语言作为输入,输出也是高级语言的编译器。例如: 自动并行化编译器经常采用一种高级语言作为输入,转换其中的代码,并用并行代码注释对它进行注释(如OpenMP)或者用语言构造进行注释(如FORTRAN的DOALL指令)。
预处理器(preprocessor)
作用是通过代入预定义等程序段将源程序补充完整。
编译器前端(frontend)
前端主要负责解析(parse)输入的源程序,由词法分析器和语法分析器协同工作。词法分析器负责把源程序中的‘单词’(Token)找出来,语法分析器把这些分散的单词按预先定义好的语法组装成有意义的表达式,语句 ,函数等等。 例如“a = b + c;”前端词法分析器看到的是“a, =, b , +, c;”,语法分析器按定义的语法,先把他们组装成表达式“b + c”,再组装成“a = b + c”的语句。 前端还负责语义(semantic checking)的检查,例如检测参与运算的变量是否是同一类型的,简单的错误处理。最终的结果常常是一个抽象的语法树(abstract syntax tree,或 AST),这样后端可以在此基础上进一步优化,处理。
编译器后端(backend)
编译器后端主要负责分析,优化中间代码(Intermediate representation)以及生成机器代码(Code Generation)。
一般说来所有的编译器分析,优化,变型都可以分成两大类: 函数内(intraproceral)还是函数之间(interproceral)进行。很明显,函数间的分析,优化更准确,但需要更长的时间来完成。
编译器分析(compiler analysis)的对象是前端生成并传递过来的中间代码,现代的优化型编译器(optimizing compiler)常常用好几种层次的中间代码来表示程序,高层的中间代码(high level IR)接近输入的源程序的格式,与输入语言相关(language dependent),包含更多的全局性的信息,和源程序的结构;中层的中间代码(middle level IR)与输入语言无关,低层的中间代码(Low level IR)与机器语言类似。 不同的分析,优化发生在最适合的那一层中间代码上。
常见的编译分析有函数调用树(call tree),控制流程图(Control flow graph),以及在此基础上的 变量定义-使用,使用-定义链(define-use/use-define or u-d/d-u chain),变量别名分析(alias analysis),指针分析(pointer analysis),数据依赖分析(data dependence analysis)等等。
上述的程序分析结果是编译器优化(compiler optimization)和程序变形(compiler transformation)的前提条件。常见的优化和变新有:函数内嵌(inlining),无用代码删除(Dead code elimination),标准化循环结构(loop normalization),循环体展开(loop unrolling),循环体合并,分裂(loop fusion,loop fission),数组填充(array padding),等等。 优化和变形的目的是减少代码的长度,提高内存(memory),缓存(cache)的使用率,减少读写磁盘,访问网络数据的频率。更高级的优化甚至可以把序列化的代码(serial code)变成并行运算,多线程的代码(parallelized,multi-threaded code)。
机器代码的生成是优化变型后的中间代码转换成机器指令的过程。现代编译器主要采用生成汇编代码(assembly code)的策略,而不直接生成二进制的目标代码(binary object code)。即使在代码生成阶段,高级编译器仍然要做很多分析,优化,变形的工作。例如如何分配寄存器(register allocatioin),如何选择合适的机器指令(instruction selection),如何合并几句代码成一句等等。
编译语言与解释语言对比
许多人将高级程序语言分为两类: 编译型语言 和 解释型语言 。然而,实际上,这些语言中的大多数既可用编译型实现也可用解释型实现,分类实际上反映的是那种语言常见的实现方式。(但是,某些解释型语言,很难用编译型实现。比如那些允许 在线代码更改 的解释型语言。)
历史
上世纪50年代,IBM的John Backus带领一个研究小组对FORTRAN语言及其编译器进行开发。但由于当时人们对编译理论了解不多,开发工作变得既复杂又艰苦。与此同时,Noam Chomsky开始了他对自然语言结构的研究。他的发现最终使得编译器的结构异常简单,甚至还带有了一些自动化。Chomsky的研究导致了根据语言文法的难易程度以及识别它们所需要的算法来对语言分类。正如现在所称的Chomsky架构(Chomsky Hierarchy),它包括了文法的四个层次:0型文法、1型文法、2型文法和3型文法,且其中的每一个都是其前者的特殊情况。2型文法(或上下文无关文法)被证明是程序设计语言中最有用的,而且今天它已代表着程序设计语言结构的标准方式。分析问题(parsing problem,用于上下文无关文法识别的有效算法)的研究是在60年代和70年代,它相当完善的解决了这个问题。现在它已是编译原理中的一个标准部分。
有限状态自动机(Finite Automaton)和正则表达式(Regular Expression)同上下文无关文法紧密相关,它们与Chomsky的3型文法相对应。对它们的研究与Chomsky的研究几乎同时开始,并且引出了表示程序设计语言的单词的符号方式。
人们接着又深化了生成有效目标代码的方法,这就是最初的编译器,它们被一直使用至今。人们通常将其称为优化技术(Optimization Technique),但因其从未真正地得到过被优化了的目标代码而仅仅改进了它的有效性,因此实际上应称作代码改进技术(Code Improvement Technique)。
当分析问题变得好懂起来时,人们就在开发程序上花费了很大的功夫来研究这一部分的编译器自动构造。这些程序最初被称为编译器的编译器(Compiler-compiler),但更确切地应称为分析程序生成器(Parser Generator),这是因为它们仅仅能够自动处理编译的一部分。这些程序中最着名的是Yacc(Yet Another Compiler-compiler),它是由Steve Johnson在1975年为Unix系统编写的。类似的,有限状态自动机的研究也发展了一种称为扫描程序生成器(Scanner Generator)的工具,Lex(与Yacc同时,由Mike Lesk为Unix系统开发)是这其中的佼佼者。
在70年代后期和80年代早期,大量的项目都贯注于编译器其它部分的生成自动化,这其中就包括了代码生成。这些尝试并未取得多少成功,这大概是因为操作太复杂而人们又对其不甚了解。
编译器设计最近的发展包括:首先,编译器包括了更加复杂算法的应用程序它用于推断或简化程序中的信息;这又与更为复杂的程序设计语言的发展结合在一起。其中典型的有用于函数语言编译的Hindley-Milner类型检查的统一算法。其次,编译器已越来越成为基于窗口的交互开发环境(Interactive Development Environment,IDE)的一部分,它包括了编辑器、连接程序、调试程序以及项目管理程序。这样的IDE标准并没有多少,但是对标准的窗口环境进行开发已成为方向。另一方面,尽管近年来在编译原理领域进行了大量的研究,但是基本的编译器设计原理在近20年中都没有多大的改变,它现在正迅速地成为计算机科学课程中的中心环节。
在九十年代,作为GNU项目或其它开放源代码项目的一部分,许多免费编译器和编译器开发工具被开发出来。这些工具可用来编译所有的计算机程序语言。它们中的一些项目被认为是高质量的,而且对现代编译理论感性趣的人可以很容易的得到它们的免费源代码。
大约在1999年,SGI公布了他们的一个工业化的并行化优化编译器Pro64的源代码,后被全世界多个编译器研究小组用来做研究平台,并命名为Open64。Open64的设计结构好,分析优化全面,是编译器高级研究的理想平台。
编译器是一种特殊的程序,它可以把以特定编程语言写成的程序变为机器可以运行的机器码。我们把一个程序写好,这时我们利用的环境是文本编辑器。这时我程序把程序称为源程序。在此以后程序员可以运行相应的编译器,通过指定需要编译的文件的名称就可以把相应的源文件(通过一个复杂的过程)转化为机器码了。
编译器工作方法
首先编译器进行语法分析,也就是要把那些字符串分离出来。然后进行语义分析,就是把各个由语法分析分析出的语法单元的意义搞清楚。最后生成的是目标文件,我们也称为obj文件。再经过链接器的链接就可以生成最后的可执行代码了。有些时候我们需要把多个文件产生的目标文件进行链接,产生最后的代码。我们把一过程称为交叉链接。
‘陆’ 缂栬疟鍣锛圕ompiler锛
缂栫▼涓栫晫涓镄勯瓟娉曞笀钬斺旂紪璇戝櫒
缂栬疟鍣锛屽氨濂芥瘆璁$畻链虹戝﹂噷镄勭炲囱浆璇戞満锛屽畠鏄涓绉嶅己澶х殑绋嫔簭宸ュ叿锛屽叾镙稿绩浠诲姟鏄灏嗘垜浠镡熸倝镄勯珮绾ц瑷锛埚侰/C++/Java绛夛级宸у椤湴杞鍖栦负链哄櫒鍙鐞呜В镄勪绠绾ц瑷钬斺旀眹缂栦唬镰併傚畠镄勭洰镙囦笉浠呬粎鏄褰㈠纺镄勮浆鎹锛屾洿鍦ㄤ簬瀵规墽琛屾晥鐜囧拰鍐呭瓨绌洪棿镄勬繁搴︿紭鍖栵纴纭淇濅唬镰佺殑鏁堢巼鍜屽嗳纭镐с
缂栬疟杩囩▼濡傚悓涓鍦虹簿瀵嗙殑浜ゅ搷涔愶纴鍒嗕负鍓嶅悗涓ゅぇ閮ㄥ垎銆傚墠绔锛屽傚悓涔愬洟镄勯栧腑鎸囨尌锛岄栧厛杩涜岃瘝娉曞垎鏋愶纸strong>灏嗘簮浠g爜鍒呜В涓轰竴涓涓鍙璇嗗埆镄勭﹀彿锛夛纴绱ф帴镌鏄璇娉曞垎鏋愶纸strong>纭璁ょ▼搴忕粨鏋勭殑钖堟硶镐э级锛岀劧钖庢槸璇涔夊垎鏋愶纸strong>鐞呜В浠g爜镄勭湡姝e惈涔夛级銆傝繖涓阒舵垫槸缂栬疟鍣ㄧ殑𨱔甸琼锛岀‘淇濅唬镰佺殑姝g‘镐у拰鍙璇绘с
钖庣鍒欐槸缂栬疟鍣ㄧ殑鍖犲绩镫杩愪箣澶勶纴瀹冭礋璐e皢鍓岖鐢熸垚镄勪腑闂磋〃绀猴纸IR锛夎浆鍖栦负鐗瑰畾链哄櫒镄勬寚浠ゃ傚崟阃氶亾缂栬疟鍣ㄥ儚涓涓涓撴敞镄勫伐鍖狅纴涓姝ユュ皢浠g爜镓撶(镊虫渶浣崇姸镐侊绂钥屽氶氶亾缂栬疟鍣ㄥ垯濡傚悓涓涓楂樻晥镄勫洟阒燂纴灏嗗ぇ椤圭洰鍒呜В涓哄氢釜瀛愪换锷★纴姣忎釜阃氶亾澶勭悊涓閮ㄥ垎锛屼粠钥岃妭鐪佸唴瀛樿祫婧愩
缂栬疟鍣ㄧ殑浣垮懡锛屽傚悓涓浣崭弗璋ㄧ殑绉戝﹀讹纴镓ц岀潃鍏抽敭浠诲姟锛氩畠鍒呜В婧愮▼搴忥纴鏋勫缓璇娉旷粨鏋勶绂鍦ㄤ腑闂翠唬镰佺敓鎴愬櫒镄勫岗锷╀笅锛屾瀯寤哄苟缁存姢绗﹀彿琛锛岀‘淇濆彉閲忓拰浠g爜镄勬g‘瀛桦偍锛涘悓镞讹纴瀹冨湪璇娉曟爲涓婅岃繘锛屾镆ュ苟淇澶崭换浣曟綔鍦ㄩ敊璇锛屼负浠g爜璋冭瘯鎻愪緵链夊姏鏀鎸併
缂栬疟杩囩▼镄勫叚涓阒舵碉纴鐘瑰傝压链涓庣戝︾殑瀹岀编缁揿悎锛氲瘝娉曞垎鏋愶纸strong>濡傚悓瑙f瀽璇楃瘒锛岃瘑鍒姣忎釜瀛楃︾殑钖涔夛级锛岃娉曞垎鏋愶纸strong>濡傚悓鏋勯犱箰璋憋纴鏋勫缓绋嫔簭镄勭粨鏋勶级锛岃涔夊垎鏋愶纸strong>娣卞叆鐞呜В阔充箰锛岀‘淇濋煶涔愮殑姝g‘婕斿忥级锛屼腑闂翠唬镰佺敓鎴愶纸strong>杞鍖栦负涔愬櫒镄勮皟寮︼纴鍑嗗囨紨濂忥级锛屼唬镰佷紭鍖栵纸strong>璋冩暣阔宠壊锛屾彁鍗囱〃鐜帮级锛屾渶钖庢槸浠g爜鐢熸垚锛坰trong>瀹屾垚涔愮珷锛屾満鍣ㄥ彲镓ц岀殑鎸囦护锛夈
𨱍宠薄涓涓嬶纴浣犵殑浠g爜灏卞儚涓棣栦紭缇庣殑浜ゅ搷涔愶纴缂栬疟鍣ㄥ氨鏄闾d釜镞犲舰镄勬寚鎸ワ纴鐢ㄥ畠镄勬櫤鎱у拰锷涢噺锛屽皢浣犵殑鍒涙剰杞鍖栦负链哄櫒涓栫晫镄勪箰绔犮傝繖灏辨槸缂栬疟鍣锛岄偅涓灏嗛珮绾ц瑷杞鍖栦负链哄櫒璇瑷镄勫箷钖庤嫳闆勚