现代编译器如何知道操作不会产生副作用?

时间:2009-09-16 19:59:42

标签: compiler-construction

我一直在阅读编译器将针对没有副作用的操作进行优化。 我认为这是非常保守的,但编译器如何知道。 它是否有一个副作用自由操作的查找表,或者它是否这样做呢?

4 个答案:

答案 0 :(得分:5)

它在编译期间执行代码的静态分析。

请记住,它知道代码是否仅执行本地计算,并且不执行指针数学运算或使用全局变量或修改以某种方式通过引用传入的数据。它调用的任何函数也必须是非副作用(递归这一点)。

答案 1 :(得分:1)

程序员也可能给编译器一些提示:例如参见noalias

答案 2 :(得分:1)

副作用主要是函数调用的潜在结果。

在某些情况下,可能的副作用会被编码到参数声明中。 C甚至有一个名为 restrict 的特殊限定符,除了constvolatile之外,它还向编译器提供有关可能的副作用的直接信息。

因此,如果控制通过方法或函数调用传递到被评估的立即表达式之外,则编译器需要静态分析整个代码路径或根据形式参数进行确定。

在最坏的情况下,编译器可能需要假设在方法或函数调用之后内存中的任何内容都可能已更改。

为了安排代码的最佳优化,在计算密集的代码区域中使用该语言的功能来尽可能地限制参数。在使用之前将未被表达式修饰的全局变量复制到本地;这有助于编译器的几种方式。尽可能限制所有对象的可见性。 (使用本地,然后是实例变量,然后使用类变量,避免全局任何事情。)

答案 3 :(得分:1)

简单地说,有一个副作用免费(又名)操作列表。但大多数操作只是有点副作用。例如,在Java中,如果您可以静态地告知数据的边界,则从数组中读取是纯粹的。许多其他分析,例如价值范围传播,可以帮助解决这个问题。

通常,您想要判断函数调用是否纯粹。对于内置函数,编译器可能只有一个列表(尽管编译器建模的函数,例如C中的sin,在某些情况下可能是无副作用的,并且可能会修改errno in其他

要判断函数是否是纯函数,您必须知道它不会写入转义的内存 - 即全局变量,类变量或通过返回值可访问的任何内存或参数。这称为mod-ref分析(也称为别名分析或指针分析,与逃逸分析具有相同的基本思想)。简化的算法是:

  • 在程序中收集所有名称(global xy->f*y**y等)
  • 构建一个可能指向相同内存位置的名称图表
  • 写入名称时(即*x = 5;)获取可能也写入
  • 的其他名称列表
  • 如果其中一个从函数中逃脱,那么函数就不是纯粹的。

我认为这不是最好的解释,欢迎提出建议和问题。

请注意,某些语言(如C ++)提供纯度注释(const函数,不要与纯虚函数混淆),甚至允许一些高级功能,如{{ 1}}。我被告知它们对于优化并不是特别有用,但是我没有编写C ++编译器,所以我不知道为什么。我推测,单独的编辑可能会破坏他们的乐趣。