[解決済み] 最適化されない空の無限ループを作るには?
質問
C11規格では、定数制御式のある反復処理文は最適化してはいけないことになっているようです。私は、以下のアドバイスを参考にしています。 この回答 からアドバイスを受けています。これは、特に標準草案のセクション 6.8.5 を引用しています。
制御する式が定数式でない反復文は、...実装によって終了すると仮定されることがある。
その回答では、以下のようなループは
while(1) ;
のようなループは最適化の対象にすべきではないと述べています。
では...なぜClang/LLVMは以下のループを最適化するのでしょうか(コンパイル時に
cc -O2 -std=c11 test.c -o test
)?
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
die();
printf("unreachable\n");
}
私のマシンでは、これは次のように出力されます。
begin
と表示され、次に
が不正な命令でクラッシュします。
(a
ud2
の後に置かれたトラップ
die()
).
ゴッドボルトについて
を呼び出した後、何も生成されないことがわかります。
puts
.
の下でClangに無限ループを出力させるのは、意外と難しい作業でした。
-O2
- を繰り返しテストすることができるのに
volatile
変数を繰り返しテストすることができますが、それは私が望まないメモリ読み取りを伴います。そして、もし私がこのようなことをしたら
#include <stdio.h>
static void die() {
while(1)
;
}
int main() {
printf("begin\n");
volatile int x = 1;
if(x)
die();
printf("unreachable\n");
}
...Clangが表示します。
begin
に続いて
unreachable
というように、無限ループが存在しないかのように表示されます。
最適化をオンにした状態で、Clangに適切な、メモリにアクセスしない無限ループを出力させるにはどうしたらいいでしょうか?
どのように解決するのですか?
C11規格では、6.8.5/6にこのように書かれています。
制御する式が定数式でない反復文。 156) その 入出力操作を行わず、揮発性オブジェクトにアクセスせず、本体、制御式、(for文の場合)式-3において同期または原子操作を行わない。 その本体、制御式、または(for文の場合)その式-3で、同期またはアトミック操作を実行しない場合、実装は終了すると仮定してもよい。 を終了させる。 157)
2つの脚注は規範的なものではありませんが、有用な情報を提供しています。
156) 省略された制御式は、ゼロでない定数式に置き換えられます。
157) これは、以下のような場合でも、空ループの削除などのコンパイラ変換を可能にすることを意図している。 の終了が証明できない場合でも、空のループを削除するなどのコンパイラ変換を可能にするためのものである。
あなたの場合
while(1)
は明確な定数表現なので、もしかしたら
ではなく
は終了することを想定していません。for-ever" ループは一般的なプログラミング構成であるため、そのような実装は絶望的に壊れてしまいます。
ループの後に "unreachable code" に何が起こるかは、しかし、私の知る限りでは、よく定義されていないようです。しかし、clang は確かに非常に奇妙な振る舞いをします。gcc (x86) と機械語コードを比較します。
gcc 9.2
-O3 -std=c11 -pedantic-errors
.LC0:
.string "begin"
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0
call puts
.L2:
jmp .L2
clang 9.0.0
-O3 -std=c11 -pedantic-errors
main: # @main
push rax
mov edi, offset .Lstr
call puts
.Lstr:
.asciz "begin"
gccはループを生成し、clangはただ森に突っ込んでエラー255で終了します。
私は、これが clang の非準拠の動作であることに傾いています。なぜなら、私はあなたの例をさらにこのように拡大しようとしたからです。
#include <stdio.h>
#include <setjmp.h>
static _Noreturn void die() {
while(1)
;
}
int main(void) {
jmp_buf buf;
_Bool first = !setjmp(buf);
printf("begin\n");
if(first)
{
die();
longjmp(buf, 1);
}
printf("unreachable\n");
}
私はC11を追加しました
_Noreturn
を追加して、コンパイラをさらに支援しようとしました。そのキーワードだけで、この関数がハングアップすることは明らかなはずです。
setjmp
は最初の実行で 0 を返すので、このプログラムは単に
while(1)
にぶつかり、そこで止まって "begin" とだけ表示するはずです (\n flush stdout と仮定しています)。これはgccで起こります。
ループが単に削除された場合、それは "begin" を 2 回表示した後に "unreachable" を表示するはずです。しかし、clang では ( ゴッドボルト ) では、終了コード 0 を返す前に "begin" を 1 回、そして "unreachable" を出力します。これはどう考えても間違っています。
私はここで未定義の動作を主張するケースを見つけることができないので、これはclangのバグであると私は考えています。とにかく、この動作は、プログラムをぶら下げる永遠のループに頼らなければならない (ウォッチドッグの待ち時間など) 組み込みシステムのようなプログラムにとって、clang を 100% 役に立たなくしています。
関連
-
error: 'for' loop initial declaration is only allowed in C99 mode 原因と解決方法
-
関数 'malloc' の暗黙の宣言に対する解決策
-
C 言語のポインタ配列のポインタ型、ポインタに値を割り当てるために配列名を使用、コンパイル時の警告:互換性のないポインタ型からの初期化
-
エラー: 宣言されていない識別子 'bool' の使用と C コンパイラでの問題点
-
[解決済み] Windows用Cコンパイラ?[クローズド]
-
[解決済み] C 言語で const char* を char* に変換するには?
-
[解決済み] ソケットアクセプト - "開かれているファイルが多すぎる"
-
[解決済み] Cコードの単体テスト【終了しました
-
[解決済み] プログラム終了前にmallocの後にfreeをしないと本当に何が起こるのか?
-
[解決済み] C言語のi++と++iに性能差はあるのでしょうか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
Solve Dev-c++ [エラー] 'for' ループの初期宣言は、C99 または C11 モードでのみ許可されます。
-
C/C++の再定義
-
コンパイルエラー:可変長オブジェクトが初期化されていない可能性があります。
-
C: 1を求める! + 2! + 3! + ... + n! (ループ)
-
[解決済み] flexible array member not at end of structエラーの原因は何ですか?
-
[解決済み] C言語の書式指定子 %ul と %lu の違いは何ですか?
-
[解決済み] CコードでEOFを表現する?
-
[解決済み] mallocとcallocの違い?
-
[解決済み] なぜC言語では構造体を頻繁にtypedefする必要があるのですか?
-
[解決済み] C 言語の配列へのポインタ/ポインタの配列の曖昧さ解消