Compiler construction 我应该在哪个阶段为我的编译器实现尾部调用优化

Compiler construction 我应该在哪个阶段为我的编译器实现尾部调用优化,compiler-construction,scheme,racket,tail-call-optimization,Compiler Construction,Scheme,Racket,Tail Call Optimization,我在Racket中为一种类似scheme的语言编写了一个小编译器。现在我想为我的编译器实现TCO 我的想法是:在将尾部调用转换为中间表示之前,我需要检测它们。但由此看来,TCO通常是在程序集级别通过将call更改为jmp来完成的。我被困在这里了 如有任何建议,将不胜感激 编辑:我的目标是x86汇编代码。我使用的IR是三个地址码 这是我的编译器的12个过程,平坦过程是我将源代码转换为IR的过程 (define test-passes (list `("uniquify"

我在Racket中为一种类似scheme的语言编写了一个小编译器。现在我想为我的编译器实现TCO

我的想法是:在将尾部调用转换为中间表示之前,我需要检测它们。但由此看来,TCO通常是在程序集级别通过将
call
更改为
jmp
来完成的。我被困在这里了

如有任何建议,将不胜感激

编辑:我的目标是x86汇编代码。我使用的IR是三个地址码

这是我的编译器的12个过程,平坦过程是我将源代码转换为IR的过程

(define test-passes
(list
 `("uniquify"                ,(uniquify '())                                   ,interp-scheme)
 `("reveal-functions"        ,(reveal-functions '())                           ,interp-F)
 `("convert-to-closures"     ,convert-to-closure                               ,interp-F)
 `("expose allocation"       ,expose-allocation                                ,interp-F)
 `("flatten"                 ,(flatten #f)                                     ,interp-C)
 `("instruction selection"   ,select-instructions                              ,interp-x86)
 `("liveness analysis"       ,(uncover-live (void))                            ,interp-x86)
 `("build interference"      ,(build-interference (void) (void) (void) (void)) ,interp-x86)
 `("allocate register"       ,allocate-registers                               ,interp-x86) 
 `("lower-conditionals"      ,lower-conditionals                               ,interp-x86)
 `("patch-instructions"      ,patch-instructions                               ,interp-x86)
 `("x86"                     ,print-x86                                        #f)
 ))

对我来说,开始实现尾部调用优化的过程的地方是通过一个显式的语言构造来检测它,类似于所采用的方法,因为这是可能工作的最简单的事情。这将导致一个过程识别尾部调用,另一个过程实现尾部调用


自动识别尾部调用的进一步开发成为对第一个过程的修改。进一步开发以改进优化成为对第二个程序的修改。每种方法的开发都可以独立进行[或者根本不进行]。

答案取决于您的编译目标

如果您正在编译汇编程序(或机器代码),那么您可以在代码生成器中处理尾部调用(例如,请参阅Abdulaziz Ghuloum的“编译器构造的增量方法”)

如果目标语言是类似C的(即调用构建上下文),那么根据您希望编译器的先进程度,您有几个选项。其他人提到ANF和CPS是中间形式。也可以引入蹦床。有关策略列表,请参阅Felix Winkelman的“方案实现技术”

如果您的目标语言支持尾部递归,请考虑使用策略调用将目标调用转换为目标语言中的调用的策略。


无论如何:如果您对编译Scheme感兴趣,那么请毫不犹豫地获得Christian Queinnec的LiSP:LiSP小片副本。

您可以很早就检测到尾部调用,最好是在lambda提升后立即检测(您没有明确地说出来,但很可能您的转换为闭包过程正在这样做)


然后,标记为tail的调用可以在以后的阶段降低,但这并不是简单的跳转-您必须首先清理堆栈帧(如果您使用堆栈传递函数参数)。如果只使用寄存器传递参数,则更容易。

为什么在进入IR之前需要这样做,而不是在ANF(或CPS)中这样做?,。啊,是的,谢谢你包含这些链接。@WillNess嗨,如果我已经有了IR怎么办?这真的取决于你的IR。而且,语言对你来说并不少见许多不同的IRs,例如,检查一下。如果我的目标是x86汇编呢?我已经使用了三个地址码作为我的IR。我还能有ANF或CPS吗?(如果这听起来很愚蠢,很抱歉,但我不知道从哪里开始)在这种情况下,请看一看“编译器构造的增量方法”。另外,还可以获得一份有关x86操作码的Intels手册,并学习有关函数调用的部分(如果我没记错的话,Aziz的论文中有参考资料)您将尾部调用与尾部递归的特例混淆在一起。