LLVM概述
LLVM是以C++编写的架构编译器(compiler)的框架系统- 从以下方面优化任意语言编写的的程序
- 编译时间
compile time - 链接时间
link time - 运行时间
run time - 空闲时间
idle time
- 编译时间
传统编译器设计

编译器前端 Frontend
编译器前端的任务是 解析源代码
- 检查代码正确性
- 词法分析
- 语法分析
- 语义分析
- 构建抽象语法树
LLVM的前端还会生成 中间代码(intermediate representation,IR)
优化器 Optimizer
优化器端的任务是改善代码的运行时间,例如消除冗余运算等。
编译器后端 Backend / 代码生成器CodeGenerator
- 将代码映射到目标指令集
- 生成机器语言
- 机器相关的代码优化
iOS的编译器架构
OC、c、c++ 使用的编译器前端是clang;Swift使用的是Swift;后端都是LLVM。

LLVM的设计
传统的编译器设计把前端、优化器、后端集成在一起,需要支持新语言或新硬件结构时就必须重新开发一套编译器。LLVM的设计模式很好的解决了这种不便。
LLVM的编译器前端会生成通用的中间代码(intermediate respresentationIR)- 优化器对中间代码处理不需要改变
- 需要支持新语言时只需要单独开发前端
- 需要支持新架构时只需要单独编写后端

Clang
LLVM中的一个子项目- 基于
LLVM的轻量级编译器 - 替代
GCC,提供更快的编译速度 - 负责编译
OC、C、C++ LLVM架构的编译器前端
编译流程
打印编译流程
clang -ccc-print-phases main.m

- 0、输入文件:找到源文件
- 1、预处理:包括替换宏和导入头文件
- 2、编译阶段:词法分析、语法分析、语义分析,最终生成IR
- 3、后端:通过一个个Pass去优化,每个Pass完成一部分功能,最终生成汇编代码
- 4、生成目标文件
- 5、链接:链接需要的动态库和静态库,生成目标文件
- 6、通过不同架构,生成对应的可执行文件
预处理
clang -E main.m
执行后可以看到头文件导入以及宏被替换。
词法分析
预处理后会进行词法分析,词法分析会把代码切片成一个个token。
clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
执行后可以看到每个词(token)所在的位置

语法分析
语法分析会验证语法是否正确,在词法分析的基础上把单词组合成各种语法短语,然后把节点组成抽象的语法树(Abstract Syntax Tree,AST)
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
如果找不到头文件,可以手动导入SDK
clang -isysroot SDK路径 -fmodules -fsyntax-only -Xclang -ast-dump main.m

生成中间代码IR(intermediate representation)
处理完上述步骤,代码生成器(Core Generator)会根据语法树自顶向下生成LLVM IR。OC会在这里进行runtime的桥接:property的合成、ARC处理等。
clang -S -fobjc-arc -emit-llvm main.m
LLVM的优化级别有 -O0 -O1 -O2 -O3 -Os
clang -Os -S -fobjc-arc -emit-llvm main.m
执行指令可以得到.ll文件
- IR基本语法
@ 全局标示
% 局部标示
alloca 开辟空间
align 字节对齐
i32 32位,4字节
store 写入内存
load 读取数据
call 调用函数
ret return
查看未优化的main.ll

优化后的main.ll

bitCode
xcode7以后开启了bitCode,苹果对代码做了进一步优化并生成.bc文件。通过.ll文件生成.bc文件。
clang -emit-llvm -c main.ll -o main.bc
生成汇编代码
可以通过.bc或.ll文件生成汇编代码
clang -S -fobjc-arc main.bc -o main.s
clang -S -fobjc-arc main.ll -o main.s
生成汇编代码的时候也可以进行优化
clang -Os -S -fobjc-arc main.bc -o main.s
生成目标文件
输入汇编代码,转化成机器代码,最后输出目标文件。
clang -fmodules -c main.s -o main.o
查看main.o中的符号
xcrun nm -nm main.o
可以看到print函数、test函数、main函数

_printf可以看到undefined extranal标示undefined代表它在当前文件找不到extranal代表它可以外部访问
生成可以执行文件
把编译的.o文件和dylib.a文件连接,生成mach-o文件
clang main.o -o main
执行可执行文件,可以看见输出了正确结果
