描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787111631972
内容简介
LLVM是一个*水平的编译器框架。它包含有丰富软件库,可以为编译器的初学者提供良好的学习体验,并大大降低编译器开发的学习门槛。
本书的前半部分将向您介绍怎么样去配置、构建、和安装LLVM的不同软件库、工具和外部项目。接下来,本书的后半部分将向您介绍LLVM的各种设计细节,并逐步地讲解LLVM的各个编译步骤:前段、中间表示(IR)、后端、即时编译(JIT)引擎、跨平台编译和插件接口。本书包含有大量翔实的示例和代码片段,以帮助读者平稳顺利的掌握LLVM的编译器开发环境。
本书的前半部分将向您介绍怎么样去配置、构建、和安装LLVM的不同软件库、工具和外部项目。接下来,本书的后半部分将向您介绍LLVM的各种设计细节,并逐步地讲解LLVM的各个编译步骤:前段、中间表示(IR)、后端、即时编译(JIT)引擎、跨平台编译和插件接口。本书包含有大量翔实的示例和代码片段,以帮助读者平稳顺利的掌握LLVM的编译器开发环境。
目 录
出版者的话
译者序
前言
关于作者
关于审稿人
第1章 构建和安装LLVM 1
1.1 了解LLVM版本 1
1.2 获取预构建包 2
1.2.1 获取官方预构建二进制文件 2
1.2.2 使用软件包管理器 3
1.3 从源代码构建 4
1.3.1 系统要求 4
1.3.2 获取源代码 4
1.3.3 构建和安装LLVM 5
1.3.4 Windows和Microsoft Visual Studio 10
1.3.5 Mac OS X和Xcode 12
1.4 总结 14
第2章 外部项目 15
2.1 Clang外部项目介绍 15
2.1.1 构建和安装Clang外部工具 16
2.1.2 理解Compiler-RT 17
2.1.3 实验Compiler-RT 17
2.2 使用DragonEgg插件 18
2.2.1 构建DragonEgg 19
2.2.2 使用DragonEgg和LLVM工具了解编译流程 19
2.2.3 理解LLVM测试套件 20
2.2.4 使用LLDB 21
2.2.5 libc++标准库介绍 23
2.3 总结 25
第3章 工具和设计 26
3.1 LLVM的基本设计原理及其历史 26
3.2 理解目前的LLVM 27
3.3 与编译器驱动程序交互 29
3.4 使用独立工具 30
3.5 深入LLVM内部设计 33
3.5.1 了解LLVM的基本库 33
3.5.2 介绍LLVM的C++惯例 34
3.5.3 演示可插拔的流程接口 37
3.6 编写你的第一个LLVM项目 38
3.6.1 编写Makefile 38
3.6.2 编写代码 40
3.7 关于LLVM源代码的一般建议 41
3.7.1 将代码理解为文档 42
3.7.2 请求社区的帮助 42
3.7.3 应对更新:使用SVN日志作为文档 42
3.7.4 结束语 44
3.8 总结 44
第4章 前端 45
4.1 Clang简介 45
4.1.1 前端操作 46
4.1.2 库 47
4.1.3 理解Clang诊断 49
4.2 Clang前端阶段介绍 52
4.2.1 词法分析 52
4.2.2 语法分析 58
4.2.3 语义分析 63
4.2.4 生成LLVM IR代码 65
4.3 完整的例子 65
4.4 总结 68
第5章 LLVM中间表示 69
5.1 概述 69
5.2 操作IR格式的基本工具示例 71
5.3 LLVM IR语法介绍 71
5.4 编写自定义的LLVM IR生成器 76
5.4.1 构建和运行IR生成器 79
5.4.2 使用C++后端编写代码来生成IR构造 80
5.5 在IR层执行优化 80
5.5.1 编译时优化和链接时优化 80
5.5.2 发现最佳编译器流程 82
5.5.3 流程间的依赖关系 83
5.5.4 了解流程API 85
5.5.5 自定义流程 85
5.6 总结 89
第6章 后端 90
6.1 概述 90
6.2 后端代码结构介绍 92
6.3 后端库介绍 93
6.4 如何使用TableGen实现LLVM后端 94
6.4.1 TableGen语言 95
6.4.2 代码生成器.td文件介绍 96
6.5 指令选择阶段介绍 100
6.5.1 SelectionDAG类 100
6.5.2 降级 102
6.5.3 DAG合并以及合法化 103
6.5.4 DAG到DAG指令选择 104
6.5.5 指令选择过程可视化 107
6.5.6 快速指令选择 107
6.6 调度器 107
6.6.1 指令执行进程表 108
6.6.2 竞争检测 109
6.6.3 调度单元 109
6.7 机器指令 109
6.8 寄存器分配 110
6.8.1 寄存器合并器 111
6.8.2 虚拟寄存器重写 114
6.8.3 编译目标的信息 115
6.9 前序代码和结束代码 116
6.10 机器代码框架介绍 116
6.10.1 MC指令 116
6.10.2 代码输出 117
6.11 自定义机器流程 119
6.12 总结 121
第7章 即时编译器 122
7.1 LLVM JIT引擎的基础知识介绍 122
7.1.1 介绍执行引擎 123
7.1.2 内存管理 124
7.2 llvm::JIT框架介绍 124
7.2.1 将二进制大对象写入内存 125
7.2.2 使用JITMemoryManager 125
7.2.3 目标代码输出器 125
7.2.4 目标信息 127
7.2.5 学习如何使用JIT类 127
7.3 llvm::MCJIT框架介绍 131
7.3.1 MCJIT引擎 131
7.3.2 MCJIT中模块编译过程 132
7.3.3 使用MCJIT引擎 135
7.4 使用LLVM JIT编译工具 137
7.4.1 使用lli工具 137
7.4.2 使用llvm-rtdyld工具 138
7.5 其他资源 139
7.6 总结 139
第8章 跨平台编译 140
8.1 GCC和LLVM对比 140
8.2 目标三元组介绍 141
8.3 准备自己的工具链 142
8.3.1 标准C/C++库 143
8.3.2 运行时库 143
8.3.3 汇编器和链接器 144
8.3.4 Clang前端 144
8.4 用Clang命令行参数进行交叉编译 145
8.4.1 针对目标的驱动程序选项 145
8.4.2 依赖包 145
8.4.3 交叉编译 146
8.4.4 更改系统根目录 148
8.5 生成Clang交叉编译器 149
8.5.1 配置选项 149
8.5.2 构建和安装基于Clang的交叉编译器 149
8.5.3 其他构建方法 150
8.6 测试 151
8.6.1 开发板 151
8.6.2 模拟器 151
8.7 其他资源 152
8.8 总结 152
第9章 Clang静态分析器 153
9.1 静态分析器的作用 153
9.1.1 传统警告信息和Clang静态分析器比较 153
9.1.2 符号执行引擎的高效性 156
9.2 测试静态分析器 158
9.2.1 使用驱动程序与使用编译器 158
9.2.2 了解可用的检查器 158
9.2.3 在Xcode IDE中使用静态分析器 160
9.2.4 生成HTML格式的图形
9.2.5 处理大型项目 161
9.3 使用自定义的检查器扩展静态分析器 164
9.3.1 熟悉项目架构 164
9.3.2 自定义检查器
译者序
前言
关于作者
关于审稿人
第1章 构建和安装LLVM 1
1.1 了解LLVM版本 1
1.2 获取预构建包 2
1.2.1 获取官方预构建二进制文件 2
1.2.2 使用软件包管理器 3
1.3 从源代码构建 4
1.3.1 系统要求 4
1.3.2 获取源代码 4
1.3.3 构建和安装LLVM 5
1.3.4 Windows和Microsoft Visual Studio 10
1.3.5 Mac OS X和Xcode 12
1.4 总结 14
第2章 外部项目 15
2.1 Clang外部项目介绍 15
2.1.1 构建和安装Clang外部工具 16
2.1.2 理解Compiler-RT 17
2.1.3 实验Compiler-RT 17
2.2 使用DragonEgg插件 18
2.2.1 构建DragonEgg 19
2.2.2 使用DragonEgg和LLVM工具了解编译流程 19
2.2.3 理解LLVM测试套件 20
2.2.4 使用LLDB 21
2.2.5 libc++标准库介绍 23
2.3 总结 25
第3章 工具和设计 26
3.1 LLVM的基本设计原理及其历史 26
3.2 理解目前的LLVM 27
3.3 与编译器驱动程序交互 29
3.4 使用独立工具 30
3.5 深入LLVM内部设计 33
3.5.1 了解LLVM的基本库 33
3.5.2 介绍LLVM的C++惯例 34
3.5.3 演示可插拔的流程接口 37
3.6 编写你的第一个LLVM项目 38
3.6.1 编写Makefile 38
3.6.2 编写代码 40
3.7 关于LLVM源代码的一般建议 41
3.7.1 将代码理解为文档 42
3.7.2 请求社区的帮助 42
3.7.3 应对更新:使用SVN日志作为文档 42
3.7.4 结束语 44
3.8 总结 44
第4章 前端 45
4.1 Clang简介 45
4.1.1 前端操作 46
4.1.2 库 47
4.1.3 理解Clang诊断 49
4.2 Clang前端阶段介绍 52
4.2.1 词法分析 52
4.2.2 语法分析 58
4.2.3 语义分析 63
4.2.4 生成LLVM IR代码 65
4.3 完整的例子 65
4.4 总结 68
第5章 LLVM中间表示 69
5.1 概述 69
5.2 操作IR格式的基本工具示例 71
5.3 LLVM IR语法介绍 71
5.4 编写自定义的LLVM IR生成器 76
5.4.1 构建和运行IR生成器 79
5.4.2 使用C++后端编写代码来生成IR构造 80
5.5 在IR层执行优化 80
5.5.1 编译时优化和链接时优化 80
5.5.2 发现最佳编译器流程 82
5.5.3 流程间的依赖关系 83
5.5.4 了解流程API 85
5.5.5 自定义流程 85
5.6 总结 89
第6章 后端 90
6.1 概述 90
6.2 后端代码结构介绍 92
6.3 后端库介绍 93
6.4 如何使用TableGen实现LLVM后端 94
6.4.1 TableGen语言 95
6.4.2 代码生成器.td文件介绍 96
6.5 指令选择阶段介绍 100
6.5.1 SelectionDAG类 100
6.5.2 降级 102
6.5.3 DAG合并以及合法化 103
6.5.4 DAG到DAG指令选择 104
6.5.5 指令选择过程可视化 107
6.5.6 快速指令选择 107
6.6 调度器 107
6.6.1 指令执行进程表 108
6.6.2 竞争检测 109
6.6.3 调度单元 109
6.7 机器指令 109
6.8 寄存器分配 110
6.8.1 寄存器合并器 111
6.8.2 虚拟寄存器重写 114
6.8.3 编译目标的信息 115
6.9 前序代码和结束代码 116
6.10 机器代码框架介绍 116
6.10.1 MC指令 116
6.10.2 代码输出 117
6.11 自定义机器流程 119
6.12 总结 121
第7章 即时编译器 122
7.1 LLVM JIT引擎的基础知识介绍 122
7.1.1 介绍执行引擎 123
7.1.2 内存管理 124
7.2 llvm::JIT框架介绍 124
7.2.1 将二进制大对象写入内存 125
7.2.2 使用JITMemoryManager 125
7.2.3 目标代码输出器 125
7.2.4 目标信息 127
7.2.5 学习如何使用JIT类 127
7.3 llvm::MCJIT框架介绍 131
7.3.1 MCJIT引擎 131
7.3.2 MCJIT中模块编译过程 132
7.3.3 使用MCJIT引擎 135
7.4 使用LLVM JIT编译工具 137
7.4.1 使用lli工具 137
7.4.2 使用llvm-rtdyld工具 138
7.5 其他资源 139
7.6 总结 139
第8章 跨平台编译 140
8.1 GCC和LLVM对比 140
8.2 目标三元组介绍 141
8.3 准备自己的工具链 142
8.3.1 标准C/C++库 143
8.3.2 运行时库 143
8.3.3 汇编器和链接器 144
8.3.4 Clang前端 144
8.4 用Clang命令行参数进行交叉编译 145
8.4.1 针对目标的驱动程序选项 145
8.4.2 依赖包 145
8.4.3 交叉编译 146
8.4.4 更改系统根目录 148
8.5 生成Clang交叉编译器 149
8.5.1 配置选项 149
8.5.2 构建和安装基于Clang的交叉编译器 149
8.5.3 其他构建方法 150
8.6 测试 151
8.6.1 开发板 151
8.6.2 模拟器 151
8.7 其他资源 152
8.8 总结 152
第9章 Clang静态分析器 153
9.1 静态分析器的作用 153
9.1.1 传统警告信息和Clang静态分析器比较 153
9.1.2 符号执行引擎的高效性 156
9.2 测试静态分析器 158
9.2.1 使用驱动程序与使用编译器 158
9.2.2 了解可用的检查器 158
9.2.3 在Xcode IDE中使用静态分析器 160
9.2.4 生成HTML格式的图形
9.2.5 处理大型项目 161
9.3 使用自定义的检查器扩展静态分析器 164
9.3.1 熟悉项目架构 164
9.3.2 自定义检查器
前 言
LLVM是一个非常具有启发意义的软件项目,它起始于Chris Lattner个人对编译器的热情。LLVM最初版本发行后出现的一系列事件以及后来被广泛采用的经历也遵循了一种其他开源项目常见的成功发展模式:这些项目通常是人们对某个问题的强烈好奇心的产物,并非始于某个公司。例如,第一个Linux内核的诞生源于一名芬兰学生对操作系统领域的兴趣,因而产生了强烈动机去理解和实践一个真正的操作系统应该如何工作。
对于Linux或LLVM,许多程序员的贡献使它们迅速成长为一流软件,在质量上可以与现有的任何其他竞争对手相媲美。因此,把任何一个大项目的成功归功于特定个人是不公平的。无可否认的是,在开源社区中,一个学生的软件项目想要飞跃成为复杂且健壮的软件需要一个关键因素:吸引那些愿意在该项目上花费时间的贡献者和程序员。
这样的因素天然存在于充满教育气息的校园氛围之中。教育的重要任务是教会学生理解任务的工作原理,因此对学生而言,他们可以在解开错综复杂的机制并最终掌握它们的过程中享受到胜利的喜悦。伊利诺伊大学厄巴纳–香槟分校(UIUC)的LLVM项目正是在这种环境下发展起来的,它既被用作研究原型,也被用作Lattner的硕士导师Vikram Adve讲授编译器课程的教学框架。学生们为最初的bug排查做出了贡献,这也为LLVM最终成为一个设计良好且易于学习的项目奠定了发展方向。
软件理论和实践之间的显著差异使许多计算机科学专业的学生感到困惑。计算理论中一个简洁明了的概念可能涉及多层级的实现细节,这些细节使得现实中的软件项目变得过于复杂而无法让人们掌握,特别是其所有微妙之处。巧妙的抽象设计是帮助人类大脑掌握项目所有层面的关键:从高层级的视图(抽象意义下的程序实现和工作方式)到最低层级的细节。
理论与实践之间的差异在编译器这一软件中尤为显著。对学习编译器工作原理有极大热情的学生,在理解编译器的实际实现时常常面临艰巨的挑战。尽管学校已经教授了编译器的相关理论,但在LLVM项目之前,如果充满好奇心的学生要学习实现真正的编译器,GCC项目是少数开源选项之一。
然而从最纯粹的意义上说,软件项目反映的是其创建者的观点。这些观点通过跨多个组件对模块和数据表示进行抽象来实现。但对于同一主题,程序员可能有不同的看法。因此,对于GCC这样已有近30年历史的老旧软件库而言,其中集合了不同时代的程序员的不同观点,这使得该软件越来越难以被新程序员和好学者理解。
LLVM项目不仅吸引了经验丰富的编译器程序员,还吸引了许多年轻且具有好奇心的从事科研的学生,他们从中看到一片更干净、更简单的黑客土壤,它代表了一个具有很大潜力的编译器。这一点可以从选择LLVM作为研究原型的科学论文的庞大数量得到验证。学生们做出如此选择的原因很简单:在学术界,学生通常负责项目的具体实现,因此对他们来说,掌握实验框架代码库对于研究是至关重要的。由于LLVM使用C++语言(而不是GCC中使用的C)、模块化(而不是GCC的单一庞大结构)以及更容易映射到现代编译器理论的概念,因此,很多研究人员发现修改LLVM代码以实现他们的科研想法是很容易的,并且有很多这方面成功的例子。LLVM在学术界的成功可以说是理论与实践之间缩小差距的结果。
除了作为科研工作的实验框架之外,与GCC的GPL许可证相比,LLVM项目还有更加自由的许可证,因而引起了产业界的兴趣。对于一个从学术界发展起来的项目,编写其代码的研究人员通常会担心写好的代码在用于单独的某个实验后遭遇被丢弃的命运。为了克服这种局限性,在UIUC的硕士项目中,Chris Lattner决定根据伊利诺伊大学/NCSA开源许可协议对该项目进行许可,该许可只要求保留版权声明就允许包括商用目的在内的使用。Chris的目标是使LLVM被最大限度地采用,最终结果超出预期。2012年,LLVM荣获ACM软件系统奖,这是对为科研做出杰出贡献的软件的高度认可。
许多商业公司基于不同的需求使用LLVM项目,也为该项目做出不同的贡献,扩展了基于LLVM的编译器可以使用的语言范围以及能够为其生成代码的机器范围。最终,LLVM项目具备了前所未有的成熟的库和工具,进入了新的阶段:从学术软件的实验状态,进入被商业产品使用的健壮框架状态。因此,项目的名称也从低级虚拟机(Low Level Virtual Machine)更改为缩写LLVM。
停用低级虚拟机的名称,转而使用LLVM,这一决定反映了该项目在不同时期的目标。起初,LLVM是一个硕士科研项目,目标是成为一个可以用于研究程序终身优化的框架。相关工作成果发表在2003年MICRO(微体系结构国际研讨会)的一篇名为《LLVA: A Low-level Virtual Instruction Set Architecture》的论文以及2004年CGO(代码生成和优化国际研讨会)的一篇名为《LLVM: A Compilation Framework for Lifelong Program Analysis & Transformation》的论文中。前者描述了LLVM的指令集,而后者对整个框架进行了描述。
在学术环境之外,LLVM被广泛用作一个设计良好的编译器,它具有将中间表示写入磁盘等有用的特性。在商业系统中,它从未真正像Java虚拟机(JVM)一样被使用,因此继续使用低级虚拟机名称毫无意义。另一方面,其他一些奇怪的名字仍然作为LLVM的历史遗产而存在。在磁盘文件中存储的LLVM中间表示程序称为LLVM位码。位码的名称类似于Java的字节码,但前者反映了LLVM中间表示所需的空间,与Java字节码的含义不同。
我们编写此书有双重目的。首先,由于LLVM项目发展速度很快,我们希望将其循序渐进地呈现给你,使本书的内容尽可能简单易懂,同时让你享受使用功能强大的编译器库的乐趣。 其次,我们希望唤起你开源黑客的精神去探索超出本书的概念,永远不要停止扩充知识的脚步。
祝你阅读愉快!
本书包含的内容
第1章介绍如何在Linux、Windows或Mac上安装Clang / LLVM软件包,包括有关在Visual Studio和Xcode上构建LLVM的讨论。本章还将介绍LLVM不同发行版的风格,以便于你根据自身需要选择最合适的发行版本:预构建的二进制文件、软件分发包或源代码。
第2章介绍包含于单独的软件包或仓库中的外部LLVM项目,例如额外的Clang工具、DragonEgg GCC插件、LLVM调试器(LLDB)和LLVM测试套件。
第3章解释LLVM项目中不同工具的组织形式,并通过一个实例介绍如何使用它们将源代码编译成汇编语言。本章还将介绍编译器驱动程序的工作原理,以及如何编写你的第一个LLVM工具。
第4章介绍LLVM编译器前端,即Clang项目。本章将一步一步地完整呈现前端涉及的所有步骤,同时还将解释如何编写调用前端不同功能的小程序。本章最后介绍如何使用Clang库编写一个小型编译器驱动程序。
第5章解释LLVM设计中的一个关键部分,即其中间表示(IR)。本章将解释它的重要特点、语法、结构以及如何编写生成LLVM IR的工具。
第6章介绍LLVM的编译器后端,它负责将LLVM IR转换为机器代码。本章将逐步介绍后端涉及的所有步骤,并介绍编写自己的LLVM后端所需的知识。本章最后展示如何创建一个后端编译流程。
第7章解释LLVM即时编译基础架构,它允许按需生成和执行机器代码。对于仅在运行时才知道源程序代码的应用程序来说,此技术至关重要,例如Internet浏览器中的JavaScript解释器。 本章将指导你使用正确的库来创建自己的JIT编译器。
第8章介绍如何使用Clang / LLVM在其他平台(如基于ARM的平台)下编译程序。由于程序的最终运行平台和编译平台是不同的,其中的关键步骤在于配置正确的编译环境。
第9章介绍一个功能强大的工具,该工具甚至无须运行程序,直接通过分析代码,即可查找大型源代码库中的错误。本章还将介绍如何使用你自己的错误检查程序扩展Clang静态分析器。
第10章介绍LibTooling框架和一系列基于此库构建的Clang工具,这些工具可以帮助你方便地重构源代码或者进行简单的分析。本章最后将展示如何使用该框架编写自己的C++源代码重构工具。
在撰写本书时,LLVM 3.5尚未发布。虽然本书侧重于LLVM 3.4版本,但我们计划发布附录将书中的示例更新为LLVM 3.5,这样你就可以使用最新版本的LLVM来练习本书的内容。该附录将通过https://www.packtpub.com/sites/default/files/downloads/6924OS_Appendix.pdf提供。
阅读本书需要的前提
要开始探索LLVM世界,可以使用UNIX系统、Mac OS X系统或Windows系统,只要它们配备现代C++编译器即可。LLVM源代码对所用的C++编译器要求很高,因此我们建议读者总是使用最新的C++版本。这意味着在Linux上至少需要GCC 4.8.1,在Max OS X上至少需要Xcode 5.1,在Windows上需要Visual Studio 2012。
尽管我们会解释如何使用Visual Studio在Windows上构建LLVM,但该平台并不是本书的重点,因为某些LLVM功能在该平台上无法使用。例如,LLVM在Windows上缺少可加载模块支持,但是我们要介绍的内容包括如何编写作为共享库构建的LLVM插件。在这种情况下,支持该内容的唯一方法是使用Linux或Mac OS X。
如果读者不想自己构建LLVM,可以使用预构建的二进制包,但是这也限制了读者能够使用的平台范围。
本书目标读者
本书面向有兴趣了解LLVM框架的编程爱好者、计算机科学专业学生和编译器工程师。你需要有C++背景知识,尽管不是强制性的,但至少应该了解一些编译器理论。无论你是新手还是编译专家,本书都提供了LLVM的实用介绍,并避免了过于复杂的场景。如果你对此技术感兴趣或有需求,那么本书绝对适合你。
下载示例代码
本书的示例代码可以从http://www.packtpub.com通过个人账号下载,也可以访问华章图书官网http://www.hzbook.com,通过注册并登录个人账号下载。
对于Linux或LLVM,许多程序员的贡献使它们迅速成长为一流软件,在质量上可以与现有的任何其他竞争对手相媲美。因此,把任何一个大项目的成功归功于特定个人是不公平的。无可否认的是,在开源社区中,一个学生的软件项目想要飞跃成为复杂且健壮的软件需要一个关键因素:吸引那些愿意在该项目上花费时间的贡献者和程序员。
这样的因素天然存在于充满教育气息的校园氛围之中。教育的重要任务是教会学生理解任务的工作原理,因此对学生而言,他们可以在解开错综复杂的机制并最终掌握它们的过程中享受到胜利的喜悦。伊利诺伊大学厄巴纳–香槟分校(UIUC)的LLVM项目正是在这种环境下发展起来的,它既被用作研究原型,也被用作Lattner的硕士导师Vikram Adve讲授编译器课程的教学框架。学生们为最初的bug排查做出了贡献,这也为LLVM最终成为一个设计良好且易于学习的项目奠定了发展方向。
软件理论和实践之间的显著差异使许多计算机科学专业的学生感到困惑。计算理论中一个简洁明了的概念可能涉及多层级的实现细节,这些细节使得现实中的软件项目变得过于复杂而无法让人们掌握,特别是其所有微妙之处。巧妙的抽象设计是帮助人类大脑掌握项目所有层面的关键:从高层级的视图(抽象意义下的程序实现和工作方式)到最低层级的细节。
理论与实践之间的差异在编译器这一软件中尤为显著。对学习编译器工作原理有极大热情的学生,在理解编译器的实际实现时常常面临艰巨的挑战。尽管学校已经教授了编译器的相关理论,但在LLVM项目之前,如果充满好奇心的学生要学习实现真正的编译器,GCC项目是少数开源选项之一。
然而从最纯粹的意义上说,软件项目反映的是其创建者的观点。这些观点通过跨多个组件对模块和数据表示进行抽象来实现。但对于同一主题,程序员可能有不同的看法。因此,对于GCC这样已有近30年历史的老旧软件库而言,其中集合了不同时代的程序员的不同观点,这使得该软件越来越难以被新程序员和好学者理解。
LLVM项目不仅吸引了经验丰富的编译器程序员,还吸引了许多年轻且具有好奇心的从事科研的学生,他们从中看到一片更干净、更简单的黑客土壤,它代表了一个具有很大潜力的编译器。这一点可以从选择LLVM作为研究原型的科学论文的庞大数量得到验证。学生们做出如此选择的原因很简单:在学术界,学生通常负责项目的具体实现,因此对他们来说,掌握实验框架代码库对于研究是至关重要的。由于LLVM使用C++语言(而不是GCC中使用的C)、模块化(而不是GCC的单一庞大结构)以及更容易映射到现代编译器理论的概念,因此,很多研究人员发现修改LLVM代码以实现他们的科研想法是很容易的,并且有很多这方面成功的例子。LLVM在学术界的成功可以说是理论与实践之间缩小差距的结果。
除了作为科研工作的实验框架之外,与GCC的GPL许可证相比,LLVM项目还有更加自由的许可证,因而引起了产业界的兴趣。对于一个从学术界发展起来的项目,编写其代码的研究人员通常会担心写好的代码在用于单独的某个实验后遭遇被丢弃的命运。为了克服这种局限性,在UIUC的硕士项目中,Chris Lattner决定根据伊利诺伊大学/NCSA开源许可协议对该项目进行许可,该许可只要求保留版权声明就允许包括商用目的在内的使用。Chris的目标是使LLVM被最大限度地采用,最终结果超出预期。2012年,LLVM荣获ACM软件系统奖,这是对为科研做出杰出贡献的软件的高度认可。
许多商业公司基于不同的需求使用LLVM项目,也为该项目做出不同的贡献,扩展了基于LLVM的编译器可以使用的语言范围以及能够为其生成代码的机器范围。最终,LLVM项目具备了前所未有的成熟的库和工具,进入了新的阶段:从学术软件的实验状态,进入被商业产品使用的健壮框架状态。因此,项目的名称也从低级虚拟机(Low Level Virtual Machine)更改为缩写LLVM。
停用低级虚拟机的名称,转而使用LLVM,这一决定反映了该项目在不同时期的目标。起初,LLVM是一个硕士科研项目,目标是成为一个可以用于研究程序终身优化的框架。相关工作成果发表在2003年MICRO(微体系结构国际研讨会)的一篇名为《LLVA: A Low-level Virtual Instruction Set Architecture》的论文以及2004年CGO(代码生成和优化国际研讨会)的一篇名为《LLVM: A Compilation Framework for Lifelong Program Analysis & Transformation》的论文中。前者描述了LLVM的指令集,而后者对整个框架进行了描述。
在学术环境之外,LLVM被广泛用作一个设计良好的编译器,它具有将中间表示写入磁盘等有用的特性。在商业系统中,它从未真正像Java虚拟机(JVM)一样被使用,因此继续使用低级虚拟机名称毫无意义。另一方面,其他一些奇怪的名字仍然作为LLVM的历史遗产而存在。在磁盘文件中存储的LLVM中间表示程序称为LLVM位码。位码的名称类似于Java的字节码,但前者反映了LLVM中间表示所需的空间,与Java字节码的含义不同。
我们编写此书有双重目的。首先,由于LLVM项目发展速度很快,我们希望将其循序渐进地呈现给你,使本书的内容尽可能简单易懂,同时让你享受使用功能强大的编译器库的乐趣。 其次,我们希望唤起你开源黑客的精神去探索超出本书的概念,永远不要停止扩充知识的脚步。
祝你阅读愉快!
本书包含的内容
第1章介绍如何在Linux、Windows或Mac上安装Clang / LLVM软件包,包括有关在Visual Studio和Xcode上构建LLVM的讨论。本章还将介绍LLVM不同发行版的风格,以便于你根据自身需要选择最合适的发行版本:预构建的二进制文件、软件分发包或源代码。
第2章介绍包含于单独的软件包或仓库中的外部LLVM项目,例如额外的Clang工具、DragonEgg GCC插件、LLVM调试器(LLDB)和LLVM测试套件。
第3章解释LLVM项目中不同工具的组织形式,并通过一个实例介绍如何使用它们将源代码编译成汇编语言。本章还将介绍编译器驱动程序的工作原理,以及如何编写你的第一个LLVM工具。
第4章介绍LLVM编译器前端,即Clang项目。本章将一步一步地完整呈现前端涉及的所有步骤,同时还将解释如何编写调用前端不同功能的小程序。本章最后介绍如何使用Clang库编写一个小型编译器驱动程序。
第5章解释LLVM设计中的一个关键部分,即其中间表示(IR)。本章将解释它的重要特点、语法、结构以及如何编写生成LLVM IR的工具。
第6章介绍LLVM的编译器后端,它负责将LLVM IR转换为机器代码。本章将逐步介绍后端涉及的所有步骤,并介绍编写自己的LLVM后端所需的知识。本章最后展示如何创建一个后端编译流程。
第7章解释LLVM即时编译基础架构,它允许按需生成和执行机器代码。对于仅在运行时才知道源程序代码的应用程序来说,此技术至关重要,例如Internet浏览器中的JavaScript解释器。 本章将指导你使用正确的库来创建自己的JIT编译器。
第8章介绍如何使用Clang / LLVM在其他平台(如基于ARM的平台)下编译程序。由于程序的最终运行平台和编译平台是不同的,其中的关键步骤在于配置正确的编译环境。
第9章介绍一个功能强大的工具,该工具甚至无须运行程序,直接通过分析代码,即可查找大型源代码库中的错误。本章还将介绍如何使用你自己的错误检查程序扩展Clang静态分析器。
第10章介绍LibTooling框架和一系列基于此库构建的Clang工具,这些工具可以帮助你方便地重构源代码或者进行简单的分析。本章最后将展示如何使用该框架编写自己的C++源代码重构工具。
在撰写本书时,LLVM 3.5尚未发布。虽然本书侧重于LLVM 3.4版本,但我们计划发布附录将书中的示例更新为LLVM 3.5,这样你就可以使用最新版本的LLVM来练习本书的内容。该附录将通过https://www.packtpub.com/sites/default/files/downloads/6924OS_Appendix.pdf提供。
阅读本书需要的前提
要开始探索LLVM世界,可以使用UNIX系统、Mac OS X系统或Windows系统,只要它们配备现代C++编译器即可。LLVM源代码对所用的C++编译器要求很高,因此我们建议读者总是使用最新的C++版本。这意味着在Linux上至少需要GCC 4.8.1,在Max OS X上至少需要Xcode 5.1,在Windows上需要Visual Studio 2012。
尽管我们会解释如何使用Visual Studio在Windows上构建LLVM,但该平台并不是本书的重点,因为某些LLVM功能在该平台上无法使用。例如,LLVM在Windows上缺少可加载模块支持,但是我们要介绍的内容包括如何编写作为共享库构建的LLVM插件。在这种情况下,支持该内容的唯一方法是使用Linux或Mac OS X。
如果读者不想自己构建LLVM,可以使用预构建的二进制包,但是这也限制了读者能够使用的平台范围。
本书目标读者
本书面向有兴趣了解LLVM框架的编程爱好者、计算机科学专业学生和编译器工程师。你需要有C++背景知识,尽管不是强制性的,但至少应该了解一些编译器理论。无论你是新手还是编译专家,本书都提供了LLVM的实用介绍,并避免了过于复杂的场景。如果你对此技术感兴趣或有需求,那么本书绝对适合你。
下载示例代码
本书的示例代码可以从http://www.packtpub.com通过个人账号下载,也可以访问华章图书官网http://www.hzbook.com,通过注册并登录个人账号下载。
评论
还没有评论。