GCC - 两个完全相同的函数却生成了不同的代码。为什么?

16

代码:

#define OPPOSITE(c) (*((typeof(x) *)&(x)))

int foo(volatile int x)
{
    OPPOSITE(x) = OPPOSITE(x) + OPPOSITE(x);
    return x;
}

int bar(volatile int x)
{
    OPPOSITE(x) = OPPOSITE(x) + OPPOSITE(x);
    return x;
}

结果(-Os):

foo:
        mov     DWORD PTR [rsp-4], edi
        mov     eax, DWORD PTR [rsp-4]
        mov     edx, DWORD PTR [rsp-4]
        add     eax, edx
        mov     DWORD PTR [rsp-4], eax
        mov     eax, DWORD PTR [rsp-4]
        ret
bar:
        mov     DWORD PTR [rsp-4], edi
        mov     eax, DWORD PTR [rsp-4]
        add     eax, eax
        ret

或者ARM gcc。(-O3

foo:
        sub     sp, sp, #8
        str     r0, [sp, #4]
        ldr     r3, [sp, #4]
        ldr     r2, [sp, #4]
        add     r3, r3, r2
        str     r3, [sp, #4]
        ldr     r0, [sp, #4]
        add     sp, sp, #8
        bx      lr
bar:
        sub     sp, sp, #8
        str     r0, [sp, #4]
        ldr     r0, [sp, #4]
        lsl     r0, r0, #1
        add     sp, sp, #8
        bx      lr

https://godbolt.org/z/6z5Td9GsP


2
@chqrlie:与 #define OPPOSITE(c) (c) 相同的代码生成 https://godbolt.org/z/MWsbT5nan,所以这是GCC的一个错误:不尊重volatile赋值,也不尊重右侧有两个读取的事实。(除非我错过了关于序列点和对易访问volatile对象的多个未排序访问是UB的某些内容?不,使用tmp变量的相同代码生成:https://godbolt.org/z/r4evhT913) - Peter Cordes
2
你可以删除这个相反的东西,得到相同的代码。 - user253751
1
@Jabberwocky 嗯,从默认标准gnu90到gnu11的更改意味着我上面的评论很可能是原因。但是,仍然应该使用“-std=c99 -pedantic”来消除UB情况,但它并没有。因此,这个bug是gcc在C99模式下版本5之后不正确地处理volatile序列的结果。 - Lundin
1
我们可以简化它到 int foo(volatile int x) { x = x + x; return x; } int bar(volatile int x) { x = x + x; return x; }。同样的错误。这在C11之后是未定义行为,但在C99中是有明确定义的。 - Lundin
1
@Zakk: bar具有外部链接,因此在从翻译单元之外调用时,必须有正确的代码。 - Eric Postpischil
显示剩余9条评论
1个回答

3

您可以用以下代码替换

int foo(volatile int x)
{
        x = x + x;
        return x;
}

int bar(volatile int x)
{
    x = x + x;
    return x;
}

并且产生相同的效果。除了GCC之外,没有其他C编译器会产生这种效果,因此我认为说这是某种编译器错误是合理的。


3
即使您在一个语句中使用临时变量来避免无序的易变访问(within one statement),以避免C11 UB,相同的错误代码生成仍然存在。除非您使用“-fno-ipa-icf”(https://godbolt.org/z/jK5a4j6q8)- interprocedural analysis:Identical Code Folding(https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fipa-icf)。因此,该分析流程显然是GCC内部错误的一部分。正如Lundin所指出的那样,这是GCC 5.0中的一个退步。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接