LoginSignup
6
3

More than 5 years have passed since last update.

Swift SILを出力して読んでみる

Last updated at Posted at 2018-07-27

はじめに

ざっくり各用語の解説

抽象構文木(AST)とは

構文木から不要な情報を取り除いて、関係ある情報のみを残したものです。

構文木とは

ソースコードに解析処理を行いできたもので、解析処理は文法をチェックしながら、演算子や数値等の文法要素を評価の順番に並べたリストで、このリストが自然にツリー構造になることから構文木と言われています。

LLVM IRとは

LLVM用の中間表現です。
SILからLLVM IRという中間表現に変換し、LLVMを使ってオブジェクトコードを生成します。

LLVMとは

コンパイル基盤というコンパイルのためのツールなどが中に入っています。
※Low Level Virtual Machineという略称ではないです。

SILとは

Swiftの中間言語で、SIL(swift intermediate language)と呼ばれています。

ざっくり言うとこんな感じにコンパイルされていき、
swift_compiller.png
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を出力して読んで見る

今回はこんな感じの単純なコードを作ってみました。

hello.swift
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

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3