Skip to content

Instantly share code, notes, and snippets.

@omochi
Created November 15, 2019 23:35

slidenumber: true autoscale: true

SILを読もう

@omochimetaru

SwiftcKaigi #1


自己紹介

right

  • omochimetaru
  • アイコン→
  • わいわいswiftcでしばしば登壇

SIL1

  • Swift intermediate language

  • SILコードにはSwiftコードよりも具体的な挙動が書かれている


具体的な挙動の例

  • 暗黙の型変換

  • 参照カウントの増減

  • メソッドの呼び出し


暗黙の型変換


func intToIntOptional(x: Int) -> Int? {
    return x
}
  • Swift的にはそのままreturnしているが、CPUにとってはIntからInt?への変換をしている。

  • swiftcコマンドでSILを出力する
$ swiftc -emit-sil -parse-as-library code01.swift

sil_stage canonical

import Builtin
import Swift
import SwiftShims

func intToIntOptional(x: Int) -> Int?

// intToIntOptional(x:)
sil hidden @$s6code0116intToIntOptional1xSiSgSi_tF : $@convention(thin) (Int) -> Optional<Int> {
// %0                                             // users: %2, %1
bb0(%0 : $Int):
  debug_value %0 : $Int, let, name "x", argno 1   // id: %1
  %2 = enum $Optional<Int>, #Optional.some!enumelt.1, %0 : $Int // user: %3
  return %2 : $Optional<Int>                      // id: %3
} // end sil function '$s6code0116intToIntOptional1xSiSgSi_tF'

Mangling2

// module: code01
func intToIntOptional(x: Int) -> Int?

↓

$s6code0116intToIntOptional1xSiSgSi_tF

  • モジュール名や型名を混ぜ込んだ(mangle)名前を作る

  • swift demangleコマンドで展開できる

$ swift demangle '$s6code0116intToIntOptional1xSiSgSi_tF'
$s6code0116intToIntOptional1xSiSgSi_tF 
    ---> code01.intToIntOptional(x: Swift.Int) -> Swift.Int?

Demangling

$s6code0116intToIntOptional1xSiSgSi_tF

$s: Swift5
  6: 6文字の単語
   code01
         16: 16文字の単語
           intToIntOptional
                           1: 1文字の単語
                            x
                             Si: Int
                               Sg: Optional
                                 Si: Int
                                   _: リストの区切り
                                    t: タプル
                                     F: 関数

[.code-highlight: 1-8]

// intToIntOptional(x:)
sil hidden @$s6code0116intToIntOptional1xSiSgSi_tF : $@convention(thin) (Int) -> Optional<Int> {
// %0                                             // users: %2, %1
bb0(%0 : $Int):
  debug_value %0 : $Int, let, name "x", argno 1   // id: %1
  %2 = enum $Optional<Int>, #Optional.some!enumelt.1, %0 : $Int // user: %3
  return %2 : $Optional<Int>                      // id: %3
} // end sil function '$s6code0116intToIntOptional1xSiSgSi_tF'

[.code-highlight: 6]

// intToIntOptional(x:)
sil hidden @$s6code0116intToIntOptional1xSiSgSi_tF : $@convention(thin) (Int) -> Optional<Int> {
// %0                                             // users: %2, %1
bb0(%0 : $Int):
  debug_value %0 : $Int, let, name "x", argno 1   // id: %1
  %2 = enum $Optional<Int>, #Optional.some!enumelt.1, %0 : $Int // user: %3
  return %2 : $Optional<Int>                      // id: %3
} // end sil function '$s6code0116intToIntOptional1xSiSgSi_tF'

%2 = enum $Optional<Int>, #Optional.some!enumelt.1, %0 : $Int // user: %3
%2 =: 結果を%2に代入
     enum: enumの値を構築
          $Optional<Int>: 型はOptional<Int>
                          $Optional.some!enumelt.1: caseはsome
                                                    %0: associated valueは%0
                                                       : $Int: $0の型はInt
                                                              //: コメント
                                                                 user: %3: 結果は命令%3で使う

参照カウントの増減


参照カウント

  • Swiftはクラスインスタンスのメモリ管理に参照カウントを使う

  • 初期値1
  • 0になったら解放
  • 変数代入: 古いインスタンスはrelease(-1)
  • 変数破棄: 所持するインスタンスをrelease
  • 関数呼び出し: 実引数をretain(+1), 呼び出し後にrelease

class Cat {}

func useCat(_ cat: Cat) { }

func changeCat(_ cat: Cat) -> Cat {
    return Cat()
}

func main() {
    var cat = Cat()
    useCat(cat)
    cat = changeCat(cat)
}

func main() {
    // 初期化
    var cat = Cat()

    // 呼び出し前に引数のretain
    useCat(cat)
    // 呼び出し後に引数のrelease

    // 呼び出し前に引数のretain
    cat = changeCat(cat)
    // 呼び出し後に引数のrelease
    // 古い値をrelease
    // 変数の値をrelease
}

// main()
sil hidden @$s6code024mainyyF : $@convention(thin) () -> () {
bb0:
  %0 = alloc_stack $Cat, var, name "cat"          // users: %4, %22, %21, %5, %11, %17
  %1 = metatype $@thick Cat.Type                  // user: %3
  // function_ref Cat.__allocating_init()
  %2 = function_ref @$s6code023CatCACycfC : $@convention(method) (@thick Cat.Type) -> @owned Cat // user: %3
  %3 = apply %2(%1) : $@convention(method) (@thick Cat.Type) -> @owned Cat // users: %19, %12, %15, %16, %6, %9, %10, %4
  store %3 to %0 : $*Cat                          // id: %4
  %5 = begin_access [read] [static] %0 : $*Cat    // user: %7
  strong_retain %3 : $Cat                         // id: %6
  end_access %5 : $*Cat                           // id: %7
  // function_ref useCat(_:)
  %8 = function_ref @$s6code026useCatyyAA0C0CF : $@convention(thin) (@guaranteed Cat) -> () // user: %9
  %9 = apply %8(%3) : $@convention(thin) (@guaranteed Cat) -> ()
  strong_release %3 : $Cat                        // id: %10
  %11 = begin_access [read] [static] %0 : $*Cat   // user: %13
  strong_retain %3 : $Cat                         // id: %12
  end_access %11 : $*Cat                          // id: %13
  // function_ref changeCat(_:)
  %14 = function_ref @$s6code029changeCatyAA0C0CADF : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %15
  %15 = apply %14(%3) : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %18
  strong_release %3 : $Cat                        // id: %16
  %17 = begin_access [modify] [static] %0 : $*Cat // users: %18, %20
  store %15 to %17 : $*Cat                        // id: %18
  strong_release %3 : $Cat                        // id: %19
  end_access %17 : $*Cat                          // id: %20
  destroy_addr %0 : $*Cat                         // id: %21
  dealloc_stack %0 : $*Cat                        // id: %22
  %23 = tuple ()                                  // user: %24
  return %23 : $()                                // id: %24
} // end sil function '$s6code024mainyyF'

[.code-highlight: 4]

// main()
sil hidden @$s6code024mainyyF : $@convention(thin) () -> () {
bb0:
  %0 = alloc_stack $Cat, var, name "cat"          // users: %4, %22, %21, %5, %11, %17
  %1 = metatype $@thick Cat.Type                  // user: %3
  // function_ref Cat.__allocating_init()
  %2 = function_ref @$s6code023CatCACycfC : $@convention(method) (@thick Cat.Type) -> @owned Cat // user: %3
  %3 = apply %2(%1) : $@convention(method) (@thick Cat.Type) -> @owned Cat // users: %19, %12, %15, %16, %6, %9, %10, %4
  store %3 to %0 : $*Cat                          // id: %4
  %5 = begin_access [read] [static] %0 : $*Cat    // user: %7
  strong_retain %3 : $Cat                         // id: %6
  end_access %5 : $*Cat                           // id: %7
  // function_ref useCat(_:)
  %8 = function_ref @$s6code026useCatyyAA0C0CF : $@convention(thin) (@guaranteed Cat) -> () // user: %9
  %9 = apply %8(%3) : $@convention(thin) (@guaranteed Cat) -> ()
  strong_release %3 : $Cat                        // id: %10
  %11 = begin_access [read] [static] %0 : $*Cat   // user: %13
  strong_retain %3 : $Cat                         // id: %12
  end_access %11 : $*Cat                          // id: %13
  // function_ref changeCat(_:)
  %14 = function_ref @$s6code029changeCatyAA0C0CADF : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %15
  %15 = apply %14(%3) : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %18
  strong_release %3 : $Cat                        // id: %16
  %17 = begin_access [modify] [static] %0 : $*Cat // users: %18, %20
  store %15 to %17 : $*Cat                        // id: %18
  strong_release %3 : $Cat                        // id: %19
  end_access %17 : $*Cat                          // id: %20
  destroy_addr %0 : $*Cat                         // id: %21
  dealloc_stack %0 : $*Cat                        // id: %22
  %23 = tuple ()                                  // user: %24
  return %23 : $()                                // id: %24
} // end sil function '$s6code024mainyyF'
  • 変数catを生成(%0)

[.code-highlight: 28-29]

// main()
sil hidden @$s6code024mainyyF : $@convention(thin) () -> () {
bb0:
  %0 = alloc_stack $Cat, var, name "cat"          // users: %4, %22, %21, %5, %11, %17
  %1 = metatype $@thick Cat.Type                  // user: %3
  // function_ref Cat.__allocating_init()
  %2 = function_ref @$s6code023CatCACycfC : $@convention(method) (@thick Cat.Type) -> @owned Cat // user: %3
  %3 = apply %2(%1) : $@convention(method) (@thick Cat.Type) -> @owned Cat // users: %19, %12, %15, %16, %6, %9, %10, %4
  store %3 to %0 : $*Cat                          // id: %4
  %5 = begin_access [read] [static] %0 : $*Cat    // user: %7
  strong_retain %3 : $Cat                         // id: %6
  end_access %5 : $*Cat                           // id: %7
  // function_ref useCat(_:)
  %8 = function_ref @$s6code026useCatyyAA0C0CF : $@convention(thin) (@guaranteed Cat) -> () // user: %9
  %9 = apply %8(%3) : $@convention(thin) (@guaranteed Cat) -> ()
  strong_release %3 : $Cat                        // id: %10
  %11 = begin_access [read] [static] %0 : $*Cat   // user: %13
  strong_retain %3 : $Cat                         // id: %12
  end_access %11 : $*Cat                          // id: %13
  // function_ref changeCat(_:)
  %14 = function_ref @$s6code029changeCatyAA0C0CADF : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %15
  %15 = apply %14(%3) : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %18
  strong_release %3 : $Cat                        // id: %16
  %17 = begin_access [modify] [static] %0 : $*Cat // users: %18, %20
  store %15 to %17 : $*Cat                        // id: %18
  strong_release %3 : $Cat                        // id: %19
  end_access %17 : $*Cat                          // id: %20
  destroy_addr %0 : $*Cat                         // id: %21
  dealloc_stack %0 : $*Cat                        // id: %22
  %23 = tuple ()                                  // user: %24
  return %23 : $()                                // id: %24
} // end sil function '$s6code024mainyyF'
  • 変数cat(%0)を解放

[.code-highlight: 6-9]

// main()
sil hidden @$s6code024mainyyF : $@convention(thin) () -> () {
bb0:
  %0 = alloc_stack $Cat, var, name "cat"          // users: %4, %22, %21, %5, %11, %17
  %1 = metatype $@thick Cat.Type                  // user: %3
  // function_ref Cat.__allocating_init()
  %2 = function_ref @$s6code023CatCACycfC : $@convention(method) (@thick Cat.Type) -> @owned Cat // user: %3
  %3 = apply %2(%1) : $@convention(method) (@thick Cat.Type) -> @owned Cat // users: %19, %12, %15, %16, %6, %9, %10, %4
  store %3 to %0 : $*Cat                          // id: %4
  %5 = begin_access [read] [static] %0 : $*Cat    // user: %7
  strong_retain %3 : $Cat                         // id: %6
  end_access %5 : $*Cat                           // id: %7
  // function_ref useCat(_:)
  %8 = function_ref @$s6code026useCatyyAA0C0CF : $@convention(thin) (@guaranteed Cat) -> () // user: %9
  %9 = apply %8(%3) : $@convention(thin) (@guaranteed Cat) -> ()
  strong_release %3 : $Cat                        // id: %10
  %11 = begin_access [read] [static] %0 : $*Cat   // user: %13
  strong_retain %3 : $Cat                         // id: %12
  end_access %11 : $*Cat                          // id: %13
  // function_ref changeCat(_:)
  %14 = function_ref @$s6code029changeCatyAA0C0CADF : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %15
  %15 = apply %14(%3) : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %18
  strong_release %3 : $Cat                        // id: %16
  %17 = begin_access [modify] [static] %0 : $*Cat // users: %18, %20
  store %15 to %17 : $*Cat                        // id: %18
  strong_release %3 : $Cat                        // id: %19
  end_access %17 : $*Cat                          // id: %20
  destroy_addr %0 : $*Cat                         // id: %21
  dealloc_stack %0 : $*Cat                        // id: %22
  %23 = tuple ()                                  // user: %24
  return %23 : $()                                // id: %24
} // end sil function '$s6code024mainyyF'
  • Cat.initの呼び出し(%3)、変数cat(%0)に保存

[.code-highlight: 13-15]

// main()
sil hidden @$s6code024mainyyF : $@convention(thin) () -> () {
bb0:
  %0 = alloc_stack $Cat, var, name "cat"          // users: %4, %22, %21, %5, %11, %17
  %1 = metatype $@thick Cat.Type                  // user: %3
  // function_ref Cat.__allocating_init()
  %2 = function_ref @$s6code023CatCACycfC : $@convention(method) (@thick Cat.Type) -> @owned Cat // user: %3
  %3 = apply %2(%1) : $@convention(method) (@thick Cat.Type) -> @owned Cat // users: %19, %12, %15, %16, %6, %9, %10, %4
  store %3 to %0 : $*Cat                          // id: %4
  %5 = begin_access [read] [static] %0 : $*Cat    // user: %7
  strong_retain %3 : $Cat                         // id: %6
  end_access %5 : $*Cat                           // id: %7
  // function_ref useCat(_:)
  %8 = function_ref @$s6code026useCatyyAA0C0CF : $@convention(thin) (@guaranteed Cat) -> () // user: %9
  %9 = apply %8(%3) : $@convention(thin) (@guaranteed Cat) -> ()
  strong_release %3 : $Cat                        // id: %10
  %11 = begin_access [read] [static] %0 : $*Cat   // user: %13
  strong_retain %3 : $Cat                         // id: %12
  end_access %11 : $*Cat                          // id: %13
  // function_ref changeCat(_:)
  %14 = function_ref @$s6code029changeCatyAA0C0CADF : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %15
  %15 = apply %14(%3) : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %18
  strong_release %3 : $Cat                        // id: %16
  %17 = begin_access [modify] [static] %0 : $*Cat // users: %18, %20
  store %15 to %17 : $*Cat                        // id: %18
  strong_release %3 : $Cat                        // id: %19
  end_access %17 : $*Cat                          // id: %20
  destroy_addr %0 : $*Cat                         // id: %21
  dealloc_stack %0 : $*Cat                        // id: %22
  %23 = tuple ()                                  // user: %24
  return %23 : $()                                // id: %24
} // end sil function '$s6code024mainyyF'
  • useCatの呼び出しで%3を渡す

[.code-highlight: 11, 16]

// main()
sil hidden @$s6code024mainyyF : $@convention(thin) () -> () {
bb0:
  %0 = alloc_stack $Cat, var, name "cat"          // users: %4, %22, %21, %5, %11, %17
  %1 = metatype $@thick Cat.Type                  // user: %3
  // function_ref Cat.__allocating_init()
  %2 = function_ref @$s6code023CatCACycfC : $@convention(method) (@thick Cat.Type) -> @owned Cat // user: %3
  %3 = apply %2(%1) : $@convention(method) (@thick Cat.Type) -> @owned Cat // users: %19, %12, %15, %16, %6, %9, %10, %4
  store %3 to %0 : $*Cat                          // id: %4
  %5 = begin_access [read] [static] %0 : $*Cat    // user: %7
  strong_retain %3 : $Cat                         // id: %6
  end_access %5 : $*Cat                           // id: %7
  // function_ref useCat(_:)
  %8 = function_ref @$s6code026useCatyyAA0C0CF : $@convention(thin) (@guaranteed Cat) -> () // user: %9
  %9 = apply %8(%3) : $@convention(thin) (@guaranteed Cat) -> ()
  strong_release %3 : $Cat                        // id: %10
  %11 = begin_access [read] [static] %0 : $*Cat   // user: %13
  strong_retain %3 : $Cat                         // id: %12
  end_access %11 : $*Cat                          // id: %13
  // function_ref changeCat(_:)
  %14 = function_ref @$s6code029changeCatyAA0C0CADF : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %15
  %15 = apply %14(%3) : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %18
  strong_release %3 : $Cat                        // id: %16
  %17 = begin_access [modify] [static] %0 : $*Cat // users: %18, %20
  store %15 to %17 : $*Cat                        // id: %18
  strong_release %3 : $Cat                        // id: %19
  end_access %17 : $*Cat                          // id: %20
  destroy_addr %0 : $*Cat                         // id: %21
  dealloc_stack %0 : $*Cat                        // id: %22
  %23 = tuple ()                                  // user: %24
  return %23 : $()                                // id: %24
} // end sil function '$s6code024mainyyF'
  • useCatの呼び出し前後で%3をretain, release

[.code-highlight: 20-22]

// main()
sil hidden @$s6code024mainyyF : $@convention(thin) () -> () {
bb0:
  %0 = alloc_stack $Cat, var, name "cat"          // users: %4, %22, %21, %5, %11, %17
  %1 = metatype $@thick Cat.Type                  // user: %3
  // function_ref Cat.__allocating_init()
  %2 = function_ref @$s6code023CatCACycfC : $@convention(method) (@thick Cat.Type) -> @owned Cat // user: %3
  %3 = apply %2(%1) : $@convention(method) (@thick Cat.Type) -> @owned Cat // users: %19, %12, %15, %16, %6, %9, %10, %4
  store %3 to %0 : $*Cat                          // id: %4
  %5 = begin_access [read] [static] %0 : $*Cat    // user: %7
  strong_retain %3 : $Cat                         // id: %6
  end_access %5 : $*Cat                           // id: %7
  // function_ref useCat(_:)
  %8 = function_ref @$s6code026useCatyyAA0C0CF : $@convention(thin) (@guaranteed Cat) -> () // user: %9
  %9 = apply %8(%3) : $@convention(thin) (@guaranteed Cat) -> ()
  strong_release %3 : $Cat                        // id: %10
  %11 = begin_access [read] [static] %0 : $*Cat   // user: %13
  strong_retain %3 : $Cat                         // id: %12
  end_access %11 : $*Cat                          // id: %13
  // function_ref changeCat(_:)
  %14 = function_ref @$s6code029changeCatyAA0C0CADF : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %15
  %15 = apply %14(%3) : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %18
  strong_release %3 : $Cat                        // id: %16
  %17 = begin_access [modify] [static] %0 : $*Cat // users: %18, %20
  store %15 to %17 : $*Cat                        // id: %18
  strong_release %3 : $Cat                        // id: %19
  end_access %17 : $*Cat                          // id: %20
  destroy_addr %0 : $*Cat                         // id: %21
  dealloc_stack %0 : $*Cat                        // id: %22
  %23 = tuple ()                                  // user: %24
  return %23 : $()                                // id: %24
} // end sil function '$s6code024mainyyF'
  • changeCatの呼び出し(%15)、引数に%3

[.code-highlight: 18, 23]

// main()
sil hidden @$s6code024mainyyF : $@convention(thin) () -> () {
bb0:
  %0 = alloc_stack $Cat, var, name "cat"          // users: %4, %22, %21, %5, %11, %17
  %1 = metatype $@thick Cat.Type                  // user: %3
  // function_ref Cat.__allocating_init()
  %2 = function_ref @$s6code023CatCACycfC : $@convention(method) (@thick Cat.Type) -> @owned Cat // user: %3
  %3 = apply %2(%1) : $@convention(method) (@thick Cat.Type) -> @owned Cat // users: %19, %12, %15, %16, %6, %9, %10, %4
  store %3 to %0 : $*Cat                          // id: %4
  %5 = begin_access [read] [static] %0 : $*Cat    // user: %7
  strong_retain %3 : $Cat                         // id: %6
  end_access %5 : $*Cat                           // id: %7
  // function_ref useCat(_:)
  %8 = function_ref @$s6code026useCatyyAA0C0CF : $@convention(thin) (@guaranteed Cat) -> () // user: %9
  %9 = apply %8(%3) : $@convention(thin) (@guaranteed Cat) -> ()
  strong_release %3 : $Cat                        // id: %10
  %11 = begin_access [read] [static] %0 : $*Cat   // user: %13
  strong_retain %3 : $Cat                         // id: %12
  end_access %11 : $*Cat                          // id: %13
  // function_ref changeCat(_:)
  %14 = function_ref @$s6code029changeCatyAA0C0CADF : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %15
  %15 = apply %14(%3) : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %18
  strong_release %3 : $Cat                        // id: %16
  %17 = begin_access [modify] [static] %0 : $*Cat // users: %18, %20
  store %15 to %17 : $*Cat                        // id: %18
  strong_release %3 : $Cat                        // id: %19
  end_access %17 : $*Cat                          // id: %20
  destroy_addr %0 : $*Cat                         // id: %21
  dealloc_stack %0 : $*Cat                        // id: %22
  %23 = tuple ()                                  // user: %24
  return %23 : $()                                // id: %24
} // end sil function '$s6code024mainyyF'
  • changeCatの呼び出し前後で%3をretain, release

[.code-highlight: 25-26]

// main()
sil hidden @$s6code024mainyyF : $@convention(thin) () -> () {
bb0:
  %0 = alloc_stack $Cat, var, name "cat"          // users: %4, %22, %21, %5, %11, %17
  %1 = metatype $@thick Cat.Type                  // user: %3
  // function_ref Cat.__allocating_init()
  %2 = function_ref @$s6code023CatCACycfC : $@convention(method) (@thick Cat.Type) -> @owned Cat // user: %3
  %3 = apply %2(%1) : $@convention(method) (@thick Cat.Type) -> @owned Cat // users: %19, %12, %15, %16, %6, %9, %10, %4
  store %3 to %0 : $*Cat                          // id: %4
  %5 = begin_access [read] [static] %0 : $*Cat    // user: %7
  strong_retain %3 : $Cat                         // id: %6
  end_access %5 : $*Cat                           // id: %7
  // function_ref useCat(_:)
  %8 = function_ref @$s6code026useCatyyAA0C0CF : $@convention(thin) (@guaranteed Cat) -> () // user: %9
  %9 = apply %8(%3) : $@convention(thin) (@guaranteed Cat) -> ()
  strong_release %3 : $Cat                        // id: %10
  %11 = begin_access [read] [static] %0 : $*Cat   // user: %13
  strong_retain %3 : $Cat                         // id: %12
  end_access %11 : $*Cat                          // id: %13
  // function_ref changeCat(_:)
  %14 = function_ref @$s6code029changeCatyAA0C0CADF : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %15
  %15 = apply %14(%3) : $@convention(thin) (@guaranteed Cat) -> @owned Cat // user: %18
  strong_release %3 : $Cat                        // id: %16
  %17 = begin_access [modify] [static] %0 : $*Cat // users: %18, %20
  store %15 to %17 : $*Cat                        // id: %18
  strong_release %3 : $Cat                        // id: %19
  end_access %17 : $*Cat                          // id: %20
  destroy_addr %0 : $*Cat                         // id: %21
  dealloc_stack %0 : $*Cat                        // id: %22
  %23 = tuple ()                                  // user: %24
  return %23 : $()                                // id: %24
} // end sil function '$s6code024mainyyF'
  • changeCatの結果(%15)を変数cat(%17=%0)に代入、古い値(%3)をrelease

メソッド呼び出し


プロトコルのデフォルト実装とクラス継承の関係はややこしい

  • Swift Forum: Default Protocol Implementation Inheritance Behaviour - The current situation and what/if anything should be done about it 3 2019/08/20

fit


  • 定期的に掘り返される話題

  • この投稿でもイシューチケット4 2015/12/7 への言及あり


fit


protocol P {
    func f()
}
extension P {
    func f() { print("P.f") }
}

class A: P {
    func f() { print("A.f") }
}
class B: A {
    override func f() { print("B.f") }
}

class C: P {
}
class D: C {
    func f() { print("D.f") }
}

func main() {
    A().f() // A.f
    B().f() // B.f
    (B() as A).f() // B.f
    
    C().f() // P.f
    D().f() // D.f
    (D() as C).f() // P.f
}

main()

  • BはAを継承しているのだから、Bのfをそのまま呼んでも、Aにアップキャストしてから呼んでも同じ🙂

  • DはCを継承しているのに、Dのfをそのまま呼ぶのと、Cにアップキャストしてから呼ぶので結果が違う😲


形式的な回答

  • A.fとB.fはクラスメソッド呼び出しなので、オーバーライドされた実装が呼ばれる

  • C.fはwitnessメソッド呼び出し、D.fはクラスメソッド呼び出しなので、オーバーライド関係はない


Swiftの2つのメソッド呼び出し

  • クラスメソッド呼び出し: あるクラスに対応するvtableからメソッドを引く

  • witnessメソッド呼び出し: あるプロトコルに対応するwitness tableからメソッドを引く


実装された関数6つ

// P.f()
sil hidden @$s6code031PPAAE1fyyF : 
  $@convention(method) <Self where Self : P> (@in_guaranteed Self) -> () { ... }

// A.f()
sil hidden @$s6code031AC1fyyF : $@convention(method) (@guaranteed A) -> () { ... }

// protocol witness for P.f() in conformance A
sil private [transparent] [thunk] @$s6code031ACAA1PA2aDP1fyyFTW : 
  $@convention(witness_method: P) (@in_guaranteed A) -> () { ... }

// B.f()
sil hidden @$s6code031BC1fyyF : $@convention(method) (@guaranteed B) -> () { ... }

// protocol witness for P.f() in conformance C
sil private [transparent] [thunk] @$s6code031CCAA1PA2aDP1fyyFTW : 
  $@convention(witness_method: P) <τ_0_0 where τ_0_0 : C> (@in_guaranteed τ_0_0) -> () { ... }

// D.f()
sil hidden @$s6code031DC1fyyF : $@convention(method) (@guaranteed D) -> () { ... }

生成されたテーブル6つ

sil_vtable A {
  #A.f!1: (A) -> () -> () : @$s6code031AC1fyyF  // A.f()
  #A.init!allocator.1: (A.Type) -> () -> A : @$s6code031ACACycfC  // A.__allocating_init()
  #A.deinit!deallocator.1: @$s6code031ACfD  // A.__deallocating_deinit
}

sil_vtable B {
  #A.f!1: (A) -> () -> () : @$s6code031BC1fyyF [override] // B.f()
  #A.init!allocator.1: (A.Type) -> () -> A : @$s6code031BCACycfC [override] // B.__allocating_init()
  #B.deinit!deallocator.1: @$s6code031BCfD  // B.__deallocating_deinit
}

sil_vtable C {
  #C.init!allocator.1: (C.Type) -> () -> C : @$s6code031CCACycfC  // C.__allocating_init()
  #C.deinit!deallocator.1: @$s6code031CCfD  // C.__deallocating_deinit
}

sil_vtable D {
  #C.init!allocator.1: (C.Type) -> () -> C : @$s6code031DCACycfC [override] // D.__allocating_init()
  #D.f!1: (D) -> () -> () : @$s6code031DC1fyyF  // D.f()
  #D.deinit!deallocator.1: @$s6code031DCfD  // D.__deallocating_deinit
}

sil_witness_table hidden A: P module code03 {
  method #P.f!1: <Self where Self : P> (Self) -> () -> () : 
    @$s6code031ACAA1PA2aDP1fyyFTW // protocol witness for P.f() in conformance A
}

sil_witness_table hidden C: P module code03 {
  method #P.f!1: <Self where Self : P> (Self) -> () -> () : 
    @$s6code031CCAA1PA2aDP1fyyFTW // protocol witness for P.f() in conformance C
}

// main()
sil hidden @$s6code034mainyyF : $@convention(thin) () -> () {
bb0:
  %0 = metatype $@thick A.Type                    // user: %2
  // function_ref A.__allocating_init()
  %1 = function_ref @$s6code031ACACycfC : $@convention(method) (@thick A.Type) -> @owned A // user: %2
  %2 = apply %1(%0) : $@convention(method) (@thick A.Type) -> @owned A // users: %5, %4, %3
  %3 = class_method %2 : $A, #A.f!1 : (A) -> () -> (), $@convention(method) (@guaranteed A) -> () // user: %4
  %4 = apply %3(%2) : $@convention(method) (@guaranteed A) -> ()
  strong_release %2 : $A                          // id: %5
  %6 = metatype $@thick B.Type                    // user: %8
  // function_ref B.__allocating_init()
  %7 = function_ref @$s6code031BCACycfC : $@convention(method) (@thick B.Type) -> @owned B // user: %8
  %8 = apply %7(%6) : $@convention(method) (@thick B.Type) -> @owned B // users: %11, %10, %9
  %9 = class_method %8 : $B, #B.f!1 : (B) -> () -> (), $@convention(method) (@guaranteed B) -> () // user: %10
  %10 = apply %9(%8) : $@convention(method) (@guaranteed B) -> ()
  strong_release %8 : $B                          // id: %11
  %12 = metatype $@thick B.Type                   // user: %14
  // function_ref B.__allocating_init()
  %13 = function_ref @$s6code031BCACycfC : $@convention(method) (@thick B.Type) -> @owned B // user: %14
  %14 = apply %13(%12) : $@convention(method) (@thick B.Type) -> @owned B // user: %15
  %15 = upcast %14 : $B to $A                     // users: %18, %17, %16
  %16 = class_method %15 : $A, #A.f!1 : (A) -> () -> (), $@convention(method) (@guaranteed A) -> () // user: %17
  %17 = apply %16(%15) : $@convention(method) (@guaranteed A) -> ()
  strong_release %15 : $A                         // id: %18
  %19 = metatype $@thick C.Type                   // user: %21
  // function_ref C.__allocating_init()
  %20 = function_ref @$s6code031CCACycfC : $@convention(method) (@thick C.Type) -> @owned C // user: %21
  %21 = apply %20(%19) : $@convention(method) (@thick C.Type) -> @owned C // user: %23
  %22 = alloc_stack $C                            // users: %23, %27, %26, %25
  store %21 to %22 : $*C                          // id: %23
  // function_ref P.f()
  %24 = function_ref @$s6code031PPAAE1fyyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %25
  %25 = apply %24<C>(%22) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
  destroy_addr %22 : $*C                          // id: %26
  dealloc_stack %22 : $*C                         // id: %27
  %28 = metatype $@thick D.Type                   // user: %30
  // function_ref D.__allocating_init()
  %29 = function_ref @$s6code031DCACycfC : $@convention(method) (@thick D.Type) -> @owned D // user: %30
  %30 = apply %29(%28) : $@convention(method) (@thick D.Type) -> @owned D // users: %33, %32, %31
  %31 = class_method %30 : $D, #D.f!1 : (D) -> () -> (), $@convention(method) (@guaranteed D) -> () // user: %32
  %32 = apply %31(%30) : $@convention(method) (@guaranteed D) -> ()
  strong_release %30 : $D                         // id: %33
  %34 = metatype $@thick D.Type                   // user: %36
  // function_ref D.__allocating_init()
  %35 = function_ref @$s6code031DCACycfC : $@convention(method) (@thick D.Type) -> @owned D // user: %36
  %36 = apply %35(%34) : $@convention(method) (@thick D.Type) -> @owned D // user: %37
  %37 = upcast %36 : $D to $C                     // user: %39
  %38 = alloc_stack $C                            // users: %39, %43, %42, %41
  store %37 to %38 : $*C                          // id: %39
  // function_ref P.f()
  %40 = function_ref @$s6code031PPAAE1fyyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %41
  %41 = apply %40<(C)>(%38) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
  destroy_addr %38 : $*C                          // id: %42
  dealloc_stack %38 : $*C                         // id: %43
  %44 = tuple ()                                  // user: %45
  return %44 : $()                                // id: %45
} // end sil function '$s6code034mainyyF'

関数の内訳

  • P.f(): $s6code031PPAAE1fyyF

  • A.f(): $s6code031AC1fyyF

  • protocol witness for P.f() in conformance A: $s6code031ACAA1PA2aDP1fyyFTW

  • B.f(): $s6code031BC1fyyF

  • protocol witness for P.f() in conformance C: $s6code031CCAA1PA2aDP1fyyFTW

  • D.f(): $s6code031DC1fyyF


  • A.fに関してはクラスメソッドと、witnessメソッドの2つが生成されている
class A: P {
    func f() { print("A.f") }
}

  • C.fに関してはwitnessメソッドだけが生成されていて、クラスメソッドはない
class C: P {
}

A.fのwitnessメソッドの実装

// protocol witness for P.f() in conformance A
sil private [transparent] [thunk] @$s6code031ACAA1PA2aDP1fyyFTW : 
  $@convention(witness_method: P) (@in_guaranteed A) -> () {
// %0                                             // user: %1
bb0(%0 : $*A):
  %1 = load %0 : $*A                              // users: %2, %3
  %2 = class_method %1 : $A, #A.f!1 : (A) -> () -> (), 
    $@convention(method) (@guaranteed A) -> () // user: %3
  %3 = apply %2(%1) : $@convention(method) (@guaranteed A) -> ()
  %4 = tuple ()                                   // user: %5
  return %4 : $()                                 // id: %5
} // end sil function '$s6code031ACAA1PA2aDP1fyyFTW'

[.code-highlight: 7-9]

// protocol witness for P.f() in conformance A
sil private [transparent] [thunk] @$s6code031ACAA1PA2aDP1fyyFTW : 
  $@convention(witness_method: P) (@in_guaranteed A) -> () {
// %0                                             // user: %1
bb0(%0 : $*A):
  %1 = load %0 : $*A                              // users: %2, %3
  %2 = class_method %1 : $A, #A.f!1 : (A) -> () -> (), 
    $@convention(method) (@guaranteed A) -> () // user: %3
  %3 = apply %2(%1) : $@convention(method) (@guaranteed A) -> ()
  %4 = tuple ()                                   // user: %5
  return %4 : $()                                 // id: %5
} // end sil function '$s6code031ACAA1PA2aDP1fyyFTW'
  • class_method命令でvtableから関数を取り出し、呼び出す。

C.fのwitnessメソッドの実装

// protocol witness for P.f() in conformance C
sil private [transparent] [thunk] @$s6code031CCAA1PA2aDP1fyyFTW : 
  $@convention(witness_method: P) <τ_0_0 where τ_0_0 : C> (@in_guaranteed τ_0_0) -> () {
// %0                                             // user: %2
bb0(%0 : $*τ_0_0):
  // function_ref P.f()
  %1 = function_ref @$s6code031PPAAE1fyyF : 
    $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %2
  %2 = apply %1<τ_0_0>(%0) : 
    $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
  %3 = tuple ()                                   // user: %4
  return %3 : $()                                 // id: %4
} // end sil function '$s6code031CCAA1PA2aDP1fyyFTW'

[.code-highlight: 7-10]

// protocol witness for P.f() in conformance C
sil private [transparent] [thunk] @$s6code031CCAA1PA2aDP1fyyFTW : 
  $@convention(witness_method: P) <τ_0_0 where τ_0_0 : C> (@in_guaranteed τ_0_0) -> () {
// %0                                             // user: %2
bb0(%0 : $*τ_0_0):
  // function_ref P.f()
  %1 = function_ref @$s6code031PPAAE1fyyF : 
    $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %2
  %2 = apply %1<τ_0_0>(%0) : 
    $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
  %3 = tuple ()                                   // user: %4
  return %3 : $()                                 // id: %4
} // end sil function '$s6code031CCAA1PA2aDP1fyyFTW'
  • P.fのデフォルト実装を呼び出す。vtableは参照しない。

AとBのvtable

sil_vtable A {
  #A.f!1: (A) -> () -> () : @$s6code031AC1fyyF  // A.f()
  #A.init!allocator.1: (A.Type) -> () -> A : @$s6code031ACACycfC  // A.__allocating_init()
  #A.deinit!deallocator.1: @$s6code031ACfD  // A.__deallocating_deinit
}

sil_vtable B {
  #A.f!1: (A) -> () -> () : @$s6code031BC1fyyF [override] // B.f()
  #A.init!allocator.1: (A.Type) -> () -> A : @$s6code031BCACycfC [override] // B.__allocating_init()
  #B.deinit!deallocator.1: @$s6code031BCfD  // B.__deallocating_deinit
}
  • BのテーブルのA.fの欄がB.f()($s6code031BC1fyyF)でオーバライドされている

CとDのvtable

sil_vtable C {
  #C.init!allocator.1: (C.Type) -> () -> C : @$s6code031CCACycfC  // C.__allocating_init()
  #C.deinit!deallocator.1: @$s6code031CCfD  // C.__deallocating_deinit
}

sil_vtable D {
  #C.init!allocator.1: (C.Type) -> () -> C : @$s6code031DCACycfC [override] // D.__allocating_init()
  #D.f!1: (D) -> () -> () : @$s6code031DC1fyyF  // D.f()
  #D.deinit!deallocator.1: @$s6code031DCfD  // D.__deallocating_deinit
}
  • CのテーブルにC.fの欄がない。Dのテーブルで新規のD.fとして登録されている。

AとCのwitness table

sil_witness_table hidden A: P module code03 {
  method #P.f!1: <Self where Self : P> (Self) -> () -> () : 
    @$s6code031ACAA1PA2aDP1fyyFTW // protocol witness for P.f() in conformance A
}

sil_witness_table hidden C: P module code03 {
  method #P.f!1: <Self where Self : P> (Self) -> () -> () : 
    @$s6code031CCAA1PA2aDP1fyyFTW // protocol witness for P.f() in conformance C
}
  • AとCのwitnessメソッドがそれぞれ登録されている。

  • BとDのwitness tableはない。


A().f()の呼び出し

  %0 = metatype $@thick A.Type                    // user: %2
  // function_ref A.__allocating_init()
  %1 = function_ref @$s6code031ACACycfC : $@convention(method) (@thick A.Type) -> @owned A // user: %2
  %2 = apply %1(%0) : $@convention(method) (@thick A.Type) -> @owned A // users: %5, %4, %3
  %3 = class_method %2 : $A, #A.f!1 : (A) -> () -> (), $@convention(method) (@guaranteed A) -> () // user: %4
  %4 = apply %3(%2) : $@convention(method) (@guaranteed A) -> ()
  strong_release %2 : $A                          // id: %5

[.code-highlight: 5-6]

  %0 = metatype $@thick A.Type                    // user: %2
  // function_ref A.__allocating_init()
  %1 = function_ref @$s6code031ACACycfC : $@convention(method) (@thick A.Type) -> @owned A // user: %2
  %2 = apply %1(%0) : $@convention(method) (@thick A.Type) -> @owned A // users: %5, %4, %3
  %3 = class_method %2 : $A, #A.f!1 : (A) -> () -> (), $@convention(method) (@guaranteed A) -> () // user: %4
  %4 = apply %3(%2) : $@convention(method) (@guaranteed A) -> ()
  strong_release %2 : $A                          // id: %5
  • class_method命令でA.fのエントリを取り出して呼び出し

B().f()の呼び出し

  %6 = metatype $@thick B.Type                    // user: %8
  // function_ref B.__allocating_init()
  %7 = function_ref @$s6code031BCACycfC : $@convention(method) (@thick B.Type) -> @owned B // user: %8
  %8 = apply %7(%6) : $@convention(method) (@thick B.Type) -> @owned B // users: %11, %10, %9
  %9 = class_method %8 : $B, #B.f!1 : (B) -> () -> (), $@convention(method) (@guaranteed B) -> () // user: %10
  %10 = apply %9(%8) : $@convention(method) (@guaranteed B) -> ()
  strong_release %8 : $B                          // id: %11

[.code-highlight: 5-6]

  %6 = metatype $@thick B.Type                    // user: %8
  // function_ref B.__allocating_init()
  %7 = function_ref @$s6code031BCACycfC : $@convention(method) (@thick B.Type) -> @owned B // user: %8
  %8 = apply %7(%6) : $@convention(method) (@thick B.Type) -> @owned B // users: %11, %10, %9
  %9 = class_method %8 : $B, #B.f!1 : (B) -> () -> (), $@convention(method) (@guaranteed B) -> () // user: %10
  %10 = apply %9(%8) : $@convention(method) (@guaranteed B) -> ()
  strong_release %8 : $B                          // id: %11
  • class_method命令でB.fのエントリを取り出して呼び出し
  • A.fではないので特にオーバライドは無関係

(B() as A).f()の呼び出し

  %12 = metatype $@thick B.Type                   // user: %14
  // function_ref B.__allocating_init()
  %13 = function_ref @$s6code031BCACycfC : $@convention(method) (@thick B.Type) -> @owned B // user: %14
  %14 = apply %13(%12) : $@convention(method) (@thick B.Type) -> @owned B // user: %15
  %15 = upcast %14 : $B to $A                     // users: %18, %17, %16
  %16 = class_method %15 : $A, #A.f!1 : (A) -> () -> (), $@convention(method) (@guaranteed A) -> () // user: %17
  %17 = apply %16(%15) : $@convention(method) (@guaranteed A) -> ()
  strong_release %15 : $A                         // id: %18

[.code-highlight: 5-7]

  %12 = metatype $@thick B.Type                   // user: %14
  // function_ref B.__allocating_init()
  %13 = function_ref @$s6code031BCACycfC : $@convention(method) (@thick B.Type) -> @owned B // user: %14
  %14 = apply %13(%12) : $@convention(method) (@thick B.Type) -> @owned B // user: %15
  %15 = upcast %14 : $B to $A                     // users: %18, %17, %16
  %16 = class_method %15 : $A, #A.f!1 : (A) -> () -> (), $@convention(method) (@guaranteed A) -> () // user: %17
  %17 = apply %16(%15) : $@convention(method) (@guaranteed A) -> ()
  strong_release %15 : $A                         // id: %18
  • Bの値をAにupcast命令でアップキャストした後、class_method命令でA.fのエントリを取り出して呼び出し
  • テーブルのその欄に登録されているB.fが呼ばれる

C().f()の呼び出し

  %19 = metatype $@thick C.Type                   // user: %21
  // function_ref C.__allocating_init()
  %20 = function_ref @$s6code031CCACycfC : $@convention(method) (@thick C.Type) -> @owned C // user: %21
  %21 = apply %20(%19) : $@convention(method) (@thick C.Type) -> @owned C // user: %23
  %22 = alloc_stack $C                            // users: %23, %27, %26, %25
  store %21 to %22 : $*C                          // id: %23
  // function_ref P.f()
  %24 = function_ref @$s6code031PPAAE1fyyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %25
  %25 = apply %24<C>(%22) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
  destroy_addr %22 : $*C                          // id: %26
  dealloc_stack %22 : $*C                         // id: %27

[.code-highlight: 8-9]

  %19 = metatype $@thick C.Type                   // user: %21
  // function_ref C.__allocating_init()
  %20 = function_ref @$s6code031CCACycfC : $@convention(method) (@thick C.Type) -> @owned C // user: %21
  %21 = apply %20(%19) : $@convention(method) (@thick C.Type) -> @owned C // user: %23
  %22 = alloc_stack $C                            // users: %23, %27, %26, %25
  store %21 to %22 : $*C                          // id: %23
  // function_ref P.f()
  %24 = function_ref @$s6code031PPAAE1fyyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %25
  %25 = apply %24<C>(%22) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
  destroy_addr %22 : $*C                          // id: %26
  dealloc_stack %22 : $*C                         // id: %27
  • P.fのデフォルト実装を直接呼び出し
  • witnessテーブルは参照しない

D().f()の呼び出し

  %28 = metatype $@thick D.Type                   // user: %30
  // function_ref D.__allocating_init()
  %29 = function_ref @$s6code031DCACycfC : $@convention(method) (@thick D.Type) -> @owned D // user: %30
  %30 = apply %29(%28) : $@convention(method) (@thick D.Type) -> @owned D // users: %33, %32, %31
  %31 = class_method %30 : $D, #D.f!1 : (D) -> () -> (), $@convention(method) (@guaranteed D) -> () // user: %32
  %32 = apply %31(%30) : $@convention(method) (@guaranteed D) -> ()
  strong_release %30 : $D                         // id: %33

[.code-highlight: 5-6]

  %28 = metatype $@thick D.Type                   // user: %30
  // function_ref D.__allocating_init()
  %29 = function_ref @$s6code031DCACycfC : $@convention(method) (@thick D.Type) -> @owned D // user: %30
  %30 = apply %29(%28) : $@convention(method) (@thick D.Type) -> @owned D // users: %33, %32, %31
  %31 = class_method %30 : $D, #D.f!1 : (D) -> () -> (), $@convention(method) (@guaranteed D) -> () // user: %32
  %32 = apply %31(%30) : $@convention(method) (@guaranteed D) -> ()
  strong_release %30 : $D                         // id: %33
  • class_method命令でD.fを呼び出し
  • C.fでは無いのでオーバーライドは無関係(そもそもされていない)

(D() as C).f()の呼び出し

  %34 = metatype $@thick D.Type                   // user: %36
  // function_ref D.__allocating_init()
  %35 = function_ref @$s6code031DCACycfC : $@convention(method) (@thick D.Type) -> @owned D // user: %36
  %36 = apply %35(%34) : $@convention(method) (@thick D.Type) -> @owned D // user: %37
  %37 = upcast %36 : $D to $C                     // user: %39
  %38 = alloc_stack $C                            // users: %39, %43, %42, %41
  store %37 to %38 : $*C                          // id: %39
  // function_ref P.f()
  %40 = function_ref @$s6code031PPAAE1fyyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %41
  %41 = apply %40<(C)>(%38) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
  destroy_addr %38 : $*C                          // id: %42
  dealloc_stack %38 : $*C                         // id: %43

[.code-highlight: 5, 9-10]

  %34 = metatype $@thick D.Type                   // user: %36
  // function_ref D.__allocating_init()
  %35 = function_ref @$s6code031DCACycfC : $@convention(method) (@thick D.Type) -> @owned D // user: %36
  %36 = apply %35(%34) : $@convention(method) (@thick D.Type) -> @owned D // user: %37
  %37 = upcast %36 : $D to $C                     // user: %39
  %38 = alloc_stack $C                            // users: %39, %43, %42, %41
  store %37 to %38 : $*C                          // id: %39
  // function_ref P.f()
  %40 = function_ref @$s6code031PPAAE1fyyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %41
  %41 = apply %40<(C)>(%38) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
  destroy_addr %38 : $*C                          // id: %42
  dealloc_stack %38 : $*C                         // id: %43
  • upcast命令でDをCにアップキャストした後、P.fのデフォルト実装を直接呼び出し

witnessテーブルの参照

func invokeP<X: P>(_ x: X) {
    x.f()
}

// invokeP<A>(_:)
sil hidden @$s6code037invokePyyxAA1PRzlF : $@convention(thin) <X where X : P> (@in_guaranteed X) -> () {
// %0                                             // users: %3, %1
bb0(%0 : $*X):
  debug_value_addr %0 : $*X, let, name "x", argno 1 // id: %1
  %2 = witness_method $X, #P.f!1 : <Self where Self : P> (Self) -> () -> () : 
    $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %3
  %3 = apply %2<X>(%0) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
  %4 = tuple ()                                   // user: %5
  return %4 : $()                                 // id: %5
} // end sil function '$s6code037invokePyyxAA1PRzlF'

[.code-highlight: 6-8]

// invokeP<A>(_:)
sil hidden @$s6code037invokePyyxAA1PRzlF : $@convention(thin) <X where X : P> (@in_guaranteed X) -> () {
// %0                                             // users: %3, %1
bb0(%0 : $*X):
  debug_value_addr %0 : $*X, let, name "x", argno 1 // id: %1
  %2 = witness_method $X, #P.f!1 : <Self where Self : P> (Self) -> () -> () : 
    $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> () // user: %3
  %3 = apply %2<X>(%0) : $@convention(witness_method: P) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0) -> ()
  %4 = tuple ()                                   // user: %5
  return %4 : $()                                 // id: %5
} // end sil function '$s6code037invokePyyxAA1PRzlF'
  • witness_method命令によるwitnessテーブルからの関数取り出しと呼び出し
  • テーブル参照をインスタンスからではなくメタタイプに対して行う

Footnotes

  1. https://github.com/apple/swift/blob/master/docs/SIL.rst

  2. https://github.com/apple/swift/blob/master/docs/ABI/Mangling.rst

  3. https://forums.swift.org/t/default-protocol-implementation-inheritance-behaviour-the-current-situation-and-what-if-anything-should-be-done-about-it/28049

  4. https://bugs.swift.org/browse/SR-103

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment