[英]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
"%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.