軽量 let その3 - Scheme VM を書く

昨日動かなかったコードは動くようになった。
次は let1 の対応を let に展開することだ。理論上はコンパイラを直せば動く事になっているけどそうはいかないのだろうな。念のため新しい版にしておくか。

let 対応

  • find-free, find-sets の let 対応
  • let の expand をやめる
    • ただし named let は展開が必要
  • let の compile
  • letrec が動かない
    • 引数の積み方が逆だった

list->string が動かない

(let1 a 3
  (let1 b (lambda () (print a))
    (b)))

のように入れ子になっているときに a の参照は可能だが locals には (a) しかつまれていないのでおかしくなる。
b の参照を不可能にしつつやるならば (a) () とするか。

根本的なバグ発見

  • let ([a (lambda ...)]) の lambda が free variable を参照すると今のモデルでは死んでしまう
    • この a を他の手続きに渡されて、全然違う文脈で呼ばれると REFER_LOCAL(1, 0) とか謎の参照をしてしまう
  • つまり let は 2種類に分ける必要がありそう
    • lambda 式で手続きを作るもの => closure を作る
    • 上記以外 => 通常の local env let

手順

  • let/let1 を let-proc, let-val に変換する
  • let-proc は ((lambda (a) ...) val) に変換する
  • let-val は現在の let1 に変換する

まてよ、let-proc, let-val のどちらに変換すべきかの根拠がむずかしいな。右辺が free variables を参照する手続きを返すときなのだけど、コンパイル時に決めるのは無理なのじゃないかな。
(let ([a (+ b 1)] みたいなのは let-val になってほしいが。。かといって、free variable を右辺に含んでいるだけでだめとかとなるとあまりメリットがないな。

つまり現段階では軽量 let 作戦は失敗だ。考慮すべき事象があったのだけど漏れていた。未熟なり。

仕切り直し

大変残念だがどこまで巻き戻すかと次の作業をまとめないといけない。くじけそうだががんばろう。
vm/compiler 的には Rev-34に戻せば良い

  • 最適化のしやすさのため VM を書き直した(命令列をベクタに)
  • beta-reduction や inline の最適化のお試しコードを書いた
  • inline 展開には 高速な let が必要ということが分かる
  • 軽量 let を考える

という流れだったのだな。

考える方向性としては

  • VMC++ で書く方向へ逃げる
  • 最適化を深追いして軽量 let を必要としない inline 展開をあみだす
  • 最適化を beta-reduction だけですませる

などがあるが。
そもそもの書き直しの動機が高速化なので、もう少し深追いすべきだろう。


明日の自分がやるべきことは

  • 軽量 let の実装をもう少し考える
    • closure の作成がとても軽くなるとか
  • 関係ないけど lambda lifting とか