yarv2llvmの例外処理

mandel.rbを動かすためには例外処理を実装する必要があることがわかりました。ブロックの中でbreakを使っていて、これを実行するためには2レベルの関数呼び出し(Range#eachがはさまれる)をreturnする必要があるからです。LLVMの仕様をみてみるといくつか例外処理のメカニズムが提供されていますが、今回は次のようにして例外処理を実現したいなと考えています。

  1. 例外処理の主役はinvoke/unwind命令
  2. invoke命令はcall命令に似ていますが、出口が2つあって1つは通常のret命令を実行したときのもの、もう1つはunwind命令を使ったときのもの。invoke命令はその2つの出口をブロックの形で指定できる。
  3. ブロックを伴うメソッド呼び出しにはinvoke命令を使用する。そして、unwindを使ったときの出口として例外ハンドラを指定する。通常の出口はこれまでと同じ。ただし、ブロックをメソッド実行前後で分割する必要がある。
  4. invoke命令は残念ながら出口は二つしかない。しかし、Rubyの例外は複数種類ありえる。たとえば、throwを使わない例外も:return, :break, :redoと3種類ある。
  5. そこで、invokeのunwind出口には例外をディスパッチするハンドラを指定する
  6. さらに不幸なことにunwindには戻値を指定できない。つまり、直接例外の種類をcallerに教える方法がない。そこで、callerのフレームに例外の種類を格納する変数を用意し、そこに書き込む。幸い、ブロックにはcallerのフレームに書き込むためにフレームのポインタが渡っているので、callerのフレームにcalleeから書き込むことはそれほど大変ではない。
  7. 例外ハンドラでは例外の種類の変数の値に従って所定の場所にジャンプする。ただし、この関数では処理できない例外はもう1度unwindを実行する。さらに上位の関数が拾ってくれる(はず・・・)