よくわかってなくてだいぶはまったが、やっとわかったのでまとめておく。 x86_64のCPUを前提とする。
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