質問: * CPU: Intel Core i7 7700K * OS: Windows10 Pro 64bit * GCC: version 7.1.0 (i686-win32-dwarf-rev0, Built by MinGW-W64 project)
回答: 解決しましたので報告いたします。 GCCのマニュアルを から入手し、いただいたご助言をもとに調べたところ、以下のような記述を見つけました。 > > GCC 7.1 Manual: 6.45.2.5 Input Operands の constraintの項目より > > > When you list more than one possible location (for example, ‘"irm"’), the compiler chooses the most efficient one based on the current context. > > > これによると、オペランド制約文字を並べて書いた場合はコンパイラが最も適したものを選んでくれるようです。 実際にどのようなコードが生成されるのか、以下のソースファイルをコンパイルして実験してみました。 ``` // test2.c: // x86版gccの拡張インラインアセンブラでオペランド制約文字の実験をするテストプログラム #include #pragma region 簡単な拡張インラインアセンブラ構文を含むインライン関数 // 被乗数(32bit)と乗数(32bit)から積(64bit)を求める。除数(v)は制約"rm"で渡す。 // u: 被乗数 // v: 乗数 // w_high: 積の上位32bitが格納される領域へのポインタ // return: 積の下位32bit __inline unsigned __int32 multiply_rm(unsigned __int32 u, unsigned __int32 v, unsigned __int32 *w_high) { unsigned __int32 ret; __asm__("mull %3" : "=a"(ret), "=d"(*w_high) : "0"(u), "rm"(v)); return (ret); } #pragma endregion #pragma region 制約文字"rm"のオペランドに即値を渡した場合 __declspec(dllexport) unsigned __int32 __stdcall do_test_1(unsigned __int32 *w_high) { return (multiply_rm(200, 500, w_high)); } // // ↓↓↓生成されたアセンブリコード(抜粋)↓↓↓ // // movl 4(%esp), %ecx // movl $200, %eax // movl $500, %edx // /APP // # 16 "test2.c" 1 // mull %edx // # 0 "" 2 // /NO_APP // movl %edx, (%ecx) // ret $4 // #pragma endregion #pragma region 制約文字"rm"のオペランドにメモリ上の値を渡した場合 __declspec(dllexport) unsigned __int32 __stdcall do_test_2(unsigned __int32 u, unsigned __int32 v, unsigned __int32 *w_high) { return (multiply_rm(u, v, w_high)); } // // ↓↓↓生成されたアセンブリコード(抜粋)↓↓↓ // // movl 12(%esp), %ecx // movl 4(%esp), %eax // /APP // # 16 "test2.c" 1 // mull 8(%esp) // # 0 "" 2 // /NO_APP // movl %edx, (%ecx) // ret $12 // #pragma endregion #pragma region 制約文字"rm"のオペランドに別の計算の結果の値を渡した場合 __declspec(dllexport) unsigned __int32 __stdcall do_test_3(unsigned __int32 u1, unsigned __int32 u2, unsigned __int32 v1, unsigned __int32 v2, unsigned __int32 *w_high) { return (multiply_rm(u1 + u2, v1 + v2, w_high)); } // // ↓↓↓生成されたアセンブリコード(抜粋)↓↓↓ // // movl 20(%esp), %ecx // movl 8(%esp), %eax // movl 16(%esp), %edx // addl 4(%esp), %eax // addl 12(%esp), %edx // /APP // # 16 "test2.c" 1 // mull %edx // # 0 "" 2 // /NO_APP // movl %edx, (%ecx) // ret $20 // #pragma endregion ``` コンパイルには以下のコマンドを使用しました。 ``` gcc -m32 -S test2.c -O2 ``` 生成されたコードの抜粋をソースファイル中にコメントとして併記してあります。 これを見ると、MUL命令のオペランドには状況によってレジスタまたはメモリ参照が期待した通りに使い分けられています。