fukuoka.exのhisawayです.
この記事は下記の@piacerex さんのコラムのMac版になります.
- 「Elixirコンパイラ①をElixirで作る:無謀にもWindowsでLLVM→LLVMを自前ビルド」
- 「Elixirコンパイラ②をElixirで作る:LLVM IRをElixirで生成(ASTはElixirマクロ互換)→再帰下降構文解析→ネイティブコード生成」
Linux版も作成予定です.
動作環境
Mac OS: 10.14.6
Erlang/OTP: 22
Elixir: 1.9.0
LLVM
MacOS付属のXCode4.0からApple LLVMと呼ばれるものが使われています.
wikipediaのXCode(英語)によるとXCode4.0はMacOS 10.6.6(Snow Leopard)以上のバージョンで動作するようなので記事のお試しがしたいだけなら,インストールの必要はなさそうです.
追記
ただし,ErlangをMakeする際に使用するCコンパイラによっては性能(実行速度)が変わるようです.
今日知った衝撃の事実。Elixir は,Erlang を GCC でコンパイルするのか,Clang でコンパイルするのか,Apple Clang でコンパイルするのかによって,性能がかなり異なる。
元記事と全く同じにする場合は本家のClangをインストールしなくてはなりません.
幸いなことにHomebrewでClang及びGCCがインストールできるので,これに甘えましょう.
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を返すだけのプログラムをコンパイルしましょう.
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
; 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マクロ互換)→再帰下降構文解析→ネイティブコード生成」
とは言ってもほぼ同じです.
違うのはサポートする対象を表す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
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コンパイラ」の補足をしました.