iOS底层探索-LLVM

294 阅读4分钟

1、编译器

  • 作用是 将源代码编译成可执行程序

1.1、编译器构成

  • 编译器由前端编译器优化器后端代码生成器3部分组成
    • 前端编译器
      • 前端编译器的任务是解析源代码,会进行 词法分析语法分析语义分析检查源代码是否存在错误,然后 构建抽象语法树(AST)
      • LLVM的前端还会生成中间代码(IR)
    • 优化器
      • 优化器负责对 中间代码IR 进行各种优化,改善代码的运行时间,如消除冗余计算等
    • 后端代码生成器
      • 中间代码IR 映射到目标指令集,生成指定架构的二进制文件,并进行机器相关的代码优化

1.2、传统编译器

  • 传统编译器将前端编译器、优化器、后端代码生成器统一在一起,输入源代码后生成对应的机器码 image.png
  • 但这种模式缺乏灵活性,编译器只能针对特定源代码类型

1.3、LLVM编译器

  • LLVM编译器将前端编译器、优化器、后端代码生成器进行了分离,在编译器中使用通用代码IR表示,因此LLVM可以为任何语言独立编写前端,也可以为任意硬件架构独立编写后端 image.png
  • 不同的编程语言经过LLVM 对应的前端编译器生成IR,经过 优化器调优优化后的IR 根据要执行的硬件不同,找到对应的 后端代码生成器匹配硬件架构指令集来生成机器语言
  • 我们常用的 Clang编译器 就是LLVM编译器中专门负责C、C++、Object-C语言的前端编译器;swiftc编译器 是LLVM编译器中专门负责Swift语言的前端编译器

clang编译

  • 不同于python这种 解释型语言 读到哪行执行哪行,编译型语言需要先编译成机器语言才能执行 image.png
    • 经过 Clang 编译后生成.out文件 image.png
    • Clang 存放位置可以在终端调用

      open /usr/bin

2、编译流程

2.1、打印源码的编译阶段

clang -ccc-print-phases main.m image.png

  • 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 image.png

  • [];char*等完全切成块
2.3.2、语法分析

词法分析 --> 语法分析;作用是检验语法是否正确,将词法分析打散的内容组装成语句,将所有节点 组成抽象语法树AST

clang -fmodules -fsyntax-only -Xclang -ast-dump main.m image.png

2.3.3、生成中间代码IR(intermediate representation )

代码生成器 从上到下遍历抽象语法树,翻译成IR,生成.ll文件

clang -S -fobjc-arc -emit-llvm main.m

  • Object-C代码在这步会进行 Runtime的桥接property合成ARC处理image.png
IR语法
符号作用
@全局标识
%局部标识
alloca开辟空间
i3232个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选项中直接选取 image.png
bitCode

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

clang -emit-llvm -c main.ll -o main.bc

  • 也可以在XCode中的bitCode选项中直接选取 image.png
  • 但由于很多第三方库没有支持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