【问题标题】:At which stage should I implement tail call optimization for my compiler我应该在哪个阶段为我的编译器实现尾调用优化
【发布时间】:2016-09-30 14:47:15
【问题描述】:

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

我的想法是:我需要在将尾调用转换为中间表示之前检测它们。但是从这个page 看来,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)
 ))

【问题讨论】:

  • 为什么在进入 IR 之前需要这样做,而不是在 ANF(或 CPS)中这样做?
  • ANF, CPS, IR.
  • 啊,是的,感谢您提供这些链接。
  • @WillNess 嗨,如果我已经有 IR 怎么办?
  • 这真的取决于你的 IR。此外,对于许多不同的 IR,语言对您来说并不少见,例如,请查看 nanopass.org

标签: compiler-construction scheme racket tail-call-optimization


【解决方案1】:

答案取决于您的编译目标。

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

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

如果您的目标语言支持尾递归,请考虑使用将 Scheme 调用翻译成目标语言调用的策略。

无论如何:如果您对编译 Scheme 感兴趣,那么请不要犹豫,获取一份 LiSP:Christian Queinnec 的 Lisp in Small Pieces。

【讨论】:

  • 如果我的目标是 x86 程序集怎么办?我已经使用了三个地址代码作为我的 IR。我还能有 ANF 或 CPS 吗?(对不起,如果这听起来很愚蠢,但我不知道从哪里开始)
  • 在这种情况下,请查看“编译器构造的增量方法”。还可以获得一份 Intel 的 x86 操作码手册并研究函数调用部分(如果我没记错的话,Aziz 的论文中有参考。)
【解决方案2】:

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

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

【讨论】:

  • 您通常会将尾调用与尾递归的特殊情况混淆。
【解决方案3】:

您可以很早就检测到尾调用,最好是在 lambda 提升之后立即检测到(您没有明确说明,但很可能您的转换到闭包传递正在这样做)。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-04
    • 2011-10-13
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多