关于尾部调用,我的一点贡献
下文摘自我翻译《Expert .NET 2.0 IL Assembler》第13章中的一节,这本书是IL的权威之作,作者就是IL开发Team的:
尾部调用
尾部调用类似于方法调用(jmp),二者都是放弃当前方法,并将参数传递到尾部调用(跳转)的方法上。然而,由于尾部调用的参数必须在计算堆栈上被显式加载(与跳转不同,尾部调用丢弃了当前方法的堆栈帧[1](stack frame),它保护了栈的框架并可以使用已经被加载的参数),与跳转不同,尾部调用不需要被调用方法的全部签名匹配调用方法的签名;而只要求返回类型是相同的或兼容的。尾部调用在大规模的递归方法实现中是非常有用的;调用方的栈帧在尾部调用的过程中会被丢弃,因此,不管递归有多么的深,栈的溢出都是没有风险的。对于功能性语言来说这是很重要的,它们会使用递归来代替循环。
尾部调用是通过在call、callvirt或calli指令前加上前缀tail.指令来识别的:
l tail. (0xFE 0x14) 将随后的调用指令标注为尾部调用。该指令不使用参数和栈。和其他前缀指令unaligned.和volatite.一样,ILAsm要求该指令与其后随的指令之间至少有一个空格符分隔。
跳转方法和尾部调用的区别在于尾部调用指令对(tail call pair)原则上是可验证的,只要该指令后面紧跟着ret指令(这取决于调用参数的可验证性)。和其他前缀指令一样,跳过前缀并直接转移到前缀指令(如call、callvirt或calli)是非法的。
[1] 译注:stack frame,即堆栈帧,是堆栈中的一块区域,它保存着一个函数的返回地址,和该函数内部使用的局部数据(Local Data),它是由函数入口处的SUB ESP、48h之类的语句来建立的。
详细内容参见http://www.zaoxue.com/article/tech-32942.htm。
此外,给出尾部调用的几个异常:
0x80131899 |
Can not pass byref to a tail call. |
0x |
Missing ret. |
0x8013189B |
Void ret type expected for tail call. |
0x |
Tail call return type not compatible. |
0x8013189D |
Stack not empty after tail call. |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2007-03-31 WPF笔记(2.4 Grid)——Layout
2007-03-31 Description of AJAX
2007-03-31 SQL大量数据操作 切身经验总结
2007-03-31 WPF笔记(2.3 StackPanel)——Layout
2007-03-31 The description of GameHall and my program experience for interview
2007-03-31 WPF笔记(2.2 DockPanel)——Layout
2007-03-31 WPF笔记(1.10 绘图)——Hello,WPF!