LLVM平台,短短几年间,改变了众多编程语言的走向,也催生了一大批具有特色的编程语言的出现,不愧为编译器架构的王者,也荣获2012年ACM软件系统奖 —— 题记
版权声明:本文为 西风逍遥游 原创文章,转载请注明出处 西风世界 [http://blog.csdn.net/xfxyy_sxfancy](http://blog.csdn.net/xfxyy_sxfancy)
### 现代编译器架构
编译器技术,作为计算机科学的皇后,从诞生起,就不断推进着计算机科学的发展,编译器的发展史,简直就是计算机发展史的缩影,而编译器的架构也逐句变得更加优雅,独立性更强。
但说到编译器的架构,可能还留存着编译原理课程的印象,5个经典流程:
词法分析 -> 语法分析 -> 语义分析 -> 中间代码优化 -> 目标代码生成
一般,我们会将编译器分为一个前端,一个后端,前端负责处理源代码,后端负责生成目标代码。
但软件工程,就是在不断的抽象和分层,分层解决问题是重要的特点,分层能够增加层之间的独立性,更好的完成任务。
### LLVM中间代码优化
LLVM的一大特色就是,有着独立的、完善的、严格约束的中间代码表示。这种中间代码,就是LLVM的字节码,是LLVM抽象的精髓,前端生成这种中间代码,后端自动进行各类优化分析,让用LLVM开发的编译器,都能用上最先见的后端优化技术。
![](https://box.kancloud.cn/2016-06-03_5750ee19a439e.png)
LLVM另外一大特色就是自带JIT,要知道,这可是在原来很难想象的技术,一个编译器要想实现JIT,是需要进行大量努力的,即时翻译代码,还要兼顾效率和编译时间,可不是一件简单的事情。
但如果你用上了LLVM,JIT只是其中的副产品,直接就可以使用的。
LLVM将中间代码优化这个流程做到了极致,LLVM工具链,不但可以生成所支持的各个后端平台的代码,更可以方便的将各语言的前端编译后的模块链接到一起,你可以方便的在你的语言中调用C函数。
![](https://box.kancloud.cn/2016-06-03_5750ee1b42a50.png "")
### 可读的中间代码
LLVM中间代码是非常易读的,而且拥有很多高级结构,例如类型和结构体、元数据等,使用起来非常方便。
~~~
; Declare the string constant as a global constant.
@.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00"
; External declaration of the puts function
declare i32 @puts(i8* nocapture) nounwind
; Definition of main function
define i32 @main() { ; i32()*
; Convert [13 x i8]* to i8 *...
%cast210 = getelementptr [13 x i8], [13 x i8]* @.str, i64 0, i64 0
; Call puts function to write out the string to stdout.
call i32 @puts(i8* %cast210)
ret i32 0
}
; Named metadata
!0 = !{i32 42, null, !"string"}
!foo = !{!0}
~~~
这是一段HelloWorld的LLVM字节码,我们发现很清晰,而且几乎所有的位置都有注明类型,这也是在强调,LLVM是强类型的,每个变量和临时值,都要有明确的类型定义。
下面是结构体的声明:
~~~
%mytype = type { %mytype*, i32 }
~~~
非常遗憾的是,这个结构体的定义只有类型序列信息,没有对应子成员的名称,这是让编译器前端自行保存和查表,来记录这些信息。
C函数的调用非常方便,只需要简单的声明
~~~
declare i32 @printf(i8* noalias nocapture, ...)
declare i32 @atoi(i8 zeroext)
~~~
你可以将源码用LLVM编译成.bc,然后用llc编译成.o,再拿Clang链接上各个库就可以了。