カスタム仮想マシンに末尾呼び出しを実装するにはどうすればよいですか?
元の関数のローカルスタックをポップオフし、次に引数を取得してから、新しい引数をプッシュする必要があることはわかっています。しかし、関数のローカルスタックをポップオフした場合、新しい引数をどのようにプッシュする必要がありますか?それらはスタックからポップされたばかりです。
カスタム仮想マシンに末尾呼び出しを実装するにはどうすればよいですか?
元の関数のローカルスタックをポップオフし、次に引数を取得してから、新しい引数をプッシュする必要があることはわかっています。しかし、関数のローカルスタックをポップオフした場合、新しい引数をどのようにプッシュする必要がありますか?それらはスタックからポップされたばかりです。
ここでは、従来の「スタックベース」の仮想マシンについて説明しているのは当然だと思います。
現在の関数のローカルスタックをポップオフして、スタック以外の「レジスタ」にまだ関連する部分を保持します(「関連する部分」は、明らかに、次の再帰的な末尾呼び出しの引数です)、次に(関数のすべてのローカルスタックと引数がクリーンアップされます)再帰呼び出しの引数をプッシュします。たとえば、最適化する関数が次のようなものであるとします。
def aux(n, tot):
if n <= 1: return tot
return aux(n-1, tot * n)
最適化しないと、次のようなバイトコードがシンボリックに生成される可能性があります。
AUX: LOAD_VAR N
LOAD_CONST 1
COMPARE
JUMPIF_GT LAB
LOAD_VAR TOT
RETURN_VAL
LAB: LOAD_VAR N
LOAD_CONST 1
SUBTRACT
LOAD_VAR TOT
LOAD_VAR N
MULTIPLY
CALL_FUN2 AUX
RETURN_VAL
CALL_FUN2は、「2つの引数を持つ関数を呼び出す」ことを意味します。最適化により、次のようになる可能性があります。
POP_KEEP 2
POP_DISCARD 2
PUSH_KEPT 2
JUMP AUX
もちろん、私はシンボリックバイトコードを作成していますが、その意図が明確であることを願っています。これは、スタックから最上位のエントリをPOP_DISCARD n
破棄する通常のポップですが、それらを「どこかに」保持するバリアントです(例:アプリケーションには直接アクセスできず、VM自体の機械にのみアクセスできる補助スタック-このような文字を含むストレージは、VMの実装について説明するときに「レジスタ」と呼ばれることもあります)、「レジスタ」を空にしてVMの通常のスタックに戻すマッチング。n
POP_KEEP n
PUSH_KEPT n
あなたはこれを間違った見方をしていると思います。古い変数をスタックからポップしてから新しい変数をプッシュする代わりに、すでにそこにある変数を(慎重に)再割り当てするだけです。これは、コードを同等の反復アルゴリズムに書き直した場合に発生する最適化とほぼ同じです。
このコードの場合:
int fact(int x, int total=1) {
if (x == 1)
return total;
return fact(x-1, total*x);
}
だろう
fact:
jmpne x, 1, fact_cont # if x!=1 jump to multiply
retrn total # return total
fact_cont: # update variables for "recursion
mul total,x,total # total=total*x
sub x,1,x # x=x-1
jmp fact #"recurse"
スタックに何かをポップしたりプッシュしたりする必要はなく、単に再割り当てするだけです。
明らかに、これは、終了条件を2番目に配置することでさらに最適化でき、ジャンプをスキップできるため、操作が少なくなります。
fact_cont: # update variables for "recursion
mul total,x,total # total=total*x
sub x,1,x # x=x-1
fact:
jmpne x, 1, fact_cont # if x!=1 jump to multiply
retrn total # return total
もう一度見てみると、この「アセンブリ」はこのC ++をよりよく反映しており、再帰呼び出しを明らかに回避しています。
int fact(int x, int total=1)
for( ; x>1; --x)
total*=x;
return total;
}