わいわいswiftc ワークショップ Vol.3 福岡 事前準備

# コレ何
https://iosdiscord.connpass.com/event/151576/ の資料です

# はじめに
このドキュメントは、わいわいswiftc ワークショップ Vol.3 福岡の事前準備のドキュメントです。
Swiftコンパイラというマンモスプロジェクトをビルドするので、これをやらないと当日ワークショップに参加しても作業できないのでご注意ください。

コンパイラのビルドにかかなり時間を要するので、寝てる間にビルドを進めるのがおすすめです。

<font color="red">5/16: Publicにしました。が課題レポジトリは消えてるので注意。</font>
<font color="red">1/14: 必要なものを更新しました。Python2系じゃないとcloneスクリプトとビルドスクリプトは動きません。</font>
<font color="red">1/20: 手順を更新しました。cmakeのバージョン3.15.1をビルドしないといけないです</font>


# 必要なもの
- Xcode 11.x　(動作確認をしているので11.3が望ましい)
- ninja (最新版に上げていると良い, Homebrewなどでインストール可能です)
- cmake 3.15.1 (**3.16 以降だと動かないです。**cmakeを個別でビルドする方法を下記に掲載してあるので、そちらを参照してください)
- 55GB程度の空きStorage
- Python 2系 
  - ※ 現在入ってるPythonが3系の場合、pyenv等のPythonバージョン管理ツールでpython2を入れることをおすすめします。


# 準備手順
## レポジトリをCloneする

Terminalを開いて、ホームディレクトリでも好きなところで`waiwai-workshop-3rd`というディレクトリを作ってください。
※ `waiwai-workshop-3rd`じゃなくてもいいです。

```shell
$ cd ~ # 例ではホームディレクトリですが、どこでも良いです
$ mkdir waiwai-workshop-3rd
```

~~次に、Swiftコンパイラをgithubから`waiwai-workshop-3rd`にクローンします。(https://github.com/freddi-kit/waiwai-swiftc-silopt-workshop~)~~   **削除しました**。

今回は、ワークショップ向けにカスタマイズしたSwiftコンパイラを利用します。
**必ずクローンしたものはswiftというディレクトリにリネーム**してください。

```shell
$ cd waiwai-workshop-3rd
$ git clone git@github.com:freddi-kit/waiwai-swiftc-silopt-workshop.git swift
$ cd swift
```

以下、`swift`ディレクトリにいる想定で書きます。

## 依存してるプロジェクトをクローンする

次に、Swiftコンパイラに依存しているプロジェクトをクローンします。swiftディレクトリにスクリプトがあるので、以下のコマンドを叩いてください。

```shell
# swift ディレクトリにて
$ utils/update-checkout --clone
```

※ 設定では各プロジェクトのクローンするブランチを意図的に変えています。
※ 前のセクションで`swift`にリネームする作業を怠っていると、ここらへんからつまづきます。

一応ブランチを確認して、`waiwai-question`になっているかを確認してください。

```shell
# swift ディレクトリにて
$ git branch
* waiwai-question
```

## cmake version 3.15.1をビルドする
cmakeが必要なのですが、cmakeのバージョンが 3.15.1でないとビルドスクリプトが正常に動かないです。
なので、cmakeを個別にビルドしてそれをビルドスクリプトに利用します。

先程の`依存してるプロジェクトをクローンする`にて、cmakeはclone済みなので、そちらに移動します。

```shell
# swift ディレクトリにて
$ cd ../cmake
```

そして下記のコマンドを叩いてください。

```shell
# cmake ディレクトリにて
$ ./bootstrap && make   
```
暫く待つと、`./bin/`以下にcmakeがビルドされています。
途中でjava関係のポップアップが出る可能性がありますが、無視して`OK`を押してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/d2311faf-aad3-9484-f848-d5fbf3b69767.png)


___※ わかる人向け: `make install` しないでください。___

ビルドが終わったら、swiftディレクトリに戻りましょう。

```shell
# cmake ディレクトリにて
$ cd ../swift # もといたswiftディレクトリに戻る
```

## コンパイラをビルドする

最後に、ビルドスクリプトを使ってビルドをします。
`--cmake` オプションで先程ビルドしたcmakeを指定します。

```shell
# swift ディレクトリにて
$ utils/build-script --debug --xcode --skip-build-benchmarks --cmake ../cmake/bin/cmake
```

ワークショップではこれで生成されたXcode Projectを使って、Swiftコンパイラのコードを書きかえていきます。


かなり時間がかかるので注意してください。おすすめは寝る前にやって朝起きたらもう準備できている状態。
※ `Ctrl+C` とかでビルドを中断すると、最悪ビルドしたコンパイラがちゃんと動かなくなる可能性があるので時間があってかつマシンパワーに余力があるときに行いましょう

### もしコンパイラのビルド失敗したら？
もしビルド等に失敗したら、

- 最新のレポジトリの状態をcloneして最初からやり直してみる
- 使っている環境とは別のpython 2系の別環境をpyenvで導入してやってみてください。
- それでもだめであれば、エラーログを書いて質問してください

ただし、下記の`docs_html`での失敗は、ドキュメントの生成が失敗してるだけでXcode Projectが正常に生成されてある可能性が高いので無視して次のステップに行ってみてください。

```shell
=== BUILD AGGREGATE TARGET docs_html OF PROJECT Swift WITH CONFIGURATION Debug ===

Check dependencies

# ~~~~~~~~~中略~~~~~~~~~

** BUILD FAILED **

The following build commands failed:
PhaseScriptExecution CMake\ Rules /パス/waiwai-workshop-3rd/build/Xcode-DebugAssert/swift-macosx-x86_64/docs/Swift.build/Debug/docs_html.build/Script-225217FDFF4641299E9F0141.sh
(1 failure)
utils/build-script: fatal error: command terminated with a non-zero exit status 65, aborting
```


## Xcode Projectを開く
では、Xcode Projectを開いていきます。Xcodeは`swift`ディレクトリから見て次の場所にあるのでopenコマンドなどを使って開きましょう。

```shell
# swift ディレクトリにて
$ open ../build/Xcode-DebugAssert/swift-macosx-x86_64/Swift.xcodeproj/
```

最初Xcode Projectを開くと、「targetが多すぎるのでschemeどうするか」という旨のポップアップが出ますが、targetは以降の作業で指定するので「Manual」っぽいことを書いてある選択肢を選んでください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/3c03d46c-f8d2-1bb5-803c-e0b50ffe33c1.png)

このポップアップはCloseで大丈夫です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/3b143c6d-6f64-4b0b-dc53-75c225affebe.png)



# Xcode Projectに`sil-opt`Schemeを追加する
では、そのSchemeを追加します。今回のワークショップでは`sil-opt`コマンドを使うので、それをビルドするSchemeを追加します。

まず、Xcode左上の`No Scheme`をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/48cf5011-afc7-e684-3e90-2c65b3c6b3bc.png)

プルダウンが出てくるので、`New Scheme`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/f60b4ada-94a5-cb26-ac02-11f4fe09caa1.png)

ここで、どのtargetでSchemeを作るかが出てきます。まず、`target`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/79ae587f-48a9-ecab-6c48-36ca5301dfbc.png)

このような大量のリストが出てくるので、`sil-opt`を探して選択してください。キーボードでなにかしら文字を入力をすると絞り込み検索ができます。図では、`sil-opt`で絞り込みをした結果です。
※ 絞り込みをしてもだいぶスクロールして探さないと見つからないので注意してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/a96aff15-b3c6-3ac6-0047-c95a7e1dcb9c.png)

元のポップアップで`OK`を押すとSchemeが追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/e406ef28-3a78-9bd0-a50f-2cfc44266da9.png)

# `sil-opt`Schemeを編集して、コマンドラインからデバッグできるようにする
次に、コマンドラインから`sil-opt`コマンドを叩いても、Xcodeで設定したBreak Pointなどが働くようにします。

さっきのスキームをクリックして、`Edit Scheme`を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/a57f1352-5b7c-aab2-a1f3-e511a9914723.png)


設定ポップアップが出るので、左の`Run`という項目を選択して、`Info`タブの`Launch`という項目にある、`Wait for executable to be launched`にチェックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/ea04b77f-e44f-9425-bc4b-5b2cf08ed83d.png)

`Close`して、設定はおわりです。ここまでお疲れさまでした。🍵

# 設定がうまく行っているかテストする
では、実際に`sil-opt`がうまく準備してあるかをテストしましょう。

では実際にSILOptimizerのコードを書き換えます。

`Command + Shift + o`でプロジェクト内検索を開き、`WaiWaiOptimizer`と入力します。すると、`WaiWaiOptimizer.cpp`が見つかるので、それを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/be950a07-b92b-2060-6002-3bd6ebe6a258.png)

以下のようなコードが出てくると思います。

```cpp:WaiWaiOptimizer.cpp
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"
#include "swift/SILOptimizer/Utils/Local.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "llvm/Support/CommandLine.h"
#include <iostream>

using namespace swift;
using namespace std;

namespace {
class WaiWaiOptimizer : public swift::SILFunctionTransform {
  /// The entry point to the transformation.
  void run() override {
  }
};
}


SILTransform *swift::createWaiWaiOptimizer() {
  return new WaiWaiOptimizer();
}
```

これはC++のコードです。今回のワークショップはこのコードを使った課題があるので、実際に事前に触ってみましょう。
`run()`関数の中身を以下のように変更してみます。`cout`はSwiftでの`print`にあたるものと思ってください。
`endl`というのが文末にありますが、これがないと改行されません。

```cpp:WaiWaiOptimizer.cppの16行目らへん
  ...
  void run() override {
      cout << "Hello, Optimizer!" << endl;
  }
  ...
```

変更後はこんなコードになりますね。

```cpp:WaiWaiOptimizer.cpp
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"
#include "swift/SILOptimizer/Utils/Local.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "llvm/Support/CommandLine.h"
#include <iostream>

using namespace swift;
using namespace std;

namespace {
class WaiWaiOptimizer : public swift::SILFunctionTransform {
  /// The entry point to the transformation.
  void run() override {
      cout << "Hello, Optimizer!" << endl;
  }
};
}


SILTransform *swift::createWaiWaiOptimizer() {
  return new WaiWaiOptimizer();
}
```

このコードを必要な部分だけ少しだけ解説します。

Swiftを書いたことがあるならチョット読めると思いますが、`WaiWaiOptimizer`というクラスは`SILFunctionTransform`を継承しています。

```cpp:WaiWaiOptimizerクラスの宣言部
class WaiWaiOptimizer : public swift::SILFunctionTransform {
```

これは、「`WaiWaiOptimizer`というOptimizerのPassがあって、これは関数を最適化(Optimize)するPassである」という意味になります。Passとはなにかについては詳しくはワークショップで話しますが、SILをOptimizeするモジュールと考えてください。

SwiftコンパイラはSILOptimizerというフェーズでコードを最適化しますが、Passと呼ばれるモジュールがそれぞれ目的の最適化を行います。

また、Passの最適化のアルゴリズムは`run`関数に書いていきます。今回は、`Hello, Optimizer!`と画面に表示するだけで何も最適化しないPassを作りました。

では、試しにこのPassを読んだら本当に`Hello, Optimizer!`と出るかを確かめてみます。
その前に、さっき追加した`cout`のところにBreak Pointをつけたら動くかも確かめてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/f146c070-fe71-06a3-50e4-9c1070c6dc64.png)

ここまで来たら、`Command + R`を押して、ビルドと実行をします。時間はそこまでかからないです。
Runをする準備ができたら、`Wainting to attach to sil-opt : sil-opt`と出るので、コマンドライン上から`sil-opt`起動してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/abd654c2-7bfb-18e9-af45-e4c3b3e76fb9.png)

その前に、`sil-opt`で最適化するためのSILコードを用意します。適当にそこまで複雑じゃないコードのSILコードを以下のコマンドを叩いて用意してください。

```shell
$ swiftc example.swift -emit-silgen -o example.sil
```

先程の`swift`ディレクトリの目線に戻ります。

ビルドされた`sil-opt`コマンドは、`../build/Xcode-DebugAssert/swift-macosx-x86_64/Debug/bin/` にあるので、そこにある`sil-opt`コマンドを以下のように叩きます。

`sil-opt`に`-wai-wai-optimizer`オプションを付けると、先程の`WaiWaiOptimizer`が呼ばれます。

```shell
# swift ディレクトリにて
$ ../build/Xcode-DebugAssert/swift-macosx-x86_64/Debug/bin/sil-opt -wai-wai-optimizer さっき生成したSILのあるディレクトリ/example.sil -o optimized-example.sil
```

下記のように`Hello, Optimizer!`がいくつか出力されて、かつBreak Pointが働いたら成功です。

```shell
# swift ディレクトリにて
$ ../build/Xcode-DebugAssert/swift-macosx-x86_64/Debug/bin/sil-opt -wai-wai-optimizer さっき生成したSILのあるディレクトリ/example.sil -o optimized-example.sil
Hello, Optimizer!
Hello, Optimizer!
Hello, Optimizer!
... # SILのコードによってHello, Optimizer!の数が違うので注意
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152231/0287c0a8-607f-5ff8-d823-c7a060ef0416.png)

成果物は、`optimized-example.sil`として出力されます。しかし、`WaiWaiOptimizer`の`run`関数には最適化するものを何も書いてないので、元の`example.sil`と比較しても特に目立った変更ははありません。


# おわり

ここまで来たら、準備は完了です。ワークショップ当日に問題をPushしますので、それをPullして解いていくことになります。

お疲れさまでした、当日は頑張ってください！皆様のご来場お待ちしております〜！


# 追記事項
ワークショップ本体のリンク載せておきます。
https://qiita.com/freddi_/items/43130e5f2a6e9d33b0ca
https://qiita.com/freddi_/items/9690f53e267855ffe4b7
https://qiita.com/freddi_/items/38df6bd64904b5ccaa73