1、编译器
- 作用是 将源代码编译成可执行程序
1.1、编译器构成
- 编译器由
前端编译器、优化器和后端代码生成器3部分组成- 前端编译器
- 前端编译器的任务是解析源代码,会进行
词法分析、语法分析、语义分析、检查源代码是否存在错误,然后 构建抽象语法树(AST) - LLVM的前端还会生成中间代码(IR)
- 前端编译器的任务是解析源代码,会进行
- 优化器
- 优化器负责对 中间代码IR 进行各种优化,改善代码的运行时间,如消除冗余计算等
- 后端代码生成器
- 将 中间代码IR 映射到目标指令集,生成指定架构的二进制文件,并进行机器相关的代码优化
- 前端编译器
1.2、传统编译器
- 传统编译器将前端编译器、优化器、后端代码生成器统一在一起,输入源代码后生成对应的机器码
- 但这种模式缺乏灵活性,编译器只能针对特定源代码类型
1.3、LLVM编译器
- LLVM编译器将前端编译器、优化器、后端代码生成器进行了分离,在编译器中使用
通用代码IR表示,因此LLVM可以为任何语言独立编写前端,也可以为任意硬件架构独立编写后端 - 不同的编程语言经过LLVM 对应的前端编译器生成IR,经过 优化器调优,优化后的IR 根据要执行的硬件不同,找到对应的 后端代码生成器匹配硬件架构指令集来生成机器语言
- 我们常用的 Clang编译器 就是LLVM编译器中专门负责C、C++、Object-C语言的前端编译器;swiftc编译器 是LLVM编译器中专门负责Swift语言的前端编译器
clang编译
- 不同于python这种 解释型语言 读到哪行执行哪行,编译型语言需要先编译成机器语言才能执行
- 经过 Clang 编译后生成
.out文件 - Clang 存放位置可以在终端调用
open /usr/bin
- 经过 Clang 编译后生成
2、编译流程
2.1、打印源码的编译阶段
clang -ccc-print-phases main.m
- 0:输入文件:找到源文件
- 1:预处理阶段:宏的替换、头文件导入
- 2:编译阶段:词法分析、语法分析、语义分析、语法正确检测,生成IR
- 3:后端:生成汇编代码
- 4:通过汇编代码生成目标文件
- 5:链接:链接需要的动态库与静态库,生成 目标文件
- 6:通过不同的架构模式,生成该架构模式下的 可执行文件
2.2、预处理
处理代码里面#开头的代码,替代宏,删除注释,展开头文件
clang -E main.m
2.3、编译阶段
2.3.1、词法分析
预处理阶段完成后,就会进行词法分析,把代码切割成一个一个Token
clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
- 将
[]、;、char、*等完全切成块
2.3.2、语法分析
词法分析 --> 语法分析;作用是检验语法是否正确,将词法分析打散的内容组装成语句,将所有节点 组成抽象语法树AST
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
2.3.3、生成中间代码IR(intermediate representation )
代码生成器 从上到下遍历抽象语法树,翻译成IR,生成.ll文件
clang -S -fobjc-arc -emit-llvm main.m
- Object-C代码在这步会进行 Runtime的桥接、property合成、ARC处理 等
IR语法
| 符号 | 作用 |
|---|---|
| @ | 全局标识 |
| % | 局部标识 |
| alloca | 开辟空间 |
| i32 | 32个bit,4字节 |
| store | 写入内存 |
| load | 读取数据 |
| call | 调用函数 |
| ret | 返回 |
IR的优化
LLVM的优化级别分为 -O0、-O1、-O2、-O3、-Os
clang -Os -S -fobjc-arc -emit-llvm main.m -o main.ll
- 也可以在XCode中的
Optimization Level选项中直接选取
bitCode
开启bitCode苹果会做进一步优化,生成.bc的中间代码,我们通过优化后的IR代码生成.bc代码
clang -emit-llvm -c main.ll -o main.bc
- 也可以在XCode中的
bitCode选项中直接选取 - 但由于很多第三方库没有支持bitCode,所以目前大部分工程的bitCode都是关闭的
2.4、汇编(生成目标文件)
2.4.1、生成汇编代码
我们最终 通过 .ll 或者 .bc 生成汇编代码
clang -S -fobjc-arc main.bc -o main.s
clang -S -fobjc-arc main.ll -o main.s
2.4.2、汇编代码优化
生成的汇编代码也可以优化
clang -Os -S -fobjc-arc main.m -o main.s
2.4.3、生成目标文件
汇编器以汇编代码作为输入,将汇编代码转换为机器代码,最终输出 目标文件
clang -fmodules -c main.s -o main.o
通过nm命令,查看main.o中的符号
xcrun nm -nm main.o
(undefined) external _printf
0000000000000000 (__TEXT,__text) external _test
000000000000000a (__TEXT,__text) external _main
- undefined :当前文件暂时找不到该符号
- external :该符号是外部可以访问的
2.5、链接(生成可执行文件)
链接器把编译产生的 目标文件 和 库文件(.dylib,.a)生成Mach-O文件:
clang main.o -o main