了解 LLVM 编译器

2022-03-02  本文已影响0人  _涼城

LLVM概述

LLVM是架构编译器的框架系统,以C++编写而成,用于优化任意程序语言编写的程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time)。

LLVM的设计

LLVM的一大特色就是,有着独立的、完善的、严格约束的中间代码表示。这种中间代码,就是LLVM的字节码,是LLVM抽象的精髓,前端生成这种中间代码,后端自动进行各类优化分析,让用LLVM开发的编译器,都能用上最先见的后端优化技术。


传统编译器设计

LLVM将中间代码优化这个流程做到了极致,LLVM工具链,不但可以生成所支持的各个后端平台的代码,更可以方便的将各语言的前端编译后的模块链接到一起,你可以方便的在你的语言中调用C函数。

LLVM的设计

iOS的编译器架构

OCCC++ 使用的编译器前端是 Clang ,后端都是LLVM,如下图所示

iOS的编译器

Clang概述

Clang 是一个 C++ 编写、基于 LLVM、发布于 LLVM BSD 许可证下的 C/C++/Objective C/Objective C++ 编译器

Clang 编译流程

通过命令可以打印源码的编译阶段
clang -ccc-print-phases 源文件路径

得到结果如下:

  1. 输入文件:找到源文件
    input, "main.m", objective-c

  2. 预处理阶段:这个过程处理包括宏的替换,头文件的导入
    preprocessor, {0}, objective-c-cpp-output

  3. 编译阶段:进行词法分析、语法分析、检测语法是否正确,最终生成IR
    compiler, {1}, ir

  4. 后端:这里LLVM会通过一个一个的pass去优化,每个pass做一些事情,最终生成汇编代码
    backend, {2}, assembler

  5. 汇编代码生成目标文件
    assembler, {3}, object

  6. 链接:链接需要的动态库和静态库,生成可执行文件
    linker, {4}, image(镜像文件)

  7. 绑定:通过不同的架构,生成对应的可执行文件
    bind-arch, "x86_64", {5}, image

预处理阶段

    这个阶段主要是处理包括宏的替换,头文件的导入,可以执行命令 clang -E 源文件路径,执行完毕可以看到头文件的导入和宏的替换。define则在预处理阶段会被替换,所以经常被是用来进行代码混淆,目的是为了 app 安全,实现逻辑是:将 app 中核心类、核心方法等用系统相似的名称进行取别名,然后在预处理阶段就被替换,来达到代码混淆的目的。

clang -E main.m

编译阶段

词法分析

预处理完成后就会进行词法分析,这里会把代码切成一个个 token,比如大小括号、等于号、还有字符串等,

clang -fmodules -fsyntax-only -Xclang -dump-tokens 源文件路径
语法分析

语法分析,它的任务是验证语法是否正确,在词法分析的基础上将单词序列组合成各类此法短语,如程序、语句、表达式 等等,然后将所有节点组成抽象语法树(Abstract Syntax Tree AST),语法分析程序判断程序在结构上是否正确。

clang -fmodules -fsyntax-only -Xclang -ast-dump 源文件路径

生成中间代码IR

完成以上步骤后,就开始生成中间代码 IR 了,代码生成器(Code Generation)会将语法树自顶向下遍历逐步翻译成 LLVM IR,可以通过下面命令可以生成 .ll 的文本文件,查看 IR 代码。

clang -S -fobjc-arc -emit-llvm 源文件路径 -o 输出文件路径

Objective-C 代码在这一步会进行 runtime 桥接,property 合成、ARC 处理等。

IR 基本语法
Optimization Level

当然,IR 文件在 Objective-C 中是可以进行优化的,一般设置是在 target - Build Setting - Optimization Level(优化器等级)中设置。

Optimization Level 优化级别
LLVM 的优化级别分别是 -O0-O1 -O2 -O3 -Os,下面是带优化的生成中间代码 IR 的命令
clang -Os -S -fobjc-arc -emit-llvm 源文件路径 -o 输出文件路径
BitCode

xcode7 以后开启 bitcode ,苹果会做进一步优化,生成 .bc 的中间代码,我们通过优化后的 IR 代码生成 .bc代码

clang -emit-llvm -c .ll源文件 -o .bc文件

后端

LLVM 在后端主要是会通过一个个的 Pass 去优化,每个 Pass 做一些事情,最终生成汇编代码。我们通过最终的 .bc 或者 .ll 代码生成汇编代码:

 clang -S -fobjc-arc .bc 文件 -o .s 文件
 clang -S -fobjc-arc .ll 文件 -o .s 文件

生成汇编代码也可以进行优化

clang -Os -S -fobjc-arc .m 文件 -o .s 文件

生成目标文件

目标文件的生成,是汇编器以汇编代码作为插入,将汇编代码转换为机器代码,最后输出目标文件(object file)

clang -fmodules -c .s 文件 -o .o 文件

可以通过 nm 命令,查看下 main.o 中的符号

xcrun nm -nm .o 文件

链接

链接主要是链接需要的动态库和静态库,生成可执行文件,其中静态库会和可执行文件合并,动态库是独立的。连接器把编译生成的 .o 文件和 .dyld.a 文件链接,生成一个 mach-o 文件

clang .o 文件 -o 输出文件

LLVM 工程编译

下载源码

git clone https://github.com/llvm/llvm-project.git

安装 cmake

brew install cmake

通过 xcode 编译 LLVM

通过 ninja 编译 LLVM

上一篇下一篇

猜你喜欢

热点阅读