本节书摘来自华章出版社《编译与反编译技术实战 》一书中的第2章,第2.3节,庞建民 主编 ,刘晓楠 陶红伟 岳 峰 戴超 编著,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
2.3 编译器的设计与实现概述
根据不同的用途和侧重点,编译程序可以进一步分类,换句话说,有许多不同种类的编译器变体。譬如:用于帮助程序开发和调试的编译程序称为诊断编译程序,这类编译器可对程序进行详细检查并报告错误;另一类侧重于提高目标代码效率的编译程序称为优化编译程序,这类编译器通常使用多种混合的“变换”来改善程序的性能,但这往往是以编译器的复杂性和编译时间的增加为代价的。通常,将运行编译程序的机器成为宿主机,将运行编译程序所产生的目标代码的机器称为目标机。如果一个编译程序产生不同于其宿主机指令集的机器代码,则称它为交叉编译程序(Cross Compiler)。还有一类编译器,其目标机器可以改变,而不需要重写它的与机器无关的组件,这类编译器称为可再目标编译器(Retargetable Compiler),通常,这类编译器难以生成高效的代码,因为其难以利用特殊情况和目标机器特性。目前,很多编译程序同时提供了调试、优化、交叉编译等多种功能,用户可以通过“编译选项”进行选择。
编译器本身也是一个程序,这个程序是怎么编写的呢?早期人们使用汇编语言编写编译器。虽然用汇编语言编写的编译器代码效率很高,但由于汇编语言编程与高级语言编程相比难度较大,对编译器这种复杂的系统编写起来效率不高,因此,后来人们改用高级语言来编写编译器。随着编译技术的逐步成熟,一些专门的编译器编写工具相继涌现,比较成熟和通用的工具有词法分析器生成器(如LEX)和语法分析器生成器(如YACC)等。还有一些工具,如用于语义分析的语法制导翻译工具、用于目标代码生成的自动的代码生成器、用于优化的数据流工具等。下面简单介绍利用一些工具实现一个新的语言编译器的基本流程。
2.3.1 利用Flex和Bison实现词法和语法分析
在UNIX环境中编写程序,你往往会邂逅神秘的LEX和YACC,而GNU/Linux用户则会邂逅Flex和Bison。
Flex是一个与LEX兼容的词法分析器生成器,可以用它来生成一个新的语言的词法分析器,Flex就是由Vern Paxon实现的一个LEX,使用它既可以节省时间,也可以提高正确性。
Bison是一个与YACC兼容的语法分析器生成器,可以用它来生成一个新的语言的语法分析器,使用它也可以提高正确性并节省开发时间,实际上,Bison是一个可以把符合 LALR(1)文法规范的上下文无关文法转换成 C语言程序的语法分析器生成器,是一个GNU版本的YACC。
实现一种新语言,需要做的工作主要包括设计文法、进行语法制导的翻译、优化和代码生成,而后续的工作还可以由LLVM的相关工具提供支持。
2.3.2 利用LLVM实现代码优化和代码生成
LLVM是一个包含一系列模块化可重用编译器和工具链技术的项目。LLVM主要的子项目有LLVM Core libraries、Clang、Dragonegg、LLDB等。其中LLVM Core libraries
(LLVM核心库)提供了一个不依赖于目标平台的优化器,同时还为许多典型架构的CPU提供了代码生成的支持。这些库是围绕着一个有详细说明的中间代码表示形式(LLVM IR)建立起来的。也就是说,只要能够把待设计的语言翻译成LLVM IR这种中间语言,就可以利用LLVM完成代码优化和代码生成的工作。当然,这要求目标CPU架构必须是LLVM已经支持的,否则就得自己完成代码生成的工作。Clang是一个LLVM自身的C/C++/Objective-C编译器,目标是提供快速的编译。
Dragonegg的功能是把LLVM的优化器、代码生成器和GCC 4.5的分析器结合在一起,这样就使得LLVM能够编译像Ada、Fortran等其他GCC编译器前端支持的语言,且能够拥有一些Clang不支持的C特性(如OpenMP等)。LLDB则是建立在LLVM库和Clang之上的一个非常好的本地调试器。