你好,我是编辑王惠。

阶段性的总结复习和验证成果是非常重要的。所以,在8月7日到8月12日这为期一周的期中复习时间里,我们先来巩固一下“真实编译器解析篇”中的重点知识。你可以通过学习委员朱英达总结梳理的划重点内容,以及涵盖了关键知识点的7张思维导图,来回顾7种语言编译器的核心概念与算法。

另外,宫老师还精心策划了10道考试题,让你能在行至半程之时,做好自检,及时发现知识漏洞,到时候一起来挑战一下吧!

在期中复习周的最后,我还会邀请一位优秀的同学来做一次学习分享。通过他的学习故事,你也可以借此对照一下自己的编译原理学习之路。

好,下面我们就一起来复习这些核心的编译原理概念与算法知识吧。


Java编译器(javac)

Java是一种广泛使用的计算机编程语言,主要应用于企业级Web应用开发、大型分布式系统以及移动应用开发(Android)。到现在,Java已经是一门非常成熟的语言了,而且它也在不断进化、与时俱进,泛型、函数式编程、模块化等特性陆续都增加了进来。与此同时,Java的编译器和虚拟机中所采用的技术,也比 20 年前发生了天翻地覆的变化。

Java的字节码编译器(javac)是用Java编写的,它实现了自举。启动Java编译器需要Java虚拟机(默认是HotSpot虚拟机,使用C++编写)作为宿主环境。

javac编译器的编译过程,主要涉及到了这样一些关键概念和核心算法:

参考资料:

  1. 关于注解的官方教程,参考这个链接
  2. 关于数据流分析的理论性内容,参考龙书(Compilers Principles, Techniques and Tools)第二版的9.2和9.3节。也可以参考《编译原理之美》 的第2728讲,那里进行了比较直观的讲述。
  3. 关于半格这个数学工具,可以参考龙书第二版的9.3.1部分,也可以参考《编译原理之美》的第28讲
  4. Java语言规范第六章,参考Java虚拟机指令集

Java JIT编译器(Graal)

对于编译目标为机器码的Java后端的编译器来说,主要可以分AOT和JIT两类:如果是在运行前一次性生成,就叫做提前编译(AOT);如果是在运行时按需生成机器码,就叫做即时编译(JIT)。Java以及基于JVM的语言,都受益于JVM的JIT编译器。

JDK的源代码中,你能找到src/hotspot目录,这是 JVM 的运行时:HotSpot虚拟机,它是用C++编写的,其中就包括JIT编译器。

Graal是Oracle公司推出的一个完全用Java语言编写的JIT编译器。Graal编译器有两个特点:内存安全(相比C++实现的Java JIT编译器而言);与Java配套的各种工具(比如ID)更友好、更丰富。

Java JIT编译器的编译过程,主要涉及到了这样一些关键概念和核心算法:

金句摘录:“编译器开发的真正的工作量,都在中后端。”

参考资料:

  1. GraalVM项目的官方网站;Graal的Github地址;Graal项目的出版物
  2. 基于图的IR的必读论文:程序依赖图-J. Ferrante, K. J. Ottenstein, and J. D. Warren. The program dependence graph and its use in optimization. July 1987;Click的论文-A Simple Graph-Based Intermediate Representation;介绍Graal IR的论文-Graal IR: An Extensible Declarative Intermediate Representation
  3. 关于优化算法:多态内联-Inlining of Virtual Methods;逃逸分析-Escape Analysis for Java;部分逃逸分析-Partial Escape Analysis and Scalar Replacement for Java

Python编译器(CPython)

Python诞生于上个世纪90年代初,作者是荷兰计算机程序员吉多·范罗苏姆(Guido van Rossum)。Python语言的特点是:自身语法简单,容易掌握,强调一件事情只能用一种方法去做;具备丰富的现代语言特性,如OOP、FP等;其实现机制决定了易于集成C++扩展,不仅便于利用一些已有的、经典开源的高性能的C/C++库,同时也可以很方便地编写自己的C++扩展,实现一些高性能模块。

另外,Python使用了pgen这样的生成编译器的工具。pgen能够基于语法规则生成解析表(Parse Table),供语法分析程序使用。你可以通过修改规则文件来修改Python语言的语法,pgen能给你生成新的语法解析器。它是把EBNF转化成一个NFA,然后再把这个NFA转换成DFA。基于这个DFA,在读取Token的时候,编译器就知道如何做状态迁移,并生成解析树。Python用的是 LL(1) 算法。

CPython编译器编译器的编译过程,主要涉及到了这样一些关键概念和核心算法:

参考资料:

  1. python.org网站:下载3.8.1版本的源代码
  2. GDB的安装和配置:参考这篇文章
  3. Python的开发者指南网站。
  4. pgen的工具程序:Parser/pgen
    注:由于CPython最新的Master分支上的代码调整,此处pgen的链接地址调整为CPython3.9版本分支上的pgen相关代码。
  5. Python的字节码的说明
  6. Python的内置类型

JavaScript编译器(V8)

V8是谷歌公司在2008年推出的一款JavaScript编译器,主要由C++编写而成。V8主要应用于Chrome浏览器,后来也被开源社区中诸如Node.js等项目所使用。其最为突出的特点就是“快”,由于JavaScript是在浏览器下载完页面后马上编译并执行,它对编译速度有更高的要求。因此,V8采用了一系列技术手段优化编译和启动阶段运行速度。

在设计上,V8结合了分阶段懒解析、空间换时间等设计思路,突出了解析、启动阶段运行的时间开销。

另外,V8的很多地方体现出了与Java编译器异曲同工之处。比如,它将JavaScript源代码的编译,分为了由Ignition字节码解释执行和TurboFan的JIT编译机器代码执行两部分组成,类似于Java编译器的字节码解释执行和Graal优化编译后执行两阶段;TurboFan编译器的IR也采用了Sea of Nodes,这一点类似于Java的Graal编译器,且也涉及到了内联优化和逃逸分析算法。

其运行方式分为两类:

因为JavaScript是动态类型语言,因此对函数参数类型的推断以及针对性优化是一个V8的核心技术。V8涉及到的其他优化算法有:

参考资料:

  1. V8项目的官网,以及V8的源代码-官方文档
  2. 了解V8的解析器为什么速度非常快:Blazingly fast parsing, part 1: optimizing the scannerBlazingly fast parsing, part 2: lazy parsing
  3. 了解Ignition的设计:Ignition Design Doc,宫老师在Github上也放了一个拷贝
  4. 了解Ignition的字节码:Understanding V8’s bytecode
  5. V8的指针压缩技术:Pointer Compression in V8
  6. 介绍V8基于推理的优化机制:An Introduction to Speculative Optimization in V8
  7. 关于Ignition字节码做优化的论文:Register equivalence optimization,宫老师在Github上也放了一份拷贝

Julia的编译器

Julia语言最初发行于2012年,其最初是为了满足高性能数值分析和计算科学的需要而设计的。Julia同时兼具了静态编译型和动态解释型语言的优点:一方面它的性能很高,可以跟Java和C语言媲美;另一方面,它又是动态类型的,编写程序时不需要指定类型。

Julia编译器的特点是:

Julia编译器的编译过程,主要涉及到了这样一些关键概念和核心算法:

参考资料:

  1. LLVM的官网,以及LLVM的源代码
  2. Julia的开发者文档中有对如何使用LLVM的介绍:Working with LLVM
  3. 对LLVM中的各种Pass的介绍:LLVM’s Analysis and Transform Passes
  4. 《编译原理之美》的第25讲第26讲:宫老师对LLVM后端及其命令行工具做了介绍,并且还手工调用LLVM的API,示范了针对不同的语法结构(比如if结构)应该如何生成LLVM IR,最后即时编译并运行。你可以参考一下。

Go语言编译器(gc)

Go语言是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言,又名Golang。Go广泛应用于Google的产品以及许多其他组织和开源项目,其创建的初衷就是主要面向于部署于大量服务器之间的分布式程序,也就是我们今天所说的“云”。因此,Go的主要优势聚焦于服务端高并发场景。

Go语言编译器的特点是:

Go语言编译器的编译过程,主要涉及到了这样一些关键概念和核心算法:

参考资料:

  1. 介绍gc编译器的主要结构:Introduction to the Go compiler官方文档。
  2. 介绍gc编译器的SSA:Introduction to the Go compiler’s SSA backend官方文档。
  3. Go compiler internals: adding a new statement to Go - Part 1Part2。在这两篇博客里,作者做了一个实验:如果往Go里面增加一条新的语法规则,需要做哪些事情。我们能够很好地、贯穿性地了解一个编译器的方法。
  4. 介绍gc编译器的SSA优化规则描述语言的细节:Go compiler: SSA optimization rules description language
  5. 介绍Go汇编的细节:A Primer on Go AssemblyA Quick Guide to Go’s Assembler。gc编译器采用的汇编语言是它自己的一种格式,是“伪汇编”。

MySQL的编译器

MySQL是一个开放源码的关系数据库管理系统,原开发者为瑞典的MySQL AB公司,后几经辗转,目前归属于Oracle旗下产品。在过去,MySQL性能高、成本低、可靠性好,因此成为了最流行的开源数据库。SQL可以称得上是最成功的DSL(特定领域语言)之一,MySQL中的SQL解析模块则是这门DSL的非常具有可参考性的一个实现。MySQL使用C++编写,有少量几个代码文件是用C语言编写的。

MySQL的编译器的特点是:

MySQL的编译器的编译过程,主要涉及到了这样一些关键概念和核心算法:

词法分析和语法分析

语义分析

机器无关优化

机器相关优化

参考资料:

  1. 下载MySQL的源代码;跟踪MySQL的执行过程,要用Debug模式编译MySQL,具体步骤可以参考这篇开发者文档
  2. MySQL的内行手册:MySQL Internals Manual。它能给我们提供一些重要的信息,但文档内容经常跟源代码的版本不同步,比如介绍源代码的目录结构的信息就过时了。需要注意一下。
  3. bison的手册
  4. 如果要加深对MySQL内部机制的了解,宫老师推荐了两本书:OReilly的《Understanding MySQL Internals》,以及《Expert MySQL》。

评论