Cのif文はどうやってCPUで動く? アセンブリ上での表現
等号
ifeq.c
int a=1;
int b=2;
int c=3;
if(a==b){
c=4;
}
c=5;
ifeq.s
mov DWORD PTR -4[rbp], 1
mov DWORD PTR -8[rbp], 2
mov DWORD PTR -12[rbp], 3
mov eax, DWORD PTR -4[rbp]
cmp eax, DWORD PTR -8[rbp]
jne .L2
mov DWORD PTR -12[rbp], 4
.L2:
mov DWORD PTR -12[rbp], 5
不等号
ifneq.c
int a=1;
int b=2;
int c=3;
if(a!=b){
c=4;
}
c=5;
ifneq.s
mov DWORD PTR -4[rbp], 1
mov DWORD PTR -8[rbp], 2
mov DWORD PTR -12[rbp], 3
mov eax, DWORD PTR -4[rbp]
cmp eax, DWORD PTR -8[rbp]
je .L2
mov DWORD PTR -12[rbp], 4
.L2:
mov DWORD PTR -12[rbp], 5
分析
if(a==b)
の場合は jne
if(a!=b)
の場合は je
へ変換されています。
フラグレジスタ
jne及びjeの動作を理解するにはフラグレジスタの理解が欠かせません。
x86CPUにはフラグレジスタと呼ばれる特殊なレジスタがあり、各ビットにはそれぞれ異なる意味が割り当てられています。
代表的なビット構成は次のとおりです(一部省略):
|ID|VIP|VIF|AC| OF| DF| IF| TF| SF| ZF| 0| AF| 0| PF| 1| CF|
ZF(Zero Flag) は6番目のビットです。
ZF = 1 の場合は「直前の演算結果が 0(等しい)」を意味します。
je(jump if equal)= ZF が 1 ならラベル先へ移動
jne(jump if not equal)= ZF が 0 ならラベル先へ移動
if(a==b)
mov eax, DWORD PTR -4[rbp] ; 変数 a
cmp eax, DWORD PTR -8[rbp] ; 変数 b
jne .L2
mov DWORD PTR -12[rbp], 4
.L2:
mov DWORD PTR -12[rbp], 5
cmp
命令にて変数a
及びb
を比較し、一致した場合はZF=1となる。
jne
はZF=0
の場合にラベル先へ移動する。
つまり、一致しない場合はL2
へ跳ぶ
一致した場合はmov DWORD PTR -12[rbp], 4
を実行する。
if(a!=b)
mov eax, DWORD PTR -4[rbp] ; 変数 a
cmp eax, DWORD PTR -8[rbp] ; 変数 b
je .L2
mov DWORD PTR -12[rbp], 4
.L2:
mov DWORD PTR -12[rbp], 5
cmp
命令にて変数a
及びb
を比較し、一致ない場合はZF=0となる。
je
はZF=1
の場合にラベル先へ移動する。
つまり、一致する場合はL2
へ跳ぶ
一致しない場合はmov DWORD PTR -12[rbp], 4
を実行する。
完全なコード(参考)
長いので折り畳みます
ifeq.c
ifeq.c
#include <stdio.h>
int main() {
int a=1;
int b=2;
int c=3;
if(a==b){
c=4;
}
c=5;
return 0;
}
ifeq.s
ifeq.s
.file "ifeq.c"
.intel_syntax noprefix
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
push rbp
.seh_pushreg rbp
mov rbp, rsp
.seh_setframe rbp, 0
sub rsp, 48
.seh_stackalloc 48
.seh_endprologue
call __main
mov DWORD PTR -4[rbp], 1
mov DWORD PTR -8[rbp], 2
mov DWORD PTR -12[rbp], 3
mov eax, DWORD PTR -4[rbp]
cmp eax, DWORD PTR -8[rbp]
jne .L2
mov DWORD PTR -12[rbp], 4
.L2:
mov DWORD PTR -12[rbp], 5
mov eax, 0
add rsp, 48
pop rbp
ret
.seh_endproc
.def __main; .scl 2; .type 32; .endef
.ident "GCC: (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r3) 14.2.0"
ifneq.c
ifneq.c
#include <stdio.h>
int main() {
int a=1;
int b=2;
int c=3;
if(a!=b){
c=4;
}
c=5;
return 0;
}
ifneq.s
ifneq.s
.file "ifneq.c"
.intel_syntax noprefix
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
push rbp
.seh_pushreg rbp
mov rbp, rsp
.seh_setframe rbp, 0
sub rsp, 48
.seh_stackalloc 48
.seh_endprologue
call __main
mov DWORD PTR -4[rbp], 1
mov DWORD PTR -8[rbp], 2
mov DWORD PTR -12[rbp], 3
mov eax, DWORD PTR -4[rbp]
cmp eax, DWORD PTR -8[rbp]
je .L2
mov DWORD PTR -12[rbp], 4
.L2:
mov DWORD PTR -12[rbp], 5
mov eax, 0
add rsp, 48
pop rbp
ret
.seh_endproc
.def __main; .scl 2; .type 32; .endef
.ident "GCC: (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders, r3) 14.2.0"
コンパイル.bash
gcc -S -masm=intel ifeq.c -o ifeq.s
gcc -S -masm=intel ifneq.c -o ifneq.s