为什么尾调用优化需要操作码?

时间:2014-11-20 15:11:03

标签: c# .net theory cil tail-call-optimization

所以I've read many times before技术上.NET 支持尾调用优化(TCO),因为它有操作码,只有C#不生成它。

我不确定为什么TCO需要操作码或它会做什么。据我所知,能够进行TCO的要求是递归调用的结果不与当前函数范围中的任何变量组合。如果你没有那个,那么我看不到操作码如何阻止你必须保持堆栈框架打开。如果你有这个,那么编译器总是不能轻易地将它编译成迭代的东西吗?

那么操作码有什么意义呢?显然有些东西我不见了。在完全可以使用TCO的情况下,不能总是在编译器级别处理,而不是在操作码级别处理?什么是不可能的例子?

2 个答案:

答案 0 :(得分:8)

按照您提供的链接,这是我认为的部分,非常密切地回答您的问题。


Source

  

CLR和尾部调用

     

当您处理由CLR管理的语言时,有两种编译器正在运行。编译器从您的语言源代码到IL(C#开发人员将其称为csc.exe),然后是从IL到本机代码的编译器(在运行时调用的JIT 32/64位编译器)或NGEN时间)。 源 - > IL和IL->原生编译器都理解尾调用优化。但是IL->本机编译器 - 我将其简称为JIT - 具有关于是否最终将使用尾调用优化的最终决定权。 source-> IL编译器可以帮助生成有助于进行尾调用的IL,包括使用“tail”。 IL前缀(稍后会详细介绍)。通过这种方式,源 - > IL编译器可以构造它生成的IL,以说服JIT进行尾调用。但是JIT总是可以选择做任何想做的事。

     

JIT何时进行尾调用?

     

我问起了我在JIT工作的大厅里的邻居Fei Chen和Grant Richins,在什么条件下各种JIT将采用尾调优化。完整的答案相当详细。快速总结是JIT尝试尽可能地使用尾调用优化,但是有很多原因导致无法使用尾调用优化。 尾调用是非选项的一些原因:

     
      
  • 来电后呼叫者不会立即返回(duh: - ))
  •   
  • 调用者和被调用者之间的堆栈参数是不兼容的,这种方式需要在调用者执行之前在调用者的帧中移动东西
  •   
  • 来电者和被叫者返回不同类型
  •   
  • 我们内联调用(内联比尾调用更好,并为更多优化打开了大门)
  •   
  • 安全阻碍
  •   
  • 调试器/分析器关闭了JIT优化
  •   

在你的问题的上下文中最有趣的部分,在我看来,在许多场景中,我非常清楚上面提到的示例安全性......

在许多情况下,.NET中的安全性取决于堆栈的准确性...... 在运行时 ..这就是为什么,正如上面强调的那样,负担被{{3编译器和(运行时)CIL到本机的JIT编译器,最后的说法是后者。

答案 1 :(得分:1)

猜猜:在像x86汇编程序这样的简单语言中你可以管理堆栈"手动",你不需要操作码 - 你可以恰当地设置调用堆栈。

但是在像.NET CIL这样的更高级别的东西中,堆栈是为你部分管理的,并且调用函数的整个行为是单个操作码(例如调用)。因此,您需要一个不同的操作码来实现TCO - 一个将控制流传递给此函数的操作,但不创建新的堆栈帧"。