uchan note

プログラミングや電子工作の話題を書きます

MOVZX r64, r/m8は何のために存在するのか分からない

MOVZX は,レジスタやメモリの値を,それよりビット数の大きいレジスタに 0 拡張しつつコピーする x86-64 の命令です。 その中の MOVZX r64, r/m8 の存在意義が分からなかったので,どなたかご存知の方は教えてください。

MOVZX

x86x86-64 では,レジスタの中の一部分を個別に利用する機能があります。 例えば RAX レジスタでは,下位 8 ビットを AL,下位16 ビットを AX,下位32 ビットを EAX としてアクセス可能です。

MOV AL, byte [RDI]

とすると,RDI レジスタが指すメモリアドレスから 1 バイトを読んで AL にコピーできます。 このとき RAX レジスタの他の部分は変更されません。

MOVZX は,RAX の他のビットを 0 にしつつ 1 バイトのデータをコピーするのに便利です。

MOVZX EAX, byte [RDI]

こうすると AL に 1 バイトがコピーされつつも,RAX の他のビットは 0 になります。 第 1 オペランドが EAX なのに RAX の上位 32 ビットも 0 になりますが,それは x86-64 の仕様です。 64 ビットレジスタの下位 32 ビットを変更すると,自動で上位 32 ビットが 0 クリアされるのです。

MOVZX r64, r/m8

Intel SDM で機械語を見てみるとこのようになってます。

命令 機械語
MOVZX r32, r/m8 0F B6 /r
MOVZX r64, r/m8 REX.W + 0F B6 /r
MOVZX r32, r/m16 0F B7 /r
MOVZX r64, r/m16 REX.W + 0F B7 /r

REX.W は 1 バイトのプレフィクスなので,機械語が 1 バイト増えます。 しかし,先ほど書いたように第 1 オペランドが r32 でも r64 でも効果は同じなため, このプレフィックス付きの機械語が定義されている意味が分かりません。

もしかしたら,REX.W を付けるバージョンは本当に意味がないけれども, 規則性を満たすために項目だけは用意されている,ということなのでしょうか。

追加議論

osdev-jp Slack において,R8L のような,x86-64 で登場した新しい汎用レジスタを第 2 オペランドに指定する場合に r64 バージョンを利用するのではないか? という指摘があり,調べましたが,どうやらそうでもないようです。

0000000000000000 <.text>:
   0:   0f b6 04 38             movzx  eax,BYTE PTR [rax+rdi*1]
   4:   0f b7 04 38             movzx  eax,WORD PTR [rax+rdi*1]
   8:   0f b6 c3                movzx  eax,bl
   b:   48 0f b6 c3             movzx  rax,bl
   f:   41 0f b6 c0             movzx  eax,r8b
  13:   49 0f b6 c0             movzx  rax,r8b

NASM では R8L ではなく R8B と書かないといけませんでした。まあそこは本質ではありませんので気にしないようにします。

アセンブル結果を見ると分かりますが,r32 バージョンでも 1 バイトプレフィクスが付くものの R8L を第 2 オペランドに指定できました。 ということで,やはり R8L を使う場合でも r64 バージョンを選択する積極的な意味はありませんね。