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