インライン アセンブリでのレジスタの使用および保持
Microsoft 固有の仕様
一般に、__asm
ブロックの開始時にレジスタに値が割り当てられるとは想定しないでください。 レジスタの値は、個別の __asm
ブロック間で保持されることは保証されていません。 インライン コードのブロックを終了し、別のブロックを開始する場合、2 番目のブロックのレジスタに依存して、最初のブロックの値を保持することはできません。 __asm
ブロックは、通常の制御フローから任意のレジスタ値の結果を継承します。
__fastcall
呼び出し規約を使用する場合、コンパイラにより、スタックではなくレジスタに関数の引数が渡されます。 これにより、どのパラメーターがどのレジスタに登録されているかが関数で判別されないため、__asm
ブロックのある関数で問題が発生するおそれがあります。 関数が EAX でパラメーターを受け取って、EAX に他の何かを直ちに格納した場合、元のパラメーターは失われます。 また、__fastcall
で宣言されたすべての関数で ECX レジスタを保持する必要があります。
このようなレジスタの競合を回避するため、__asm
ブロックを含む関数に __fastcall
規則を使用しないでください。 /Gr コンパイラ オプションを使用してグローバルに __fastcall
規則を指定する場合は、__asm
ブロックを含むすべての関数を __cdecl
または __stdcall
で宣言します。 (__cdecl
属性により、その関数に対して C 呼び出し規約を使用するようにコンパイラに指示が出されます)。/Gr を使用してコンパイルしない場合は、__fastcall
属性を使用して関数を宣言しないでください。
__asm
を使用して C/c++ 関数でアセンブリ言語を記述する場合、EAX、EBX、ECX、EDX、ESI、または EDI レジスタを保持する必要はありません。 たとえば、「インラインアセンブリを使用した関数の記述」の POWER2.C の例の場合、power2
関数により、EAX レジスタの値は保持されません。 ただし、レジスタ アロケーターは、これらのレジスタを使用してブロック間で __asm
値を格納できないため、これらのレジスタの使用はコードの品質に影響します。 また、インライン アセンブラー コードで EBX、ESI、EDI を使用すると、コンパイラに、プロローグおよびエピローグ関数のレジスタを保存して復元するように強制します。
使用する他のレジスタ (DS、SS、SP、BP、flags レジスタなど) は、__asm
ブロックのスコープに対して保持する必要があります。 ESP および EBP レジスタを変更する理由がない場合 (スタックを切り替える場合など) は、それらを保持しておく必要があります。 「インライン アセンブリの最適化」も参照してください。
一部の SSE 型では 8 バイトのスタック アラインメントが必要であり、コンパイラに動的なスタック アラインメント コードの出力が強制されます。 アラインメント後にローカル変数と関数パラメーターの両方にアクセスできるようにするために、コンパイラは 2 つのフレーム ポインターを保持します。 コンパイラによりフレーム ポインターの省略 (FPO) が実行されると、EBP と ESP が使用されます。 コンパイラで FPO が実行されない場合は、EBX と EBP が使用されます。 コードが確実に正しく実行されるようにするには、asm コードで EBX を変更しないでください。これは、関数が動的なスタック アラインメントを必要とする場合にフレーム ポインターが変更される場合があるためです。 8 バイトのアラインメントされた型を関数の外に移動するか、EBX を使用しないようにします。
Note
インライン アセンブリ コードが STD または CLD 命令を使用して方向フラグを変更する場合は、フラグを元の値に戻す必要があります。
Microsoft 固有の仕様はここまで