この記事は Haskell Advent Calendar 2021 の21日目の記事です。
2020年に発表されたApple Silicon (Arm) Macは、2021年に新チップ “M1 Pro/Max” が、2022年には新チップ “M1 Ultra” が登場し、ますます勢いを増しています。Mac使いの皆さんはもう手にされましたか?
新しいアーキテクチャーにはハードウェアだけではなくソフトウェアの対応も重要です。この記事では、2022年3月時点のApple Silicon MacへのHaskellエコシステムの対応状況をまとめます。
この記事では、なるべくRosetta 2を使わず、Armネイティブに動作する環境を構築することを目指します。
インストールにどれを使うか
UnixでHaskell環境を構築する場合、
- パッケージマネージャーを使う
- GHCupを使う
- Stackを使う
の3つの選択肢があるかと思います。
Mac向けのパッケージマネージャーと言えばHomebrewやMacPortsです。HomebrewはArmネイティブなGHCのインストールに対応しています。一方、MacPortsでGHCをインストールするとIntel版のGHCがインストールされるようです(当該Portfile)。
GHCupはArmネイティブなGHC等のインストールに対応しています。現時点で最もおすすめな方法です。
後述しますが、stackの公式のインストール方法を使うとIntel版のstackがインストールされるので、要注意です。
GHC
Macに限らない最近のGHCの動向については以前記事を書きました:
ここではArmあるいはMac特有のGHCの事情について書いていきます。
GHC 8.10系
GHC 8.10系は8.10.5以降でApple Siliconに対応しています。
最新のGHC 8.10.7は2021年8月27日にリリースされました。この記事の初出の時点(2021年12月)ではこれが一番安定した選択肢でしたが、その後9.0.2や9.2.2がリリースされたので、現時点(2022年3月)でどれが一番安定しているかは筆者からは何とも言えません。
実行ファイルの生成にはLLVMの opt
コマンドと llc
コマンドが必要になります。これについてはGHCupまたはstackの項目で説明します。
8.10.7の既知の問題:
- GHCiでCtrl-A等のキーボードショートカットを使うと表示がバグります。起動時の環境変数に
TERM=dumb
を指定すると回避できます。関連issue:- Unreasonable line-editor behaviour (#20022) · Issues · Glasgow Haskell Compiler / GHC · GitLab
- Bump terminfo to fix varargs (#20307) · Issues · Glasgow Haskell Compiler / GHC · GitLab
- GHCi: buggy terminal behaviour on Apple Silicon (#20722) · Issues · Glasgow Haskell Compiler / GHC · GitLab
-
Bump terminfo submodule to 0.4.1.5 (!6489) · Merge requests · Glasgow Haskell Compiler / GHC · GitLab
- backport needed:8.10 タグがついているのでもしも 8.10.8 が出る場合は修正されるはずです。
- Intel版のstackからArm版のGHC 8.10.7を起動すると、アセンブリーに関するエラー(
error: invalid instruction mnemonic
とかerror: unexpected token at start of statement
)が出る場合があります。Arm版のstackを使うか、より新しいGHCを使いましょう。
GHC 9.0系
GHC 9.0系は9.0.2以降でApple Siliconに対応しています。
最新のGHC 9.0.2は2021年12月25日にリリースされました。
実行ファイルの生成にはLLVMの opt
コマンドと llc
コマンドが必要になります。これについてはGHCupまたはstackの項目で説明します。
9.0.2の既知の問題:
- FFIを使うプログラムをコンパイルすると
'ffitarget_arm64.h' file not found
と言われる。- cabalに
--ghc-option="`pkg-config --cflags libffi`"
を指定するか、stackに--extra-include-dirs="`xcrun --show-sdk-path`/usr/include/ffi"
を指定するか、環境変数でC_INCLUDE_PATH="`xcrun --show-sdk-path`/usr/include/ffi"
を指定すると回避できます。 - GHC 9.2.1 on an m1 Mac error:
ffitarget_arm64.h
file not found (#20592) · Issues · Glasgow Haskell Compiler / GHC · GitLab
- cabalに
GHC 9.2系
GHC 9.2系は最初からApple Siliconに対応しています。また、AArch64 NCGが実装されているためLLVMがなくても実行ファイルを生成できます(GHCのバックエンドについてはこの記事を参照)。
ただ、9.2.1には深刻なバグがあるため、必ず9.2.2(2022年3月5日リリース)以降を使うようにしてください。
GHCup
公式ページの手順
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
に従うとArmネイティブなGHCupがインストールされます。そこでインストールされるツール類もArmネイティブなものになります。
もしもRosetta 2でx86_64なGHCupを導入したい場合は、
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | arch -x86_64 sh
という風に実行すると良いでしょう(arch -x86_64
に注意)。
GHCupをすでにインストール済みで、自分がどちらを使っているか確認したい場合は、
file ~/.ghcup/bin/ghcup
を実行すると良いでしょう。Armネイティブな場合は Mach-O 64-bit executable arm64
という風に表示されるはずです。もしもIntel版のGHCupがインストール済みで、Arm版のGHCupを入れ直したい場合は ~/.ghcup
以下を全削除するか ghcup nuke
コマンドを実行してから、改めてインストールしましょう。
LLVMのセットアップ
GHC 8.10系とGHC 9.0系でArmネイティブな実行ファイルを出力するにはLLVMの opt
コマンドと llc
コマンドが必要です。HomebrewやMacPortsでLLVM 12を入れましょう。
opt
コマンドと llc
コマンドにPATHが通っていれば良いのですが、HomebrewやMacPortsのデフォルトではPATHが通っていなかったり、コマンド名にサフィックスがついたりしています。その場合、GHCの実行時に -pgmlo
/ -pgmlc
コマンドでコマンド名を教えてあげるというのが一つの方法ですが、毎回それをするのは面倒なのでセットアップ時に環境変数で指定するのが良いでしょう。
Homebrewの場合は brew install llvm@12
して
# GHC 8.10.7を入れる場合
OPT=/opt/homebrew/opt/llvm@12/bin/opt LLC=/opt/homebrew/opt/llvm@12/bin/llc ghcup install ghc 8.10.7 --force
# GHC 9.0.2を入れる場合
OPT=/opt/homebrew/opt/llvm@12/bin/opt LLC=/opt/homebrew/opt/llvm@12/bin/llc ghcup install ghc 9.0.2 --force
という感じで、MacPortsの場合は sudo port install llvm-12
して
# GHC 8.10.7を入れる場合
OPT=/opt/local/bin/opt-mp-12 LLC=/opt/local/bin/llc-mp-12 ghcup install ghc 8.10.7 --force
# GHC 9.0.2を入れる場合
OPT=/opt/local/bin/opt-mp-12 LLC=/opt/local/bin/llc-mp-12 ghcup install ghc 9.0.2 --force
という感じで ghcup
を実行します。--force
オプションにより、すでに当該バージョンのGHCがインストール済みであっても新しい設定で再インストールを行うことができます。
再インストールするとデフォルトのGHCの設定がリセットされるので、適宜
ghcup set ghc 8.10.7 # または ghcup set ghc 9.0.2
を実行しましょう。
cabalコマンド (cabal-install)
GHCupで3.4.0.0以降のcabalコマンドを導入できます。
haskell-language-server
GHCupを使うことで1.4.0以降のhaskell-language-serverコマンドを導入できます。
なお、現時点ではVisual Studio CodeのHaskell拡張でHLSを自動インストールさせるとIntel版のHLSが入るようです。
関連Issue:
Stack
Stack 2.7.5の時点ではMac向けの公式バイナリーはIntel版しかありません。公式のインストール方法
curl -sSL https://get.haskellstack.org/ | sh
ではIntel版のstackが入ります。
一方、ArmネイティブなGHCupやHomebrewを使うとArm版のstackをインストールすることができます。
なお、Stack本体のアーキテクチャーと関係なく、 --arch x86_64
, --arch aarch64
のオプションで使用するGHCのアーキテクチャーを選択することができます。とはいえ、GHC 8.10.7の既知の問題のところに書いた理由で、Armネイティブなstackを使うのがおすすめです。
GHCupでstackをインストールした場合は、 stack upgrade
を使ってはいけません。GHCupを使ってstackのバージョンを管理してください。
LLVMのセットアップ
GHC 8.10系とGHC 9.0系でArmネイティブな実行ファイルを出力するにはLLVMの opt
コマンドと llc
コマンドが必要です。HomebrewやMacPortsでLLVM 12を入れましょう。
opt
コマンドと llc
コマンドにPATHが通っていれば良いのですが、HomebrewやMacPortsのデフォルトではPATHが通っていなかったり、コマンド名にサフィックスがついたりしています。その場合、GHCの実行時に -pgmlo
/ -pgmlc
コマンドでコマンド名を教えてあげるというのが一つの方法ですが、毎回それをするのは面倒なのでセットアップ時に環境変数で指定するのが良いでしょう。
Homebrewの場合は brew install llvm@12
して
# GHC 8.10.7を入れる場合
OPT=/opt/homebrew/opt/llvm@12/bin/opt LLC=/opt/homebrew/opt/llvm@12/bin/llc stack setup 8.10.7 --arch aarch64 --reinstall
# GHC 9.0.2を入れる場合
OPT=/opt/homebrew/opt/llvm@12/bin/opt LLC=/opt/homebrew/opt/llvm@12/bin/llc stack setup 9.0.2 --arch aarch64 --reinstall
という感じで、MacPortsの場合は sudo port install llvm-12
して
# GHC 8.10.7を入れる場合
OPT=/opt/local/bin/opt-mp-12 LLC=/opt/local/bin/llc-mp-12 stack setup 8.10.7 --arch aarch64 --reinstall
# GHC 9.0.2を入れる場合
OPT=/opt/local/bin/opt-mp-12 LLC=/opt/local/bin/llc-mp-12 stack setup 9.0.2 --arch aarch64 --reinstall
という感じで stack setup
を実行します。--reinstall
オプションにより、すでに当該バージョンのGHCがインストール済みであっても新しい設定で再インストールを行うことができます。