C99中是否允许这种冗余加载/存储优化?

时间:2013-06-04 08:13:03

标签: c optimization standards

请考虑以下事项:

extern void bar(int *restrict);

void foo(int *restrict p) {
  int tmp;
  bar(&tmp);
  *p = tmp;
}

C99规范是否允许将foo优化为以下内容?

extern void bar(int *restrict);

void foo(int *restrict p) {
  bar(p);
}

我在-O3模式下尝试了gcc,Clang和Intel Compiler,并且都没有生成反映上述优化的代码。这让我怀疑这种优化打破了规范。如果不允许,那么它在规范中的含义是什么?

注意:我的问题的灵感来自this SO question

4 个答案:

答案 0 :(得分:18)

答案是肯定的否定,这是不允许的。

考虑如果foo和bar相互递归会发生什么。例如,bar的实现:

void bar(int *restrict p)
{
    static int q;
    if (p == &q) {
        printf("pointers match!\n");
    } else if (p == NULL) {
        foo(&q);
    }
}

bar永远不会取消引用p,因此限制限定符无关紧要。很明显,静态变量q不能与foo中的自动变量tmp具有相同的地址。因此,foo无法将其参数传递回bar,并且不允许给定的优化。

答案 1 :(得分:1)

要使编译器进行优化,必须确保无论bar如何实现,以及如何调用foo,明确定义的行为都不会改变。
由于编译器不知道bar的实现和对foo的调用,所以当它编译foo时,这种情况的理论存在就足以阻止优化,即使它在现实中不会发生。

以下是这种情况的一个例子。重点是: 1.参数p指向只写存储器(例如存储器映射的I / O) 2. bar与只写指针一起使用是不安全的(可能是它写入然后再读回来,期望相同的值)。
函数foo可以安全地与只写指针一起使用,因为它只写p。即使bar不安全也是如此,因为bar永远不会获得p。根据建议的优化,bar会获得p,这可能会造成麻烦。

以下是包含bar的文件和foo调用的示例。

static int increment;

void bar(int *restrict p) {
    (*p)=0;
    if (increment) (*p)++;
}

void foo(int *restrict p);

int main(int ac, char **av) {
    int *p = get_io_addr();    /* Get a write-only memory mapped I/O address */
    increment = atoi(av[1]);
    foo(p);
    return 0;
}

答案 2 :(得分:1)

简要介绍this SO questionthis wikipedia enrty表明restrict关键字只能在函数的参数中生效。但是,特别是阅读the C99 standard,第6.7.3.1节,可以清楚地表明restrict适用于使用restrict的整个上下文。所以通过使用

void foo(int *restrict p);

您要保证p指向的内存块的唯一读取和写入将通过p

但是,即使有了这些信息,在编译foo时,编译器也不知道bar将对它发送的信息做什么。例如,考虑:

void bar (unsigned long long int *p) {
    *p = ((unsigned long long int) p) % 2000;
    }

结果取决于指针集的值,这意味着在编译foo时,您建议的优化假设无法确定,因为如果您建议进行优化,结果会有所不同。< / p>

答案 3 :(得分:0)

这两个代码不等价:在第一种情况下,函数“bar”接收“tmp”的指针(可能使用该值),即本地(和非初始化!)变量。在第二种情况下,“bar”直接在“p”上工作,通常它会在“* p”中找到不同的值。如果“bar”函数将其参数声明为“仅输出”(例如,通过OUT宏在M $ VS中),则情况会有所不同,因为变量的初始值将被(应该被忽略)。 (注意:大多数VS版本实际上都将OUT宏定义为什么都没有。太可悲了......)