【发布时间】:2012-09-30 15:13:02
【问题描述】:
我一直在设计一种小型编程语言,我想要一个相对简单的后端选项。由于所有通常的原因(例如“我无法遵循好的建议”和“我不知道汇编”),C 是一个相对不错的选择,更重要的是,我的语言中并没有太多不能用 C 轻松表达(更多的是关于类型检查器,没有 GC 或重型运行时来适应) - 除了一件事:优化尾调用。
这不是一个交易破坏者:我真的不需要它,因为该语言具有循环结构,但它也应该是功能性的,所以如果可能的话,TCO 似乎是一个有价值的东西。 Trampoline 或lazy-thunk 解决方案会起作用,但似乎(根据文献,我还没有没有分析过,而且无论如何也没有任何使用信息)对性能有显着影响。不希望实现阻止用户以有效的方式表达问题。
This question 让我认为应该可以创建一个相对简单的特定于平台的“tailcall”块,以替换“return”并强制执行我想要的行为。但是,由于对 x86 不够熟悉,我不知道如何控制 C 的自动堆栈操作,我应该将跳转的目标放在哪里等等。(在我看来,它在函数启动后扩展了堆栈。 .. 所以我需要在尾调用之前缩小堆栈,这有点不必要,或者在堆栈操作之后以某种方式在目标主体的开始处进行跳转,这听起来几乎是不可能的编辑: 用于静态未知目标 ..?)
所以:
- 可以这样做吗?
- 可以安全完成吗?
- 这是否明智?
我应该重复一遍,我知道可以在 C(即我的高级语言,而不是 C)中实现 工作 的尾调用消除,我只是想添加特定于编译器和平台的优化,以帮助它或多或少与“真实事物”无法区分。而且我主要想使用 C,因为我对它非常有信心,而我从未使用过 LLVM,并且只有最基本的 x86 阅读知识,所以我希望跳过这一步并让输出正常工作。我知道我将来一定要好好学习这两样东西,但是……
(即我问这个是因为这个想法卡在我的脑海里,它让我觉得可能有更好的方法来做它(tm),而不是因为我需要解决方案获得一个工作后端。)
【问题讨论】:
-
这让我想起了Continuation Passing Style,特别是Chicken,一个Scheme to C 编译器。你可能想研究这些。
-
我不明白你为什么需要内联汇编。您可以发出在函数开头带有标签的 C 代码,然后将尾部调用替换为重新绑定函数参数并转到标签。
-
TCE 只是将递归尾调用更改为
while(1) { body }语句,同时在“再次执行”之前跟踪参数。编辑:基本上是@Ferruccio 所说的;p -
当然,我已经为递归这样做了,但我的意思是尾调用静态未知函数指针的一般情况。除非我完全误解了你,否则
goto/while解决方案对此无济于事。 -
更好地检查 GCC 通过一些优化生成的代码,它非常擅长消除尾递归(甚至是一些非尾递归)。