web-dev-qa-db-ja.com

asm、asm volatile、clobbering memoryの違い

ロックフリーのデータ構造とタイミングコードを実装する場合、コンパイラの最適化を抑制することが必要になることがよくあります。通常、これはasm volatileを使用してclobberリストにmemoryを使用してこれを行いますが、asm volatileまたはプレーンasmのクラバリングメモリのみが表示される場合があります。

これらの異なるステートメントは、コード生成にどのような影響を及ぼしますか(特にGCCでは、移植性が低いため)。

参考までに、これらは興味深いバリエーションです。

asm ("");   // presumably this has no effect on code generation
asm volatile ("");
asm ("" ::: "memory");
asm volatile ("" ::: "memory");
59
jleahy

GCCドキュメントの "Extended Asm"ページ を参照してください。

asmの後にキーワードvolatileを記述することにより、asm命令が削除されないようにすることができます。 [...] volatileキーワードは、命令に重要な副作用があることを示します。 GCCは、到達可能な場合、volatile asmを削除しません。

そして

出力オペランドのないasm命令は、volatile asm命令と同様に扱われます。

どの例にも出力オペランドが指定されていないため、asmおよびasm volatileフォームは同じように動作します:コード内に削除できないポイントを作成します(到達不能であることが証明されない限り)。

これは何もしないこととまったく同じではありません。コード生成を変更するダミーasmの例については この質問 を参照してください-その例では、ループを1000回ループするコードは、ループの16回の繰り返しを計算するコードにベクトル化されますすぐに;ただし、ループ内にasmが存在すると、最適化が抑制されます(asmに1000回到達する必要があります)。

"memory" clobberは、GCCがasmブロックによって任意のメモリが任意に読み書きできると想定するため、コンパイラがロードまたはストア全体の順序を変更するのを防ぎます。

これにより、GCCはアセンブラ命令全体でレジスタにキャッシュされたメモリ値を保持せず、そのメモリへのストアまたはロードを最適化しません。

(ただし、CPUが別のCPUに関してロードとストアを並べ替えるのを防ぐことはできませんが、そのためには実際のメモリバリア命令が必要です。)

54

asm ("")は何もしません(少なくとも、何もしないはずです)。

asm volatile ("")も何もしません。

asm ("" ::: "memory")は、単純なコンパイラフェンスです。

asm volatile ("" ::: "memory") AFAIKは前と同じです。 volatileキーワードは、このAssemblyブロックを移動できないことをコンパイラーに伝えます。たとえば、すべての呼び出しで入力値が同じであるとコンパイラが判断した場合、ループから引き上げられます。コンパイラがどのような条件下でアセンブリの配置を最適化しようとするアセンブリについて十分に理解していると判断するかはわかりませんが、volatileキーワードはそれを完全に抑制します。とはいえ、コンパイラーが宣言された入力または出力を持たないasmステートメントを移動しようとした場合、非常に驚​​くでしょう。

ちなみに、volatileは、出力値が未使用であると判断した場合、コンパイラーが式を削除できないようにします。ただし、これは出力値がある場合にのみ発生する可能性があるため、asm ("" ::: "memory")には適用されません。

6
Lily Ballard

リリーバラードの答え の完全性のために、Visual Studio 2010は同じことを行う_ReadBarrier()_WriteBarrier()および_ReadWriteBarrier()を提供しています(VS2010 tは64ビットアプリのインラインアセンブリを許可します)。

これらは命令を生成しませんが、コンパイラの動作に影響します。良い例は here です。

MemoryBarrier()lock or DWORD PTR [rsp], 0を生成します

3
James