1

バインドされた関数パラメーターを多用する関数型プログラミング言語の設計についてのアイデアがあります。コンパイラ実装の一部として、x86アセンブリでバインドされた関数パラメータを表現しようとしています。

var add  = function(x,y) { return x + y; };
var add2 = add.bind({}, 2);
console.log( add2(3) );      // prints 5

相互運用性の理由から、ベア関数ポインターを作成したいので、最初の概念は、ヒープに実行可能メモリを割り当て、(a)追加のパラメーターをプッシュして(b)ターゲット関数を呼び出すスタブにコピーすることです。これは標準ライブラリの一部であり、x86アセンブリプログラムの残りの部分から使用できるネイティブ関数ポインタを返します。

このアプローチで問題が発生したと思います。スタブがcallターゲット関数に到達するために使用する場合、スタックにはリターンアドレスが含まれ、関数の引数として解釈されることになります。また、スタブがjmpターゲット関数に到達するために使用する場合、呼び出し元も呼び出し先も、関数が戻ったときにスタックをクリーンアップする方法を正確に知りません。

これはどのように解決できますか?レジスターはこの動作のフラグとして永続的に予約できると思いますが、それはほとんどエレガントではありません。

関数型言語のネイティブコンパイラでbind()はどのように実装されますか?

4

2 に答える 2

1

さらに考えてみると、callee-cleanup 規則を使用し、すべてのリターン アドレスを手動で管理することで、これを行うことができると思います。これは stdcall に似ていますが、call/ret を使用できないため (?)、同一ではありません。

擬似コード:

main:
   ; create stub, copy in 0x02 and &add, the resulting function pointer goes in add2
   local add2 = _create_trampoline

   ; make the call
   push [return address]
   push 0x03 ;arg1
   jmp  add2

; the resulting stub, held on the heap somewhere
add2:
   push 0x02 ;bound argument
   jmp  add

; var add(x,y)
add:
   local x = pop
   local y = pop

   eax = x + y;       

   jmp pop

このようにしaddて、スタックがレイアウトされていることがわかり、y x [ptr]実行が正しく返されます。

これでcall/retを失うのは少し劇的に思えます.関数のスタックフレームaddはかなり貧弱なので、より良い解決策を期待して少なくともさらに24時間は質問を開いたままにします.


編集:さらに考えてみると、バインドされたトランポリンで戻りアドレスを運ぶだけで、cdecl、caller-cleanup、call/ret、およびすべてを保持できます(1つのレジスタを上書きするか、スタックに移動して戻すだけです)。

擬似コード:

main:
   ; create stub, copy in 0x02 and &add, the resulting function pointer goes in add2
   local add2 = _magic(0x02, &add);

   ; make the call
   push 0x03;
   call add2;

add2:
   ebx = pop;     ;the return address goes in a temporary
   push 0x02;
   push ebx;
   jmp add

; var add(x,y)
add:
   push ebp;
   mov ebp, esp;

   ; local variables are [ebp+8] and [ebp+12]
   perform calculation into eax

   leave
   ret

その結果、バインドされた関数パラメーターをヒープ上の実行可能オブジェクトとして実装し、cdecl 呼び出し規則を維持するための非常に簡潔な手法が得られます。このアプローチを実装する際に問題が生じることは間違いありませんが、実行可能であり、それほど非効率的ではないことを期待しています。

于 2012-06-30T07:25:10.850 に答える
0

事前に渡されたパラメータをメモリに保存できません。次に、「add2」が表示されたら、メモリからパラメータを収集し、それらをスタックにプッシュし、他のパラメータを(必要に応じて)スタックにプッシュしてから、通常のように関数呼び出しを行いますか?

私はほとんど大声で考えていますが、これが答えかどうかはわかりませんが、私にはうまくいくようです。

于 2012-06-30T06:00:40.823 に答える