初次探索SIL文件

2,350 阅读3分钟

生成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.agesetter方法
  %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,swiftInt是结构体
  %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,swiftInt是结构体
  %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源码,查看类的结构了。这个我们放在后面文章分析。

swift_allocObject分析Class的组成