23

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

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

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

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

A. _ 元の理由を教えてください。と

B. _ 今すぐ望ましい効果を達成する方法は?

Ubuntu と GCC 4.6.3 を使用しています。


注: 私が説明に最も近いのは、GCC 2.5より前では、次の方法で2.5で実装された「noreturn」属性を偽装できるということです。

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

typedef void voidfn ();

volatile voidfn fatal;

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

しかし、そのシナリオは AES コードには当てはまらないようです。久しぶりに組み立てましたが、ジャンプとかなら分かると思います。

4

3 に答える 3

15

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

于 2013-01-12T00:56:51.743 に答える
9

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

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

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

§6.2.5/19は言う:

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

そして§6.3.2.1/1は言う:

左辺値voidは、オブジェクト型または;以外の不完全な型を持つ式です。53) [...]

したがって、voidは左辺値ではないため、型修飾子(、、、constおよびvolatilerestrictは型の式には意味がありませんvoid。したがって、C99準拠のコンパイラではconst voidvolatile voidは意味がありません(ただし、ポインタ意味がconst voidあります)。const volatile

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

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

これは制約であるため、準拠するコンパイラは診断を発行する必要があります(§5.1.1.3/ 1)。volatile voidしたがって、 C99では関数を返すことはできません。

volatile voidをしていたのか、私にはわからず、推測もできません。あなたが見ているAESコードには、おそらくクリーンアップされていない古い断片が含まれていると思います。

于 2013-01-12T00:18:23.967 に答える
7

参考文献

証拠

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

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

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

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

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

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

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

解決

最終的に私に手がかりを与えたのは、同僚との議論でした。関数が返らないと宣言した場合、コンパイラは何か違うことをしなければなりません。GCC のドキュメントを調べると、これが確認されました。

A. 本来の理由

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

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

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

B. 今すぐ望ましい効果を達成する:

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

望ましさ

この手法を多用することは絶対にお勧めできません。まず、各コンパイラのカスタマイズに依存します。第二に、「ノーリターン」ケースの処理方法を変更しないコンパイラに依存します。第 3 に、後続のメンテナが混乱する可能性があります。

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

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

于 2013-01-14T19:21:20.573 に答える