Generating SIL files

Since the SIL generation article was covered last time, let’s briefly examine the SIL file. Type the same code as last time in main.swift:

import Foundation

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

var person = Teacher()
person.age = 6
Copy the code

View it after generating the main.sil file

Declare the Teacher class

When you open the file, the first thing you see is the Teacher’s statement:

class Teacher {
  @_hasStorage @_hasInitialValue var age: Int { get set }
  @_hasStorage @_hasInitialValue var name: String { get set }
  @objc deinit
  init(a)
}

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

// person
sil_global hidden @main.person : main.Teacher : $Teacher
Copy the code
  • @_hasStorageRepresents a storage property
  • @_hasInitialValueThat means it has an initial value
  • @sil_globalRepresents a global variable

Here we declare the Teacher class and define a global person attribute that belongs to the Teacher class

The main function

Every program starts with main, swift is no exception, but the main function in Swift is hidden. The main. Swift file represents the entire main function, and the code written in the file is run in main.

Let’s look at the main function in the SIL file

Int main(int argc, char * argv[])
sil @main : $@convention(c) (Int32.UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8- > > > >)Int32 {
bb0(%0 : $Int32.%1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) :// Initialize the global variable person
  alloc_global @main.person : main.Teacher       // id: %2
// Create a reference to the address of the global variable previously initialized by alloc_global
  %3 = global_addr @main.person : main.Teacher : $*Teacher // users: %7, %8
// Teacher
  %4 = metatype $@thick Teacher.Type              // user: %6
// a reference to the __allocating_init() method
  %5 = function_ref @main.Teacher.__allocating_init() -> main.Teacher : $@convention(method) (@thick Teacher.Type) - >@owned Teacher // user: %6
// Call the function __allocating_init and pass in the argument element type Teacher
  %6 = apply %5(%4) : $@convention(method) (@thick Teacher.Type) - >@owned Teacher // user: %7
// Store the result of the __allocating_init function into a reference to person
  store %6 to %3 : $*Teacher                      // id: %7
// Start accessing a reference to the global variable address
  %8 = begin_access [read] [dynamic] %3 : $*Teacher // users: %9, %11
// Load the contents into %9
  %9 = load %8 : $*Teacher                        // users: %16, %14, %15, %10
// Add one to the reference count
  strong_retain %9 : $Teacher                     // id: %10
// End the visit
  end_access %8 : $*Teacher                       // id: %11
// Create the literal 6
  %12 = integer_literal $Builtin.Int64.6         // user: %13
// Generate an Int value of 6. In swift Int is a structure
  %13 = struct $Int (%12 : $Builtin.Int64) / /user: % 15 / /Teacher.agethesetterMethods 14 = %class_method %9 : $Teacher#,Teacher.age!setter : (Teacher) - > (Int) - > (),$@convention(method) (IntThe @guaranteed Teacher) - > () / /user: %15 // callsetterMethod, passed inIntValue 6, and the class instance itself %15 =apply13, 14 (% % % 9) :$@convention(method) (IntThe @guaranteed Teacher) -> () // The reference count is reduced by onestrong_release%9 :// Create literal 0 %17 =integer_literal $Builtin.Int32, 0 / /user: %18 // GeneratedIntThe value of 0,swiftIn theIntIs the structure %18 =struct $Int32 (%17 : $Builtin.Int32) / /user: %19 // Finally 0 frommainFunction returns outreturn %18 : $Int32       $Teacher                    // idCreate literal 0 %17 =integer_literal $Builtin.Int32, 0 / /user: %18 // GeneratedIntThe value of 0,swiftIn theIntIs the structure %18 =struct $Int32 (%17 : $Builtin.Int32) / /user: %19 // Finally 0 frommainFunction returns outreturn %18 : $Int32                             // id19: %} / /end sil function 'main'
Copy the code

The main function basically does what we wrote, creating an instance of Teacher via alloc_init and assigning 6 to setter methods of age. But there are a lot of details, like the retain and release methods that Xcode automatically added to show up, a simple 6 that is struct initialized, and so on.

Here are some set collocations:

  • Initialize a reference
// Initialize the global variable person
  alloc_global @main.person : main.Teacher
// Create a reference to the address of the global variable previously initialized by alloc_global
  %3 = global_addr @main.person : main.Teacher 
Copy the code
  • Calling a method
// a reference to the __allocating_init() method
  %5 = function_ref @main.Teacher.__allocating_init() -> main.Teacher : $@convention(method) (@thick Teacher.Type) - >@owned Teacher
// Call the function __allocating_init and pass in the argument element type Teacher
  %6 = apply %5(%4) : $@convention(method) (@thick Teacher.Type) - >@owned Teacher
Copy the code
  • Get the basic type of swFIT
// Create the literal 6
  %12 = integer_literal $Builtin.Int64.6
// Generate an Int value of 6. In swift Int is a structure
  %13 = struct $Int (%12 : $Builtin.Int64)
Copy the code

These are not commented in detail

Explore alloc init

An implementation of this method can be found by searching the SIL file for @main.teacher.__allocating_init () as found in the main function:

// 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) :// Assign an object with a reference of type Teacher
  %1 = alloc_ref $Teacher                         // user: %3
  // function_ref Teacher.init()
// Call 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'
Copy the code

This method is relatively simple. First, allocate space to the reference object, and then call init on the reference object.

// 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
// Initialize age to 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
// Initialize name to 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'
Copy the code

The init method looks long, but it actually does two properties. Note that assignment calls the store directly, not the setter method called.

Assembly validation

We can set a sign breakpointTeacher.initAnd then runWe can see that the stack above meets the expectations of the analysis, and we point to__allocating_initLook at theWe can see the allocation space callswift_allocObjectSo that we can locate the Swift source and see the structure of the class. We will analyze this later.

fromswift_allocObjectAnalyze the composition of a Class