编译原理

本文参考

前言

一般可以将编程语言分为两种,编译语言直译式语言

像C++,Objective C都是编译语言。编译语言在执行的时候,必须先通过编译器生成机器码,机器码可以直接在CPU上执行,所以执行效率较高。

像JavaScript,Python等都是直译式语言(脚本语言)。直译式语言不需要经过编译的过程,而是在执行的时候通过一个中间的解释器将代码解释为CPU可以执行的代码。所以,较编译语言来说,直译式语言效率低一些,但是编写的更灵活,也就是为啥JS大法好。

iOS开发目前的常用语言是:Objective和Swift。二者都是编译语言,换句话说都是需要编译才能执行的。二者的编译都是依赖于Clang + LLVM. 篇幅限制,本文只关注Objective C,因为原理上大同小异。

可能会有同学想问,我不懂编译的过程,写代码也没问题啊?这点我是不否定的。但是,充分理解了编译的过程,会对你的开发大有帮助。本文的最后,会以以下几个例子,来讲解如何合理利用XCode和编译

  • __attribute__

  • Clang警告处理

  • 预处理

  • 插入编译期脚本

  • 提高项目编译速度

对于不想看我啰里八嗦讲一大堆原理的同学,可以直接跳到本文的最后一个章节。LLVM源码编译

三段式设计

编译主要步骤 1. 源代码(source code) -> 2. 预处理器(preprocessor) -> 3. 编译器(compiler) -> 4. 汇编程序(assembler) -> 5. 目标代码(object code) -> 6. 链接器(Linker) -> 7. 可执行文件(executables)

自己编译LLVM

废话不多说,直接进入正题😍

准备工作

Clang 需要用 CMake 和 Ninja 来编译,可以通过 Homebrew 安装

  • 安装 Homebrew

  • 安装 CMake 3.12.4

  • 安装 Ninja 1.8.2

    源码下载

    下载 LLVM (clang 9)

源码编译

1. 使用 Ninja 编译

2. 使用 Xcode 编译

选择自动创建schemes,选择targetALL_BUILD,然后开始漫长的编译预计1500s

出现以下错误时,可能是权限导致,加上sudo再次尝试编译

编译成功后将权限还原到默认

安装

iOS编译

Objective C采用Clang(swift采用swift)作为编译器前端,LLVM(Low level vritual machine)作为编译器后端。

简单的编译过程如图

编译器前端

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

编译器后端

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

  • LVVM优化器会进行BitCode的生成,链接期优化等等

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

编译过程

  • 编译信息写入辅助文件,创建文件架构 .app 文件

  • 处理文件打包信息

  • 执行 CocoaPod 编译前脚本,checkPods Manifest.lock

  • 编译.m文件,使用 CompileC 和 clang 命令

  • 链接需要的 Framework

  • 编译 xib

  • 拷贝 xib ,资源文件

  • 编译 ImageAssets

  • 处理 info.plist

  • 执行 CocoaPod 脚本

  • 拷贝标准库

  • 创建 .app 文件和签名

预处理&词法分析

  • 替换宏定义

  • 头文件的导入

  • 编译条件

  • 把代码细拆分成最小的Token(包含代码位置),如大小括号,

    等于号还有字符串等

语法分析

  • 验证语法是否正确

  • 将所有节点生成AST

中间代码IR

  • CodeGen 会负责将AST自上而下递归遍历翻译成 LLVM IR

  • IR 是编译过程的前端输出,后端的输入

编译优化

  • LLMV在编译时会做些优化,Xcode编译设置中可设置级别

  • 开启Bitcode会进一步优化

  • Pass 是LLVM优化的一个节点,可以自定义

生成汇编

生成目标

生成可执行文件

执行

Last updated

Was this helpful?