美文网首页APP & program
Xcode的编译过程

Xcode的编译过程

作者: Captian_Jack | 来源:发表于2022-05-10 23:58 被阅读0次

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

image

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

相关文章

  • Xcode编译原理

    Xcode编译原理 1.xcode 编译的过程 源文件.c --> 编译 --> 目标文件.o --> 链接 --...

  • Xcode编译过程

    Xcode编译过程 打包过程 源码编译 静态库链接 资源编译、优化、导入 配置文件生成 签名打包 Xcode 遇到...

  • Xcode 编译过程

    编译过程分为四个步骤 1.预处理(Pre-process) 把宏替换删除注释展开头文件产生 .i 文件 2.编译 ...

  • xcode编译过程

    在 iOS 开发的过程中,Xcode 为我们提供了非常完善的编译能力,正常情况下,我们只需要 Command + ...

  • Xcode的编译过程

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

  • 浅析XCode编译过程

    作为iOS开发,XCode一天编译几十上百遍是常事。cmd+B,两指一按,哗啦啦的就开始build了。但是,我们是...

  • iOS编译速度提升

    iOS编译速度提升 Xcode编译过程 预编译pch文件(如果有) 编译各种资源文件 copy静态资源 compi...

  • 2021-10-04 iOS 编译

    应用编译的过程大致为:预处理、编译、汇编、链接等过程。Xcode将这些过程包装为一个Commond+B,其中Xco...

  • iOS面试题整理自用(二)

    聊聊Xcode 中的编译过程 基本的编译过程分为四个步骤:预处理(Pre-process):把宏替换,删除注释,展...

  • iOS app的编译过程

    iOS app的编译过程 在 iOS 开发的过程中,Xcode 为我们提供了非常完善的编译能力,正常情况下,我们只...

网友评论

    本文标题:Xcode的编译过程

    本文链接:https://www.haomeiwen.com/subject/dtgrurtx.html