もっと大きなプロジェクトをclangでコンパイルしているうちに、わずらわしいバグがありました。LLVM最適化のバグまたは未定義の動作?
次の小さな例で考えてみましょう:最後の行は厄介なようですが、このコードは、実際のプロジェクトでは(、どのような方法でこのようなコードをCの標準に違反していない私の知る限り
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
}
をプリプロセッサマクロが過剰に使用されるために表示されます)。
最適化レベル-O1以上でclang(バージョン3.1以上)でこれをコンパイルすると、メモリ内の間違った位置にコードが書き込まれます。次のように打ち鳴らすによって生成されたアセンブリファイルの
重要な部分は、/ LLVMを読む: (これはインテルに使用されているあなたの人々になるように、ガスの構文は次のとおりです。!注意してください)他に
[...]
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倍の乗算はできません(しかし、インテルの場合はARMよりもARMアセンブラに慣れています:-))、Cコードの最後の行は ' (unsigned char *)(* table)+ 5 + i * e + j) 'だから、アセンブラの出力を正しく解釈するには、これらのブレースを" e + 5 "の周りに置いてください。 – user2116939
はい、私はかなり確信しています。これは、インテルではなく、GASの構文であるため、 '%rsi'が'%rbx + 6%* rdx =(e + 5)を保持することを意味する 'movq%r8、%rsi'と' imulq%rdx、%rsi ' )*%rdx'。 –
はい、今私はこれを見ることができます。ちょっと奇妙なことがあってもコードが充分に正当であるため、オプティマイザのバグのように見えます(しかし、マクロは奇妙な出力を生成する可能性があります)。 – user2116939