コンパイラーはローカルvolatileを定数フォールドできますか?


25

この単純なコードを考えてみましょう:

void g();

void foo()
{
    volatile bool x = false;
    if (x)
        g();
}

https://godbolt.org/z/I2kBY7

への潜在的な呼び出しgccclang最適化もされていないことがわかりgます。これが私の理解で正しいです:抽象マシンは、と仮定することでvolatile一定の折りので変数は、(例えば、ハードウェア・マッピングされたことに起因する)いつでも変更される可能性falseに初期化をifチェックは間違っているだろう。

しかし、MSVCはへの呼び出しをg完全に排除します(volatileただし、読み取りと書き込みを維持します!)。これは標準に準拠した動作ですか?


背景:デバッグ出力をオンザフライでオン/オフできるようにするために、この種のコンストラクトを時々使用します:コンパイラは常にメモリから値を読み取る必要があるため、デバッグ中に変数/メモリを変更すると、それに応じて制御フローが変更されます。MSVC出力は値を再読み取りしますが、それを無視します(たぶん、定数の折りたたみやデッドコードの除去のため)、これはもちろん、ここでの私の意図を無効にします。


編集:

  • 読み取りと書き込みの排除については、volatileここで説明します。コンパイラーがローカルの揮発性変数を最適化することを許可されていますか?(ネイサンに感謝!)標準は、これらの読み取りと書き込み発生する必要があることを十分に明確に考えています。しかし、その議論は、コンパイラーがそれらの読み取りの結果を当然のこととして受け取り、それに基づいて最適化することが合法かどうかをカバーしていません。私はこれが標準では不十分/不特定であると思いますが、誰かが私を間違っていると証明したら私は幸せです。

  • もちろんx、問題を回避するために非ローカル変数を作成することもできます。この質問は、好奇心から外れています。


3
これは明らかなコンパイラのバグのようです。
Sam Varshavchik

1
私の知る限り、これはあたかもルールの下で合法です。コンパイラは、オブジェクトが揮発性であっても、その状態を変更して折りたたむことができないことを証明できます。私はそれを答えに入れるほどの自信はありませんが、私はそれが正しいと感じています。
NathanOliver

3
しかし、変数がデバッガーによって変更される可能性があるというOPの主張は合理的だと思います。たぶん誰かがMSVCでバグレポートを提出するべきです。
ブライアン、

2
@curiousguy結果を破棄したり、正確な値を想定したりしても、まだ読んでいます。
Deduplicator

2
興味深いことに、それはx64に対してのみ実行されます。x86バージョンはまだグラム()を呼び出しますgodbolt.org/z/nc3Y-f
ジェリーエレミヤ

回答:


2

[intro.execution](段落番号は異なります)を使用してMSVCの動作を説明できると思います。

自動ストレージ期間を持つ各オブジェクトのインスタンスは、そのブロックへの各エントリに関連付けられています。そのようなオブジェクトは存在し、ブロックの実行中およびブロックが中断されている間、最後に格納された値保持します...

この規格では、揮発性glvalueを介した読み取りの削除は許可されていませんが、上記の段落は値を予測できると解釈できますfalse


ところで、C標準(N1570 6.2.4 / 2)は、

オブジェクトは存在し、一定のアドレスを持ち、その存続期間を通して最後に格納された値を保持します。34


34)揮発性オブジェクトの場合、最後のストアはプログラムで明示的である必要はありません。

Cメモリ/オブジェクトモデルの自動ストレージ期間を持つオブジェクトへの非明示的なストアがあるかどうかは不明です。


ターゲットプラットフォームで非明示的なストアが可能な場合にコンパイラが知っていることに同意する
MM

1
これが真の場合、ローカルの揮発性オブジェクトは(少なくともMSVCでは)完全に無意味ですか?volatile最適化の目的で無視された場合、追加によって余計な読み取り/書き込み以外に何かが発生することはありますか?
Max Langhof

1
@MaxLanghof完全に無意味なものと、期待したい効果を期待できないという違いがあります。
Deduplicator

1
@MaxLanghof浮動小数点計算の中間結果が80ビットレジスタに昇格され、精度の問題が発生することがあります。これはgcc主義であり、そのようなすべてのdoubleをvolatileとして宣言することによって回避されると思います。参照:gcc.gnu.org/bugzilla/show_bug.cgi
id

1
@philipxy PS私の回答参照してくださいアクセスは実装定義であることがわかっています。問題はアクセス(オブジェクトがアクセスされる)ではなく、値の予測です。
言語弁護士

2

TL; DRコンパイラーは、各揮発性アクセスに対して必要なことをすべて実行できます。しかし、ドキュメントはあなたに伝えなければなりません。


この規格では、プログラムが許可する「揮発性アクセス」およびその他の「観察可能な動作」(「副作用」によって実現)のシーケンスを定義し、実装は「as-if」ルールに従って尊重する必要があります。

しかし、標準は言う(私の太字の強調):

草案、プログラミング言語の標準C ++
ドキュメント番号:N4659日付
:2017-03-21

§10.1.7.1 cv-qualifiers

5 揮発性glvalueを介したアクセスのセマンティクスは、実装によって定義されます。[…]

同様に、インタラクティブデバイスの場合(私の太字を強調):

§4.6プログラムの実行

5整形式プログラムを実行する適合実装は、同じプログラムと同じ入力を使用して、抽象マシンの対応するインスタンスの可能な実行の1つと同じ観察可能な動作を生成するものとします。[...]

7適合実装の最小要件は次のとおりです。

(7.1)—揮発性glvalueを介したアクセスは、抽象マシンのルールに従って厳密に評価されます。
(7.2)—プログラムの終了時に、ファイルに書き込まれるすべてのデータは、抽象的なセマンティクスに従ってプログラムを実行した場合に生じる可能性のある結果の1つと同一でなければなりません。
(7.3)—対話型デバイスの入力と出力のダイナミクスは、プログラムが入力を待機する前にプロンプ​​ト出力が実際に配信されるような方法で行われるものとします。インタラクティブデバイスを構成するものは、実装によって定義されます。

これらをまとめて、プログラムの観察可能な動作と呼びます。[...]

(いずれにせよ、プログラムに対してどの特定のコードが生成されるかは、標準では指定されていません。)

したがって、規格では、揮発性アクセスは、抽象的なマシンの副作用の抽象的なシーケンスと、一部のコード(多分)が定義する結果として発生する可能性のある動作から除外できないと述べていますが、オブジェクトコードまたは実際の世界には何も反映されません。コンパイラのドキュメントで揮発性アクセスの構成要素が指示されてない限り、動作。インタラクティブデバイス用の同上。

機械の抽象的な副作用の抽象的なシーケンスや、一部のコード(おそらく)が定義する結果として発生する可能性のある動作に対して揮発性に関心がある場合そのように言います。しかし、生成される対応するオブジェクトコードに関心がある場合は、コンパイラとコンパイルのコンテキストでそれを解釈する必要があります

慢性的に人々は、揮発性アクセスのために抽象的なマシンの評価/読み取りが実装された読み取りを引き起こし、抽象的なマシンの割り当て/書き込みが実装された書き込みを引き起こすと誤って信じています。この信念の根拠はなく、実装ドキュメントにそういうことはありません。実装が「揮発性アクセス」に対して実際に何かを行うと/ iffが言った場合、人々はそのことを期待して正当化されます-多分、特定のオブジェクトコードの生成。


1
つまり、これは「言及したすべての副作用がノーオペレーションであるマシンを思いついた場合、すべてのプログラムをノーオペレーションにコンパイルすることで合法的なC ++実装を持っている」ということになります。もちろん、抽象的な機械の副作用はトートロジー的に抽象的なので、実際に観察可能な効果に関心があります。これには副作用のいくつかの基本的な概念が必要であり、「揮発性アクセスは明示的なメモリアクセス命令につながる」(標準が問題ではない場合でも)がその一部であるため、私は実際に「言う抽象セマンティクスの代わりにコードが必要な場合」それでも、+ 1。
Max Langhof

はい、実装の品質が重要です。それにもかかわらず、「オブザーバブル」[sic]はファイルへの書き込み以外は実装定義です。ブレークポイントを設定したり、特定の実際のメモリにアクセスしたり、「揮発性」を無視したりできるようにしたい場合は、抽象揮発性読み取りおよび書き込みでコンパイラライターに適切なコードを出力させる必要があります。PS C ++では、「副作用」は観測自体の動作には使用されず、部分式の部分順序評価を記述するために使用されます。
philipxy

[原文のまま]を説明する気?あなたはどのソースを引用していますか、そして間違いは何ですか?
Max Langhof

Re sicとは、実装がそうであると言っている場合に限り、抽象的なマシンで観測可能であることは、現実の世界でのみ観測可能であることを意味します。
philipxy

1
実装では、対話型デバイスなどがないと主張できるため、どのプログラムでも何でも実行でき、それでも正しいと言えますか?(注:インタラクティブデバイスへの重点がわかりません。)
curiousguy

-1

チェックをスキップすることは合法だと思います。

誰もが引用したい段落

34)揮発性オブジェクトの場合、最後のストアはプログラムで明示的である必要はありません

実装は、そのようなストアがいつでも、または任意の揮発性変数に対して可能であると想定する必要があることを意味しません。実装は、どのストアが可能かを認識しています。たとえば、このような暗黙の書き込みは、デバイスレジスターにマップされた揮発性変数に対してのみ発生し、そのようなマッピングは外部リンケージを持つ変数に対してのみ可能であると想定することは完全に合理的です。または、実装では、そのような書き込みはワードサイズのワード境界のメモリ位置にのみ発生すると想定する場合があります。

そうは言っても、MSVCの動作はバグだと思います。通話を最適化する現実的な理由はありません。そのような最適化は準拠しているかもしれませんが、不必要に悪です。


なぜそれが悪いのか説明してくれませんか?コードショーでは、文字どおり関数を呼び出すことはできません。
David Schwartz、

@DavidSchwartzローカルvolatile変数のセマンティクスを指定した後にのみ、結論を下すことができます(上記の引用段落を参照)。標準自体ノートvolatile値が実装に用い不明で変更することができ、実装にヒントことになっています。
Max Langhof

@MaxLanghof実装が未知のものを正しく処理できる方法はありません。実際に役立つプラットフォームはvolatile、そのプラットフォーム上で使用できるものと使用できないものを指定することであり、その仕様の範囲外では、常にがらくたになります。
David Schwartz、

@DavidSchwartzもちろん可能です-抽象マシンのセマンティクス(特に、読み取りと書き込み)に従ってください。それは正しく最適化できないかもしれません-それが標準のポイントです。さて、それはノートであり、それゆえ規範的ではありません、そして私たちが両方とも言ったように、実装は何をするかを特定volatileし、それに固執することができます。私の要点は、コード自体(C ++標準/抽象マシンによる)ではg、呼び出し可能かどうかを判別できないことです。
Max Langhof

@DavidSchwartz暗黙の書き込みがないため、コードからはフォローされないため、コードはそのようなものを何も表示しません。
n。「代名詞」m。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.