一个编译器的主要流程

编译器简介

编译器的作用是将源程序翻译成另一种语言,例如,将 JAVA 代码编译为二进制 .class 文件以跨平台运行,将 JSP 文件编译成 Servlet 代码,将 HTML 模板编译为 JavaScript 代码,这和解释型语言(几乎所有的脚本语言)有所不同,后者是在运行时直接解释运行。
编译器的设计目标是:

  • 极致的性能优化,这通常是最具挑战的目标

  • 高效编译,即编译应该控制在合理的时间内

  • 正确的优化,即不能改变源程序的含义

一个语言的编译器分为两个部分,一个是分析部分,一个是综合部分,我们称分析部分为编译器的前端,综合部分为编译器的后端。分析部分包括:

分析部分

  • 词法分析
    这一步将源程序组织成词素序列,并输出词法单元,词法单元类似 <id, index>,index 为符号表对应的索引。

  • 语法分析
    根据词法单元生成可表示语法结构的中间形式,例如语法树,这个步骤会帮助编译器在接下来的过程中对源程序进行分析。

  • 语义分析
    这一步骤会做类型检查,如果出现类型错误,编译器会在这个阶段发现。如果程序语言支持自动类型转换,会将某些数据类型做转换。这一阶段会分析更加细致的语义信息,并补充符号表或语法树。

综合部分

这一部分会做很多更加复杂的工作,包括中间代码生成(生成中间表示会存在整个编译周期)和优化工作。随着计算机架构的不断更新,多核 CPU 的出现,如何使计算机指令执行效率得到提高,充分利用机器性能成为编译器的目标与挑战。
这一阶段会生成易于生成,易于翻译成目标机器代码的中间代码,例如『三地址代码』:

1
2
3
4
t1 = inttofloat(60)
t2 = id3 * t1
t3 = id2 + t2
id1 = t3

并优化,生成目标语言,如汇编语言。然后由汇编器生成目标机器代码。

通常,在分析部分开始之前,还会有预处理器,所做的工作是将『宏』转换到代码语句中。