为什么在 GCC 中错误使用 __attribute__((pure)) 没有给出警告?
Why is no warning given for the wrong use of __attribute__((pure)) in GCC?
我正在尝试理解纯函数,并且一直在阅读有关该主题的 Wikipedia article。我写的最小示例程序如下:
#include <stdio.h>
static int a = 1;
static __attribute__((pure)) int pure_function(int x, int y)
{
return x + y;
}
static __attribute__((pure)) int impure_function(int x, int y)
{
a++;
return x + y;
}
int main(void)
{
printf("pure_function(0, 0) = %d\n", pure_function(0, 0));
printf("impure_function(0, 0) = %d\n", impure_function(0, 0));
return 0;
}
我用 gcc -O2 -Wall -Wextra
编译了这个程序,期望用 __attribute__((pure))
修饰 impure_function()
时应该发出错误,或者至少是警告。但是,我没有收到任何警告或错误,程序也 运行 没有问题。
用__attribute__((pure))
标记impure_function()
是不是不正确?如果是这样,为什么即使使用 -Wextra
和 -Wall
标志,它编译时也没有任何错误或警告?
提前致谢!
纯函数是 optimizing compiler. Probably, gcc
don't care about pure functions when you pass just -O0
to it (the default optimizations). So if f
is pure (and defined outside of your translation unit 的提示,例如在某些外部库中),GCC 编译器 可能 将 y = f(x) + f(x);
优化为
{
int tmp = f(x); /// tmp is a fresh variable, not appearing elsewhere
y = tmp + tmp;
}
但是如果 f
是 不是 纯(这是通常的情况:想想 f
调用 printf
或 malloc
), 这样的优化是被禁止的。
标准数学函数,如 sin
or sqrt
are pure (except for IEEE rounding mode craziness, see http://floating-point-gui.de/ and Fluctuat 等),它们足够复杂,可以进行计算,使此类优化值得。
您可以使用 gcc -O2 -Wall -fdump-tree-all
编译您的代码以猜测编译器内部发生了什么。您可以添加 -fverbose-asm -S
标志以获取生成的 *.s
汇编程序文件。
您还可以阅读 Bismon draft report(尤其是其第 1.4 节)。它可能会给出一些与您的问题相关的直觉。
在您的具体情况下,我猜测 gcc
是您的电话 inlining;然后纯度就不那么重要了。
如果您有时间,可以考虑编写自己的幻灯片GCC plugin to make such a warning. You'll spend months in writing it! These old,即使详细信息已过时,也可能对您有用。
理论层面,要注意Rice's theorem。其结果是纯函数的完美优化可能是不可能的。
注意位于孟买的 GCC Resource Center。
这样做是不正确的,您有责任正确使用该属性。
看这个例子:
static __attribute__((pure)) int impure_function(int x, int y)
{
extern int a;
a++;
return x + y;
}
void caller()
{
impure_function(1, 1);
}
GCC(带有 -O1
)为函数 caller
生成的代码是:
caller():
ret
如您所见,impure_function
调用已完全删除,因为编译器将其视为 "pure"。
如果 GCC 看到它的定义,它可以在内部自动将函数标记为 "pure":
static __attribute__((noinline)) int pure_function(int x, int y)
{
return x + y;
}
void caller()
{
pure_function(1, 1);
}
生成的代码:
caller():
ret
因此,在编译器可见的函数上使用此属性没有意义。它应该在定义不可用时使用,例如,当函数在另一个 DLL 中定义时。这意味着当它在适当的地方使用时,编译器将无法执行完整性检查。因此实现警告不是很有用(尽管并非毫无意义)。
我认为没有什么可以阻止 GCC 开发人员实施此类警告,除了必须花费的时间。
我正在尝试理解纯函数,并且一直在阅读有关该主题的 Wikipedia article。我写的最小示例程序如下:
#include <stdio.h>
static int a = 1;
static __attribute__((pure)) int pure_function(int x, int y)
{
return x + y;
}
static __attribute__((pure)) int impure_function(int x, int y)
{
a++;
return x + y;
}
int main(void)
{
printf("pure_function(0, 0) = %d\n", pure_function(0, 0));
printf("impure_function(0, 0) = %d\n", impure_function(0, 0));
return 0;
}
我用 gcc -O2 -Wall -Wextra
编译了这个程序,期望用 __attribute__((pure))
修饰 impure_function()
时应该发出错误,或者至少是警告。但是,我没有收到任何警告或错误,程序也 运行 没有问题。
用__attribute__((pure))
标记impure_function()
是不是不正确?如果是这样,为什么即使使用 -Wextra
和 -Wall
标志,它编译时也没有任何错误或警告?
提前致谢!
纯函数是 optimizing compiler. Probably, gcc
don't care about pure functions when you pass just -O0
to it (the default optimizations). So if f
is pure (and defined outside of your translation unit 的提示,例如在某些外部库中),GCC 编译器 可能 将 y = f(x) + f(x);
优化为
{
int tmp = f(x); /// tmp is a fresh variable, not appearing elsewhere
y = tmp + tmp;
}
但是如果 f
是 不是 纯(这是通常的情况:想想 f
调用 printf
或 malloc
), 这样的优化是被禁止的。
标准数学函数,如 sin
or sqrt
are pure (except for IEEE rounding mode craziness, see http://floating-point-gui.de/ and Fluctuat 等),它们足够复杂,可以进行计算,使此类优化值得。
您可以使用 gcc -O2 -Wall -fdump-tree-all
编译您的代码以猜测编译器内部发生了什么。您可以添加 -fverbose-asm -S
标志以获取生成的 *.s
汇编程序文件。
您还可以阅读 Bismon draft report(尤其是其第 1.4 节)。它可能会给出一些与您的问题相关的直觉。
在您的具体情况下,我猜测 gcc
是您的电话 inlining;然后纯度就不那么重要了。
如果您有时间,可以考虑编写自己的幻灯片GCC plugin to make such a warning. You'll spend months in writing it! These old,即使详细信息已过时,也可能对您有用。
理论层面,要注意Rice's theorem。其结果是纯函数的完美优化可能是不可能的。
注意位于孟买的 GCC Resource Center。
这样做是不正确的,您有责任正确使用该属性。
看这个例子:
static __attribute__((pure)) int impure_function(int x, int y)
{
extern int a;
a++;
return x + y;
}
void caller()
{
impure_function(1, 1);
}
GCC(带有 -O1
)为函数 caller
生成的代码是:
caller():
ret
如您所见,impure_function
调用已完全删除,因为编译器将其视为 "pure"。
如果 GCC 看到它的定义,它可以在内部自动将函数标记为 "pure":
static __attribute__((noinline)) int pure_function(int x, int y)
{
return x + y;
}
void caller()
{
pure_function(1, 1);
}
生成的代码:
caller():
ret
因此,在编译器可见的函数上使用此属性没有意义。它应该在定义不可用时使用,例如,当函数在另一个 DLL 中定义时。这意味着当它在适当的地方使用时,编译器将无法执行完整性检查。因此实现警告不是很有用(尽管并非毫无意义)。
我认为没有什么可以阻止 GCC 开发人员实施此类警告,除了必须花费的时间。