01.LLVM
一、 编程语言类型
1.1 解释型语言
python 、 JS 就属于常见的 解释型语言 。他们无需生成 可执行文件 就能够执行。
他们的执行过程如下:

1.2 编译型语言
我们常用的 C\C++ 、 Objective-C 、 Swift 则属于 编译型语言 需要经过下面的过程生成 可执行文件 后才能执行。

二、 LLVM 概述
LLVM 是 构架编译器(compiler) 的 框架系统 ,以 C++ 编写而成,用于优化以任意程序语言编写的程序的 编译时间(compile-time) 、 链接时间(link-time) 、 运行时间(run-time) 以及 空闲时间(idle-time) ,对开发者保持开放,并兼容已有脚本。
LLVM 计划启动于2000年,最初由美国UIUC大学的 ChrisLattner 博士主持开展。2006年 ChrisLattner 加盟 AppleInc 并致力于 LLVM 在 Apple 开发体系中的应用。 Apple 也是 LLVM 计划的主要资助者。
目前 LLVM 已经被 苹果IOS开发工具 、 Xilinx Vivado 、 Facebook 、 Google 等各大公司采用。
ChrisLattner也是Swift之父。给大佬🧎♂️了
2.1 传统编译器设计

编译器前端
编译器前端的任务是 解析源代码 。它会进行: 词法分析 , 语法分析 , 语义分析 ,检查源代码是否存在错误 ,然后构建 抽象语法树(Abstract Syntax Tree, AST) 。 LLVM 的前端还会生成 中间代码(intermediate representation,IR) 。
优化器
优化器负责进行 各种优化 , 改善代码的运行时间 ,例如消除冗余计算等。
后端 / 代码生成器
将代码映射到 目标指令集 , 生成机器语言 , 并且 进行机器相关的代码优化 。
2.2 iOS的编译器架构
ObjectiveC / C / C++ 使用的编译器前端是 Clang , Swift 是 Swift , 后端 都是 LLVM 。

2.3 LLVM的设计
当编译器决定支持多种源语言或多种硬件架构时, LLVM 最重要的地方就来了。
其他的编译器如 GCC 是非常成功的一款编译器,但由于它是作为整体应用程序设计的,因此它的用途受到了很大的限制。
LLVM 设计的最重要方面是,使用 通用的代码表示形式(IR) ,它是用来在编译器中表示代码的形式。所以 LLVM 可以为任何编程语言 独立编写前端 ,并且可以为任意硬件架构 独立编写后端 。
三、 Clang
对于我们的开发人员来说,接触最多的就是我们的 Clang 。
Clang 是 LLVM 项目中的一个子项目。它是基于 LLVM架构 的 轻量级编译器 ,诞生之初是为了替代 GCC ,提供更快的编译速度。它是负责编译 C、C++、Objecte-C 的编译器,它属于整个 LLVM 架构中的,编译器前端。对于开发者来说,研究 Clang 可以给我们带来很多好处。
四、 Objective-C 的编译流程探索
我们以下面的代码为例进行探索
4.1 编译阶段概览
通过下面的命令我们可以列出完整的编译阶段:

0:输入文件
找到源文件
1:预处理
这个过程处理包括宏的替换,头文件的导入
2:编译
进行词法分析、语法分析、检测语法是否正确,最终生成IR
3:后端
这里LLVM会通过一个一个的Pass(可以理解为一个节点)去优化,每个Pass做一些事情,最终生成汇编代码
4:汇编
生成目标文件
5:链接
链接需要的动态库和静态库,生成相应的镜像可执行文件
6:绑定结构
根据不同的系统架构,生成对应的可执行文件
4.2 预处理

通过对比,很明显的发现这里的宏定义被替换掉了。
4.3 编译阶段
词法分析
这里会把代码切成一个个 Token ,比如 大小括号 , 等于号 还有 字符串 等。

语法分析
语法分析的任务是 验证语法是否正确 。在词法分析的基础上将单词序列组合成各类语法短语,如 “程序” , “语句” , “表达式” 等,然后将所有节点组成 抽象语法树(AbstractSyntaxTree,AST) 。语法分析其目的就是 对源程序进行分析判断,在结构上是否正确 。

生产中间代码IR
这里会生成 .ll 文件,抽取一段 addResult 相关代码简单进行一下分析:
main 函数:
编译优化
这里 debug 模式下默认没有进行优化。

我们传入对应的优化等级,再生成一次 .ll 看看(这里用了和 release 一样的 Os ):
addResult
main
这里发现优化后的中间代码简介了不少,并且代码中冗余的计算逻辑直接生成了结果

bitCode
这是 xcode7 以后开启 bitcode 苹果会做进一步的优化,生成 bc 的中间代码。我们可以试试用优化后的 IR 代码生成 bc 代码。
这里的
bc文件无法使用文本方式进行查看
生成汇编代码
可以通过的 .bc 或者 .ll 代码生成汇编代码
对比一下优化前后的汇编代码,发现优化后的指令少了很多:

生成目标文件
目标文件的生成,是 汇编器 以汇编代码作为 输入 ,将 汇编代码 转换为 机器代码 ,最后输出 目标文件(object-file) ,这个阶段就是属于 编译器后端 的工作了。
查看符号
输出:
_NSLog是一个是undefined external的undefined表示在当前文件暂时找不到符号
_NSLog
external表示这个符号是外部可以访问的
有兴趣可以移步 通过符号表找到符号 有介绍查找
_NSLog的过程
生成可执行文件
连接器 把编译产生的 .o文件 和 (dylib .a)文件 ,生成一个 mach-o文件(可执行文件) 。
报错:

因为这里用到了 Foundation 框架内的符号 NSLog 不能直接生成,我们加上参数
生成成功:

查看符号
输出:
这里的符号就已经和对应的框架对应起来了,如: _NSLog (from Foundation) 。而且 mach-o 文件中的偏移地址也有了。
五、 Swift 编译流程
分析输出AST
swiftc main.swift -dump-parse
分析并且检查类型输出AST
swiftc main.swift -dump-ast
生成中间体语言(SIL),未优化
swiftc main.swift -emit-silgen
生成中间体语言(SIL),优化后的
swiftc main.swift -emit-sil
生成LLVM中间体语言 (.ll文件)
swiftc main.swift -emit-ir
生成LLVM中间体语言 (.bc文件)
swiftc main.swift -emit-bc
生成汇编
swiftc main.swift -emit-assembly
编译生成可执行.out文件
swiftc -o main.o main.swift
参考
Clang 1 Which OSX library to link against (command line) to use NSLog?
Last updated
Was this helpful?