问题描述
所以,从技术上讲,.NET 确实支持尾调用优化(TCO),因为它具有操作码,而C#不会生成它。
So I've read many times before that technically .NET does support tail call optimization (TCO) because it has the opcode for it, and just C# doesn't generate it.
我不确定TCO为什么需要操作码或需要什么操作码会做。据我所知,能够执行TCO的要求是,递归调用的结果不得与当前函数范围内的任何变量组合。如果您没有,那我就看不到操作码如何阻止您必须保持堆栈框架打开。如果有的话,那么编译器不能总是轻松地将其编译为迭代的东西吗?
I'm not exactly sure why TCO needs an opcode or what it would do. As far as I know, the requirement for being able to do TCO is that the results of a recursive call are not combined with any variables in the current function scope. If you don't have that, then I don't see how an opcode prevents you from having to keep a stack frame open. If you do have that, then can't the compiler always easily compile it to something iterative?
那么操作码的意义是什么?显然,我缺少一些东西。如果完全可以使用TCO,难道不是总在编译器级别而不是在操作码级别处理TCO?
So what is the point of an opcode? Obviously there's something I'm missing. In cases where TCO is possible at all, can't it always be handled at the compiler level than at the opcode level? What's an example of where it can't?
推荐答案
在您已经提供的链接之后,这在我看来似乎是什么? ,非常接近地回答您的问题。
Following the links you already provided, this is the part which seems to me, answers your question pretty closely..
在处理CLR管理的语言时,正在使用两种编译器。从语言的源代码一直到IL(C#开发人员将其称为csc.exe),然后是从IL到本地代码的编译器(在运行时调用的JIT 32/64位编译器)或NGEN时间)。 source-> IL和IL->本机编译器都了解尾调用优化。但是IL-> native编译器(我将其简称为JIT)具有关于是否最终使用尾部调用优化的最终决定权。 源-> IL编译器可以帮助生成有助于进行尾部调用的IL,包括使用 tail。 IL前缀(稍后会详细介绍)。这样,源-> IL编译器可以构造其生成的IL,以说服JIT进行尾部调用。但是,JIT总是可以选择执行自己想做的事情。
When you're dealing with languages managed by the CLR, there are two kinds of compilers in play. There's the compiler that goes from your language's source code down to IL (C# developers know this as csc.exe), and then there's the compiler that goes from IL to native code (the JIT 32/64 bit compilers that are invoked at run time or NGEN time). Both the source->IL and IL->native compilers understand the tail call optimization. But the IL->native compiler--which I'll just refer to as JIT--has the final say on whether the tail call optimization will ultimately be used. The source->IL compiler can help to generate IL that is conducive to making tail calls, including the use of the "tail." IL prefix (more on that later). In this way, the source->IL compiler can structure the IL it generates to persuade the JIT into making a tail call. But the JIT always has the option to do whatever it wants.
JIT何时发出尾声?
我问费飞Chen和Grant Richins是我的邻居,碰巧在JIT上工作,在各种条件下,各种JIT将采用尾部调用优化。完整答案相当详细。快速总结是,JIT会尽可能尝试使用尾部调用优化,但是有很多原因导致无法使用尾部调用优化。 不能进行尾部呼叫的某些原因:
I asked Fei Chen and Grant Richins, neighbors down the hall from me who happen to work on the JIT, under what conditions the various JITs will employ the tail call optimization. The full answer is rather detailed. The quick summary is that the JITs try to use the tail call optimization whenever they can, but there are lots of reasons why the tail call optimization can't be used. Some reasons why tail calling is a non-option:
- 呼叫后呼叫者不会立即返回(duh :-) )
- 调用方和被调用方之间的堆栈参数不兼容,这需要在被调用方执行之前在调用方的框架内转移内容
- Caller和被叫方返回不同的类型
- 我们内联调用(内联比尾部调用更好,并且为更多优化打开了大门)
- 安全性受到阻碍
- 调试器/分析器关闭了JIT优化
- Caller doesn't return immediately after the call (duh :-))
- Stack arguments between caller and callee are incompatible in a way that would require shifting things around in the caller's frame before the callee could execute
- Caller and callee return different types
- We inline the call instead (inlining is way better than tail calling, and opens the door to many more optimizations)
- Security gets in the way
- The debugger / profiler turned off JIT optimizations
在您的问题中,最有趣的部分是我的观点,在许多情况下,这是示例上面提到的...
The most interesting part in context of your question, which makes it super clear in my opinion, among many scenarios, is example of security mentioned above...
在许多情况下,.NET中的安全性取决于堆栈的准确性... 在运行时 ..这就是为什么如上所示,此负担由源均分担给编译器和(运行时)CIL-本地的JIT编译器,最终的决定权在于后者。
Security in .NET in many cases depends on the stack being accurate... at runtime.. Which is why, as highlighted above, the burden is shared by both the source to CIL compiler, and (runtime) CIL-to-native JIT compilers, with the final say being with the latter.
这篇关于为什么尾部调用优化需要操作码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!