在用clang编译一个更大的项目时,我偶然发现了一个令人烦恼的错误。
考虑以下小例子:
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或更高级别使用clang(版本3.1或更高版本)进行编译会导致代码写入内存中的错误位置。
clang / 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*)
,该错误就会消失。通常,该错误似乎对任何更改都非常敏感。
答案 0 :(得分:5)
我很确定这是一个优化错误。它在LLVM-2.7和LLVM-3.1中重新编写,这是我有权访问的唯一版本。
我将a bug发布到LLVM Bugzilla。
此SSCCE证明了这个错误:
#include <stdio.h>
unsigned long int * table;
void foo( unsigned long int q )
{
unsigned long int i,j,e;
e = 0;
for (i = 1; i <= 256; i *= q)
e++;
e--;
for (i = 0; i < q; i++)
for (j = 0; j < e; j++)
((unsigned char*)(table) + 13 )[i*e + j] = 0; // bug here
}
int main() {
unsigned long int v[8];
int i;
memset(v, 1, sizeof(v));
table = v;
foo(2);
for(i=0; i<sizeof(v); i++) {
printf("%d", ((unsigned char*)v)[i]);
}
puts("");
return 0;
}
应打印
1111111111111000000000000000011111111111111111111111111111111111
GCC下的和“clang -O0”。使用LLVM观察到的错误输出是
0000000011111111111110000000011111111111111111111111111111111111
感谢您注意到这一点!