简体   繁体   English

如何在自定义VM中实现尾部调用

[英]How to implement tail calls in a custom VM

How can I implement tail calls in a custom virtual machine? 如何在自定义虚拟机中实现尾部调用?

I know that I need to pop off the original function's local stack, then it's arguments, then push on the new arguments. 我知道我需要弹出原始函数的本地堆栈,然后是参数,然后推入新参数。 But, if I pop off the function's local stack, how am I supposed to push on the new arguments? 但是,如果我弹出该函数的本地堆栈,该如何推入新参数呢? They've just been popped off the stack. 它们刚刚从堆栈中弹出。

I take it for granted that we're discussing a traditional "stack-based" virtual machine here. 我认为我们在这里讨论传统的“基于堆栈”的虚拟机是理所当然的。

You pop off the current function's local stack preserving the still-relevant parts in non-stack "registers" (where the "relevant parts" are, clearly, the argument for the forthcoming recursive tail call), then (once all of the function's local stack and arguments are cleaned up) you push the arguments for the recursive call. 您弹出当前函数的本地堆栈, 将仍然相关的部分保留在非堆栈“寄存器”中 (其中“相关部分”显然是即将到来的递归尾调用的参数),然后(一旦函数的所有局部堆栈和参数已清理),您可以为递归调用推入参数。 Eg, suppose the function you're optimizing is something like: 例如,假设您要优化的功能类似于:

def aux(n, tot):
  if n <= 1: return tot
  return aux(n-1, tot * n)

which without optimization might produce byte-code symbolically like: 如果不进行优化,则可能会象征性地产生字节码,例如:

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

the CALL_FUN2 means "call a function with two arguments". CALL_FUN2的意思是“调用带有两个参数的函数”。 With the optimization, it could become sometime like: 通过优化,它有时会变成:

   POP_KEEP     2
   POP_DISCARD  2
   PUSH_KEPT    2
   JUMP         AUX

Of course I'm making up my symbolic bytecodes as I go along, but I hope the intent is clear: POP_DISCARD n is the normal pop that just discards the top n entries from the stack, but POP_KEEP n is a variant that keeps them "somewhere" (eg in an auxiliary stack not directly accessible to the application but only to the VM's own machinery -- storage with such a character is sometimes called "a register" when discussing VM implementation) and a matching PUSH_KEPT n which empties the "registers" back into the VM's normal stack. 当然,我会不断完善自己的符号字节码,但我希望意图很明确: POP_DISCARD n是普通的流行音乐,只会丢弃堆栈中的前n个条目,但是POP_KEEP n是保留它们的变体。某处”(例如,在应用程序不能直接访问的辅助堆栈中,而只能由VM自己的机器访问-在讨论VM实现时,有时将具有这种字符的存储称为“寄存器”)和匹配的PUSH_KEPT n清空“寄存器” ”返回虚拟机的正常堆栈。

I think you're looking at this the wrong way. 我认为您看错了这条路。 Instead of popping the old variables off the stack and then pushing the new ones, simply reassign the ones already there (carefully). 无需将旧变量从堆栈中弹出然后推入新变量,只需(小心地)重新分配已经存在的变量即可。 This is roughly the same optimization that would happen if you rewrote the code to be the equivalent iterative algorithm. 如果将代码重写为等效的迭代算法,则与执行优化时大致相同。

For this code: 对于此代码:

 int fact(int x, int total=1) {
     if (x == 1)
         return total;
     return fact(x-1, total*x);
 }

would be 将会

 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"

There's no need to pop or push anything on the stack, merely reassign. 无需弹出或压入堆栈中的任何内容,只需重新分配即可。
Clearly, this can be further optimized, by putting the exit condition second, allowing us to skip a jump, resulting in fewer operations. 显然,这可以通过将退出条件放在第二位来进一步优化,从而允许我们跳过跳转,从而减少了操作。

 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

Looking again, this "assembly" better reflects this C++, which clearly has avoided the recursion calls 再看一遍,这个“汇编”更好地反映了这个C ++,它显然避免了递归调用

int fact(int x, int total=1)
    for( ; x>1; --x)
        total*=x;
    return total;
} 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM