2015/09/15

第63回 Cocoa勉強会関西でSwift中間言語について話しました

Cocoa勉強会関西にて、Swift中間言語 (SIL: Swift Intermediate Language) について話しました。

swiftcでオプション-emit-silgenを使うと、Swiftの内部で利用されている中間言語を出力することができます。

この言語については公式の説明は一切ないのですが、雰囲気でなんとなく読める部分もあるので、個人的に気になったlazyの用いたときの中間言語などを読んでみました。

補足

スライド15枚目から19枚目までは、 @starfruits_jさんによる今のうちに知っておきたい Swiftの高速化 + 3D Touch API のArrayが空かどうかについてのコードをSILで読むことで、それを補足する説明になっています。

lazyについての補足

Lazy stored propertyについて発表しました #関モバ - yashigani?.days の追記でlazy var T?lazy var T!で挙動が違うみたいなことを言っていたので、これをSILで確かめてみたところ、値を代入するときの挙動が異なることがわかりました。

スライド24はSILを読んで、手動でSwiftに再変換したものです。 内部的にはlazyOptionalを利用して実装しています。 get時の動作は同じですが、setの操作が大きく異なります。 ImplicitlyUnwrappedOptionalのときはnilを代入しようとすると、そのOptional値がnilになるため、再初期化が必要になります。

代入についての補足

@starfruits_jさん他数名に、代入についてのスライドがなかったと突っ込みがあったので、ここで補足。

簡単に言うと、let _ = vでは、SILではswitch_enumを利用しており、かつalloc_stackを利用してないという点が速度に寄与しているように思えます。

元のSwiftソースが次のようだとすると、

func check_let(v: Int?) -> Bool {
    if let _ = v {
        return true
    }
    return false
}

func check_nil(v: Int?) -> Bool {
    if v != nil {
        return true
    }
    return false
}

これをSILに変換したコードは次のようになります。 check_letははじめのブロックがswitch_enumだけになって非常にシンプルです。

// Contents.check_let (Swift.Optional<Swift.Int>) -> Swift.Bool
sil hidden @_TF8Contents9check_letFGSqSi_Sb : $@convention(thin) (Optional<Int>) -> Bool {
bb0(%0 : $Optional<Int>):
  debug_value %0 : $Optional<Int>  // let v       // id: %1
  switch_enum %0 : $Optional<Int>, case #Optional.Some!enumelt.1: bb1, default bb2 // id: %2

bb1(%3 : $Int):                                   // Preds: bb0
     : // trueの処理
bb2:
     : // falseの処理
bb3(%14 : $Bool):                                 // Preds: bb1 bb2
  return %14 : $Bool                              // id: %15

check_nilは次のように複雑になっています。

// Contents.check_nil (Swift.Optional<Swift.Int>) -> Swift.Bool
sil hidden @_TF8Contents9check_nilFGSqSi_Sb : $@convention(thin) (Optional<Int>) -> Bool {
bb0(%0 : $Optional<Int>):
  debug_value %0 : $Optional<Int>  // let v       // id: %1
  // function_ref Swift.Bool._getBuiltinLogicValue (Swift.Bool)() -> Builtin.Int1
  %2 = function_ref @_TFSb21_getBuiltinLogicValuefSbFT_Bi1_ : $@convention(method) (Bool) -> Builtin.Int1 // user: %14
  // function_ref static Swift.!= infix <A where A: Swift.Equatable> (Swift.Optional<A>, Swift.Optional<A>) -> Swift.Bool
  %3 = function_ref @_TZFSsoi2neuRq_Ss9Equatable_FTGSqq__GSqq___Sb : $@convention(thin) <τ_0_0 where τ_0_0 : Equatable> (@in Optional<τ_0_0>, @in Optional<τ_0_0>) -> Bool // user: %13
  %4 = alloc_stack $Optional<Int>                 // users: %5, %13, %17
  store %0 to %4#1 : $*Optional<Int>              // id: %5
  // function_ref Swift.Optional.init <A> (Swift.Optional<A>.Type)(nilLiteral : ()) -> Swift.Optional<A>
  %6 = function_ref @_TFSqCurfMGSqq__FT10nilLiteralT__GSqq__ : $@convention(thin) <τ_0_0> (@out Optional<τ_0_0>, @thin Optional<τ_0_0>.Type) -> () // user: %9
  %7 = metatype $@thin Optional<Int>.Type         // user: %9
  %8 = alloc_stack $Optional<Int>                 // users: %9, %10, %16
  %9 = apply %6<Int>(%8#1, %7) : $@convention(thin) <τ_0_0> (@out Optional<τ_0_0>, @thin Optional<τ_0_0>.Type) -> ()
  %10 = load %8#1 : $*Optional<Int>               // user: %12
  %11 = alloc_stack $Optional<Int>                // users: %12, %13, %15
  store %10 to %11#1 : $*Optional<Int>            // id: %12
  %13 = apply %3<Int>(%4#1, %11#1) : $@convention(thin) <τ_0_0 where τ_0_0 : Equatable> (@in Optional<τ_0_0>, @in Optional<τ_0_0>) -> Bool // user: %14
  %14 = apply %2(%13) : $@convention(method) (Bool) -> Builtin.Int1 // user: %18
  dealloc_stack %11#0 : $*@local_storage Optional<Int> // id: %15
  dealloc_stack %8#0 : $*@local_storage Optional<Int> // id: %16
  dealloc_stack %4#0 : $*@local_storage Optional<Int> // id: %17
  cond_br %14, bb1, bb2                           // id: %18

bb1:                                              // Preds: bb0
     :

関連項目

0 件のコメント:

コメントを投稿

注: コメントを投稿できるのは、このブログのメンバーだけです。