简介
LLVM 是一套提供编译器基础设施的开源我的项目,是用 C++ 编写,蕴含一系列模块化的编译器组件和工具链,用来开发编译器前端和后端。它是为了任意一种编程语言而写成的程序,利用虚构技术发明出编译期间、链接期间、执行期间以及“闲置期间”的优化。
LLVM 的命名源自于底层虚拟机(Low Level Virtual Machine)的首字母缩写,导致不理解它的人认为它是相似于 JVM(Java Virtual Machine)的虚拟机,实际上这个我的项目的范畴并不局限于创立一个虚拟机,而是包含 LLVM 中介码(LLVM IR)、LLVM 调试工具、LLVM C++ 规范库等一系列编译工具及低端工具技术的汇合。
传统的动态编译器设计是三阶段设计,其次要组件是前端、优化器和后端。
前端负责词法剖析、语法分析、语义剖析、生成中间代码等性能。
优化器负责进行各种转换以尝试进步代码的运行工夫,例如打消冗余计算,并且通常或多或少独立于语言和指标。
后端(也称为代码生成器)负责将代码映射到指标指令集。除了编写正确的代码外,它还负责生成利用所反对架构的不寻常个性的良好代码。编译器后端的常见局部包含指令抉择、寄存器调配和指令调度。
该模型同样实用于解释器和 JIT 编译器。JVM 也是该模型的一个实现,它应用 Java 字节码作为前端和优化器之间的接口。
而 LLVM 被设计为反对多种源语言或指标架构,它提供了一套适宜编译器零碎的两头语言,如果编译器在其优化器中应用这个两头语言示意,则能够为任何能够编译到它的语言编写前端,并且能够为任何能够从它编译的指标编写后端。
应用这种设计,移植编译器以反对新的源语言只须要实现新的前端,即能够重用现有的优化器和后端;同样想减少反对新的指标架构也只须要实现新的后端。而如果按传统设计,前端和后端理论是耦合在一起,实现新的源语言或反对新的指标架构将须要从头开始,要反对 N 指标和 M 源语言将须要 N*M 个编译器。
LLVM IR
LLVM 提供了一套适宜编译器零碎的两头语言(Intermediate Representation,IR),有大量变换和优化都围绕其实现,通过变换和优化后的两头语言,能够转换为指标平台相干的汇编语言代码。
该两头语言与具体的语言、指令集、类型零碎无关,其中每条指令都是动态单赋值模式(SSA),即每个变量只能被赋值一次。这有助于简化变量之间的依赖剖析。
以下是简略的 LLVM IR 代码:
define i32 @add1(i32 %a, i32 %b) {
entry:
%tmp1 = add i32 %a, %b
ret i32 %tmp1
}
define i32 @add2(i32 %a, i32 %b) {
entry:
%tmp1 = icmp eq i32 %a, 0
br i1 %tmp1, label %done, label %recurse
recurse:
%tmp2 = sub i32 %a, 1
%tmp3 = add i32 %b, 1
%tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3)
ret i32 %tmp4
done:
ret i32 %b
}
上述代码对应的 C 语言代码为:
unsigned add1(unsigned a, unsigned b) {return a+b;}
unsigned add2(unsigned a, unsigned b) {if (a == 0) return b;
return add2(a-1, b+1);
}
从这个例子能够看出,LLVM IR 是一种强类型的精简指令集(RISC)。像真正的 RISC 指令集一样,它反对简略指令的线性序列,如加法、减法、比拟和分支。这些指令采纳三地址模式,这意味着它们承受肯定数量的输出并在不同的寄存器中产生后果。LLVM IR 反对标签,通常看起来像一种奇怪的汇编语言模式。
与大多数 RISC 指令集不同,LLVM 应用简略的类型零碎进行强类型化(例如,i32
是一个 32 位整数,i32**
是一个指向 32 位整数的指针),并且机器的一些细节被形象掉了。例如,调用约定是通过指令和显式参数 call
形象进去的。ret
与机器代码的另一个显着区别是 LLVM IR 不应用一组固定的命名寄存器,它应用一组有限的以 % 字符命名的长期寄存器。
LLVM IR 反对三种表达形式:人类可读的汇编、在 C ++ 中对象模式、序列化后的 bitcode 模式。
编译
LLVM 容许代码被动态的编译,蕴含在传统的 GCC 零碎底下,者通过实时编译(JIT)机制将两头示意转换为机器码(相似 Java)。
LLVM 类型零碎蕴含根本类型(整数或是浮点数)及五个复合类型(指针、数组、向量、构造及函数),在 LLVM 具体语言的类型建制能够以联合根本类型来示意,举例来说,C++ 所应用的 class 能够被示意为构造、函数及函数指针的数组所组成。
LLVM 提供了 Clang 作为官网的编译器前端,同时反对 C、C++、Objective-C 和 Objective-C++ 语言。次要来自 Apple 公司的资助反对,Clang 的目标用以取代 GCC 零碎底下的 C / Objective-C 编译器,在当代的零碎,它较为容易与集成开发环境(IDE)集成,而且对于线程有更好的反对。许多 GCC 的前端也曾经能够与其运行,LLVM 目前反对 Ada、C 语言、C++、D 语言、Fortran、Haskell、Julia、Objective-C、Rust 及 Swift 等语言的编译。