iOS 编译详解 LLVM Clang

栏目: IOS · 发布时间: 5年前

内容简介:我们有很多维度可以将计算机语言进行分类,其中以编译/执行方式为维度,可以将计算机语言分为:编译型语言和直译式语言的编译过程如下iOS 在 Xcode 5 版本前使用的是 GCC 编译器,在 Xcode 5 中将 GCC 彻底抛弃,替换为了 LLVM 。LLVM 包含了编译器前端、优化器和编译器后端三大模块。其中 Swift 除了在编译器前端和 Objective-C 稍有不同,其他模块都差不多。

我们有很多维度可以将计算机语言进行分类,其中以编译/执行方式为维度,可以将计算机语言分为:

  • 编译型语言

    • C++ Objective C Swift Kotlin
    • 先通过编译器生成机器码,机器码可以直接在 CPU 上执行
    • :+1: 执行效率较高
    • :-1: 调试周期长
  • 直译式语言(脚本语言)

    • JavaScript Python
    • 不需要经过编译,在执行时通过一个中间的解释器将代码解释为 CPU 可以执行的代码
    • :+1: 编写调试方便
    • :-1: 执行效率低

编译型语言和直译式语言的编译过程如下

iOS 编译详解 LLVM Clang

iOS 编译工具

iOS 在 Xcode 5 版本前使用的是 GCC 编译器,在 Xcode 5 中将 GCC 彻底抛弃,替换为了 LLVM 。LLVM 包含了编译器前端、优化器和编译器后端三大模块。其中 Swift 除了在编译器前端和 Objective-C 稍有不同,其他模块都差不多。

Objective-C 采用 Clang 作为编译器前端

iOS 编译详解 LLVM Clang

Swift 采用 Swift 作为编译器前端

iOS 编译详解 LLVM Clang

LLVM

作者 Chris Lattner

iOS 编译详解 LLVM Clang

Chris Lattner 在2000年开发了一个叫作 Low Level Virtual Machine 的编译器开发 工具 套件,后来涉及范围越来越大,可以用于常规编译器,JIT 编译器,汇编器,调试器,静态分析工具等一系列跟编程语言相关的工作,于是就把简称 LLVM 这个简称作为了正式的名字。Chris Lattner 后来又开发了 Clang ,使得 LLVM 直接挑战 GCC 的地位。

2005年加入苹果,将苹果使用的 GCC 全面转为 LLVM。

2010年开始主导开发 Swift 语言。

2017年离开了 Apple 入职特斯拉负责自动驾驶软件的开发,并于同年下半年入职 Google 加入深度学习与人工智能研发团队。

LLVM 简介

LLVM 是一个开源的,模块化和可重用的编译器和工具链技术的集合,或者说是一个编译器套件。

可以使用 LLVM 来编译 Kotlin,Ruby,Python,Haskell,Java,D,PHP,Pure,Lua 和许多其他语言

LLVM 核心库还提供一个优化器,对流行的 CPU 做代码生成支持。

LLVM 同时支持 AOT 预先编译和 JIT 即时编译

2012年,LLVM 获得美国计算机学会 ACM 的软件系统大奖,和 UNIX,WWW,TCP/IP,Tex,JAVA 等齐名。

LLVM IR

LLVM IR 是 LLVM 优化和进行代码生成的关键,因为 IR 提供了独立于任何特定机器架构的源语。

在这一点上 IR 和 JVM 的 Java bytecode 很像,两者都是用于表述计算的模型,但两者所处的抽象层次不同。Java bytecode 更高层(更抽象),包含了大量类 Java 的面向对象语言的操作。LLVM IR 则更底层(更接近机器)。IR 的存在意味着它可以作为多种语言的后端,这样就能够提供语言无关的优化同时还能够方便的针对多种 CPU 的代码生成。

编译器的架构分为前端、优化器和后端。传统编译器(如 CGG )的前端和后端没有完全分离,耦合在了一起,因而如果要支持一门新的语言或硬件平台,需要做大量的工作。而 LLVM 和传统编译器最大的不同点在于,前端输入的任何语言,在经过编译器前端处理后,生成的中间码都是 IR 格式的。

传统的静态编译器

iOS 编译详解 LLVM Clang

LLVM 编译器

iOS 编译详解 LLVM Clang

这样做的优点是如果需要支持一种新的编程语言,那么我们只需要实现一种新的前端。如果我们需要支持一种新的硬件设备,那我们只需要实现一个新的后端。而优化阶段因为是针对了统一的 LLVM IR ,所以它是一个通用的阶段,不论是支持新的编程语言,还是支持新的硬件设备,这里都不需要对优化阶段做修改。所以从这里可以看出 LLVM IR 的作用。

LLVM IR 主要有三种格式:一种是在内存中的编译中间语言;一种是硬盘上存储的二进制中间语言(以 .bc 结尾),最后一种是可读的中间格式(以 .ll 结尾)。这三种中间格式是完全等价的。

Bitcode

Bitcode 是 LLVM 编译器的中间代码的一种编码,本质上就是我们前面提到的 IR 三种格式中的其中一种,是存储在磁盘上的二进制文件。

之所以要把 Bitcode 拿出来单独说,是因为 Apple 单独对 Bitcode 进行了额外的优化。从 Xcode 7 开始,Apple 支持在提交 App 编译产物的同时提交 App 的 Bitcode (非强制),并且之后对提交了 Bitcode 的 App 都单独进行了云端编译打包。也就是说,即便在提交时已经将本地编译好的 ipa 提交到 App Store,Apple 最终还是会使用 Bitcode 在云端再次打包,并且最终用户下载到手机上的版本也是由 Apple 在云端编译出来的版本,而非开发人员在本地编译的版本。

这里有一篇文章 Xcode 7 Bitcode的工作流程及安全性评估 ,揭示了360团队如何通过一个小 trick 来验证 Apple 审核人员安装的 App 是直接由本地编译出来的版本还是云端通过 Bitcode 编译出来的版本,并由此发现了一个可能绕过审核的漏洞。

Apple 之所以这么做,一是因为 Apple 可以在云端编译过程中做一些额外的针对性优化工作,而这些额外的优化是本地打包环境所无法实现的。二是 Apple 可以为安装 App 的目标设备进行二进制优化,减少安装包的下载大小。比如我们在本地编译生成的ipa是同时包含了多个CPU架构的(armv7 ,arm64 ),对于 iPhone X 而言 armv7 的架构文件就是无用文件。而 Apple 可以在云端为不同的设备编译出对应 CPU 架构的 ipa ,这样 iPhone X 下载到的 App 就只包含了所需的 arm64 架构文件。更为黑科技的是,由于 Bitcode 是无关设备架构的,它可以被转化为任何被支持的 CPU 架构,包括现在还没被发明的 CPU 架构。以后如果苹果新出了一款新手机并且 CPU 也是全新设计的,在苹果后台服务器一样可以从这个 App 的 Bitcode 开始编译转化为新 CPU 上的可执行程序,可供新手机用户下载运行这个 App ,而无需开发人员重新在本地编译打包上传。

Clang & Swift

Clang 编译器

Clang 是 LLVM 的子项目,是 C、C++ 和 Objective-C 编译器,目标是替代传统编译器 GCC 。Clang 在整个 Objective-C 编译过程中扮演了编译器前端的角色,同时也参与到了 Swift 编译过程中的 Objective-C API 映射阶段。

Clang 的主要功能是输出代码对应的抽象语法树( AST ),针对用户发生的编译错误准确地给出建议,并将代码编译成 LLVM IR。

Clang 的特点是编译速度快,模块化,代码简单易懂,诊断信息可读性强,占用内存小以及容易扩展和重用等。

我们以 Xcode 为例,Clang 编译 Objective-C 代码的速度是 Xcode 5 版本前使用的 GCC 的3倍,其生成的 AST 所耗用掉的内存仅仅是 GCC 的五分之一左右。

iOS 编译详解 LLVM Clang

Clang 的主要工作:

CodeGen

Swift 编译器

和 Clang 一样,Swift 编译器主要负责对Swift源代进行静态分析和纠错,并转换为 LLVM IR 。他是 Swift 编译的前端模块。不过和 Clang 不同,Swift 编译器会多出 SIL optimizer ,它会先将 Swift 文件转换成中间代码 SIL ,然后再根据 SIL 生成 IR 。是不是觉得很复杂,Swift 编译器会在编译其间生成两种不同的中间代码,这是为什么呢?下面会有详细的解释。

Swift 的主要工作:

  • 解析:解析器负责生成没有任何语义或类型信息的抽象语法树( AST ),并针对输入源的语法问题发出警告或错误
  • 词法分析:获取解析的 AST 并将其转换为格式良好,完全类型检查的AST形式,为源代码中的词法问题发出警告或错误
  • Clang 导入器:导入 Clang 模块并将它们导出的 C 或 Objective-C API 映射到相应的 Swift API
  • SIL 生成:将经过类型检查的 AST 降级为 SIL
  • SIL 规范化:执行额外的数据流诊断(例如使用未初始化的变量)
  • SIL 优化:为程序执行额外的高级 Swift 特定优化,包括自动引用计数优化,虚拟化和通用专业化
  • LLVM IR 生成:将 SIL 降级到 LLVM IR

为什么要增加 SIL 层

Swift 中间语言( SWIFT Integration Layer )是一种高级的,特定于 Swift 的中间语言,适用于进一步分析和优化 Swift 代码。SIL 属于 High-Level IR ,其相对于LLVM IR 的抽象层级更高,而且是特定于 Swift 语言的。

由于源码和 LLVM IR 之间存在着非常大的抽象鸿沟,IR 不适用对源码进行分析和检查。因此 Clang 使用了 Analysis 通过 CFG (控制流图)来对代码进行分析和检查。但是 CFG 本身不够精准,且不在主流程上(会和 IR 生成过程并行执行),因此 CFG 和 IR 生成中会出现部分重复分析,做无用功。

而在 Swift 的编译过程中,SIL 会在生成 LLVM IR 之前做好所有的分析和规范化,并在 IRGen 的帮助下降级到 LLVM IR ,避免了部分重复任务,也使得整个编译流程更加统一。

iOS 编译详解 LLVM Clang
iOS 编译详解 LLVM Clang

而且因为 Swift 在编译时就完成了方法绑定直接通过地址调用属于强类型语言,方法调用不再是像 Objective-C 那样的消息转发,这样编译就可以获得更多的信息用在后面的后端优化上。因此我们可以在 SIL 上对 Swift 做针对性的优化,而这些优化是 LLVM IR 所无法实现的。

这些优化包括:

  • 临界拆分:不支持任意的基础 block 参数通过终端进行临界拆分
  • 泛型优化:分析泛型函数的特定调用,并生成新的特定版本的函数.然后将泛型的特定用法全部重写为对应的特定函数的直接调用
  • witness和虚函数表的去虚拟化优化:通过给定类型去查找关联的类的虚函数表或者类型的 witness 表,并将虚函数调用替换为调用函数映射
  • 内联优化:对于transparent函数进行内联
  • 内存提升:将 alloc_box 结构优化为 alloc_stack
  • 引用计数优化
  • 高级领域特定优化:对基础的 Swift 类型容器(类似 ArrayString )实现了高级优化

通过分析和检查的安全 SIL 会被 IRGen 转换成 LLVM IR,并进一步接受 LLVM 的优化。

##动手实操

  1. 首先,我们写一个简单的程序,只有一个入口函数和简单的逻辑。
#import <Foundation/Foundation.h>
#define DEFINEEight 8

void test(int a, int b) {
    int c = a + b - DEFINEEight;
}
复制代码
  1. 写好代码后,通过以下命令,LLVM 会预处理你的代码,比如把宏嵌入到对应的位置,头文件的导入,去除注释等。
clang -E main.m
复制代码

得到的就是这样的代码

# 1 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/Foundation.framework/Headers/FoundationLegacySwiftCompatibility.h" 1 3
# 185 "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h" 2 3
# 2 "main.m" 2


void test(int a, int b) {
    int c = a + b - 8;
}
复制代码
  1. 预处理完成后就会进行词法分析,这里会把代码切成一个个 Token ,每一个 Token 都代表了一个特征元素。

    Token 的分类

    • 关键字:语法中的关键字, if else while for 等。

    • 标识符:变量名

    • 字面量:值,数字,字符串

    • 特殊符号:加减乘除等符号

    通过以下代码可以得到词法分析后的代码。

    clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m
    复制代码

    得到的词法分析代码

    void 'void'      [StartOfLine]  Loc=<main.m:4:1>
    identifier 'test'        [LeadingSpace] Loc=<main.m:4:6>
    l_paren '('             Loc=<main.m:4:10>
    int 'int'               Loc=<main.m:4:11>
    identifier 'a'   [LeadingSpace] Loc=<main.m:4:15>
    comma ','               Loc=<main.m:4:16>
    int 'int'        [LeadingSpace] Loc=<main.m:4:18>
    identifier 'b'   [LeadingSpace] Loc=<main.m:4:22>
    r_paren ')'             Loc=<main.m:4:23>
    l_brace '{'      [LeadingSpace] Loc=<main.m:4:25>
    int 'int'        [StartOfLine] [LeadingSpace]   Loc=<main.m:5:5>
    identifier 'c'   [LeadingSpace] Loc=<main.m:5:9>
    equal '='        [LeadingSpace] Loc=<main.m:5:11>
    identifier 'a'   [LeadingSpace] Loc=<main.m:5:13>
    plus '+'         [LeadingSpace] Loc=<main.m:5:15>
    identifier 'b'   [LeadingSpace] Loc=<main.m:5:17>
    minus '-'        [LeadingSpace] Loc=<main.m:5:19>
    numeric_constant '8'     [LeadingSpace] Loc=<main.m:5:21 <Spelling=main.m:2:21>>
    semi ';'                Loc=<main.m:5:32>
    r_brace '}'      [StartOfLine]  Loc=<main.m:6:1>
    eof ''          Loc=<main.m:6:2>
    复制代码
  2. 然后是语法分析,验证语法是否正确。确认无误后将所有节点组成抽象语法树 AST 。

  3. clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
    复制代码

    得到的 AST

    |-FunctionDecl 0x7f9a2108a0c0 <line:4:1, line:6:1> line:4:6 test 'void (int, int)'
    | |-ParmVarDecl 0x7f9a21089f48 <col:11, col:15> col:15 used a 'int'
    | |-ParmVarDecl 0x7f9a21089fc0 <col:18, col:22> col:22 used b 'int'
    | `-CompoundStmt 0x7f9a2108a348 <col:25, line:6:1>
    |   `-DeclStmt 0x7f9a2108a330 <line:5:5, col:32>
    |     `-VarDecl 0x7f9a2108a1e0 <col:5, line:2:21> line:5:9 c 'int' cinit
    |       `-BinaryOperator 0x7f9a2108a308 <col:13, line:2:21> 'int' '-'
    |         |-BinaryOperator 0x7f9a2108a2c0 <line:5:13, col:17> 'int' '+'
    |         | |-ImplicitCastExpr 0x7f9a2108a290 <col:13> 'int' <LValueToRValue>
    |         | | `-DeclRefExpr 0x7f9a2108a240 <col:13> 'int' lvalue ParmVar 0x7f9a21089f48 'a' 'int'
    |         | `-ImplicitCastExpr 0x7f9a2108a2a8 <col:17> 'int' <LValueToRValue>
    |         |   `-DeclRefExpr 0x7f9a2108a268 <col:17> 'int' lvalue ParmVar 0x7f9a21089fc0 'b' 'int'
    |         `-IntegerLiteral 0x7f9a2108a2e8 <line:2:21> 'int' 8
    `-<undeserialized declarations>
    复制代码

    为了方便查看,我们将 AST 以树状图的形式表示

    iOS 编译详解 LLVM Clang

    节点的分类:

    TranslationUnitDecl:根节点,表示一个源文件

    Decl:声明

    Expr:表达式

    Literal:字面量,是特殊的 Expr

    Stmt:语句

  4. 拿到 AST 后 Clang 静态分析器( Clang static analyzer )会对代码进行静态分析。

    Clang static analyzer 的架构包含了一个 Analyzer core 核心分析引擎和用于检查具体代码的 checkers ,所有的 checkers 都是基于 analyzer core 提供的基础功能来实现具体的代码检查的。

    AST 生成后 Clang static analyzer 会使用 checkers 对代码进行检查,比如是否使用了未声明的变量等等。你也可以编写新的 checkers 来添加自定义检查。这种方式能够方便用户扩展对代码检查规则或者对 bug 类型进行扩展,但是这种架构也有不足,每执行完一条语句后,分析引擎会遍历所有 checker 中的回调函数,所以 checker 越多,速度越慢。

    Clang 的静态分析器不仅能够将出现问题的代码位置暴露出来,还能够提供多个修复代码的方法。

  5. 完成这些步骤后就可以开始 IR 中间代码的生成了,CodeGen 会负责将 AST 自上向下遍历逐步翻译成 LLVM IR。IR 是编译过程中前端的输出后端的输入。

    clang -S -fobjc-arc -emit-llvm main.m -o main.ll
    复制代码

    生成的代码如下(此处仅截取了 test 方法)

    ; Function Attrs: noinline nounwind optnone ssp uwtable
    define void @test(i32, i32) #0 {
      %3 = alloca i32, align 4
      %4 = alloca i32, align 4
      %5 = alloca i32, align 4
      store i32 %0, i32* %3, align 4
      store i32 %1, i32* %4, align 4
      %6 = load i32, i32* %3, align 4
      %7 = load i32, i32* %4, align 4
      %8 = add nsw i32 %6, %7
      %9 = sub nsw i32 %8, 8
      store i32 %9, i32* %5, align 4
      ret void
    }
    复制代码

    是不是看的头大了。其实 IR 也不是很难,稍微了解一下 IR 的语法就能够读懂其中的逻辑了。

    LLVM IR 语法:

    ; 注释

    @ 全局

    % 局部

    alloca 分配内存空间

    i32 32bit,即4个字节

    align 内存对齐

    Store 写入内存

    load 读取内存

    icmp 两个整数值比较,返回布尔值

    br 选择分支,根据 cond 来转向 label,不根据条件跳转的话类似 goto

    indirectbr 根据条件间接跳转到一个 label,而这个 label 一般是在一个数组里,所以跳转目标是可变的,由运行时决定的

    label 代码标签

    如果有学习过机器码的同学有没有发现,IR代码其实已经很像机器码了。

  6. 这里 LLVM 会去做些优化工作,在 Xcode 的编译设置里也可以设置优化级别 -01 / -03 / -0s ,还可以写些自己的 Pass

    Pass 是 LLVM 优化工作的一个节点,一个节点做些事,一起加起来就构成了 LLVM 完整的优化和转化。

    我们可以通过在上面一段命令中加入 -O3 / -O2 优化参数来控制优化登记。

    clang -O3 -S -fobjc-arc -emit-llvm main.m -o main.ll
    复制代码

    下面通过一个小例子,来展示 LLVM 对 IR 所做的具体优化。我们先写一个方法,包含一个循环语句。

    int main() {
        int i = DEFINEEight;
        while (i < 10) {
            i++;
            printf("%d",i);
        }
        return 0;
    }
    复制代码

    使用 -O3 参数生成 IR

    ; Function Attrs: nounwind ssp uwtable
    define void @demo() local_unnamed_addr #0 {
      %1 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 1), !clang.arc.no_objc_arc_exceptions !9
      %2 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 2), !clang.arc.no_objc_arc_exceptions !9
      %3 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 3), !clang.arc.no_objc_arc_exceptions !9
      %4 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 4), !clang.arc.no_objc_arc_exceptions !9
      %5 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 5), !clang.arc.no_objc_arc_exceptions !9
      %6 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 6), !clang.arc.no_objc_arc_exceptions !9
      %7 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 7), !clang.arc.no_objc_arc_exceptions !9
      %8 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 8), !clang.arc.no_objc_arc_exceptions !9
      %9 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 9), !clang.arc.no_objc_arc_exceptions !9
      %10 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 10), !clang.arc.no_objc_arc_exceptions !9
      ret void
    }
    复制代码

    此时我们将循环条件 (i < 10) 修改为 (i < 100)

    ; Function Attrs: nounwind ssp uwtable
    define void @demo() local_unnamed_addr #0 {
      br label %1
    
    ; <label>:1:                                      ; preds = %1, %0
      %2 = phi i32 [ 0, %0 ], [ %3, %1 ]
      %3 = add nuw nsw i32 %2, 1
      %4 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32 %3), !clang.arc.no_objc_arc_exceptions !9
      %5 = icmp eq i32 %3, 100
      br i1 %5, label %6, label %1
    
    ; <label>:6:                                      ; preds = %1
      ret void
    }
    复制代码

    可以发现当循环次数较低时,生成的 IR 会直接将所有循环的逻辑都写出来,将执行效率最大化。而当循环次数过大时,则会退而使用更为复杂的逻辑去实现。 除了上面的这种简单优化,LLVM 还提供了其他优化 Pass:

    • 各种类,方法,成员变量等的结构体的生成,并将其放到对应的 Mach-O 的 section 中
    • Non-Fragile ABI 合成 OBJC_IVAR_$_ 偏移值常量
    • ObjCMessageExpr 翻译成相应版本的 objc_msgSendsuper 翻译成 objc_msgSendSuper
    • strongweakcopyatomic 合成 @property 自动实现 settergetter
    • @synthesize 的处理
    • 生成 block_layout 数据结构
    • _block__weak
    • _block _invoke
    • ARC 处理,插入 objc_storeStrongobjc_storeWeak 等 ARC 代码
    • ObjCAutoreleasePoolStmt 转 objc_autorealeasePoolPush / Pop,自动添加 [super dealloc] ,给每个 ivar 的类合成 .cxx_destructor 方法自动释放类的成员变量。
  7. 如果开启了 Bitcode , 苹果会做进一步的优化

  8. clang -emit-llvm -c main.m -o main.bc
    复制代码
  9. 生成汇编

    clang -S -fobjc-arc main.m -o main.s
    复制代码
  10. 生成目标文件

    clang -fmodules -c main.m -o main.o
    复制代码
  11. 生成可执行文件

    clang main.o -o main
    复制代码
  12. 执行

    ./main
    复制代码

总结

LLVM 编译过程

  • 预处理
  • 词法分析
  • 语法分析
  • 生成 AST
  • 静态分析
  • 生成 LLVM IR
  • 编译器优化
  • Bitcode (可选)
  • 生成汇编
  • 生成目标文件
  • 生成可执行文件

LLVM 分工

编译器前端的任务是进行:预处理,语法分析,语义分析,生成中间代码( IR )。在这个过程中,会进行类型检查,如果发现错误或者警告会标注出来在哪一行。

编译器后端会进行机器无关的代码优化,生成机器语言,并且进行机器相关的代码优化。iOS 的编译过程,后端的处理如下

LLVM 机器码生成器会针对不同的架构,比如 arm64 等生成不同的机器码。

编译器前端的工作

iOS 编译详解 LLVM Clang

编译器后端

iOS 编译详解 LLVM Clang

编译器后端生成的产物

iOS 编译详解 LLVM Clang

实际应用

深入了解 LLVM 和 Clang ,以及他们提供的开发工具,我们能够实现很多有意思的功能。比如通过 Libclang、libTooling ,我们可以实现语法树分析、语言转化(例如将 Objective-C 转换为 Swift )。我们还可以开发自己的 Clang 插件,对我们的代码做个性化的检查。我们还可以通过写 Pass 实现自定义的代码优化、代码混淆。我们甚至可以开发自己的新语言,你需要做的仅仅是写一个编译器前端,将你的代码转换成 LLVM IR 。

除此之外,了解了编译内部的实现过程和细节,也同样有助于我们在解决问题时找到新的思路。 OCEval 是 iOS 的一个动态执行热修复的第三方开源库,和 js 的 eval 函数类似,这个库可以将字符串形式的 Objective-C 代码转换成实际的运行代码并执行,其中非常重要的一个实现细节就是实现了一套简单的词法分析和语法分析,将字符串转成了 AST 并最终获得执行所需要的方法名、参数等。


以上所述就是小编给大家介绍的《iOS 编译详解 LLVM Clang》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python 3网络爬虫开发实战

Python 3网络爬虫开发实战

崔庆才 / 人民邮电出版社 / 2018-4 / 99

本书介绍了如何利用Python 3开发网络爬虫,书中首先介绍了环境配置和基础知识,然后讨论了urllib、requests、正则表达式、Beautiful Soup、XPath、pyquery、数据存储、Ajax数据爬取等内容,接着通过多个案例介绍了不同场景下如何实现数据爬取,后介绍了pyspider框架、Scrapy框架和分布式爬虫。 本书适合Python程序员阅读。一起来看看 《Python 3网络爬虫开发实战》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换