GCフレンドリなヒープインターフェース

ちょっと方針転換して、JITコードからでもヒープを触れるようにすることを考える。要するにexpanderやbytecode VM用のcompilerもコンパイルしたいので。

必要最低限のintrinsic

この手のインフラでGCフレンドリ性を比較的熱心に考察しているのはLLVMで、(ほぼGHC専用な気がするが)実績のあるGC intrinsicを提供している。
LLVMの提供するGC intrinsicは

  • gcroot - 指定オブジェクトと指定メタデータをオブジェクトとして宣言する
  • gcwrite - gcリファレンス(になる可能性のあるデータ領域)を書き込む
  • gcread - gcリファレンスから読み込む

の3種類しかない。なので、これらを提供すれば一応GCフレンドリなILとなることができる。
もっとも、nmoshは現状Boehm GCを採用していて、Boehm GCは保守的GC(= プログラムの扱う全てのデータのうち、ポインタである可能性のあるデータを自動的にポインタと見做してオブジェクトグラフがトラバースできるGC)なので、これらのintrinsicで何かを行う必要はない。
ただ、常識的なGCアルゴリズムは少なくともwrite barrierを必要とするので、今のうちからこれを想定しておくのは(ポータビリティ的には)良い。
多くのScheme実装はこのレベルのヒープアクセスを許可していない。その代わり、"高レベル"アクセサ群を提供している。

高レベルアクセサ

nmoshの組み込みインターフェースでは、S式やVectorをトラバースするための"cursor"オブジェクトを作成し、このcursorオブジェクトがGC参照を管理する。(Cursorの指すpairやvectorGCから見て参照されている扱いになり、cursorは明示的に破棄する必要がある。)
一般的なScheme組み込みインターフェースでは、C言語APIとしてvector?やpair?やcarやcdrを提供している。

これらの組み込みインターフェース(C API)をintrinsicとして提供すれば、原理的にはILから直接Schemeオブジェクトを取り扱うことが可能になる。
高レベルアクセサは単純に関数として呼び出す手もあるが、必要ならばGC intrinsicと構造体アクセスの組み合わせに"lower"することでインライン化出来る。
例えば、fixnumの値を取り出すコード:

(var s0 (fixnum_p obj))
(when s0 
  (var s1 (fixnum_value obj))

は、以下のようにlowerされる(下位1bitが1の時にfixnumというheap形式を採用しているとする)

(var s0 (gc-read obj 0))
(var s1 (and-i32 1 s0))
(var b0 (= 1 s1))
(when b0
  (var s1 (and-i32 -2 s0)))

GC interfaceとIL

この手のGC interfaceをILに組み込むかどうかはデザインチョイスと言える。
BoehmGCを採用している環境や、V8のようなシンプルな世代別、stop-worldコレクタを使用していれば、特にGCインターフェースとの統合は必要ない。V8は生成コードが型安定であることを期待してこれらのtrackをlazyに行っているようにみえる(参照の生成をコンパイルしたコードに含める必要がない?)。
LuaJIT IRは専用のbarrier命令がある。

普通の(動的言語の)bytecode VMは、個々のbytecode命令が暗黙にGCフレンドリになるようにVMがデザインされている。明示的なGCフレンドリ性を要求するbytecodeインタプリタは存在するのだろうか。。?