Xcode的编译过程

今天我们来对iOS开发的常用工具Xcode的编译流程进行一个简单的了解和分析

OC:source code > Clang -> LLVM -> Backend -> Executable
Clang做的事情是词法分析-> token流->语法分析-> AST -> LLVM IR

AST(Abstract Syntax Tree 抽象语法树)
IR(intermediate representation 中间代码)


swift:source code > swiftc -> LLVM -> Backend -> Executable
swiftc:swift AST -> Raw Swift IL -> Canonical Swift IL -> LLVM IR

Raw Swift IL : Swift特有的中间代码
Canonical Swift IL:将Raw Swift IL进行降级简化成更加简洁的中间代码版本

swift的编译器前端是swiftc,于Clang相比,LLVM的前端编译过程中,AST和IR之间,多了一层SIL(Swift Intermediate Language),这么做的目的是希望弥补clang编译器的一些缺陷,比如无法执行一些高级分析,可靠的诊断和优化,而 AST 和LLVM IR 都不是合适的选择。因此,SIL应运而生,用来解决现有的缺陷


AST(Abstract Syntax Tree 抽象语法树)

生成AST的过程

词法分析(lexical analysis
也叫扫描器,让源代码的字符流根据构词规范生成token流

tokenize:tokenize就是按照一定的规则,例如token令牌(通常代表关键字,变量名,语法符号等),将代码分割为一个个的“串”,也就是语法单元)。涉及到词法解析的时候,常会用到tokennize。

语法分析(parse analysis)是编译过程的一个逻辑阶段。语法分析的任务是在词法分析的基础上将单词序列组合成语法树,如“程序”,“语句”,“表达式”等等.语法分析程序判断源程序在结构上是否正确。源程序的结构由上下文无关文法描述。

image

Clang:

AST的转换例子

@property (nonatomic, strong) NSString *haoyuString;

PS:属性(Property)是Objective-C语言的其中一个特性,它把类对象中的实例变量及其读写方法统一的封装

生成的AST如下:

 ObjCPropertyDecl 0x7f96f693a400 <line:13:1, col:41> col:41 haoyuString 'NSString *' readwrite nonatomic strong
 | |-ObjCMethodDecl 0x7f96f693a478 <col:41> col:41 implicit - haoyuString 'NSString *'
 | |-ObjCMethodDecl 0x7f96f693a4f8 <col:41> col:41 implicit - setHaoyuString: 'void'
 | | `-ParmVarDecl 0x7f96f693a578 <col:41> col:41 haoyuString 'NSString *'

常见的数据类型声明在AST中表示

 NSString *str = @"hahahah"; //oc中赋值代码

AST中的表示为:

 VarDecl 0x7fd06cc91ae8 <line:24:5, col:22> col:15 str 'NSString *__strong' cinit //声明局部变量
 | | | `-ObjCStringLiteral 0x7fd06cc91ba8 <col:21, col:22> 'NSString *' //声明变量类型
 | | |  `-StringLiteral 0x7fd06cc91b88 'char [8]' lvalue "hahahah" //右边的字符串有char类型表示

String:StringLiteral
NSInteger ,Int: IntegerLiteral
Float: FloatingLiteral
Array: ObjCArrayLiteral
Dictionary : ObjCDictionaryLiteral

将源代码生成语法树 AST:

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

Swiftc:

生成AST的方式和Clang类似,这里着重介绍下swiftc编译器的SIL(Swift Intermediate Language )

1.生成的main.swift文件中编写如下代码

import Foundation
class Teacher {
    var age: Int = 18
    var name: String = "Tom"
}

var person = Teacher()
person.age = 6

通过终端进入main.swift所在的文件夹,输入如下指令:

swiftc -emit-sil main.swift  //生成了main.sil文件

// 打开`main.sil` 文件,首先看到了Teacher的声明
class Teacher {
  @_hasStorage @_hasInitialValue var age: Int { get set }
  @_hasStorage @_hasInitialValue var name: String { get set }
  @objc deinit
  init()
}

@_hasStorage @_hasInitialValue var person: Teacher { get set }

// person
sil_global hidden @main.person : main.Teacher : $Teacher
  1. @_hasStorage表示的是储存属性
  2. @_hasInitialValue表示的是具有初始值
  3. @sil_global表示的是全局变量
  4. 一个析构方法deinit
  5. 一个初始化函数init()

这里声明了Teacher类,并定义了一个全局的person属性,属于Teacher类

2.看下main.sil文件中的main 函数

每个程序的开始都是main函数,swift语言也不例外,但是swift中的main函数被隐藏了,main.swift文件就代表了整个main函数,在文件里写的代码会在main中运行

// main函数,相当于c程序入口函数int main(int argc, char * argv[])
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
//%0,%1……在SIL也叫寄存器,这里我们可以理解为我们日常开发的常量,
//一旦赋值之后就不可以在修改,如果SIL中还要继续使用,那么就不断的累加数字
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
//初始化全局变量person  alloc_global是创建一个全局变量
  alloc_global @main.person : main.Teacher       // id: %2
//创建对之前由alloc_global初始化的全局变量地址的引用
//global_addr是拿到全局变量的地址,赋值给%3
  %3 = global_addr @main.person : main.Teacher : $*Teacher // users: %7, %8
//元类型Teacher
//metatype是拿到Teacher的Metaldata赋值给%4
  %4 = metatype $@thick Teacher.Type              // user: %6
// 方法__allocating_init()的引用
//接下来就是将__allocating_init()的函数地址赋值给%5
  %5 = function_ref @main.Teacher.__allocating_init() -> main.Teacher : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %6
//调用函数__allocating_init,并传入参数元类型Teacher
//apply是调用函数,这里是调用%5也就是__allocating_init(),%4是参数,并将返回值给%6
  %6 = apply %5(%4) : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %7
//将函数__allocating_init的结果存入person的引用
//然后将%6的值存储到%3,也就是我们刚刚创建的全局变量的地址
  store %6 to %3 : $*Teacher                      // id: %7
//开始访问全局变量地址的引用
  %8 = begin_access [read] [dynamic] %3 : $*Teacher // users: %9, %11
//将内容载入%9
  %9 = load %8 : $*Teacher                        // users: %16, %14, %15, %10
//引用计数加一
  strong_retain %9 : $Teacher                     // id: %10
//结束访问
  end_access %8 : $*Teacher                       // id: %11
//创建字面量6
  %12 = integer_literal $Builtin.Int64, 6         // user: %13
//生成Int值6,swift中Int是结构体
  %13 = struct $Int (%12 : $Builtin.Int64)        // user: %15
//Teacher.age的setter方法
  %14 = class_method %9 : $Teacher, #Teacher.age!setter : (Teacher) -> (Int) -> (), $@convention(method) (Int, @guaranteed Teacher) -> () // user: %15
//调用setter方法,传入Int值6,和类实例本身
  %15 = apply %14(%13, %9) : $@convention(method) (Int, @guaranteed Teacher) -> ()
//引用计数减一
  strong_release %9 :
//创建字面量0
  %17 = integer_literal $Builtin.Int32, 0         // user: %18
//生成Int值0,swift中Int是结构体
  %18 = struct $Int32 (%17 : $Builtin.Int32)      // user: %19
//最后将0从main函数中返回出去
  return %18 : $Int32                         // id: %16
} // end sil function 'main'
  1. @main 这里是标示我们当前main.swift的入口函数,SIL中的标示符以 @作为前缀
  2. %0,%1……在SIL也叫寄存器,这里我们可以理解为我们日常开发的常量,一旦赋值之后就不可以在修改,如果SIL中还要继续使用,那么就不断的累加数字
  3. alloc_global是创建一个全局变量
  4. global_addr是拿到全局变量的地址,赋值给%3
  5. metatype是拿到Teacher的Metaldata赋值给%4
  6. 接下来就是将__allocating_init()的函数地址赋值给%5
  7. apply是调用函数,这里是调用%5也就是__allocating_init(),%4是参数,并将返回值给%6
  8. 然后将%6的值存储到%3,也就是我们刚刚创建的全局变量的地址
  9. 然后是构建Int并return

3. 这里面有些固定搭配

1.初始化一个引用

//初始化全局变量person
  alloc_global @main.person : main.Teacher
//创建对之前由alloc_global初始化的全局变量地址的引用
  %3 = global_addr @main.person : main.Teacher 

2.调用一个方法

// 方法__allocating_init()的引用
  %5 = function_ref @main.Teacher.__allocating_init() -> main.Teacher : $@convention(method) (@thick Teacher.Type) -> @owned Teacher
//调用函数__allocating_init,并传入参数元类型Teacher
  %6 = apply %5(%4) : $@convention(method) (@thick Teacher.Type) -> @owned Teacher

3.获得swfit的基本类型

//创建字面量6
  %12 = integer_literal $Builtin.Int64, 6
//生成Int值6,swift中Int是结构体
  %13 = struct $Int (%12 : $Builtin.Int64)

swiftc命令:

生成可执行文件:swiftc -o main.out main.swift

生成抽象语法树的命令(AST):swiftc main.swift -dump-ast

生成中间语言(SIL):swiftc main.swift -emit-sil

LLVM中间表示层(LLVM IR):swiftc main.swift -emit -ir

生成汇编语言:swiftc main.swift -emit-assembly



作者:Captian_Jack
链接:https://www.jianshu.com/p/625e4ebbb4c3
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值