簡體   English   中英

gcc中的優化級別改變了c程序的行為

[英]Optimization levels in gcc changing c program behaviour

在gcc中使用不同的優化級別編譯此代碼時,我看到了我不期望的行為。

函數測試應使用1填充64位無符號整數,將shift_size位向左移位,並將32位低位作為32位無符號整數返回。

當我用-O0編譯時,我得到了我期望的結果。

當我使用-O2編譯時,如果我嘗試移位32位或更多,我就不會。

實際上,如果我在x86上移位大於或等於位寬的32位整數,那么我得到的結果就是我所期望的結果,這是僅使用移位大小的5個低位的移位。

但我正在轉移64位數字,所以轉移<64應該是合法的嗎?

我認為這是我的理解中的錯誤而不是編譯器中的錯誤,但我無法弄明白。

我的機器:gcc(Ubuntu / Linaro 4.4.4-14ubuntu5)4.4.5 i686-linux-gnu

#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>

uint32_t test(unsigned int shift_size) {
    uint64_t res = 0;
    res = ~res;
    res = res << shift_size; //Shift size < uint64_t width so this should work
    return res; //Implicit cast to uint32_t
}

int main(int argc, char *argv[])
{
    int dst;
    sscanf(argv[1], "%d", &dst); //Get arg from outside so optimizer doesn't eat everything
    printf("%" PRIu32 "l\n", test(dst));
    return 0;
}

用法:

$ gcc -Wall -O0 test.c 
$ ./a.out 32
0l
$ gcc -Wall -O2 test.c 
$ ./a.out 32
4294967295l

gcc -S -Wall -O0 test.c

gcc -S -Wall -O2 test.c

"%u" (或"%lu" )和uint32_t不一定兼容。 嘗試

    #include <inttypes.h>

    //printf("%ul\n", test(dst));
    printf("%" PRIu32 "l\n", test(dst));

使用"%u" (或"%lu" )說明符打印uint32_t值可以調用未定義的行為。

我能夠重復這一點。 這是與-O2生成的代碼的相關位:

    movl    $-1, %eax
    movl    $-1, %edx
    sall    %cl, %eax
    xorl    %edx, %edx
    testb   $32, %cl
    cmovne  %eax, %edx
    cmovne  %edx, %eax    ; This appears to be the instruction in error.
                          ; It looks as though gcc thought that %edx might still
                          ; be zero at this point, because if the shift count is
                          ; >= 32 then %eax should be zero after this.

這是與-O0的等效位:

    movl    -16(%ebp), %eax
    movl    -12(%ebp), %edx
    shldl   %cl,%eax, %edx
    sall    %cl, %eax
    testb   $32, %cl
    je      L3
    movl    %eax, %edx
    xorl    %eax, %eax         ; correctly zeros %eax if shift count >= 32
L3:
    movl    %eax, -16(%ebp)
    movl    %edx, -12(%ebp)

編譯器是:

i686-apple-darwin11-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)

感謝您發布您的gcc -S輸出。 我看了一眼,雖然它略有不同,但關鍵部分與我在機器上看到的錯誤相同。

看起來它可能是一個32位特定的編譯器錯誤給我。 使用問題和gcc 4.2.1中的代碼,只要我使用gcc -m32 -O2 ...編譯,我就可以重現bug。

但是,如果我添加一個調試printf:

uint32_t test(unsigned int shift_size) {
    uint64_t res = 0;
    res = ~res;
    res = res << shift_size; //Shift size < uint64_t width so this should work
    printf("res = %llx\n", res);
    return res; //Implicit cast to uint32_t
}

然后問題就消失了。

下一步是查看生成的代碼以嘗試識別/確認錯誤。

它看起來像一個bug。 我的猜測是編譯器折疊了最后兩行:

res = res << shift_size
return (uint32_t)res;

成:

return ((uint32_t)res) << shift_size;

后者現在定義為32或更大。

我不記得C99說的是什么,但似乎在gcc中,uint32_t包含至少 32位,並且可能包含更多,所以當你優化它時,它使用64位var,這在64位機器中更快。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM