26

OCaml ランタイムで例外がどのように処理され、非常に軽量になるのか知りたいです。setjmp/longjmp を使用しますか、それとも各関数で特別な値を返し、それを伝播しますか?

longjmp はシステムに少し負担をかけるように思えますが、例外が発生した場合にのみ、各関数の戻り値をチェックしながら、関数を呼び出した後にすべての値をチェックする必要があります。多くのチェックとジャンプがあり、パフォーマンスが最悪のようです。

OCaml が C とどのようにインターフェースするか ( http://caml.inria.fr/pub/docs/manual-ocaml/manual032.html#toc142 ) を調べ、callback.h を見ると、例外は次を使用してタグ付けされているようです。オブジェクトのメモリ アラインメント ( #define Is_exception_result(v) (((v) & 3) == 2) )。これは、その実装が longjmp を使用せず、各関数呼び出しの後に各関数の結果をチェックしていることを示しているようです。あれですか?または、C 関数は既に例外をキャッチしようとしていて、それをこの形式に変換しますか?

ありがとうございました!

4

1 に答える 1

49

OCaml 例外処理

使いませんsetjmp/longjmp。が評価されるtry <expr> with <handle>と、ハンドラに関する情報を含む「トラップ」がスタックに配置されます。最上位のトラップのアドレスはレジスタ¹ に保持され、発生すると、このトラップに直接ジャンプし、一度にいくつかのスタック フレームを巻き戻します (これは、各リターン コードをチェックするよりも優れています)。トラップは、前のトラップのアドレスも格納します。このアドレスは、発生時にレジスタに復元されます。

¹: または十分なレジスターがないアーキテクチャーのグローバル

コードで確認できます。

  • バイトコードのコンパイル: 635 ~ 641 行目、2 つのバイトコードがed 式Kpushtrap/Kpoptrapを囲んでいますtry..with
  • ネイティブ コンパイルLpushtrap/Lpoptrap: 254 ~ 260 行目、式の周りの命令
  • バイトコードのバイトコード実行PUSHTRAP(トラップ/ハンドラを配置)、POPTRAP(削除、エラーでない場合)、およびRAISE(トラップにジャンプ)
  • mipsamd64でのネイティブ コードの出力 (例)

比較setjmp

Ocaml は非標準の呼び出し規則を使用し、呼び出し先保存レジスタがほとんどまたはまったくないため、これ (および末尾再帰) が効率的になります。私は (専門家ではありませんが) これが、Clongjmp/setjmpがほとんどのアーキテクチャーでそれほど効率的でない理由だと思います。たとえば、この x86_64 setjmp 実装を参照してください。これは、以前のトラップ メカニズムに加えて呼び出し先レジスタの保存とまったく同じように見えます。

これは、 C/OCaml インターフェイスで考慮されます。C コードから Caml 関数を呼び出す通常の方法は、caml_callbackOCaml ランドの例外をキャッチしません。必要に応じて、特定のものを使用する必要があります。これによりcaml_callback_exnトラップハンドラーがセットアップされ、C 呼び出し規約の呼び出し先保存レジスタが保存/復元されます。例を参照してください。レジスタを保存し、このラベルにジャンプして例外トラップをセットアップするamd64 コード。

于 2011-12-19T20:54:33.810 に答える