iOS编译简析

前言

一般的编译器都是由三部分构成,从源码到机器码基本上都要经过这三部分。

  • 编译器前端(FrontEnd): 词法分析,语法分析,语义分析,将源代码抽象为语法树 AST,继而生成中间代码 IR;
  • 优化器(Optimizer): 对得到的中间代码 IR 进行优化;
  • 编译器后端(BackEnd): 将得到的中间代码转化为各平台的机器码,如 x86,ARM 等。
编译器架构.png

从 GCC 到 LLVM 以及大部分编译器都是这种结构。

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 后端。

LLVM架构.png

如果所示,不同的前端后端使用统一的中间代码 LLVM Intermediate Representation (LLVM IR),如果需要支持一种新的编程语言,那么只需要实现一个新的前端,如果需要支持一种新的硬件设备,那么只需要实现一个新的后端,优化阶段是一个通用的阶段,它针对的是统一的 LLVM IR,不论是支持新的编程语言,还是支持新的硬件设备,都不需要对优化阶段做修改。

主要子项目:

  • LLVM 核心库
  • 编译器前端 Clang
  • LLDB
  • libc ++和 libc++
  • lld

Clang

Clang 是 LLVM 项目的一个子项目,是 C 系列(C、C++、OC)的编译器前端。相对于 GCC,Clang 具有以下优点

  • 编译速度快:在某些平台上,Clang 的编译速度显著的快过 GCC(Debug 模式下编译 OC 速度比 GGC 快 3 倍)
  • 占用内存小:Clang 生成的 AST 所占用的内存是 GCC 的五分之一左右
  • 模块化设计:Clang 采用基于库的模块化设计,易于 IDE 集成及其他用途的重用
  • 诊断信息可读性强:在编译过程中,Clang 创建并保留了大量详细的元数据 (metadata),有利于调试和错误报告
  • 设计清晰简单,容易理解,易于扩展增强

主要流程

Clang.png
  • 预处理(Pre-process):include 扩展、标记化处理、去除注释、条件编译、宏删除、宏替换。 对C输出.i, 对C++输出 .ii, 对 OC 输出 .mi, 对Objective-C++输出 .mii
  • 词法分析 (Lexical Analysis):将代码切成一个个 token,比如大小括号,等于号还有字符串等。是计算机科学中将字符序列转换为标记序列的过程;
  • 语法分析(Semantic Analysis):验证语法是否正确,然后将所有节点组成抽象语法树 AST 。由 Clang 中 Parser 和 Sema 配合完成;
  • 静态分析(Static Analysis):使用它来表示用于分析源代码以便自动发现错误;
  • 中间代码生成(Code Generation):开始 IR 中间代码的生成了,CodeGen 会负责将语法树自顶向下遍历逐步翻译成 LLVM IR。

SwiftC

SwiftC 是 Swift 语言的编译器前端。

主要流程

Swiftc.png
  • Parse: 词法分析组件,生成 AST;
  • Sema(Semantic Analysis):对 AST 进行类型检查,转换为格式正确且类型检查完备的 AST;
  • Clang Importer: 负责导入 Clang 模块,并将导出的 C 或 Objective-C API 映射到相应的 Swift API 中。最终导入的 AST 可以被语义分析引用。
  • SIL Gen:由 AST 生成 Raw SIL(原生 SIL,代码量很大,不会进行类型检查);
  • SIL 保证转换:SIL 保证转换阶段负责执行额外且影响程序正确性的数据流诊断,转换后的最终结果是规范的 SIL;
  • SIL 优化:该阶段负责对程序执行额外的高级且专用于 Swift 的优化,包括(例如)自动引用计数优化、去虚拟化、以及通用的专业化;

Swift 编译过程引入 SIL 有几个优点:

  • 完成的变数程序的语义(Fully represents program semantics );
  • 既能进行代码的生成,又能进行代码分析(Designed for both code generation and analysis );
  • 处在编译管线的主通道(Sits on the hot path of the compiler pipeline );
  • 架起桥梁连接源码与 LLVM,减少源码与 LLVM 之间的抽象鸿沟(Bridges the abstraction gap between source and LLVM)

IR

LLVM IR 有三种表示形式。

  • text:便于阅读的文本格式,类似于汇编语言,拓展名.ll
  • bitcode:二进制格式,拓展名.bc
  • memory:内存格式

LLVM 后端

主要流程

LLVM后端.png
  • 优化(Optimize):LLVM 会去做些优化工作;在 Xcode 的编译设置里也可以设置优化级别-01,-03,-0s;优化级参数位于参数位于Build Settings -> Apple Clang - Code Generation ->Optimization Level。是利用 LLVM 的 Pass 去处理的,我们可以自己去自定义 Pass。
  • 生成目标文件(Assemble):生成 Target 相关 Object(Mach-o);
  • 链接(Link):生成 Executable 可执行文件。

相关命令

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

// 生成优化后的中间体语言(SIL),并将结果导入到LLVMSwift.sil文件中
swiftc LLVMSwift.swift -emit-sil  -o LLVMSwift.sil 

// 生成优化后的中间体语言(SIL),并将sil文件中的乱码字符串进行还原,并将结果导入到LLVMSwift.sil文件中
swiftc LLVMSwift.swift -emit-sil | xcrun swift-demangle > LLVMSwift.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瘦身优化中专门去讲解。


有一个技术的圈子与一群志同道合的朋友非常重要,来我的技术公众号及博客,这里只聊技术干货。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容