拡張するキャスト(Intel記法のアセンブラ)

よくわかってなくてだいぶはまったが、やっとわかったのでまとめておく。 x86_64のCPUを前提とする。

64bitレジスタでのキャストと32bitレジスタ

64bitレジスタで符号拡張するキャスト

符号拡張するキャストというのは、要はsigned型へのキャストすること。

1byteと2byteはmovsxで、4byteのみmovsxdを使う。

# 1byteにキャスト
movsx rax, al

# 2byteにキャスト
movsx rax, ax

# 4byteにキャスト
movsxd rax, eax

64bitレジスタでゼロ拡張するキャスト

ゼロ拡張するキャストというのは、要はunsigned型へのキャストすること。

8byteの値をゼロ拡張して1byteと2byteへのキャストしたい場合はmovzxで64bitレジスタに転送すればよい。

例えば以下でよい。

# 8byteの値を1byteの値にゼロ拡張してキャストする
movzx rax, al

# 8byteの値を2byteの値にキャストする
movzx rax, ax

8byteの値をゼロ拡張して4byteへのキャストしたい場合、movzxではできない。 ではどうするかというと、movで4byteレジスタから4byteレジスタに転送してやればよい。

例えば以下。

# 8byteの値を4byteの値にキャストする
mov eax, eax

これでうまくいく理由は32bitレジスタにデータを転送すると暗黙的に上位ビットがゼロ拡張されるため。

サンプルコード

この辺の挙動を確認するためのアセンブラを書いた。

  .intel_syntax noprefix
  .data
.LC0:
  .string "%lX\n"
  .text
  .global main
main:
  push rbp
  mov rbp, rsp

  mov rax, 68719476735  # 0xFFFFFFFF
  mov eax, eax  # cast to unsigned 4byte type
  push rax

  pop rsi  # arg 1

  lea rdi, .LC0[rip]  # arg 0

  mov rax, 0
  call printf@PLT

  push 0  # num 0
  pop rax
  mov rsp, rbp
  pop rbp
  ret

このコード(asm.sとする)をgccコンパイルして実行するには以下のコマンドでできる。

gcc -o asm asm.s
./asm

実行すると、4byteへのキャストした結果として、0xFFFFFFFFの出力が確認できる。 ちなみに、以下の行をコメントアウトするとキャストなしの結果として、0xFFFFFFFFF(9桁のF)の出力が確認できる。

mov eax, eax