web-dev-qa-db-ja.com

「volatilevoidfunction(...)」は何をしますか?またはしましたか?

私は見ました 文法の観点から、C++関数で「volatile」キーワードはいくつ使用されていますか? 関数でのvolatileキーワードの使用についてですが、その質問からのケース1の明確な説明はありませんでしたした。回答者の1人による、それは無意味/役に立たないように思われたという声明だけです。

しかし、GNUCのAESソフトウェア実装は文字通り何年も使用されており、次のような多くの機能を備えているため、その声明を完全に受け入れることはできません。

INLINE volatile void functionname( /* ... */ ) {
    /* ... */
    asm( /* ... */ ) // embedded Assembly statements
    /* ... */
}

その使用には理由があったに違いありません。誰でもできます:

[〜#〜] a [〜#〜]。元の理由を教えてください。そして

[〜#〜] b [〜#〜]。今、どのように望ましい効果を達成するのですか?

私はUbuntuとGCC4.6.3を使用しています。


注意:
void fatal( /* ... */ ) { /* ... */ exit(1); }

typedef void voidfn ();

volatile voidfn fatal;

これにより、コンパイラは「致命的」が戻らないことを認識できます。

しかし、そのシナリオはAESコードには当てはまらないようです。アセンブリで何かをしてから久しぶりですが、ジャンプなどは認識できると思います。

21
EdwinW

参考文献

証拠

  • volatile void function(...)はC99に厳密に準拠していません。 (@Adamと@ouahに感謝します。@ AdamはC99仕様を掘り下げてくれて、@ ouahは上記のDRを教えてくれました。)

  • GCCは、バージョン2.5で_volatile void_の代わりに__attribute__((noreturn))を追加しましたが、バージョン2.5より前のコンパイラとのコード互換性をサポートするためにバージョン4.6.3まで_volatile void_を受け入れ続けています。 (GCCドキュメント。)

  • 上記のコードは、命令がアドレスレジスタを操作しているようには見えず、ジャンプコマンドを実行していないため、実際に制御を呼び出し元に戻します。代わりに、さまざまな値を32ビットレジスタにロードします。 (コード検査。)

  • 行323から333のコマンドは、暗号化操作をサポートする特別なオペコードを実装します。 (コード検査と「南京錠」コード。)

  • アセンブリ関数を使用するコードは、明らかにそれらが戻ることを期待しています。 (コード検査。)

  • noreturn属性は、関数が返されないことをコンパイラーに通知するため、コンパイラーはそれに基づいて最適化を行うことができます。 (GCCドキュメント。)

  • GCCドキュメントから:noreturn関数を呼び出す前に、呼び出し元の関数によって保存されたレジスターが復元されると想定しないでください。

解決

最終的に私を手がかりにしたのは同僚との話し合いでした。関数が戻らないと宣言した場合、コンパイラは別のことをしなければなりません。 GCC文書の調査により、これが確認されました。

A.元の理由

次の質問を自問する必要があります。

質問:AESコードは、特に値を32ビットレジスタにロードし、それらに対して操作を実行します。 コードの残りの部分にどのように答えを返すのですか?

Answer:GCCの最適化は、戻り時に値を上書きしていた呼び出し元の関数のレジスターが保存されないことを意味します。アセンブリ言語関数での計算結果は、後続のコードで使用できるようにレジスタに残ります。

B.今すぐ目的の効果を達成する:

ほとんど放っておいてください。あなたがするかもしれない唯一のことは、_volatile void_戻り値の型を単にvoidに置き換え、そしてnoreturn属性を関数に追加することです。理論的には、これはまったく同じ効果があるはずです。実際には、それは壊れていません、それを修正しないでください。

望ましさ

この手法を広範に使用することは絶対にお勧めしません。まず、各コンパイラのカスタマイズに依存します。第二に、それはそれらのコンパイラが「戻り値なし」の場合の処理​​方法を変更しないことに依存します。第三に、それはその後のメンテナを混乱させる可能性があります。

このようなことが理にかなっている唯一の状況は、高度に専門化されたマシンコードを利用して、他の方法では不可能な速度の向上を達成する場合です。それでも、トレードオフとのバランスを取る必要があります。

この例では、正確に2つのコンパイラがサポートされており、マシンが利用できる特定のハードウェアサポートを備えている場合に限ります。それ以外の場合は、すべて標準のCコードで処理されます。それは大変な努力です。あなたがそれをする前にそれが報われることを確認してください。

6
EdwinW

gccドキュメント(2015年2月まで) によると、Cでの関数の戻り値としての_volatile void_(C++ではない)は、関数の__attribute__((noreturn))と同等です。関数が戻らないことをコンパイラーに通知します。

12
Chris Dodd

C99標準は§6.7.3/ 3でこれを述べています:

修飾型に関連付けられたプロパティは、左辺値である式に対してのみ意味があります。114)

114)実装は、constではないvolatileオブジェクトをストレージの読み取り専用領域に配置する場合があります。さらに、そのアドレスが使用されない場合、実装はそのようなオブジェクトにストレージを割り当てる必要はありません

§6.2.5/ 19は言う:

voidタイプは、空の値のセットで構成されます。完了できない不完全な型です。

そして§6.3.2.1/ 1は言う:

lvalueは、オブジェクトタイプまたはvoid以外の不完全なタイプの式です。53) [...]

したがって、voidは左辺値ではないため、型修飾子(constvolatile、およびrestrict)は、型void。したがって、C99準拠のコンパイラでは、const voidvolatile voidは無意味です(ただし、ポインタからconst voidconst volatile意味のある)。

さらに、§6.9.1/ 3の制約により、関数は修飾された型のvoidを返すことができません。

関数の戻り値の型は、voidまたは配列型以外のオブジェクト型でなければなりません。

これは制約であるため、適合コンパイラ必須診断を発行します(§5.1.1.3/ 1)。したがって、volatile voidを返す関数はC99では許可されていません。

volatile voidが以前何をしていたかについては、私にはわかりませんし、実際に推測することもできません。あなたが見ているAESコードは、おそらくクリーンアップされていない古いくだらないものを持っているだけだと思います。

9
Adam Rosenfield