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 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。