ビットシフトをアセンブラで書く

Cソースの一部アセンブラ化に慣れてきました。
今回はビットシフトをアセンブラに置き換えてみます。

では、元ソース。0xffを左に1ビットシフトするプログラムです。

takk@deb9:~/tmp$ cat -n t.c
     1  #include <stdio.h>
     2  int main()
     3  {
     4          unsigned long a;
     5          a = 0xff;
     6          a = a << 1;
     7          printf("%08x\n",a);
     8  }
takk@deb9:~/tmp$

実行すると、最下位bitには0が入って0x1feになります。

takk@deb9:~/tmp$ gcc t.c
takk@deb9:~/tmp$ ./a.out
000001fe
takk@deb9:~/tmp$

objdump -dでmain関数のアセンブラを見てみます。

00000000000006b0 <main>:
 6b0:   55                      push   %rbp
 6b1:   48 89 e5                mov    %rsp,%rbp
 6b4:   48 83 ec 10             sub    $0x10,%rsp
 6b8:   48 c7 45 f8 ff 00 00    movq   $0xff,-0x8(%rbp)
 6bf:   00
 6c0:   48 d1 65 f8             shlq   -0x8(%rbp)
 6c4:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 6c8:   48 89 c6                mov    %rax,%rsi
 6cb:   48 8d 3d a2 00 00 00    lea    0xa2(%rip),%rdi        # 774 <_IO_stdin_used+0x4>
 6d2:   b8 00 00 00 00          mov    $0x0,%eax
 6d7:   e8 84 fe ff ff          callq  560 <printf@plt>
 6dc:   b8 00 00 00 00          mov    $0x0,%eax
 6e1:   c9                      leaveq
 6e2:   c3                      retq
 6e3:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 6ea:   00 00 00
 6ed:   0f 1f 00                nopl   (%rax)

ビットシフトっぽい、shlq命令が使われていますが、シフト量を指定している箇所が見つかりません。
2ビットシフトでビルドして、再度アセンブラを見てみることにします。

takk@deb9:~/tmp$ cat -n t.c
     1  #include <stdio.h>
     2  int main()
     3  {
     4          unsigned long a;
     5          a = 0xff;
     6          a = a << 2;
     7          printf("%08x\n",a);
     8  }
takk@deb9:~/tmp$ gcc t.c
takk@deb9:~/tmp$ ./a.out
000003fc
takk@deb9:~/tmp$
00000000000006b0 <main>:
 6b0:   55                      push   %rbp
 6b1:   48 89 e5                mov    %rsp,%rbp
 6b4:   48 83 ec 10             sub    $0x10,%rsp
 6b8:   48 c7 45 f8 ff 00 00    movq   $0xff,-0x8(%rbp)
 6bf:   00
 6c0:   48 c1 65 f8 02          shlq   $0x2,-0x8(%rbp)
 6c5:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 6c9:   48 89 c6                mov    %rax,%rsi
 6cc:   48 8d 3d a1 00 00 00    lea    0xa1(%rip),%rdi        # 774 <_IO_stdin_used+0x4>
 6d3:   b8 00 00 00 00          mov    $0x0,%eax
 6d8:   e8 83 fe ff ff          callq  560 <printf@plt>
 6dd:   b8 00 00 00 00          mov    $0x0,%eax
 6e2:   c9                      leaveq
 6e3:   c3                      retq
 6e4:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 6eb:   00 00 00
 6ee:   66 90                   xchg   %ax,%ax

シフト量1bitと2bit見比べてみます。
1bitシフト

 6c0:   48 d1 65 f8             shlq   -0x8(%rbp)

2bitシフト

 6c0:   48 c1 65 f8 02          shlq   $0x2,-0x8(%rbp)

1bitの場合は、シフト量のパラメータが不要なんですね。

では、Cソースを、アセンブラに置き換えて実行してみましょう。

takk@deb9:~/tmp$ cat -n t.c
     1  #include <stdio.h>
     2  int main()
     3  {
     4          unsigned long a;
     5          a = 0xff;
     6          asm("shlq -0x8(%rbp)");
     7          printf("%08x\n",a);
     8  }
takk@deb9:~/tmp$ gcc t.c
takk@deb9:~/tmp$ ./a.out
000001fe
takk@deb9:~/tmp$

うまくいきました。

次は、右シフトをやってみます。
左シフトがshlqだったので、右ソフトはおそらく、shrqってところでしょう。

takk@deb9:~/tmp$ cat -n t.c
     1  #include <stdio.h>
     2  int main()
     3  {
     4          unsigned long a;
     5          a = 0x8ff;
     6          asm("shrq $3,-0x8(%rbp)");
     7          printf("%08x\n",a);
     8  }
takk@deb9:~/tmp$ gcc t.c
takk@deb9:~/tmp$ ./a.out
0000011f
takk@deb9:~/tmp$

当てずっぽうでしたが、うまくいきました。