[メモ]SML#をmacOS Big Sur にインストール



久しぶりに、SML#を触ってみようと、macOS Big Sur(バージョン11.6)にインストールしようとしたら上手くいかない。

[公式サイト](https://smlsharp.github.io/ja/downloads/)によれば以下のコマンドだけでいけるはずなのだが。。。

```shell
brew tap smlsharp/smlsharp
brew install smlsharp
```

上記のコマンドを実行すると以下のメッセージを吐いて止まる。

```
SMLSHARP_HEAPSIZE=32M:2G ./minismlsharp -B src -nostdpath -filemap=filemap -O2 -c -o src/basis/main/General.o src/basis/main/General.sml
command failed at status 1: getVersion /usr/local/Cellar/llvm/12.0.1/bin/llc -version
```

１行目が実行したコマンドで、２行目がエラーメッセージと思われる。

./minismlsharpというおそらくブートストラップ用のコンパイラを実行するとエラーが出るっぽい。
試しに引数なしで ./minismlsharp を実行しても同じエラーがでる。

SML#のソースコードで該当しそうなところを見てみる。

```SML:src/compiler/libs/toolchain/main/LLVMUtils.sml
  local
    val version = ref NONE
    val defaultTarget = ref ""
    fun get () =
        let
          val command = [EXPAND (Config.LLC ()), ARG "-version"]
          val output = ShellUtils.system command
          val v = searchLine
                    (fn ["LLVM", "version", ""] => NONE
                      | ["LLVM", "version", version] => SOME version
                      | _ => NONE)
                    (#stdout output)
          val t = searchLine
                    (fn ["Default", "target:", ""] => NONE
                      | ["Default", "target:", target] => SOME target
                      | _ => NONE)
                    (#stdout output)
       in
         case (v, t) of
           (SOME ver, SOME target) =>
           (version := v;
            defaultTarget := target;
            {version = ver, target = target})
         | _ =>
           raise ShellUtils.Fail
                   {command = "getVersion " ^ ShellUtils.join command,
                    status = OS.Process.failure,
                    output = output}
       end
  in
  fun getVersion () =
      case !version of
        SOME x => x
      | NONE => #version (get ())
  fun getDefaultTarget () =
      case !defaultTarget of
        "" => #target (get ())
      | s => s
  end
```

たぶん、llcコマンドの実行結果から、LLVMのバージョンとデフォルトのターゲットを取得しようとしているのだろう。
で、うまく取得できないと例外を投げるようだ。

その後、いろいろ試した結果、LLVMのバージョンが二桁になると例外を投げてるんじゃね？　と割といい加減な理由が思い浮かんだのだが、まあ、ものは試しとやってみた。
ちなみに、エラーが出ていたときに使っていたLLVMのバージョンは１２で、変更後のバージョンは９にした。

実際にやった作業は、まずは

```shell
brew install flex
brew install bison
brew install llvm@9
```

と、LLVMのバージョン９を導入。
その途中でflexとbisonも必要そうだったのでインストールしておいた。

その次に、~/.bash_profileの最後にLLVMのバージョン９を使うように以下を追加。

```~/.bash_profile
# ...（略）

#
# LLVM@9
#
export PATH="/usr/local/opt/llvm@9/bin:$PATH"
export LDFLAGS="-L/usr/local/opt/llvm@9/lib"
export CPPFLAGS="-I/usr/local/opt/llvm@9/include"
```

あとは、変更した.bash_profileを読み込んで、configureしてmake & make installで無事にsmlsharpがインストールできた。

```shell
source ~/.bash_profile
./configure
make
make install
```

# 蛇足

本当は、minismlsharpをビルドするときに手をいれたかったのだが、驚いたことに、 ```precompiled/x86_64.ll.xz``` というファイルから圧縮されていたLLVM用アセンブリで書かれたソースコードを展開してビルドしてminismlsharpを作成していたので、さすがに無理でした。

ちなみに、エラーメッセージに使われている"getVersion"あたりのコードが以下のような感じ。


```asm
@14428=private unnamed_addr constant<{[4x i8],i32,i8*,void(...)*,void(...)*,i32}><{[4x i8]zeroinitializer,i32 -805306344,i8*null,void(...)*bitcast(i8*(i8*)*@_SMLL1t_153 to void(...)*),void(...)*bitcast(i8*(i8*,i8*)*@_SMLL1t_359 to void(...)*),i32 -2147483647}>,align 8
@_SMLDN9LLVMUtils13defaultTargetE_147=private global<{[4x i8],i32,i8*}><{[4x i8]zeroinitializer,i32 -1342177272,i8*getelementptr inbounds(i8,i8*getelementptr inbounds(<{[4x i8],i32,[1x i8]}>,<{[4x i8],i32,[1x i8]}>*@20270,i32 0,i32 0,i32 0),i32 8)}>,align 8
@14429=private unnamed_addr constant<{[4x i8],i32,[12x i8]}><{[4x i8]zeroinitializer,i32 -2147483636,[12x i8]c"getVersion \00"}>,align 8
```

こんな感じのが２５万行…じゃなくて、２５０万行ある（一瞬、数え間違えた…）。手を入れるとか普通に無理。

（最後の行付近のスクショ）

![スクリーンショット 2021-10-08 19.49.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20722/a9cf33c7-33ac-f0ee-c5f9-f166cfef9339.png)