Upgrade to Pro — share decks privately, control downloads, hide ads and more …

SILから読み解くSwiftのポリモーフィズムとStructの優位性

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

 SILから読み解くSwiftのポリモーフィズムとStructの優位性

※後半のStructの挙動について補足をしています。ブログについてもご確認くださいm
https://zenn.dev/h1d3mun3/articles/d85a448cd2edae

Nagoya.swift #wで登壇してきたときの資料です。
https://japan-region-swift.connpass.com/event/376480/

ポリモーフィズムについてSILの目線から挙動を紐解いています。

Avatar for Hidemune Takahashi

Hidemune Takahashi

April 25, 2026

Other Decks in Technology

Transcript

  1. 自己紹介 • h1d3mun3(űũƄŷŮ) • おしごと • GO Inc, / iOSエンジニア

    • タクシーアプリを作っている 🚕🚕🚕 • 最近Preductsのデスクを買った 💸💸💸
  2. sil [ossa] @main : $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {

    bb0(%0 : $Int32, %1 : $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>): %2 = integer_literal $Builtin.Word, 1 // user: %4 // function_ref _allocateUninitializedArray<A>(_:) %3 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %4 %4 = apply %3<Any>(%2) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %5 (%5, %6) = destructure_tuple %4 // users: %23, %7 %7 = begin_borrow %5 // users: %21, %8 %8 = struct_extract %7, #Array._buffer // user: %9 %9 = struct_extract %8, #_ArrayBuffer._storage // user: %10 %10 = struct_extract %9, #_BridgeStorage.rawValue // user: %11 %11 = unchecked_ref_cast %10 to $__ContiguousArrayStorageBase // user: %12 %12 = ref_tail_addr %11, $Any // user: %19 %13 = string_literal utf8 "hello world" // user: %18 %14 = integer_literal $Builtin.Word, 11 // user: %18 %15 = integer_literal $Builtin.Int1, -1 // user: %18 %16 = metatype $@thin String.Type // user: %18 // function_ref String.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) %17 = function_ref @$sSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %18 %18 = apply %17(%13, %14, %15, %16) : $@convention(method) (Builtin.RawPointer, Builtin.Word, Builtin.Int1, @thin String.Type) -> @owned String // user: %20 %19 = init_existential_addr %12, $String // user: %20 store %18 to [init] %19 // id: %20 end_borrow %7 // id: %21 // function_ref _finalizeUninitializedArray<A>(_:) %22 = function_ref @$ss27_finalizeUninitializedArrayySayxGABnlF : $@convention(thin) <τ_0_0> (@owned Array<τ_0_0>) -> @owned Array<τ_0_0> // user: %23 %23 = apply %22<Any>(%5) : $@convention(thin) <τ_0_0> (@owned Array<τ_0_0>) -> @owned Array<τ_0_0> // users: %32, %29 // function_ref default argument 1 of print(_:separator:terminator:) %24 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA0_ : $@convention(thin) () -> @owned String // user: %25 %25 = apply %24() : $@convention(thin) () -> @owned String // users: %31, %29 // function_ref default argument 2 of print(_:separator:terminator:) %26 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA1_ : $@convention(thin) () -> @owned String // user: %27 %27 = apply %26() : $@convention(thin) () -> @owned String // users: %30, %29 // function_ref print(_:separator:terminator:) %28 = function_ref @$ss5print_9separator10terminatoryypd_S2StF : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> () // user: %29 %29 = apply %28(%23, %25, %27) : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> () destroy_value %27 // id: %30 destroy_value %25 // id: %31 destroy_value %23 // id: %32 %33 = integer_literal $Builtin.Int32, 0 // user: %34 %34 = struct $Int32 (%33) // user: %35 return %34 // id: %35 } // end sil function 'main'
  3. 読んでみよう! %13 = string_literal utf8 "hello world" // ιʔείʔυ্ͷจࣈྻ %17

    = function_ref String.init(...) // StringͷΠχγϟϥΠβ΁ͷࢀরΛऔಘ %18 = apply %17(%13, ...) // Stringʹม׵(ͭ·ΓॳظԽ) %19 = init_existential_addr %12, $String // AnyܕͷശʹೖΕΔ store %18 to [init] %19 // ॻ͖ࠐΉ
  4. Classと継承 class Animal { func eat() { } } class

    Human: Animal { override func eat() { print("΋͙΋͙") } } class Cat: Animal { override func eat() { print(“ͪΎʙΔͪΎʙΔͪΌ͓ͪΎʔΔ♪") } } let human: Animal = Human() human.eat() // ΋͙΋͙
  5. vtableからの検索は、class_method で行う。 SIL上のVTable表現 sil_vtable Animal { #Animal.eat: (Animal) -> ()

    -> () : @$s4main6AnimalC3eatyyF // Animal.eat() #Animal.init!allocator: (Animal.Type) -> () -> Animal : @$s4main6AnimalCACycfC // Animal.__allocating_init() #Animal.deinit!deallocator: @$s4main6AnimalCfD // Animal.__deallocating_deinit } sil_vtable Human { #Animal.eat: (Animal) -> () -> () : @$s4main5HumanC3eatyyF [override] // Human.eat() #Animal.init!allocator: (Animal.Type) -> () -> Animal : @$s4main5HumanCACycfC [override] // Human.__allocating_init() #Human.deinit!deallocator: @$s4main5HumanCfD // Human.__deallocating_deinit } sil_vtable Cat { #Animal.eat: (Animal) -> () -> () : @$s4main3CatC3eatyyF [override] // Cat.eat() #Animal.init!allocator: (Animal.Type) -> () -> Animal : @$s4main3CatCACycfC [override] // Cat.__allocating_init() #Cat.deinit!deallocator: @$s4main3CatCfD // Cat.__deallocating_deinit }
  6. 実行部分 • Humanの初期化 %5 = function_ref Human.__allocating_init // ΠχγϟϥΠβࢀর %6

    = apply %5(%4) // Human()Λੜ੒ %7 = upcast %6 to $Animal // ← HumanΛAnimalʹΞοϓΩϟετ store %7 to [init] %3 // let human: Animal ʹ֨ೲ • Humanの実行 %9 = load_borrow %3 // humanΛआΓΔ %10 = class_method %9, #Animal.eat // ← vtable͔Βeat()Λ୳͢ʂ %11 = apply %10(%9) // ࣮ࡍʹݺͿ
  7. ProtocolとStruct protocol Animal { func eat() } struct Human: Animal

    { func eat() { print("΋͙΋͙") } } struct Cat: Animal { func eat() { print("ͪΎʙΔͪΎʙΔͪΌ͓ͪΎʔΔ♪") } } let human: some Animal = Human() human.eat()
  8. 探すときは witness_method を使う SIL上のWitness Table sil_witness_table hidden Human: Animal module

    main { method #Animal.eat: <Self where Self : Animal> (Self) -> () -> () : @$s4main5HumanVAA6AnimalA2aDP3eatyyFTW // protocol witness for Animal.eat() in conformance Human } sil_witness_table hidden Cat: Animal module main { method #Animal.eat: <Self where Self : Animal> (Self) -> () -> () : @$s4main3CatVAA6AnimalA2aDP3eatyyFTW // protocol witness for Animal.eat() in conformance Cat }
  9. 実行部分 • Humanの初期化 alloc_global @human // humanͱ͍͏άϩʔόϧม਺ͷྖҬΛ֬อ %3 = global_addr

    @human // ͦͷΞυϨεΛ%3ʹೖΕΔ %4 = metatype $@thin Human.Type // HumanܕͷϝλλΠϓΛऔಘ %5 = function_ref Human.init() // ΠχγϟϥΠβͷࢀরΛऔಘ %6 = apply %5(%4) // Human()Λ࣮ߦ store %6 to [trivial] %3 // humanʹ֨ೲ • Humanの実行 %8 = load [trivial] %3 // humanΛϩʔυ %9 = alloc_stack $Human // ελοΫʹྖҬ֬อʢStructͳͷͰελοΫʂʣ store %8 to [trivial] %9 // ελοΫʹॻ͖ࠐΉ %11 = witness_method $Human, #Animal.eat // witness table͔Βeat()Λݕࡧ %12 = apply %11<Human>(%9) // eat()ΛݺͿ
  10. 違い • Class • メモリ管理: ヒープ • ARCの有無: あり •

    Protocol + Struct • メモリ管理: スタック • Arcの有無: なし
  11. ARCとは let a = Human() // ࢀরΧ΢ϯτ+1 let b =

    a // ࢀরΧ΢ϯτ+1 // bͷείʔϓऴྃ → ࢀরΧ΢ϯτ-1 // aͷείʔϓऴྃ → ࢀরΧ΢ϯτ-1 → 0ʹͳͬͨͷͰղ์ • 変数の参照が変更されるたびにカウントを管理する必要 • このカウントは複数スレッドが同時に更新しても問題ない必要 • 複数コアだったらほかコアのことも考えないといけない。 → おっそーい。