Swift Compilerへの入門
こんにちは。ソフトウェアエンジニアの@kitasuke です。
今回は、 Swift Compiler を学ぶにはどのような入門方法があるかを簡単にまとめました。
今回紹介する内容は 「Swift Compiler入門」 本からの抜粋なので、もう少し詳しく知りたい人はこちらも参考にしてみてください。
やり方は大きく分けて下記の通り3つの方法があります。
- ソースコードを見ずに学ぶ
- ソースコードを読んで学ぶ
- ソースコードを触って学ぶ
それぞれ簡単に説明します。
ソースコードを見ずに学ぶ
まずは最初の入門方法としてお勧めの、ソースコードを見ずに学ぶ方法です。 Swift Compiler は Swiftコミュニティによって開発されていて、下記のガイドラインの通りオープンな場所で活発に議論されています。それらの議論から多くのことを学べるので、大まかな内容を紹介します。
Swift Forums
Swift Forums では、下記の4つのカテゴリに分けられて議論が行われていて、興味のある内容を覗いて見るだけでも勉強になります。
- Announcements
Swift のリリース情報や、新しいプロジェクトの概要などの重要なアナウンスする場所 - Using Swift
Swift や周辺ツールに関する色々な質問や要望する場所 - Development
Swift に関連する開発や実装に関して議論する場所 - Swift Evolution
Swift への新しい仕様・機能を提案する場所
Swift Bugs
Swift Bugs では、バグに関する議論が行われていて、気になる挙動や現状の仕様などを確認できます。
コントリビュートしたい方向けに Starter Bug ラベルが用意されています。 比較的簡単なバグ修正の報告がまとまっているので、興味がある方はチャレンジしてみてください。
公式ドキュメント
Swiftリポジトリに公式ドキュメントが多数用意されています。
まずは Lexicon.rst を見て、 Swift Compiler でよく使われる用語の意味を知るのがよいです。 その次は、 Type Checker, Generics, SIL など代表的なトピックに触れるのをおすすめします。
ソースコードを読んで学ぶ
次に、ソースコードを読んで学ぶ方法です。 先程紹介した議論もソースコードについての知識がないと理解できない内容も多くあるので、ソースコードを読むのが一番の勉強になります。
Swift Compiler のパイプラインは、 Parse, Sema, SILGen, IRGen, LLVM モジュールです。 それぞれ簡単に説明します。
Parse
Parse モジュール は、 字句・構文解析 を行い 型情報なしの AST(Abstract Syntax Tree) を生成します。 この辺りのソースコードを読むと、Swiftの文法により詳しくなれます。
シンタックスエラーなどの診断はこのモジュールで行っています。 下記に一覧が定義されているので、どういう条件でエラーが発生するか流れを追うのもよいです。
Sema
Sema モジュール は、 意味解析 を行い 型情報付きの AST を生成します。 この辺りのソースコードを読むと、型推論についてより詳しくなれます。
型解決に関するエラーなどの診断はこのモジュールで行っています。 下記に一覧が定義されているので、どういう条件でエラーが発生するか流れを追うのもよいです。
SILGen
SILGen モジュール は、最適化を行い SIL(Swift Intermediate Language) を生成します。 この辺りのソースコードを読むと、Swiftに特化した最適化により詳しくなれます。
下記に全ての最適化パスが定義されているので、興味のあるパスを一つずつ見ていくのがよいです。
IRGen
IRGen モジュール は、 LLVM への変換を行うために LLVM IR を生成します。 この辺りのソースコードを読むと、LLVM 用の IR(Intermediate Representation) についてより詳しくなれます。
言語特有の中間表現である SIL から それらの概念を除いた一般的な中間表現の LLVM IR にどう変換するかの過程を知れます。
LLVM
LLVM モジュール は、プリミティブな最適化を行いターゲットアーキテクチャ向けに オブジェクトファイル を生成します。 この辺りのソースコードを読むと、 LLVM についてより詳しくなれます。
Swift Compiler 向けにいくつか LLVM 自体に変更を加えている箇所もありますが、どちらかというと LLVM の勉強に近い印象です。
以上がざっくりとした各モジュールの説明です。 興味を持ったモジュールから手を付けるのが良いと思います。
ソースコードを触って学ぶ
最後に、ソースコードを触って学ぶ方法です。 ソースコードを読んでもどのような挙動になるか分からないことも多いので、デバッグしながら理解するのが良いです。
例えばバグ修正をしたい場合に、 Swift Compiler をデバッグしたり、テストコードを追加したりします。 それらのやり方を簡単に説明します。
環境構築
まずはSwift Compiler のビルド方法の説明です。
依存ツールのインストールやソースコードのクローンなどの事前準備は、リポジトリの README に従ってください。
ビルド用に使うスクリプトは utils/build-script です。 これに用途に合ったオプションを付けて実行します。
例えば、 Xcode でデバッグするためにビルドする場合は下記のコマンドを使います。 Swift Compilerだけ Debug ビルドにするため --debug-swift
を付け、それ以外はデバッグ情報付きの Release ビルドにするため -r(--release-debuginfo)
を付け、そのビルド結果から Xcodeプロジェクトを生成するため -x(--xcode)
を付けます。
$ utils/build-script -r --debug-swift -x
また、テストが成功するか試したいときは下記のコマンドを使います。 ユニットテストを実行するために -r
を付けます。
$ utils/build-script -rt
その他にも色々なオプションがあるので、気になる方はヘルプを参照してください。
デバッグ
Xcode の LLDB を使う方法を説明します。 Debug ビルドの成果物なら LLDB が使えるので、試しにエントリポイント等にブレークポイントをセットしてみてください。 大まかな処理の流れや、どのような値を持っているか確認するのに非常に便利です。
特定のコマンドを実行したい場合は、スキーム管理画面の Arguments Passed on Launch で引数を渡します。例えば Xcode で swiftc -emit-sil path/to/file.swift
相当を実行したいときは、 下記のようにセットします。
- -frontend
- -emit-sil
- path/to/file.swift
また、ターミナルで実行したプロセスをXcodeにアタッチする方法もあります。そちらのほうが色々なコマンドを試したいときに、何回も引数をセットせずに済むので楽です。
テスト
テストコードを読むだけでも勉強になります。 また何か修正をした際はテストケースを追加するよう推奨されています。 その仕組みを大まかに説明します。
Swift Compiler は主に lit(LLVM Integrated Tester) をテストに使用します。名前から分かるとおり、 LLVM で使われているテスト用ツールです。 lit には Substitutions という仕組みがあります。それと FileCheck というツールを組み合わせてテストコードを書くのが一般的です。
例えば、コンパイルした出力結果の内容をテストしたい場合は、下記のようなテストコードになります。
// RUN: %target-run-simple-swift | %FileCheck %s
print("foo")
print("bar")
CHECK: foo
CHECK-NEXT: bar
RUN: %target-run-simple-swift
で Swiftコードをビルドして、その結果を | %FileCheck %s
のパイプで FileCheck に渡します。 次に print("foo")
の実行結果となる foo
を CHECK: foo
コマンドで確認しています。 その後に print("bar")
の実行結果となる bar
を CHECK-NEXT: bar
コマンドで確認しています。
見慣れないコマンドですが、慣れればある程度意味がわかるようになります。
まとめ
Swift Compiler のことは議論に参加しても学べますし、コントリビューションをしても学べます。 何を達成したいかによって方法は変わりますが、何をすれば分からない場合は紹介した順番どおりに進めるのが良いです。皆で楽しく勉強してSwiftをより良くしていきましょう。