MacOSで「ElixirでElixirコンパイラを作る」の補足

[fukuoka.ex](https://fukuokaex.connpass.com/)のhisawayです．
この記事は下記の@piacerex さんのコラムのMac版になります．

- 「[Elixirコンパイラ①をElixirで作る：無謀にもWindowsでLLVM→LLVMを自前ビルド](https://qiita.com/piacerex/items/1b277177ca1a612cf6de)」
- 「[Elixirコンパイラ②をElixirで作る：LLVM IRをElixirで生成（ASTはElixirマクロ互換）→再帰下降構文解析→ネイティブコード生成](https://qiita.com/piacerex/items/ee2dae6015414171d10d)」

Linux版も作成予定です．


---
### 動作環境
Mac OS: 10.14.6
Erlang/OTP: 22
Elixir: 1.9.0

# LLVM
MacOS付属のXCode4．0からApple LLVMと呼ばれるものが使われています．

[wikipediaのXCode(英語)](https://en.wikipedia.org/wiki/Xcode#Version_comparison_table)によるとXCode4.0はMacOS 10.6.6（Snow　Leopard）以上のバージョンで動作するようなので記事のお試しがしたいだけなら，インストールの必要はなさそうです．

### 追記
ただし，ErlangをMakeする際に使用するCコンパイラによっては性能（実行速度）が変わるようです．
 
> 今日知った衝撃の事実。Elixir は，Erlang を GCC でコンパイルするのか，Clang でコンパイルするのか，Apple Clang でコンパイルするのかによって，性能がかなり異なる。

https://twitter.com/zacky1972/status/1192259616236691456

元記事と全く同じにする場合は本家のClangをインストールしなくてはなりません．

幸いなことにHomebrewでClang及びGCCがインストールできるので，これに甘えましょう．

```bash
brew install clang

```

## 入っているか確認

```
cc --version
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
```

ちゃんとコマンドラインツールに入ってますね．
ここで出てきた`Target`はあとで使います．

## LLVM/ClangでC++コードをコンパイル

シンプルにするために0を返すだけのプログラムをコンパイルしましょう．

```C++:ret.cpp
int main(){
  return 0;
}
```

```
> clang++ ret.cpp -o ret
> ret
> echo $status
0
```

## LLVM IRの生成
`std::cout << "Hello" << endl;`とかにすると1000行ちょいのファイルが出来上がるので，大人しく0を返すだけのプログラムを使います．

```
> clang++ hello.cpp -S -emit-llvm -O0
> llc ret.ll
> clang++ ret.s -o ret
> ./ret
> echo $status
```

```llvm:ret.ll
; ModuleID = 'ret.cpp'
source_filename = "ret.cpp"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.14.0"

; Function Attrs: noinline norecurse nounwind optnone ssp uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 0, i32* %1, align 4 ; 0
  ret i32 0
}

attributes #0 = { noinline norecurse nounwind optnone ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{!"clang version 8.0.0 (tags/RELEASE_800/final)"}
```

---

ここからは②の内容になります．
「[Elixirコンパイラ②をElixirで作る：LLVM IRをElixirで生成（ASTはElixirマクロ互換）→再帰下降構文解析→ネイティブコード生成](https://qiita.com/piacerex/items/ee2dae6015414171d10d)」

とは言ってもほぼ同じです．

違うのはサポートする対象を表す`target triple`だけです．
`cc --version`からコピペしてくれば動きます．
具体的には，関数`emit_prefix`が変わります．

```
> cc --version
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
```

```elixir
def emit_prefix() do
        # 下記の設定は手元PCでは必須では無い模様（お手元でうまく動かないときは文字列中に含めてください）
        # source_filename = "add.cpp"
        # target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"

        """
        target triple = "x86_64-apple-darwin18.6.0"
        """ |> IO.puts 
    end
```

# まとめ
簡単ですが，MacOSで「ElixirでElixirコンパイラ」の補足をしました．