9
在用clang編譯一個更大的項目時,我偶然發現了一個惱人的bug。LLVM優化錯誤或未定義的行爲?
考慮下面的小例子:
unsigned long int * * fee();
void foo(unsigned long int q)
{
unsigned long int i,j,k,e;
unsigned long int pows[7];
unsigned long int * * table;
e = 0;
for (i = 1; i <= 256; i *= q)
pows[e++] = i;
pows[e--] = i;
table = fee(); // need to set table to something unknown
// here, otherwise the compiler optimises
// parts of the loops below away
// (and no bug occurs)
for (i = 0; i < q; i++)
for (j = 0; j < e; j++)
((unsigned char*)(*table) + 5)[i*e + j] = 0; // bug here
}
據我所知,這代碼不違反任何方式的C標準,雖然最後一行似乎尷尬(在實際項目中,這樣的代碼由於過度使用預處理宏而出現)。
在優化級別爲-O1或更高時編譯此語句(版本3.1或更高版本)會導致代碼寫入內存中錯誤的位置。
由鐺生成的彙編文件的關鍵部分/ LLVM如下: (這是GAS的語法,所以你們誰是用來英特爾:當心!)
[...]
callq _fee
leaq 6(%rbx), %r8 ## at this point, %rbx == e-1
xorl %edx, %edx
LBB0_4:
[...]
movq %r8, %rsi
imulq %rdx, %rsi
incq %rdx
LBB0_6:
movq (%rax), %rcx ## %rax == fee()
movb $0, (%rcx,%rsi)
incq %rsi
[conditional jumps back to LBB0_6 resp. LBB0_4]
[...]
在其他字,說明做
(*table)[i*(e+5) + j] = 0;
而不是上面寫的最後一行。 + 5
的選擇是任意的,添加(或減去)其他整數會導致相同的行爲。所以 - 這是LLVM優化中的一個錯誤還是存在未定義的行爲?
編輯:請注意,如果我在最後一行中遺漏了演員表(unsigned char*)
,該錯誤消失。一般來說,該錯誤似乎對任何更改都非常敏感。
在上面的彙編代碼中看不到乘以5(但後來我比Intel更習慣ARM彙編,如果它是Intel :-)),但C代碼的最後一行轉換爲* ((unsigned char *)(* table)+ 5 + i * e + j)',所以......你確定你把這些大括號放在「e + 5」的正確解釋中嗎? – user2116939 2013-03-07 20:13:53
是的,我很確定。這是GAS語法,不是Intel,所以'movq%r8,%rsi'和'imulq%rdx,%rsi'表示'%rsi'將保存'(%rbx + 6)*%rdx =(e + 5 )*%rdx'。 – 2013-03-07 20:21:27
是的,現在我可以看到這一點。它看起來像一個優化器錯誤,因爲即使有點奇怪,代碼仍然足夠清晰(但是然後宏可以產生奇怪的輸出)。 – user2116939 2013-03-07 20:38:17