LLVM编译器基础架构与DragonEgg示例
LLVM 概述
LLVM 项目是模块化和可重用的编译器和工具链技术的集合。LLVM 与传统的虚拟机几乎没有关系。“LLVM”这个名字本身并不是一个首字母缩写词;是项目的全名。
LLVM开始作为一个研究项目,在伊利诺伊大学,与提供能够一个现代的,基于SSA编译策略目标,支持任意编程语言的,静态与动态编译。从那时起,LLVM 已经发展成为一个由多个子项目组成的伞形项目,其中许多被各种商业和开源项目用于量产, 广泛用于学术研究。LLVM 项目中的代码,根据"Apache 2.0 License with LLVM exceptions"许可的。
LLVM 的主要子项目是:
- LLVM内核库提供一个现代化的,source- and target-independent独立的 optimizer 优化,依靠 code generation support代码生成,支持许多流行的CPU(以及一些不太常见的!),这些库文件基于特定代码,称为LLVM中间代码表示(“LLVM IR”)构建的。LLVM 核心库有很好的参考文档,而且特别容易创造新语言(或移植现有的编译器),使用 LLVM 作为优化器和代码生成器。
- Clang是一个“LLVM 原生”,C/C++/Objective-C 编译器,旨在提供惊人的编译速度、极好的 error and warning messages 错误和警告消息,构建一个平台,构建超源级工具building great source level tools。 Clang Static Analyzer and clang-tidy,在代码中自动寻找错误bugs,可以使用Clang frontend前端库,提供解析C / C ++代码生成工具的大量示例。
- LLDB项目由LLVM和Clang构建,提供了大量调试great native debugger。使用 Clang AST 和表达式解析器、LLVM JIT、LLVM 反汇编器等,提供了“正常工作”"just works"的体验。在加载符号时,比 GDB 更快,存储效率更高。
- 在libc中++和 libc ++ ABI项目,提供了一个标准的适配和高性能实现的C ++标准库,包括C ++ 11和C ++ 14的完全支持。
- 编译器RT项目,提供了高效tune调试,如"__fixunsdfdi"的low-level code generator支持例程的实现等,当目标不具有本地指令的短序列实现核心IR操作,产生的调用时。还为动态测试工具(例如AddressSanitizer、 ThreadSanitizer、 MemorySanitizer和 DataFlowSanitizer )提供运行时库的实现 。
- MLIR子项目,一种新的方法来构建可重用和可扩展的编译架构。MLIR 旨在解决软件碎片问题,改进异构硬件的编译,显着降低构建特定领域编译器的成本,支持将现有编译器连接在一起。
- OpenMP的子项目, 在clang 中,通过OpenMP实现,提供OpenMP runtime。
- 该polly项目,使用using a polyhedral model多面体模型套件,auto-parallelism and vectorization自动并行和向量化,实现整套cache-locality optimizations缓存局部性优化。
- libclc项目,实现OpenCL标准库。
- klee项目,实现了“symbolic virtual machine符号虚拟机” ,通过程序中的所有动态路径,采用一个定理证明,试图评估在努力发现问题和证明的功能性。klee 的一个主要特性,可以在检测到错误时,生成测试用例。
- LLD项目,一个新的linker链接器。系统链接器的直接替代品,运行速度更快。
除了 LLVM 的官方子项目之外,还有各种各样的其它项目,使用 LLVM 的组件完成各种任务。通过这些外部项目,可以使用 LLVM,编译 Ruby、Python、Haskell、Rust、D、PHP、Pure、Lua 和许多其它语言。LLVM 的一个主要优势,多功能性、灵活性和可重用性,这就是为什么用于如此广泛的不同任务:从对 Lua 等嵌入式语言,进行轻量级 JIT 编译,到为大规模超级计算机编译 Fortran 代码。
与其它一样,LLVM 拥有一个广泛而友好的社区,对构建出色的低级工具感兴趣。如果有兴趣参与,最好先浏览LLVM 博客并注册LLVM 开发人员邮件列表。有关如何发送补丁、获取提交访问权限,以及版权和许可主题的信息,请参阅LLVM 开发人员规则。
DragonEgg示例
DragonEgg in action
使用gcc-4.5编译"hello world" 程序:
$ gcc hello.c -S -O1 -o -
.file "hello.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "Hello world!"
.text
.globl main
.type main, @function
main:
subq $8, %rsp
movl $.LC0, %edi
call puts
movl $0, %eax
addq $8, %rsp
ret
.size main, .-main
.ident "GCC: (GNU) 4.5.0 20090928 (experimental)"
.section .note.GNU-stack,"",@progbits
将 -fplugin=path/dragonegg.so 加到 gcc command line优化代码,通过LLVM生成codegenerator:
$ gcc hello.c -S -O1 -o - -fplugin=./dragonegg.so
.file "hello.c"
# Start of file scope inline assembly
.ident "GCC: (GNU) 4.5.0 20090928 (experimental) LLVM: 82450:82981"
# End of file scope inline assembly
.text
.align 16
.globl main
.type main,@function
main:
subq $8, %rsp
movl $.L.str, %edi
call puts
xorl %eax, %eax
addq $8, %rsp
ret
.size main, .-main
.type .L.str,@object
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "Hello world!"
.size .L.str, 13
.section .note.GNU-stack,"",@progbits
增加 -fplugin-arg-dragonegg-emit-ir或 -flto 的LLVM IR 输出 output (需要assembler output汇编输出, -S, 不是目标代码输出, -c, 否则,gcc 传递pass the LLVM IR 到 the system assembler, 肯定汇编失败):
$ gcc hello.c -S -O1 -o - -fplugin=./dragonegg.so -fplugin-arg-dragonegg-emit-ir
; ModuleID = 'hello.c'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128"
target triple = "x86_64-unknown-linux-gnu"
module asm "\09.ident\09\22GCC: (GNU) 4.5.0 20090928 (experimental) LLVM: 82450:82981\22"
@.str = private constant [13 x i8] c"Hello world!\00", align 1 ; <[13 x i8]*> [#uses=1]
define i32 @main() nounwind {
entry:
%0 = tail call i32 @puts(i8* getelementptr inbounds ([13 x i8]* @.str, i64 0, i64 0)) nounwind ; <i32> [#uses=0]
ret i32 0
}
参考链接: