关于尾部调用,我的一点贡献

下文摘自我翻译《Expert .NET 2.0 IL Assembler》第13章中的一节,这本书是IL的权威之作,作者就是IL开发Team的:
尾部调用

尾部调用类似于方法调用(jmp),二者都是放弃当前方法,并将参数传递到尾部调用(跳转)的方法上。然而,由于尾部调用的参数必须在计算堆栈上被显式加载(与跳转不同,尾部调用丢弃了当前方法的堆栈帧[1]stack frame),它保护了栈的框架并可以使用已经被加载的参数),与跳转不同,尾部调用不需要被调用方法的全部签名匹配调用方法的签名;而只要求返回类型是相同的或兼容的。尾部调用在大规模的递归方法实现中是非常有用的;调用方的栈帧在尾部调用的过程中会被丢弃,因此,不管递归有多么的深,栈的溢出都是没有风险的。对于功能性语言来说这是很重要的,它们会使用递归来代替循环。

尾部调用是通过在callcallvirtcalli指令前加上前缀tail.指令来识别的:

l         tail. (0xFE 0x14) 将随后的调用指令标注为尾部调用。该指令不使用参数和栈。和其他前缀指令unaligned.volatite.一样,ILAsm要求该指令与其后随的指令之间至少有一个空格符分隔。

跳转方法和尾部调用的区别在于尾部调用指令对(tail call pair)原则上是可验证的,只要该指令后面紧跟着ret指令(这取决于调用参数的可验证性)。和其他前缀指令一样,跳过前缀并直接转移到前缀指令(如callcallvirtcalli)是非法的。



[1] 译注:stack frame,即堆栈帧,是堆栈中的一块区域,它保存着一个函数的返回地址,和该函数内部使用的局部数据(Local Data),它是由函数入口处的SUB ESP48h之类的语句来建立的。

详细内容参见http://www.zaoxue.com/article/tech-32942.htm




此外,给出尾部调用的几个异常:

0x80131899

Can not pass byref to a tail call.

0x8013189A

Missing ret.

0x8013189B

Void ret type expected for tail call.

0x8013189C

Tail call return type not compatible.

0x8013189D

Stack not empty after tail call.

原文地址:https://www.cnblogs.com/Jax/p/1426653.html