一般的编译器都是由三部分构成.从源码到机器码基本上都要经过这三部分.
从 GCC 到 LLVM 以及大部分编译器都是这种结构。
早期 iOS 选用的是当时一家独大的 GCC 编译器作为 OC 语言的前端,但是随着时间的推移,Apple 为 OC 增加了很多特性,想要 GCC 给与实现,但是 GCC 却并没有支持,并且 GCC 本身代码耦合度较高,模块独立性比较差,并且《GCC运行环境豁免条款》限制了LLVM-GCC。这种背景下,Apple 就想找到一个高效、模块化的且开源的替换品,LLVM 进入了苹果的视线。
LLVM 最早来源于伊利诺伊大学厄巴纳-香槟分校维克拉姆·艾夫(Vikram Adve)与克里斯·拉特纳(Chris Lattner)的研究,本来目的是写一个底层的虚拟机,这也是 LLVM 名字的由来(Low Level Virtual Machine)。LLVM 是以 BSD 授权来发展的开源软件。在进入到苹果视线后,苹果公司并邀请 Chris Lattner 及其团队加入苹果,并为 LLVM 提供赞助支持。
Chris Lattner 是一个名副其实的大神,LLVM 之父,Swift 之父,Clang 主要贡献者。2005-2017 年供职苹果,前开发部高级总监,架构师;2017.1-2017.6,担任特斯拉软件副总裁,负责自动驾驶。2017.8-2020.1,加入 Google Brain 团队,加入后编写了 Swift 版的 TensorFlow。目前加入芯片创业公司 SiFive 负责其平台工程。
iOS 在 Xcode 5 版本前使用的是 GCC ,在 Xcode 5 中将 GCC 彻底抛弃,替换为了 LLVM ,这期间也是慢慢过渡过来的,由开始使用 GCC 编译->GCC 与 LLVM 共存->LLVM 编译器。
LLVM 广义上是指整个 LLVM 架构,也就是整个编译器三部分,但是狭义上讲,是指 LLVM 后端。
如果所示,不同的前端后端使用统一的中间代码 LLVM Intermediate Representation (LLVM IR),如果需要支持一种新的编程语言,那么只需要实现一个新的前端,如果需要支持一种新的硬件设备,那么只需要实现一个新的后端,优化阶段是一个通用的阶段,它针对的是统一的 LLVM IR,不论是支持新的编程语言,还是支持新的硬件设备,都不需要对优化阶段做修改。
主要子项目:
Clang 是 LLVM 项目的一个子项目,是 C 系列(C、C++、OC)的编译器前端。相对于 GCC,Clang 具有以下优点
主要流程
C
输出.i
, 对C++
输出 .ii
, 对 OC 输出 .mi
, 对Objective-C++
输出 .mii
;SwiftC 是 Swift 语言的编译器前端。
主要流程
Swift 编译过程引入 SIL 有几个优点:
LLVM IR 有三种表示形式。
.ll
;.bc
主要流程
Build Settings
-> Apple Clang - Code Generation
->Optimization Level
。是利用 LLVM 的 Pass 去处理的,我们可以自己去自定义 Pass。clang
// 假设原始文件为LLVMOC.m
// 预编译命令
clang -E LLVMOC.m -o LLVMOC.mi
// 生成AST语法树
clang -Xclang -ast-dump -fsyntax-only LLVMOC.m
// 生成IR中间代码
clang -S -emit-llvm LLVMOC.m -o LLVMOC.ll
// 生成IR中间代码并优化,
clang -O3 -S -emit-llvm LLVMOC.m -o LLVMOC.ll
// 如果开启bitcode,生成.bc文件,这也是中间码的一种形式
clang -emit-llvm -c LLVMOC.m -o LLVMOC.bc
// 产生汇编命令
clang -S LLVMOC.m -o LLVMOC.s
// 生成目标.O文件
clang -c LLVMOC.m -o LLVMOC.o
swiftc
// 假设原始文件为LLVMSwift.swift
// 分析输出AST
swiftc maLLVMSwiftin.swift -dump-parse
// 分析并且检查类型输出AST
swiftc LLVMSwift.swift -dump-ast
// 生成中间体语言(SIL),未优化
swiftc LLVMSwift.swift -emit-silgen
// 生成中间体语言(SIL),优化后的
swiftc LLVMSwift.swift -emit-sil
// 生成LLVM中间体语言 (.ll文件)
swiftc LLVMSwift.swift -emit-ir
// 生成LLVM中间体语言 (.bc文件)
swiftc LLVMSwift.swift -emit-bc
// 生成汇编
swiftc LLVMSwift.swift -emit-assembly
// 编译生成可执行.out文件
swiftc -o LLVMSwift.o LLVMSwift.swift
既然讲到了 LLVM,那就顺便讲一下 BitCode,上文也讲到了 BitCode 其实就是 IR 代码的一种编码形式。
需要说明的是 BitCode 是以 section 形式保存在可执行文件中。当我们把携带 BitCode 的 App 提交到 AppStore 后,苹果会提取出可执行文件中的 BitCode 段,然后针对不同的 CPU 架构编译和链接成不同的可执行文件变体(Variant),不同 CPU 架构的设备会自动选择合适的架构的变体进行下载。而在 BitCode 之前,我们都是把所有需要的 CPU 架构集合打包成一个 Fat Binary,结果就是用户最终下载的安装包之中有很多冗余的 CPU 架构支持代码。开启BitCode之后,编译器后端(Backend)的工作都由 Apple 接管。
BitCode的一些具体说明及注意事项后面会在iOS瘦身优化中专门去讲解。