1日でSwiftコンパイラを作る!Swiftコンパイラインターンを開催しました

こんにちは、モバイル基盤部の @giginet です。

去る3月28日、Cookpad Spring 1day Internship 2019の一環として、Swiftコンパイラコースを開講しました。

最近のSwiftコンパイラ

近年、iOSエンジニアの間ではOpen Source Swiftがホットトピックとなっています。

ここ1年ほど、わいわいswiftcというSwift言語処理系に関する勉強会が盛り上がっていますし、 先日のtry!Swiftでは、参加者がSwift自体にcontributionするOpen Source Swiftワークショップが開かれました。

Swiftコンパイラに用いられているLLVMという技術は今、多くの言語処理系で利用されています。これを学ぶことで、さまざまな言語処理系に応用することができます。

このインターンは、Swiftコンパイラを例に、LLVMに触れ、コンパイラの動作を理解することを目的に構成しました。 後半のワークショップでは、実際にMinSwiftという簡易的なSwiftコンパイラをSwiftで開発してみます。

講義

まず最初の30分は講義パートです。Swiftコンパイラの構成を見ていき、LLVMの仕組みを学びます。

Swiftコンパイラ(swiftc)の構成

f:id:gigi-net:20190404183119j:plain

まず、Swiftコンパイラがどのようなフローを経て、動作するかを見ていきました。 コンパイラと一口で言っても、swiftcはパーサーや意味解析、中間言語など、様々な要素技術から構成されています。

1日でSwiftコンパイラ全てを理解することはできないので、今回はそれぞれの役割は説明するだけで留め、このインターンでは、現在、多くの言語処理系の要となっているLLVMに焦点を当てていきました。

LLVMを学ぶ

Swiftコンパイラのうち、LLVMとの橋渡しを行うIRGenに注目し、LLVMとは一体どのようなもので、どのように動作しているかについて学びました。

LLVMとは

LLVMは、コンパイラ基盤と呼ばれるもので、機械語の生成や最適化など、コンパイラに必要なものを共通化して作れるようにした仕組みです。

この仕組みを用いれば、一から作るよりは非常に少ない労力で汎用的なコンパイラが作成できます。 現に、Swiftのみならず、非常に多くのコンパイラがLLVM上で実現されており、LLVMについて学ぶことで多くの言語処理系について理解することができます。

LLVM IR

LLVM IR(LLVM Intermediate Representation)は、LLVMで使われる中間表現です。

どの言語であっても、最終的に適切なLLVM IRを生成することで、バイナリの生成や複数アーキテクチャへの対応、最適化などをLLVMの提供する仕組みに任せることができます。

LLVM IRは、ヒューマンリーダブルであるという特徴があり、例えば以下のような C のコードを例に挙げます。

int main(void) {
    return 42;
}

このコードは、以下のような LLVM IR で表現することができます。

define i32 @main() {
  ret i32 42
}

この講義では、LLVM IRの簡単な読み方や、LLVMの最適化がどのように動作するかを扱いました。

MinSwift

簡単な講義を経て、SwiftでSwiftをコンパイルするコンパイラ、MinSwift*1 を製作しながら、コンパイラの構成や、LLVMの扱いについて学びました。

このワークショップは、LLVMが公式で提供しているLLVM Tutorialを参考に構成されており、ここで実装している架空の関数型言語Kaleidoscopeと同程度の表現力を持つコンパイラをSwiftで実装しました。

MinSwiftは以下のように動作します。

  1. Swiftのコードをパース
  2. Abstract Syntax Treeに変換
  3. LLVM IRを生成
  4. ビルドして実行オブジェクトを生成
  5. SwiftやC++からリンクして呼び出す

多くはSwiftコードのパーサーをSwiftで書いたり、LLVM IRの生成部分を実装しますが、他にも実装を通して、受講者は非常に多くのことを学ぶことができます。 例えば必要となったのは以下のようなトピックです

  • Swift Package Managerを使ったコマンドラインツールの開発
  • Swiftにおけるユニットテストの実行とTDD
  • Swiftによるパーサーの実装
  • LLVMSwift を用いたlibLLVMの利用
  • LLVM IRの読み方
  • XcodeやLLDBの扱い方

ワークショップ

f:id:gigi-net:20190404183144p:plain

ワークショップは、予め用意されているユニットテストを通しながら、ドキュメントを参考に実装を進めていく形式となっていました。

f:id:gigi-net:20190404183201p:plain

テストケースが全て通過するように、1ステップずつ実装を進めていくと、最終的にSwiftコードからLLVM IRを生成し、LLVMを使ってコンパイルできるコンパイラ、MinSwiftが完成します!

最後まで実装を行うと、以下のようなコードをコンパイルすることができるようになります。少しは実用に耐えるコンパイラになったでしょうか?

func fibonacci(_ x: Double) -> Double {
    if x < 3 {
        return 1
    } else {
        return fibonacci(x - 1) + fibonacci(x - 2)
    }
}

応用課題

最後に、それぞれ好きなテーマを探して、MinSwiftの改善に取り組んでもらいました。

例えば以下のような課題です。

  • 関数定義の拡張
  • 数値リテラルの改善
  • for文の実装
  • 変数の実装
  • 文字列リテラルの実装

課題を完了させて、独自の実装まで到達できた参加者は極僅かでしたが、一方で短い時間の中、上記のような言語機能を追加できた参加者もいました。

f:id:gigi-net:20190404183220j:plain

まとめ

普段Swiftを書き慣れていても、アプリ開発と言語処理系の開発では全くノウハウが違い、戸惑った方も多かったようです。

コンパイラインターンという割には、パーサーの実装量が多くなってしまったのは反省点です。

未経験の方には多少難しかったようですが、概ね好評を頂けたようで嬉しく思っています😊

クックパッドではSwiftコンパイラや言語処理系に興味があるエンジニアを募集しています。

*1:これはRubyコミッターの @mametter が行ったRuby処理系のワークショップ、MinRubyのオマージュとなっています https://techlife.cookpad.com/entry/2018/10/16/131000

/* */ @import "/css/theme/report/report.css"; /* */ /* */ body{ background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527163350.png'); background-repeat: repeat-x; background-color:transparent; background-attachment: scroll; background-position: left top;} /* */ body{ border-top: 3px solid orange; color: #3c3c3c; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; line-height: 1.8; font-size: 16px; } a { text-decoration: underline; color: #693e1c; } a:hover { color: #80400e; text-decoration: underline; } .entry-title a{ color: rgb(176, 108, 28); cursor: auto; display: inline; font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ Pro W3', 'Hiragino Kaku Gothic Pro', Meiryo, Osaka, 'MS Pゴシック', sans-serif; font-size: 30px; font-weight: bold; height: auto; line-height: 40.5px; text-decoration: underline solid rgb(176, 108, 28); width: auto; line-height: 1.35; } .date a { color: #9b8b6c; font-size: 14px; text-decoration: none; font-weight: normal; } .urllist-title-link { font-size: 14px; } /* Recent Entries */ .recent-entries a{ color: #693e1c; } .recent-entries a:visited { color: #4d2200; text-decoration: none; } .hatena-module-recent-entries li { padding-bottom: 8px; border-bottom-width: 0px; } /*Widget*/ .hatena-module-body li { list-style-type: circle; } .hatena-module-body a{ text-decoration: none; } .hatena-module-body a:hover{ text-decoration: underline; } /* Widget name */ .hatena-module-title, .hatena-module-title a{ color: #b06c1c; margin-top: 20px; margin-bottom: 7px; } /* work frame*/ #container { width: 970px; text-align: center; margin: 0 auto; background: transparent; padding: 0 30px; } #wrapper { float: left; overflow: hidden; width: 660px; } #box2 { width: 240px; float: right; font-size: 14px; word-wrap: break-word; } /*#blog-title-inner{*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-position: left 0px;*/ /*}*/ /*.header-image-only #blog-title-inner {*/ /*background-repeat: no-repeat;*/ /*position: relative;*/ /*height: 200px;*/ /*display: none;*/ /*}*/ /*#blog-title {*/ /*margin-top: 3px;*/ /*height: 125px;*/ /*background-image: url('https://cdn-ak.f.st-hatena.com/images/fotolife/c/cookpadtech/20140527/20140527172848.png');*/ /*background-repeat: no-repeat;*/ /*background-position: left 0px;*/ /*}*/