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に再変換したものです。
内部的にはlazy
はOptional
を利用して実装しています。
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 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。