生成SIL文件
生成SIL的文章上次已经讲过,我们这次简单分析下SIL文件。在main.swift中输入和上次一样的代码:
import Foundation
class Teacher {
var age: Int = 18
var name: String = "Tom"
}
var person = Teacher()
person.age = 6
生成main.sil文件后查看
声明Teacher类
打开文件,先映入眼帘的便是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
@_hasStorage表示的是储存属性@_hasInitialValue表示的是具有初始值@sil_global表示的是全局变量
这里声明了Teacher类,并定义了一个全局的person属性,属于Teacher类
main函数
每个程序的开始都是main函数,swift也不例外,但是swift中的main函数被隐藏了,main.swift文件就代表了整个main函数,在文件里写的代码会在main中运行。
下面看下main函数在SIL文件的体现
// main函数,相当于c程序入口函数int main(int argc, char * argv[])
sil @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {
bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>):
//初始化全局变量person
alloc_global @main.person : main.Teacher // id: %2
//创建对之前由alloc_global初始化的全局变量地址的引用
%3 = global_addr @main.person : main.Teacher : $*Teacher // users: %7, %8
//元类型Teacher
%4 = metatype $@thick Teacher.Type // user: %6
// 方法__allocating_init()的引用
%5 = function_ref @main.Teacher.__allocating_init() -> main.Teacher : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %6
//调用函数__allocating_init,并传入参数元类型Teacher
%6 = apply %5(%4) : $@convention(method) (@thick Teacher.Type) -> @owned Teacher // user: %7
//将函数__allocating_init的结果存入person的引用
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 $Teacher // id: %16
//创建字面量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: %19
} // end sil function 'main'
main函数里的内容基本上和我们代码写的一样,通过了alloc_init创建了一个Teacher的实例,然后调用age的setter方法赋值6。但是会有很多细节,比如Xcode自动帮我们添加的retain和release方法显示了出来,一个简单的6也是经过struct初始化的,等等。
这里面有些固定搭配:
- 初始化一个引用
//初始化全局变量person
alloc_global @main.person : main.Teacher
//创建对之前由alloc_global初始化的全局变量地址的引用
%3 = global_addr @main.person : main.Teacher
- 调用一个方法
// 方法__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
- 获得swfit的基本类型
//创建字面量6
%12 = integer_literal $Builtin.Int64, 6
//生成Int值6,swift中Int是结构体
%13 = struct $Int (%12 : $Builtin.Int64)
后面这些不再详细注释
探索alloc,init
再SIL文件中搜索main函数中出现的@main.Teacher.__allocating_init(),就能找到这个方法的具体实现:
// Teacher.__allocating_init()
sil hidden [exact_self_class] @main.Teacher.__allocating_init() -> main.Teacher : $@convention(method) (@thick Teacher.Type) -> @owned Teacher {
// %0 "$metatype"
bb0(%0 : $@thick Teacher.Type):
//分配一个引用类型为Teacher的对象
%1 = alloc_ref $Teacher // user: %3
// function_ref Teacher.init()
//调用init方法
%2 = function_ref @main.Teacher.init() -> main.Teacher : $@convention(method) (@owned Teacher) -> @owned Teacher // user: %3
%3 = apply %2(%1) : $@convention(method) (@owned Teacher) -> @owned Teacher // user: %4
return %3 : $Teacher // id: %4
} // end sil function 'main.Teacher.__allocating_init() -> main.Teacher'
这个方法比较简单,先是给分配空间给引用对象,然后引用对象调用init方法,我们接着看init方法:
// Teacher.init()
sil hidden @main.Teacher.init() -> main.Teacher : $@convention(method) (@owned Teacher) -> @owned Teacher {
// %0 "self" // users: %18, %14, %4, %1
bb0(%0 : $Teacher):
debug_value %0 : $Teacher, let, name "self", argno 1 // id: %1
//初始化age为18
%2 = integer_literal $Builtin.Int64, 18 // user: %3
%3 = struct $Int (%2 : $Builtin.Int64) // user: %6
%4 = ref_element_addr %0 : $Teacher, #Teacher.age // user: %5
%5 = begin_access [modify] [dynamic] %4 : $*Int // users: %6, %7
store %3 to %5 : $*Int // id: %6
end_access %5 : $*Int // id: %7
//初始化name为Tom
%8 = string_literal utf8 "Tom" // user: %13
%9 = integer_literal $Builtin.Word, 3 // user: %13
%10 = integer_literal $Builtin.Int1, -1 // user: %13
%11 = metatype $@thin String.Type // user: %13
// function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:)
%12 = function_ref @Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %13
%13 = apply %12(%8, %9, %10, %11) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %16
%14 = ref_element_addr %0 : $Teacher, #Teacher.name // user: %15
%15 = begin_access [modify] [dynamic] %14 : $*String // users: %16, %17
store %13 to %15 : $*String // id: %16
end_access %15 : $*String // id: %17
return %0 : $Teacher // id: %18
} // end sil function 'main.Teacher.init() -> main.Teacher'
init方法看着比较长,其实就做了两个属性的赋值。值得注意的是赋值直接调用的store,不是调用的setter方法。
汇编验证
我们可以下符号断点Teacher.init,然后运行
我们可以看到上面的堆栈符合分析的预期,我们点到
__allocating_init里看下
我们可以看到分配空间调用的
swift_allocObject,这样我们就可以定位swift源码,查看类的结构了。这个我们放在后面文章分析。