35727


gccでのインラインアセンブラの書き方の勉強をしているのですが、以下のような場合の書き方がわからずに困っています。やり方をご存知の方は教えていただけませんでしょうか。

# 【実現したいこと】

x86用gccの拡張インラインアセンブラ構文で、オペランドにレジスタまたはメモリ参照のみを許可し即値は禁止するようにしたい。

# 【経緯】

インラインアセンブラの実験のために以下のようなコード(ファイル名:test.c)を書いてみました。

[code]

    // test.c :
    //   gccを使用したインラインアセンブラの勉強のためのテストプログラム
    
    #include <stdio.h>
    
    // 被乗数(32bit)と乗数(32bit)から積(64bit)を求める。
    // u: 被乗数
    // v: 乗数
    // w_high: 積の上位32bitが格納される領域へのポインタ
    // return: 積の下位32bit
    __inline unsigned __int32 multiply(unsigned __int32 u, unsigned __int32 v, unsigned __int32 *w_high)
    {
        unsigned __int32 ret;
        __asm__("mull %3" : "=a"(ret), "=d"(*w_high) : "0"(u), "g"(v));
        return (ret);
    }
    
    
    // メイン関数
    int main()
    {
        // 定数の設定
        unsigned __int32 u = 50000;
        unsigned __int32 v = 2000;
        unsigned __int64 desired_w = (unsigned __int64)u * v;
        unsigned __int32 desired_w_high = (unsigned __int32)(desired_w >> 32);
        unsigned __int32 desired_w_low = (unsigned __int32)(desired_w);
    
        // 計算結果の変数の定義
        unsigned __int32 w_high;
        unsigned __int32 w_low;
    
        // 計算
        w_low = multiply(u, v, &w_high);
    
        // 計算結果の検査
        if (w_low == desired_w_low && w_high == desired_w_high)
            printf("OK.");
        else
            printf("NG.");
    }
    
[/code]

このソースを以下のようにコンパイルすると、以下のエラーメッセージが表示され、アセンブルに失敗しました。

[code]

    gcc -m32 -S test.c -O2
    gcc -m32 -c -o test.o test.s
    test.s: Assembler messages:
    test.s:26: Error: operand type mismatch for `mul'
    
[/code]

test.sの26行目付近は以下のようになっていました。

[code]

            movl    $50000, %eax
    /APP
     # 13 "test.c" 1
            mull $2000 # <=== ERROR!
     # 0 "" 2
    /NO_APP
            testl   %edx, %edx
            jne     L2
            cmpl    $100000000, %eax
            je      L12
    
[/code]

エラーメッセージが表示された理由は、MUL命令のオペランドに指定してはいけないはずの即値が指定されていることによるもののようです。  
様々な資料を調べてみたところ、gccがこのようなコードを生成した理由は（わかってみれば当然のことですが）乗数にあたるオペランド制約文字がレジスタまたはメモリ参照だけではなく即値も許可する`"g"`であったことが原因でした。  
確認のため、`"g"`の代わりのオペランド制約文字として`"r"`または`"m"`を使用すれば正常にアセンブルできました。

この問題の対処方法として、`"g"`の代わりのオペランド制約文字として`"r"`または`"m"`を使用する、あるいはそもそも最適化を行わない、といった方法も検討中ではあるのですが、まずは、
**拡張インラインアセンブラ構文のオペランド制約で「レジスタまたはメモリ参照のみ」を許可する方法はないか**
、という方向で解決できないかを調べたいと思っています。

しかし、そういった需要は特殊なものではないと思っているのですが、今のところ望む答えが見つかっていません。  
いい方法をご存知の方がおられましたら是非教えてください。  
よろしくお願いします。

# 【使用した環境】

  * CPU: Intel Core i7 7700K
  * OS: Windows10 Pro 64bit
  * GCC: version 7.1.0 (i686-win32-dwarf-rev0, Built by MinGW-W64 project)


CC BY-SA 3.0
2017-06-15T17:40:39.787
35615
2017-06-19T22:18:38.760
2017-06-19T22:18:38.760
23553
23553
question
c gcc assembly
x86用gccの拡張インラインアセンブラ構文で、オペランドにレジスタまたはメモリ参照のみを許可し即値は禁止するようにしたい


[GCCマニュアル](https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html#Simple-
Constraints)によれば「レジスタまたはメモリを許容するが、即値だけは許容しない」という制約は定義されません。

> "g"の代わりのオペランド制約文字として"r"または"m"を使用する

質問中にあるとおり、`r`（レジスタのみ）もしくは`m`（メモリのみ）を明示する必要があります。

**追記:** 質問者さんが自己解決されている通り、オペランド制約文字は`rm`のように複数同時記述ができるそうです。（知らなんだ...）

* * *

> あるいはそもそも最適化を行わない

AdHocな緊急回避としてはともかく、恒久的な対処としては不適切だと思います。自己責任でどうぞ。


CC BY-SA 3.0
2017-06-16T07:03:15.357
35633
2017-06-18T05:30:41.390
2017-06-18T05:30:41.390
49
49
35615
answer


解決しましたので報告いたします。

GCCのマニュアルを <https://gcc.gnu.org/onlinedocs/>
から入手し、いただいたご助言をもとに調べたところ、以下のような記述を見つけました。

> 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.

これによると、オペランド制約文字を並べて書いた場合はコンパイラが最も適したものを選んでくれるようです。  
実際にどのようなコードが生成されるのか、以下のソースファイルをコンパイルして実験してみました。

[code]

    // test2.c:
    //   x86版gccの拡張インラインアセンブラでオペランド制約文字の実験をするテストプログラム
    
    #include <stdio.h>
    
    #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
    
[/code]

コンパイルには以下のコマンドを使用しました。

[code]

    gcc -m32 -S test2.c -O2
    
[/code]

生成されたコードの抜粋をソースファイル中にコメントとして併記してあります。  
これを見ると、MUL命令のオペランドには状況によってレジスタまたはメモリ参照が期待した通りに使い分けられています。


CC BY-SA 3.0
2017-06-19T22:16:35.240
35727
2017-06-19T22:16:35.240
23553
35615
answer