LLVM

LLVM

LLVM是项目模块化,可重用的编译器及工具链技术的集合

传统编译器的架构

  • Front: 前端

    词法分析、语法分析、语义分析、将不同语言生成中间代码

  • Optimizer: 优化器

    中间代码优化

  • Backend: 后端

    针对不同架构生成机器码

LLVM架构

前后端不耦合,不同的前端后端,使用同一的中间代码 LLVM Intermediate Representation (LLVM IR)

  • 如果需要支持一种新的编程语言,那么只需要实现一个新的前端
  • 如果需要支持一种新的硬件设备,那么只需要实现一个新的后端
  • 优化阶段是一个通用的阶段,它针对的是统一的LLVM IR,不论是支持新的编程语言,还是支持新的硬件设备,都不需要对优化阶段做修改
  • 相比之下,GCC的前端后端耦合,所以GCC为了支持一门新的语言或者新的设备,就变得特别困难
  • LLVM现在被作为实现各种静态和运行时编译语言的通用基础结构(GCC家族、JAVA、.NET、Python、Ruby、Scheme、Haskell等)

什么是Clang

  • LLVM项目的一个子项目
  • 基于LLVM架构的C/C++/Objective-C编译器前端

相比于GCC, Clang具有如下优点

  • 编译速度快: 在某些平台上
  • Clang采用基于库的模块化设计,易于IDE集成及其他用途的重用
  • 诊断信息可读性强:在编译过程中,Clang创建并保留了大量详细的元数据,有利于调试和错误分析
  • 设计清晰简单,易于扩展增强

OC源文件的编译过程

命令行查看编译过程

1
2
3
4
5
6
7
8
9
$ clang -ccc-print-phases main.m

0: input, "main.m", objective-c
1: preprocessor, {0}, objective-c-cpp-output # 预处理器 include import 替换宏
2: compiler, {1}, ir # 编译成中间代码
3: backend, {2}, assembler # 汇编
4: assembler, {3}, object
5: linker, {4}, image # 链接
6: bind-arch, "x86_64", {5}, image # 生成针对架构的代码

查看预处理结果

1
$ clang -e main.m

词法分析,生成token

1
2
3
4
5
6
7
8
9
10
11
12
13
$ clang -fmodules -E -Xclang -dump-tokens main.m

annot_module_include '#include <s' Loc=<main.m:1:1>
void 'void' Loc=<main.m:2:1>
identifier 'main' [LeadingSpace] Loc=<main.m:2:6>
l_paren '(' Loc=<main.m:2:10>
r_paren ')' Loc=<main.m:2:11>
l_brace '{' [LeadingSpace] Loc=<main.m:2:13>
return 'return' [StartOfLine] [LeadingSpace] Loc=<main.m:3:3>
numeric_constant '0' [LeadingSpace] Loc=<main.m:3:10>
semi ';' Loc=<main.m:3:11>
r_brace '}' [StartOfLine] Loc=<main.m:4:1>
eof '' Loc=<main.m:4:2>

语法分析,生成语法书(AST, Abstract Syntax Tree)

1
$ clang -fmodules -fsyntax-only -Xclang -ast-dump main.m

生成中间代码

1
2
3
4
5
6
# LLVM IR 有3种格式
# 1.text 便于阅读的格式,类似汇编语言,拓展名.ll
$ clang -S -emit-llvm main.m
# 2.内存格式
# 3.bitcode: 二进制格式,拓展名.bc
$ clang -c -emit-llvm main.m

源码下载

  • 下载LLVM
1
$ git clone https://git.llvm.org/git/llvm.git/
  • 下载clang
1
2
$ cd llvm/tools
$ git clone https://git.llvm.org/git/clang.git/

源码编译

1
2
$ brew install cmake
$ brew install ninja
  • ninja如果安装失败,可以直接从github获取release版放入/usr/local/bin

    https://github.com/ninja-build/ninja/releases

  • 在LLVM源码统计目录下新建一个llvm_build目录,最终在llvm_build目录下生成build.ninja

1
2
3
$ cd llvm_build
$ cmake -G Ninja ../llvm -DCMAKE_INSTALL_PREFIX=LLVM的安装路径 # llvm_release
# 更多cmake相关选项,可以参考 https://llvm.org/docs/CMake.html
  • 依次执行编译、安装指令
1
2
3
4
$ ninja  # 在llvm_build 目录
# 编译完毕后 llvm_build 目录大概21.05G
$ ninja install
# 安装完毕后,安装目录大概11.92G

LLVM 应用

clang插件开发

  • 加载插件

    在Xcode项目中指定加载插件动态库:Build Setting > OTHER_CFLAGS

1
-Xclang -load -Xclang 动态库路径 -Xclang -add - plugin -Xclang 插件名称
  • Hack Xcode