hotmanの備忘録とライブラリ置き場.

競技プログラミングのためにインラインアセンブラに入門してみたので解説【入出力,基本命令編】

はじめに

僕自身が学んでる途中なので嘘などあるかも知れません
高速化に目覚めたので書いていきます
これ使うだけじゃ速くならないと思います
SIMD 使う前の準備だと思って練習してます
atcoder の C++(GCC 9.2.1)準拠です

入出力

int main(){
    int res;
    asm("":"=a"(res):"a"(2));
    cout<<res<<endl;
}

2 を a に入力して、何もせず、a を res に出力するコードです。
当然 res は 2 になります
asm("コード":"出力":"入力"); が基本の書き方になります。
セミコロンで区切れば命令を繋げられます
*2. 変数と代入

int main(){
    int res;
    asm("mov %2,%1":"=a"(res):"a"(0),"b"(2));
    cout<<res<<endl;
}

左にあるものから変数が割り当てられて行きます

つまり
"=a"とあるので%0 は a
"a"とあるので%1 は a
"b"とあるので%2 は b
といった具合です

"="は出力専用であることを示しているらしいですが普通に読めてそうでよくわからないです
また、a,b,=といった文字は制約文字と言ってなんでもいいわけでは無いです
とりあえず、a,b,c,d が変数っぽく使えるので今回はそれで行きます。
制約文字についての詳しい説明は[http://marunomaruno.web.fc2.com/gcc-inline-asm01.html?c=appendix:title]にあります

次に mov 関数について説明すると
mov x,y は y に x を代入する構文です
x,y 逆じゃないのって思うんですが、デフォルトだとこれらしいです(GAS 構文と言うらしいです)

注意の要らない基本命令

整数型限定です
64bit で渡せば 64bit で
32bit で渡せば 32bit でやってくれるみたいです
fujita nozomu さんありがとうございます!!

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">mov と movabs は引数の値の大きさで変わります。<a href="https://t.co/ejXwdw5lyh">https://t.co/ejXwdw5lyh</a>
eax や edx が 32bit で、rax や rdx は 64bit レジスタとなります。</p>— fujita nozomu (@fujitanozomu) <a href="https://twitter.com/fujitanozomu/status/1274279933200576517?ref_src=twsrc^tfw">June 20, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

int main(){
    int a;
    asm("add %2,%1":"=a"(a):"a"(1),"b"(2)); //(%1)+=(%2);
    asm("sub %2,%1":"=a"(a):"a"(1),"b"(2)); //(%1)-=(%2);
    asm("imul %2,%1":"=a"(a):"a"(1),"b"(2)); //(%1)*=(%2); imulなので注意
    asm("inc %1":"=a"(a):"a"(1));           //(%1)++;
    asm("dec %1":"=a"(a):"a"(1));           //(%1)--;
    asm("or %2,%1":"=a"(a):"a"(1),"b"(2));  //(%1)|=(%2);
    asm("and %2,%1":"=a"(a):"a"(1),"b"(2)); //(%1)&=(%2);
    asm("xor %2,%1":"=a"(a):"a"(1),"b"(2)); //(%1)^=(%2);
    asm("not %1":"=a"(a):"a"(1));           //(%1)=~(%1);
    asm("shl %1":"=a"(a):"a"(1));           //(%1)<<=1;
    asm("shr %1":"=a"(a):"a"(2));           //(%1)>>=1;
}

[http://ankokudan.org/d/dl/pdf/pdf-linuxasm.pdf]
が詳しいです

注意のいる命令(idiv)

この場合、a を(%3)で割った商を a に、余りを d に格納します
a と d は固定です。意味わかんねぇ
なので下の場合出力は 1 2 です

int main(){
    int a,d;
    // (a,d)=(a/(%3),a%(%3))
    asm("idiv %3":"=a"(a),"=d"(d):"a"(5),"b"(3),"d"(0));
    cout<<a<<" "<<d<<endl;
}

終わりに

学んだのはここまでなので、一旦ここまでにします
if,for を使える様になりたい

参考文献

https://kikairoya.hatenablog.com/entry/20100220/1266668559
https://ja.cppreference.com/w/cpp/language/asm

The source code for this blog is available on GitHub.