我想象许多典型命令式语言的编译器实现者对CPS和基于CPS的编译技术并不熟悉。在函数式编程社区中,CPS和基于CPS的编译都是非常著名的技术-后者来自Guy Steele的著作。但是,即使在FP社区中,大多数编译器也不会使用基于CPS的技术进行编译,除非该语言本身支持像这样的控件运算符call/cc
。使用的内容更像是行政范式(ANF)(有时也称为Monadic范式,由于显而易见的原因而密切相关),它与SSA的关系比CPS更紧密。
如果我没记错的话,管理标准格式之所以得名,是因为基于CPS的编译会导致中间代码中的beta-redexes与源代码中的任何内容都不对应。这些被称为“管理性旧版本”。这些可以在编译时减少,但是在执行CPS转换方面有大量研究,该CPS转换将首先输出无需管理redex的代码。然后,目标是以正常形式产生输出,其中所有“行政” redex都被减少,这就是“行政正常形式”的由来。很快就意识到,将其视为一个两步过程并没有太大的好处:CPS转换,减少管理性繁琐工作。尤其是,行政范式看起来很像(手工)实行的单子风格,在Haskell中最为明显。CPS转换可以理解为向Monadic样式的转换,而您恰好在使用CPS monad(因此,实际上有多种方法可以“转换”为与不同评估顺序相对应的Monadic样式)。但是,总的来说,您可能会使用完全不同的monad,因此,转换为monadic样式以及管理规范形式与CPS并没有特别的关系。
尽管如此,CPS与ANF相比还是有一些好处。特别是,您可以在CPS中仅通过标准优化(例如beta减少)就可以进行某些优化,而这些优化似乎是ANF的特别规则。从单子视图来看,这些规则对应于通勤转换。现在的结果是,有一种理论可以解释应该添加哪些规则以及为什么添加该规则。一个最近的一篇文章给出了一个(新的)很清晰的描述(从逻辑的角度来看)及其相关的工作部分作为一个简短而体面的调查,并引用到对我提到的主题文献。
CPS的问题与其主要优点之一相关。CPS转换允许您实现像一样的控制运算符call/cc
,但这意味着CPS中间代码中的每个非本地函数调用都必须被视为潜在地执行控制效果。如果您的语言包括控制运算符,那么这应该是应该的(尽管即使如此,大多数函数可能都没有执行任何控制恶作剧)。如果您的语言不包括控制运算符,那么在使用连续性方面存在全局不变性,而局部不变性并不明显。这意味着无法对一般的CPS代码执行优化,而对于这种行为特别良好的CPS使用来说,这是合理的。一种体现的方式是数据和控制流分析精度的变化。(CPS转换在某些方面有所帮助,而其他方面则有所伤害,尽管它所提供的帮助主要是由于重复而不是CPS本身本身。)1当然,您可以添加规则并调整分析以对此进行补偿(即,利用全局变量),但是您已经部分击败了基于CPS的编译的主要优点之一,即许多(看似)特殊用途的临时优化成为通用优化的特殊情况(尤其是beta减少) )。
最终,除非您的语言具有控制运算符,否则通常没有太多理由使用基于CPS的编译方案。弥补了我上面提到的问题之后,通常就消除了基于CPS的编译的好处,并产生了与不使用CPS等效的东西。那时,CPS只是使卷积的中间代码看起来没有太大好处。2007年基于CPS进行编译的论点解决了其中一些问题,并使用其他形式的CPS转换带来了其他好处。我之前提到的(2017)论文部分论述了论文提出的内容。
1 SSA和CPS之间的等效性是否或多或少地使这成为不可能?号的第一件事情的纸张引入此等价的状态是等价确实不工作任意 CPS代码,但它的工作用于CPS的输出变换(它们限定)为一个语言没有控制操作员。