一部のコードで厳密なエイリアシング違反がないか確認しようとしていますが、厳密なエイリアシング ルールを理解しようとして何かを見逃してしまったようです。
次のコードを想像してください。
#include <stdio.h>
int main( void )
{
unsigned long l;
l = 0;
*( ( unsigned short * )&l ) = 1;
printf( "%lu\n", l );
return 0;
}
古典的で基本的な例。GCC 4.9 ( -Wall -fstrict-aliasing -Wstrict-aliasing -O3
) では、実際に次のエラーが報告されます。
dereferencing type-punned pointer will break strict-aliasing rules
しかし、次のコードは正常にコンパイルされます。
#include <stdio.h>
int main( void )
{
unsigned long l;
unsigned short * sp;
l = 0;
sp = ( unsigned short * )&l;
*( sp ) = 1;
printf( "%lu\n", l );
return 0;
}
私の理解では、2 番目の例も構造体のエイリアス ルールに違反しています。
では、なぜコンパイルできるのでしょうか。これは GCC の精度の問題でしょうか、それとも厳密なエイリアスに関する何かを見逃したのでしょうか。
次のようなトピックも見つかりました:このコードに対して厳密なエイリアシングの警告が生成されないのはなぜですか?
-Wstrict-aliasing=2
またはを使用してコンパイルしても-Wstrict-aliasing=3
違いはありません。
ただし、-Wstrict-aliasing=1
2 番目の例ではエラーが報告されます。
GCC のドキュメントによると、レベル 1 は最も精度が低く、多くの誤検知が発生する可能性がありますが、レベル 3 は最も精度が高いとのことです...
それで、ここで何が起こっているのでしょうか? 私の理解の問題でしょうか、それとも GCC の問題でしょうか?
ボーナス質問
私は通常、プロジェクトでは GCC よりも Clang/LLVM を好みますが、Clang は厳密なエイリアシングに関する警告を何も出さないようです。
理由を知っている人はいますか?
違反を検出できないためか、コード生成時にルールに従わないためでしょうか?
ベストアンサー1
あなたの理解は正しいです。エイリアス分析は一般的に複雑で、この場合はキャストと逆参照の間で一時的なポインターを使用するだけで分析が失敗するようです。驚いたことに、GCC 4.8.2 はこのコードでより優れた処理を行い、-Wstrict-aliasing=2
レベル 1 と同様に警告を発しているので、これは回帰です。
clang に関しては、現時点ではエイリアシング違反を警告する機能がありません。最適化のルールは確実に活用されます。これを実際に確認するには、C 標準 (N1570 §6.5.2.3 9) からこの例を取り上げます。
struct t1 { int m; };
struct t2 { int m; };
int f(struct t1 *p1, struct t2 *p2) {
if (p1->m < 0)
p2->m = -p2->m;
return p1->m;
}
p1 と p2 が同じ構造体を指している場合でも、Clang (および GCC) は、p2 が p1 のエイリアスではないと想定し、前の否定が結果に影響を与えないことから、否定前の値をp1->m
返します。完全な例はこちらありとなしの出力-fstrict-aliasing
。その他の例については、ここそしてよく引用されるすべての C プログラマーが未定義の動作について知っておくべきこと; 厳密なエイリアシングの最適化は、この紹介記事の最後のトピックです。
警告がいつ実施されるかについては、開発者は静かだ、しかし、それらは言及されているclangのテストスイート、-Wstrict-aliasing=X
タイトルの下にリストされています(強調は私によるものです)
これらの旗は現在実装されていません。とにかく出力することをテストします。
したがって、いつかは起こる可能性があるようです。