GCC中的奇怪优化(删除函数)

时间:2015-06-03 03:04:17

标签: c gcc optimization

我在GCC 4.8.2中遇到了一个奇怪的优化 - -O2,它删除了一个函数。

请参阅以下代码。

increase()和increase2()是相同的,只是后者有一个printf()。

但是如果在GCC中使用-O2,则会删除increase()。

#include <stdio.h>
#include <stdint.h>
#include <arpa/inet.h>

void swap(uint64_t *vector)
{
    uint32_t *p = (uint32_t *)vector;
    uint32_t tmp = p[0];
    p[0] = htonl(p[1]);
    p[1] = htonl(tmp);
}

void increase(uint64_t *vector)
{
    swap(vector);
    (*vector)++;
    swap(vector);
}
void increase2(uint64_t *vector)
{
    swap(vector);
    (*vector)++;
    printf("touch...\n");
    swap(vector);
}


int main()
{
    uint64_t vector = 0xa;

    increase(&vector);
    printf("%lx\n", vector);

    increase2(&vector);
    printf("%lx\n", vector);

    return 1;
}

输出是:

a
touch...
10000000000000a

为什么?

提前致谢

很抱歉,我没有说清楚。

问题不在于“删除功能”,而是“功能不会影响参数”。

Matt McNabb指出了原因,即严格的混叠规则。 非常感谢你。

3 个答案:

答案 0 :(得分:5)

此代码导致未定义的行为:

uint32_t *p = (uint32_t *)vector;
uint32_t tmp = p[0];

vector指向的内存是uint64_t类型的对象,但是您通过类型为uint32_t的左值读取它。这违反了严格的别名规则。

由于您的程序始终调用此函数,因此未定义整个程序的行为。因此,当优化器切断导致UB的路径时,您可能会看到奇怪的优化工件。

另一个问题是您使用%lx打印uint64_t。除非您的系统使用64位长,否则这不会起作用。要获得正确的说明符,请使用printf("%" PRIx64 "\n", vector);。您可能需要#include <inttypes.h>

修复printf格式说明符后,我的系统会在-O2及更低版本提供以下内容,或者如果使用-fno-strict-aliasing开关:

10000000000000a
touch...
20000000000000a

-O3处的垃圾。

答案 1 :(得分:0)

如果实际删除了该功能,您的输出将更像是:

touch...
a
touch...
10000000000000a

要回答你的问题,它会打印出来,因为你告诉它按顺序打印这三行。

您对increase()的来电不会输出任何内容,然后您使用a输出printf("%lx\n", vector)

然后,您拨打increase2()并按设计输出touch...,然后使用10000000000000a输出printf("%lx\n", vector)

您是否检查了非优化输出?

答案 2 :(得分:0)

我知道你的问题是“为什么”。 @Matt McNabb很好地解释了这一点。但是如何解决呢?

通常编译器和CPU比指针更喜欢值,因为它们具有更多(和更安全)的优化空间。因此,让我们完全避免指针技巧并进行价值转换:

#include <endian.h>

void swap(uint64_t *vector)
{
    *vector = htobe64(le64toh(*vector));
}

这使您的程序行为对优化不敏感。