#はじめに
ざっくり各用語の解説
####抽象構文木(AST)とは
構文木から不要な情報を取り除いて、関係ある情報のみを残したものです。
####構文木とは
ソースコードに解析処理を行いできたもので、解析処理は文法をチェックしながら、演算子や数値等の文法要素を評価の順番に並べたリストで、このリストが自然にツリー構造になることから構文木と言われています。
####LLVM IRとは
LLVM用の中間表現です。
SILからLLVM IRという中間表現に変換し、LLVMを使ってオブジェクトコードを生成します。
####LLVMとは
コンパイル基盤というコンパイルのためのツールなどが中に入っています。
※Low Level Virtual Machineという略称ではないです。
SILとは
Swiftの中間言語で、SIL(swift intermediate language)と呼ばれています。
ざっくり言うとこんな感じにコンパイルされていき、
SwiftからASTに変換され次にSILに変換されています。
このSILを使わずとも、swiftのASTをLLVMに渡して最適化を行うことは出来るらしいです。
ただ、swiftの高レベルの意味情報の表現を、低レベルな表現しかできないLLVMにそのまま渡しても、うまく最適化を行うことができないのでSILに変換する必要があります。
今回はこのSILについて触れます。
コンパイルの段階にはFrontendとBackendという名称で分かれており、Frontendがソースコードからsilまで、backendがsilからオブジェクトコードまでを示しています。
#SILの工程
SILは段階的に変換されており、先ずASTからraw SILを作成し、診断パス(リターン分析や算術オーバーフローが起きないか等)が施されてcanonical SILに変換されます。
SILの種類 | 説明 | 生成コマンド |
---|---|---|
raw SIL | ASTから変換された未加工のSILです。 | swiftc -emit-silgen hoge.swift |
canonical SIL | raw silが診断パス(おかしな箇所がないか)を通ると、このsilが生成されます。最適化が施されており、raw SILよりもコードが少なくなっていたりします。 | swiftc -emit-sil hoge.swift |
canonical SILは以下の最適化が施されます。
- インライン展開による最適化
- 参照カウントの最適化
- メモリプロモーションの最適化
- 配列や文字列などの最適化
- 冗長的なコードの除去
canonical SILを出力して読んで見る
今回はこんな感じの単純なコードを作ってみました。
func getOne() -> Int {
let number: Int
number = 1
return number
}
では、以下のコマンドを打って canonical SIL
を見てみましょう。
swiftc -emit-sil hello.swift
すると getOne
関数がこんな感じに変換されています。
// getOne()
sil hidden @_T05hello6getOneSiyF : $@convention(thin) () -> Int {
bb0:
%0 = alloc_stack $Int, let, name "number"
%1 = integer_literal $Builtin.Int64, 1
%2 = struct $Int (%1 : $Builtin.Int64)
store %2 to %0 : $*Int
dealloc_stack %0 : $*Int
return %2 : $Int
} // end sil function '_T05hello6getOneSiyF'
1つずつ読んで見ましょう。
sil hidden @_T05hello6getOneSiyF : $@convention(thin) () -> Int {
ソースコードの定義した
getOne
関数です。
関数名もマングル化されています
マングル化・・・同じ名前の関数を複数定義出来るようにシンボル名にクラス名などを付与しています。
%0 = alloc_stack $Int, let, name "number"
ここは、型の値を格納するためメモリをスタックに割り当ててます。
割り当てられたメモリのアドレスが%0
に格納されています。
スタック・・・データを一時的に貯めるためのデータ構造
%1 = integer_literal $Builtin.Int64, 1
整数リテラル値を作成します。
リテラル・・・ソースコードにベタ書きした(この場合1)した数値とか文字のこと
%2 = struct $Int (%1 : $Builtin.Int64)
読み込み可能な構造体を作成しています。
store %2 to %0 : $*Int
%0
にあるメモリアドレスを元に、%2
の値をメモリに格納します。
格納する値は読み込み可能な型でないといけません。
dealloc_stack %0 : $*Int
%0
にあるメモリアドレスを元に、割り当てられたメモリを解放します。
return %2 : $Int
現在の関数を終了し、呼び出し元の関数に返り値と共に制御を返します。
以上です。
githubにSILの各コードについて説明が載っているので、是非読んで見てください。
##参考図書
SIL.rst
Swift Internal Introduces
LLVM